import json from datetime import UTC, datetime from pathlib import Path import pytest from ai_platform.lead_scoring.application.repository import AIProfileRecord from ai_platform.lead_scoring.application.scoring_execution import ScoringExecutionUseCases from ai_platform.lead_scoring.application.scoring_execution_repository import ( Evidence, Scoring, ScoringExecutionRun, ScoringResult, ) FIXTURE = Path(__file__).parent / "fixtures" / "mock_omni_run.json" class RunRepository: def __init__(self, run: ScoringExecutionRun, profile: AIProfileRecord): self.run = run self.profile = profile self.result: ScoringResult | None = None async def find_run(self, run_id: str): return self.run async def find_profile(self, version: str): assert version == self.run.profile_version return self.profile async def mark_running(self, run_id: str): pass async def complete(self, run_id: str, result: ScoringResult, completed_at: datetime): self.result = result async def fail(self, run_id: str, completed_at: datetime, error_code: str): pytest.fail(f"execution failed: {error_code}") async def mark_interrupted(self, completed_at: datetime): pytest.fail("unexpected recovery") class StubProvider: def __init__(self): self.models: list[str] = [] self.keys: list[str] = [] async def extract_evidence(self, *, model, snapshot, vkey, timeout): self.models.append(model) self.keys.append(vkey) return [Evidence("interest", "Asked about enrollment", "mock-message-001")] async def score(self, *, model, system_prompt, user_prompt, vkey, timeout): self.models.append(model) self.keys.append(vkey) return Scoring(80, 0.8, "Evidence supports intent") @pytest.mark.asyncio async def test_execution_uses_frozen_profile_provider_and_never_forwards_vkey_to_openai(): payload = json.loads(FIXTURE.read_text()) payload["ai_profile"] = "medium" payload["scoring_config"].update(rule_weight=0.5, ai_weight=0.5) run = ScoringExecutionRun("run-1", "pending", payload, "old-profile") profile = AIProfileRecord( "old-profile", "gemini-3.5-flash-lite", "prompt", 30, {}, False, "openai_compatible", "http://127.0.0.1:8318", ) repository = RunRepository(run, profile) old_provider = StubProvider() active_aihub_provider = StubProvider() async def no_op(): pass selected_urls: list[str] = [] def provider_for_url(url: str) -> StubProvider: selected_urls.append(url) return old_provider cases = ScoringExecutionUseCases( repository, {"aihub": active_aihub_provider}, no_op, no_op, lambda: datetime(2026, 9, 28, tzinfo=UTC), provider_for_url, ) await cases.execute(run.id, "caller-vkey-should-not-reach-openai") assert repository.result is not None and repository.result.result_mode == "hybrid" assert selected_urls == ["http://127.0.0.1:8318"] assert old_provider.models == ["gemini-3.5-flash-lite"] * 2 assert old_provider.keys == ["", ""] assert active_aihub_provider.models == []