""" SuperClaude pytest plugin Auto-loaded when superclaude is installed. Provides PM Agent fixtures and hooks for enhanced testing. Entry point registered in pyproject.toml: [project.entry-points.pytest11] superclaude = "superclaude.pytest_plugin" """ import pytest from .pm_agent.confidence import ConfidenceChecker from .pm_agent.reflexion import ReflexionPattern from .pm_agent.self_check import SelfCheckProtocol from .pm_agent.token_budget import TokenBudgetManager def pytest_configure(config): """ Register SuperClaude plugin and custom markers Markers: - confidence_check: Pre-execution confidence assessment - self_check: Post-implementation validation - reflexion: Error learning and prevention - complexity(level): Set test complexity (simple, medium, complex) """ config.addinivalue_line( "markers", "confidence_check: Pre-execution confidence assessment (min 70%)" ) config.addinivalue_line( "markers", "self_check: Post-implementation validation with evidence requirement", ) config.addinivalue_line( "markers", "reflexion: Error learning and prevention pattern" ) config.addinivalue_line( "markers", "complexity(level): Set test complexity (simple, medium, complex)" ) @pytest.fixture def confidence_checker(): """ Fixture for pre-execution confidence checking Usage: def test_example(confidence_checker): confidence = confidence_checker.assess(context) assert confidence >= 0.7 """ return ConfidenceChecker() @pytest.fixture def self_check_protocol(): """ Fixture for post-implementation self-check protocol Usage: def test_example(self_check_protocol): passed, issues = self_check_protocol.validate(implementation) assert passed """ return SelfCheckProtocol() @pytest.fixture def reflexion_pattern(): """ Fixture for reflexion error learning pattern Usage: def test_example(reflexion_pattern): reflexion_pattern.record_error(...) solution = reflexion_pattern.get_solution(error_signature) """ return ReflexionPattern() @pytest.fixture def token_budget(request): """ Fixture for token budget management Complexity levels: - simple: 200 tokens (typo fix) - medium: 1,000 tokens (bug fix) - complex: 2,500 tokens (feature implementation) Usage: @pytest.mark.complexity("medium") def test_example(token_budget): assert token_budget.limit == 1000 """ # Get test complexity from marker marker = request.node.get_closest_marker("complexity") complexity = marker.args[0] if marker else "medium" return TokenBudgetManager(complexity=complexity) @pytest.fixture def pm_context(tmp_path): """ Fixture providing PM Agent context for testing Creates temporary memory directory structure: - docs/memory/pm_context.md - docs/memory/last_session.md - docs/memory/next_actions.md Usage: def test_example(pm_context): assert pm_context["memory_dir"].exists() pm_context["pm_context"].write_text("# Context") """ memory_dir = tmp_path / "docs" / "memory" memory_dir.mkdir(parents=True) # Create empty memory files (memory_dir / "pm_context.md").touch() (memory_dir / "last_session.md").touch() (memory_dir / "next_actions.md").touch() return { "memory_dir": memory_dir, "pm_context": memory_dir / "pm_context.md", "last_session": memory_dir / "last_session.md", "next_actions": memory_dir / "next_actions.md", } def pytest_runtest_setup(item): """ Pre-test hook for confidence checking If test is marked with @pytest.mark.confidence_check, run pre-execution confidence assessment and skip if < 70%. """ marker = item.get_closest_marker("confidence_check") if marker: checker = ConfidenceChecker() # Build context from test context = { "test_name": item.name, "test_file": str(item.fspath), "markers": [m.name for m in item.iter_markers()], } confidence = checker.assess(context) if confidence < 0.7: pytest.skip(f"Confidence too low: {confidence:.0%} (minimum: 70%)") def pytest_runtest_makereport(item, call): """ Post-test hook for self-check and reflexion Records test outcomes for reflexion learning. Stores error information for future pattern matching. """ if call.when == "call": # Check for reflexion marker marker = item.get_closest_marker("reflexion") if marker and call.excinfo is not None: # Test failed - apply reflexion pattern reflexion = ReflexionPattern() # Record error for future learning error_info = { "test_name": item.name, "test_file": str(item.fspath), "error_type": type(call.excinfo.value).__name__, "error_message": str(call.excinfo.value), "traceback": str(call.excinfo.traceback), } reflexion.record_error(error_info) def pytest_report_header(config): """Add SuperClaude version to pytest header""" from . import __version__ return f"SuperClaude: {__version__}" def pytest_collection_modifyitems(config, items): """ Modify test collection to add automatic markers - Adds 'unit' marker to test files in tests/unit/ - Adds 'integration' marker to test files in tests/integration/ - Adds 'hallucination' marker to test files matching *hallucination* - Adds 'performance' marker to test files matching *performance* """ for item in items: test_path = str(item.fspath) # Auto-mark by directory if "/unit/" in test_path: item.add_marker(pytest.mark.unit) elif "/integration/" in test_path: item.add_marker(pytest.mark.integration) # Auto-mark by filename if "hallucination" in test_path: item.add_marker(pytest.mark.hallucination) elif "performance" in test_path or "benchmark" in test_path: item.add_marker(pytest.mark.performance)