""" Tests for HTTP API example scripts. Validates that the example scripts under examples/api_usage/http/ exist, are syntactically valid Python, and define the expected entry points. The actual API functionality exercised by these examples is already covered by test_rest_api.py and test_endpoints_health.py via the Flask test client. """ import ast from pathlib import Path import pytest # Root of the repository REPO_ROOT = Path(__file__).parent.parent.parent EXAMPLES_DIR = REPO_ROOT / "examples" / "api_usage" / "http" # All example scripts that should be tested — single source of truth EXAMPLE_SCRIPTS = [ "simple_working_example.py", "advanced/http_api_examples.py", "advanced/simple_http_example.py", ] def _parse_example(relative_path: str) -> tuple[Path, str, ast.Module]: """Read and parse an example file, failing if it doesn't exist. Returns (path, source_text, ast_tree). """ path = EXAMPLES_DIR / relative_path if not path.is_file(): pytest.fail(f"Example not found: {path}") source = path.read_text(encoding="utf-8") tree = ast.parse(source, filename=str(path)) return path, source, tree def _has_main_guard(tree: ast.Module) -> bool: """Check if an AST contains an ``if __name__ == '__main__':`` guard.""" for node in ast.walk(tree): if not isinstance(node, ast.If): continue test = node.test if not isinstance(test, ast.Compare): continue if len(test.ops) != 1 or not isinstance(test.ops[0], ast.Eq): continue # Match both orderings: # __name__ == "__main__" # "__main__" == __name__ left = test.left comparators = test.comparators if len(comparators) != 1: continue right = comparators[0] if ( isinstance(left, ast.Name) and left.id == "__name__" and isinstance(right, ast.Constant) and right.value == "__main__" ) or ( isinstance(right, ast.Name) and right.id == "__name__" and isinstance(left, ast.Constant) and left.value == "__main__" ): return True return False class TestHttpExamplesExist: """Verify that expected HTTP example files are present.""" def test_examples_directory_exists(self): assert EXAMPLES_DIR.is_dir(), ( f"Examples directory not found: {EXAMPLES_DIR}" ) @pytest.mark.parametrize("relative_path", EXAMPLE_SCRIPTS) def test_example_file_exists(self, relative_path): path = EXAMPLES_DIR / relative_path assert path.is_file(), f"Missing example: {path}" class TestHttpExamplesSyntax: """Verify that example scripts are syntactically valid Python.""" @pytest.mark.parametrize("relative_path", EXAMPLE_SCRIPTS) def test_example_parses(self, relative_path): """Each example file must be valid Python (no syntax errors).""" _path, _source, tree = _parse_example(relative_path) assert tree is not None @pytest.mark.parametrize("relative_path", EXAMPLE_SCRIPTS) def test_example_has_main_function(self, relative_path): """Each example script should define a main() function.""" _path, _source, tree = _parse_example(relative_path) function_names = [ node.name for node in ast.walk(tree) if isinstance(node, ast.FunctionDef) ] assert "main" in function_names, ( f"{relative_path} should define a main() function" ) @pytest.mark.parametrize("relative_path", EXAMPLE_SCRIPTS) def test_example_has_main_guard(self, relative_path): """Each example script should have an if __name__ == '__main__' guard.""" _path, _source, tree = _parse_example(relative_path) assert _has_main_guard(tree), ( f"{relative_path} should have an if __name__ == '__main__' guard" ) class TestAdvancedExampleStructure: """Verify the advanced example defines expected helper classes/functions.""" def test_ldr_client_class_defined(self): """The advanced example should define an LDRClient class.""" _path, _source, tree = _parse_example("advanced/http_api_examples.py") class_names = [ node.name for node in ast.walk(tree) if isinstance(node, ast.ClassDef) ] assert "LDRClient" in class_names, ( "Advanced example should define LDRClient class" ) def test_example_functions_defined(self): """The advanced example should define the documented example functions.""" _path, _source, tree = _parse_example("advanced/http_api_examples.py") function_names = { node.name for node in ast.walk(tree) if isinstance(node, ast.FunctionDef) } expected_examples = [ "example_quick_research", "example_settings_management", "example_research_history", ] for fn in expected_examples: assert fn in function_names, ( f"Advanced example should define {fn}()" )