| """ |
| Property-Based Testing Configuration for Auto-Dev |
| |
| This module provides Hypothesis strategies and pytest fixtures for testing |
| Auto-Dev components (EventBus, FitnessService, CapabilityGate, ContainerSandbox, |
| MementoEngine, Database Models). |
| |
| Property-based testing generates hundreds of test cases automatically to verify |
| invariants that must ALWAYS hold true. |
| """ |
|
|
| import sys |
| import os |
|
|
| |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../..')) |
|
|
| from hypothesis import strategies as st |
| import pytest |
| from datetime import datetime, timedelta |
| from typing import Any |
|
|
| |
| |
| |
|
|
| |
| fitness_scores = st.lists( |
| st.floats(min_value=0.0, max_value=1.0, allow_nan=False, allow_infinity=False), |
| min_size=0, |
| max_size=100 |
| ) |
|
|
| |
| maturity_levels = st.sampled_from(['student', 'intern', 'supervised', 'autonomous']) |
|
|
| |
| event_data = st.dictionaries( |
| keys=st.text(min_size=1, max_size=50, alphabet='abcdefghijklmnopqrstuvwxyz'), |
| values=st.one_of( |
| st.text(), |
| st.integers(), |
| st.floats(allow_nan=False, allow_infinity=False), |
| st.booleans(), |
| st.none() |
| ), |
| min_size=0, |
| max_size=20 |
| ) |
|
|
| |
| capabilities = st.sampled_from([ |
| 'file_operations', |
| 'network_access', |
| 'system_commands', |
| 'database_write', |
| 'api_calls', |
| 'user_interaction', |
| 'auto_dev.memento_skills', |
| 'auto_dev.alpha_evolver', |
| 'auto_dev.background_evolution' |
| ]) |
|
|
| |
| valid_skill_names = st.text(min_size=1, max_size=50).filter( |
| lambda x: x.replace('_', '').replace('-', '').isalnum() and len(x) > 0 |
| ) |
|
|
| |
| agent_ids = st.text(min_size=36, max_size=36, alphabet='abcdef0123456789-') |
|
|
| |
| tenant_ids = st.text(min_size=36, max_size=36, alphabet='abcdef0123456789-') |
|
|
| |
| episode_ids = st.text(min_size=36, max_size=36, alphabet='abcdef0123456789-') |
|
|
| |
| proxy_signals = st.dictionaries( |
| keys=st.sampled_from([ |
| 'execution_success', |
| 'syntax_error', |
| 'execution_latency_ms', |
| 'user_approved_proposal', |
| 'expects_delayed_eval' |
| ]), |
| values=st.one_of(st.booleans(), st.floats(min_value=0.0, max_value=10000.0)), |
| min_size=1, |
| max_size=5 |
| ) |
|
|
| |
| external_signals = st.dictionaries( |
| keys=st.sampled_from([ |
| 'invoice_created', |
| 'crm_conversion', |
| 'conversion_success', |
| 'email_bounce', |
| 'error_signal', |
| 'conversion_value' |
| ]), |
| values=st.one_of(st.booleans(), st.floats(min_value=0.0, max_value=10000.0)), |
| min_size=1, |
| max_size=6 |
| ) |
|
|
| |
| workspace_settings = st.dictionaries( |
| keys=st.sampled_from([ |
| 'auto_dev', |
| 'max_mutations_per_day', |
| 'max_skill_candidates_per_day', |
| 'enabled' |
| ]), |
| values=st.one_of( |
| st.booleans(), |
| st.integers(min_value=1, max_value=100), |
| st.dictionaries( |
| keys=st.sampled_from(['enabled', 'memento_skills', 'alpha_evolver', 'background_evolution']), |
| values=st.one_of(st.booleans(), st.integers(min_value=1, max_value=100)), |
| min_size=1, |
| max_size=4 |
| ) |
| ), |
| min_size=0, |
| max_size=4 |
| ) |
|
|
| |
| |
| |
|
|
| @pytest.fixture |
| def event_bus(): |
| """Provide EventBus instance for testing.""" |
| from core.auto_dev.event_hooks import EventBus |
| bus = EventBus() |
| yield bus |
| bus.clear() |
|
|
|
|
| @pytest.fixture |
| def fitness_service(db_session): |
| """Provide FitnessService instance for testing.""" |
| from core.auto_dev.fitness_service import FitnessService |
| return FitnessService(db_session) |
|
|
|
|
| @pytest.fixture |
| def capability_gate(db_session): |
| """Provide AutoDevCapabilityService instance for testing.""" |
| from core.auto_dev.capability_gate import AutoDevCapabilityService |
| return AutoDevCapabilityService(db_session) |
|
|
|
|
| @pytest.fixture |
| def container_sandbox(): |
| """Provide ContainerSandbox instance for testing.""" |
| from core.auto_dev.container_sandbox import ContainerSandbox |
| return ContainerSandbox() |
|
|
|
|
| @pytest.fixture |
| def memento_engine(db_session): |
| """Provide MementoEngine instance for testing.""" |
| from core.auto_dev.memento_engine import MementoEngine |
| return MementoEngine(db_session) |
|
|
|
|
| @pytest.fixture |
| def db_session(): |
| """ |
| Provide a test database session with automatic rollback. |
| |
| This fixture creates a new database session for each test and ensures |
| all changes are rolled back after the test completes. |
| """ |
| from core.models import SessionLocal |
|
|
| session = SessionLocal() |
| try: |
| yield session |
| finally: |
| session.rollback() |
| session.close() |
|
|
|
|
| @pytest.fixture |
| def sample_agent(db_session): |
| """Create a sample agent for testing.""" |
| from core.models import AgentRegistry |
| import uuid |
|
|
| agent = AgentRegistry( |
| id=str(uuid.uuid4()), |
| name="test_agent", |
| role="assistant", |
| maturity="student", |
| created_at=datetime.utcnow(), |
| updated_at=datetime.utcnow() |
| ) |
| db_session.add(agent) |
| db_session.commit() |
| return agent |
|
|
|
|
| @pytest.fixture |
| def sample_workspace_settings(): |
| """Provide sample workspace settings for testing.""" |
| return { |
| "auto_dev": { |
| "enabled": True, |
| "memento_skills": True, |
| "alpha_evolver": True, |
| "background_evolution": False, |
| "max_mutations_per_day": 10, |
| "max_skill_candidates_per_day": 5 |
| } |
| } |
|
|
|
|
| |
| |
| |
|
|
| def create_task_event( |
| episode_id: str = None, |
| agent_id: str = None, |
| tenant_id: str = None, |
| outcome: str = "success" |
| ) -> Any: |
| """Helper to create TaskEvent objects for testing.""" |
| from core.auto_dev.event_hooks import TaskEvent |
| import uuid |
|
|
| return TaskEvent( |
| episode_id=episode_id or str(uuid.uuid4()), |
| agent_id=agent_id or str(uuid.uuid4()), |
| tenant_id=tenant_id or str(uuid.uuid4()), |
| task_description="Test task", |
| outcome=outcome, |
| metadata={"test": True} |
| ) |
|
|
|
|
| def create_skill_execution_event( |
| execution_id: str = None, |
| agent_id: str = None, |
| tenant_id: str = None, |
| success: bool = True |
| ) -> Any: |
| """Helper to create SkillExecutionEvent objects for testing.""" |
| from core.auto_dev.event_hooks import SkillExecutionEvent |
| import uuid |
|
|
| return SkillExecutionEvent( |
| execution_id=execution_id or str(uuid.uuid4()), |
| agent_id=agent_id or str(uuid.uuid4()), |
| tenant_id=tenant_id or str(uuid.uuid4()), |
| skill_id="test_skill", |
| skill_name="Test Skill", |
| execution_seconds=1.5, |
| token_usage=100, |
| success=success, |
| output="Test output" |
| ) |
|
|
|
|
| |
| |
| |
|
|
| def pytest_configure(config): |
| """Configure pytest with custom markers.""" |
| config.addinivalue_line( |
| "markers", |
| "property: Mark test as property-based test using Hypothesis" |
| ) |
| config.addinivalue_line( |
| "markers", |
| "slow: Mark test as slow-running (integration tests, Docker-dependent)" |
| ) |
| config.addinivalue_line( |
| "markers", |
| "docker_required: Mark test as requiring Docker (ContainerSandbox tests)" |
| ) |
|
|