"""Step definitions for server endpoint extraction features.""" import json from pathlib import Path import yaml from behave import given, then, when @given("the server endpoint extractor is initialized") def step_initialize_server_extractor(context): """Initialize the server endpoint extractor.""" from cleveragents.discovery.server_endpoints import ServerEndpointExtractor context.server_extractor = ServerEndpointExtractor() @when("I run the server endpoint extraction") def step_run_server_extraction(context): """Run the server endpoint extraction.""" context.server_inventory = context.server_extractor.extract_endpoints() context.extraction_succeeded = context.server_inventory is not None @then("at least {count:d} endpoints should be extracted") def step_check_endpoint_count(context, count): """Check minimum number of endpoints extracted.""" actual = context.server_inventory["statistics"]["total_endpoints"] assert actual >= count, f"Expected at least {count} endpoints, got {actual}" @then("the inventory should include endpoint metadata") def step_check_endpoint_metadata(context): """Check that endpoint metadata is included.""" assert "metadata" in context.server_inventory assert "statistics" in context.server_inventory assert "endpoints_by_category" in context.server_inventory assert "all_endpoints" in context.server_inventory @then("the server inventory can be saved") def step_server_inventory_can_be_saved(context): """Verify server inventory can be saved.""" # For server endpoints, we just verify that we could save if we wanted to # The actual save is done separately assert hasattr(context, "server_extractor") assert hasattr(context, "server_inventory") @given("the server endpoints have been extracted") def step_extract_server_endpoints(context): """Extract server endpoints if not already done.""" if not hasattr(context, "server_inventory"): from cleveragents.discovery.server_endpoints import ServerEndpointExtractor context.server_extractor = ServerEndpointExtractor() context.server_inventory = context.server_extractor.extract_endpoints() # Always ensure endpoints attribute is populated context.endpoints = context.server_inventory["all_endpoints"] @when("I check the endpoint structure") def step_check_endpoint_structure(context): """Check endpoint structure.""" context.endpoints = context.server_inventory["all_endpoints"] @then("each endpoint should have a path") def step_check_endpoint_paths(context): """Check all endpoints have paths.""" for endpoint in context.endpoints: assert "path" in endpoint, f"Endpoint missing path: {endpoint}" assert endpoint["path"], f"Endpoint has empty path: {endpoint}" @then("each endpoint should have a method") def step_check_endpoint_methods(context): """Check all endpoints have methods.""" for endpoint in context.endpoints: assert "method" in endpoint, f"Endpoint missing method: {endpoint}" assert endpoint["method"], f"Endpoint has empty method: {endpoint}" @then("each endpoint should have a handler") def step_check_endpoint_handlers(context): """Check all endpoints have handlers.""" for endpoint in context.endpoints: assert "handler" in endpoint, f"Endpoint missing handler: {endpoint}" assert endpoint["handler"], f"Endpoint has empty handler: {endpoint}" @then("streaming endpoints should be identified") def step_check_streaming_endpoints(context): """Check that streaming endpoints are identified.""" streaming_count = context.server_inventory["statistics"]["streaming_endpoints"] assert streaming_count > 0, "No streaming endpoints identified" # Check that some endpoints have is_streaming flag streaming = [e for e in context.endpoints if e.get("is_streaming")] assert len(streaming) == streaming_count @when("I save the server endpoint inventory") def step_save_server_inventory(context): """Save the server endpoint inventory.""" if not hasattr(context, "server_extractor"): from cleveragents.discovery.server_endpoints import ServerEndpointExtractor context.server_extractor = ServerEndpointExtractor() context.server_inventory = context.server_extractor.extract_endpoints() context.yaml_path, context.json_path = context.server_extractor.save_inventory() @then('an OpenAPI spec should be created at "{path}"') def step_check_openapi_spec(context, path): """Check OpenAPI spec file creation.""" spec_path = Path(path) assert spec_path.exists(), f"OpenAPI spec not found at {path}" # Validate it's a valid YAML file with OpenAPI structure with open(spec_path) as f: spec = yaml.safe_load(f) assert "openapi" in spec, "Invalid OpenAPI spec - missing openapi version" assert "info" in spec, "Invalid OpenAPI spec - missing info section" assert "paths" in spec, "Invalid OpenAPI spec - missing paths section" @then("all files should contain consistent data") def step_check_file_consistency(context): """Check that all output files contain consistent data.""" with open(context.yaml_path) as f: yaml_data = yaml.safe_load(f) with open(context.json_path) as f: json_data = json.load(f) # Check that YAML and JSON have same endpoint count yaml_count = yaml_data["statistics"]["total_endpoints"] json_count = json_data["statistics"]["total_endpoints"] assert yaml_count == json_count, ( f"Inconsistent endpoint count: YAML={yaml_count}, JSON={json_count}" ) @when("I check endpoint categorization") def step_check_categorization(context): """Check endpoint categorization.""" context.categories = context.server_inventory["endpoints_by_category"] @then("endpoints should be grouped by category") def step_check_category_groups(context): """Check that endpoints are grouped by category.""" assert len(context.categories) > 0, "No categories found" # Check each category has endpoints for category, endpoints in context.categories.items(): assert len(endpoints) > 0, f"Category '{category}' has no endpoints" @then("categories should include {category}") def step_check_specific_category(context, category): """Check for specific category.""" assert category in context.categories, ( f"Category '{category}' not found. Available: {list(context.categories.keys())}" ) @when("I check for path parameters") def step_check_path_params(context): """Check for path parameters.""" context.params_endpoints = [ e for e in context.endpoints if e.get("path_params") and len(e["path_params"]) > 0 ] @then("endpoints with path parameters should be identified") def step_check_param_endpoints(context): """Check that endpoints with path parameters are identified.""" assert len(context.params_endpoints) > 0, "No endpoints with path parameters found" @then("path parameter names should be extracted") def step_check_param_names(context): """Check that parameter names are extracted.""" for endpoint in context.params_endpoints: assert "path_params" in endpoint assert isinstance(endpoint["path_params"], list) assert all(isinstance(p, str) for p in endpoint["path_params"]) @then("parameters like {param} and {param2} should be found") def step_check_specific_params(context, param, param2): """Check for specific parameters.""" all_params = set() for endpoint in context.params_endpoints: all_params.update(endpoint["path_params"]) assert param in all_params, ( f"Parameter '{param}' not found. Available: {all_params}" ) assert param2 in all_params, ( f"Parameter '{param2}' not found. Available: {all_params}" ) @when("I check authentication requirements") def step_check_auth_requirements(context): """Check authentication requirements.""" context.public_endpoints = [ e for e in context.endpoints if not e.get("requires_auth", True) ] context.protected_endpoints = [ e for e in context.endpoints if e.get("requires_auth", True) ] @then("public endpoints should be identified") def step_check_public_endpoints(context): """Check that public endpoints are identified.""" assert len(context.public_endpoints) > 0, "No public endpoints found" @then("protected endpoints should be identified") def step_check_protected_endpoints(context): """Check that protected endpoints are identified.""" assert len(context.protected_endpoints) > 0, "No protected endpoints found" @then("{endpoint} and {endpoint2} endpoints should be public") def step_check_specific_public(context, endpoint, endpoint2): """Check specific endpoints are public.""" public_paths = [e["path"] for e in context.public_endpoints] # Check if endpoints contain the keywords health_found = any(endpoint.lower() in path.lower() for path in public_paths) version_found = any(endpoint2.lower() in path.lower() for path in public_paths) assert health_found, ( f"'{endpoint}' endpoint not found in public endpoints: {public_paths}" ) assert version_found, ( f"'{endpoint2}' endpoint not found in public endpoints: {public_paths}" )