Files
repo-scoping/tests/test_quality_gates.py

242 lines
7.9 KiB
Python

from repo_scoping.acceptance import (
blocking_quality_gate_outcomes,
evaluate_candidate_capability_quality,
evaluate_candidate_graph_quality,
quality_gate_outcome_dicts,
)
from repo_scoping.core.models import (
AnalysisRun,
CandidateAbility,
CandidateCapability,
CandidateFeature,
CandidateGraph,
Repository,
SourceReference,
)
from repo_scoping.core.service import RegistryService
from repo_scoping.repo_ingestion.git import GitIngestionService
from repo_scoping.storage.sqlite import RegistryStore
def source_ref(path="src/app.py", kind="source"):
return SourceReference(
fact_id=1,
path=path,
kind=kind,
name=path,
line=1,
)
def provider_routing_capability():
return CandidateCapability(
id=10,
name="Route LLM Requests Across Providers",
description="Routes provider requests.",
inputs=[],
outputs=[],
confidence=0.9,
status="candidate",
source_refs=[source_ref("src/providers.py")],
confidence_label="high",
primary_class="llm-integration",
attributes=["utility-owned"],
features=[
CandidateFeature(
id=20,
name="HTTP API surface",
type="API",
location="src/app.py",
confidence=0.8,
status="candidate",
source_refs=[source_ref("src/app.py")],
confidence_label="high",
primary_class="API",
)
],
)
def test_quality_gates_flag_known_provider_routing_failure():
outcomes = evaluate_candidate_capability_quality(provider_routing_capability())
outcome_ids = {outcome.criterion_id for outcome in outcomes}
assert {"RREG-QC-002", "RREG-QC-003"} <= outcome_ids
assert all(outcome.outcome != "approve" for outcome in outcomes)
assert blocking_quality_gate_outcomes(outcomes)
def test_quality_gates_flag_circular_scope_evidence():
capability = CandidateCapability(
id=11,
name="Map Repository Scope",
description="Uses generated scope.",
inputs=[],
outputs=[],
confidence=0.8,
status="candidate",
source_refs=[source_ref("SCOPE.md", "generated-scope")],
confidence_label="high",
primary_class="scope-generation",
attributes=["utility-owned"],
)
outcomes = evaluate_candidate_capability_quality(capability)
assert outcomes[0].criterion_id == "RREG-QC-005"
assert outcomes[0].outcome == "requires_review"
def test_quality_gates_flag_scope_derived_candidates_for_review():
capability = CandidateCapability(
id=12,
name="Application workload deployment",
description="Extracted from SCOPE.md.",
inputs=[],
outputs=[],
confidence=0.6,
status="candidate",
source_refs=[source_ref("SCOPE.md", "scope")],
confidence_label="medium",
primary_class="infrastructure",
attributes=["scope-derived", "review-required-scope"],
)
outcomes = evaluate_candidate_capability_quality(capability)
outcome_ids = {outcome.criterion_id for outcome in outcomes}
assert {"RREG-QC-005"} <= outcome_ids
assert all(outcome.outcome == "requires_review" for outcome in outcomes)
def test_quality_gates_flag_template_contaminated_abilities():
graph = CandidateGraph(
repository=Repository(
id=1,
name="Ops Warden",
url=".",
description=None,
branch="main",
status="analyzed",
),
analysis_run=AnalysisRun(
id=1,
repository_id=1,
snapshot_id=None,
status="completed",
started_at="2026-05-15T00:00:00Z",
completed_at="2026-05-15T00:00:01Z",
error_message=None,
scanner_version="deterministic-v1",
),
abilities=[
CandidateAbility(
id=1,
name="A Git Repository Template To Bootstrap Coulomb Projects",
description="Derived from repo-seed README boilerplate.",
confidence=0.7,
status="candidate",
source_refs=[source_ref("README.md", "documentation")],
)
],
)
outcomes = evaluate_candidate_graph_quality(graph)
assert outcomes[0].criterion_id == "RREG-QC-007"
assert outcomes[0].outcome == "downgraded"
def test_quality_gate_outcomes_are_serializable_for_assessment_artifacts():
graph = CandidateGraph(
repository=Repository(
id=1,
name="Repo",
url=".",
description=None,
branch="main",
status="indexed",
),
analysis_run=AnalysisRun(
id=1,
repository_id=1,
snapshot_id=None,
status="completed",
started_at="2026-05-15T00:00:00Z",
completed_at="2026-05-15T00:00:01Z",
error_message=None,
scanner_version="deterministic-v1",
),
abilities=[
CandidateAbility(
id=1,
name="Support Repo",
description="Support repo.",
confidence=0.8,
status="candidate",
source_refs=[],
capabilities=[provider_routing_capability()],
)
],
)
payload = quality_gate_outcome_dicts(evaluate_candidate_graph_quality(graph))
assert payload
assert payload[0]["criteria_version"] == "repo-scoping-quality-criteria/v1"
def test_legacy_trusted_auto_approval_skips_quality_gate_blocked_capability(tmp_path):
store = RegistryStore(tmp_path / "registry.sqlite3")
store.initialize()
service = RegistryService(store, ingestion=GitIngestionService(tmp_path / "checkouts"))
safe, reason = service._trusted_auto_approve_capability_decision(
provider_routing_capability()
)
assert safe is False
assert "quality gates require review" in reason
assert "RREG-QC-002" in reason
def test_analysis_records_deterministic_gate_review_decision(tmp_path):
source = tmp_path / "provider-repo"
source.mkdir()
(source / "README.md").write_text("# Provider Repo\n", encoding="utf-8")
(source / "providers.py").write_text(
"provider_registry = {'openrouter': OpenRouterAdapter}\n",
encoding="utf-8",
)
store = RegistryStore(tmp_path / "registry.sqlite3")
store.initialize()
service = RegistryService(store, ingestion=GitIngestionService(tmp_path / "checkouts"))
repository = service.register_repository(name="Provider Repo", url=str(source))
summary = service.analyze_repository(repository.id, use_llm_assistance=False)
decisions = service.list_review_decisions(repository.id, summary.analysis_run.id)
gate_decision = next(
decision for decision in decisions if decision.action == "quality_gate_evaluation"
)
assert gate_decision.reviewer_type == "deterministic-gate"
assert "RREG-QC-002" in gate_decision.criterion_ids
assert gate_decision.criteria_version == "repo-scoping-quality-criteria/v1"
assert "without approving registry truth" in gate_decision.rationale
assert service.ability_map(repository.id).abilities == []
override = service.record_quality_gate_override(
repository.id,
summary.analysis_run.id,
criterion_id="RREG-QC-002",
element_type="capability",
element_id=10,
reason="Curator confirmed this repo now owns provider routing.",
notes="Future criteria update may be needed.",
)
assert override.action == "quality_gate_override"
assert override.reviewer_type == "human"
assert override.decision_kind == "override"
assert override.criterion_ids == ["RREG-QC-002"]
assert override.rationale == "Curator confirmed this repo now owns provider routing."