Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
156 lines
5.1 KiB
Python
156 lines
5.1 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from app.services.agent_asset_release_guard import ReleaseEvaluationInput
|
|
from app.services.agent_asset_release_policy import (
|
|
ReleaseGuardPolicy,
|
|
evaluate_release,
|
|
)
|
|
from app.services.agent_asset_release_recall import estimate_release_recall
|
|
|
|
|
|
def test_recall_uses_random_stratum_to_estimate_full_negative_population() -> None:
|
|
result = estimate_release_recall(
|
|
true_positive_count=90,
|
|
disagreement_false_negative_count=2,
|
|
random_negative_population_count=800,
|
|
random_reviewed_count=80,
|
|
random_false_negative_count=1,
|
|
)
|
|
|
|
assert result.estimated_false_negative_count == 12.0
|
|
assert result.recall == pytest.approx(90 / 102, abs=1e-6)
|
|
assert result.recall_lower_bound is not None
|
|
assert result.recall_lower_bound < result.recall
|
|
assert result.false_negative_upper_bound is not None
|
|
assert result.false_negative_upper_bound > result.estimated_false_negative_count
|
|
assert result.method == "stratified_random_audit_wilson_upper_bound"
|
|
|
|
|
|
def test_recall_is_exact_when_no_random_negative_stratum_exists() -> None:
|
|
result = estimate_release_recall(
|
|
true_positive_count=18,
|
|
disagreement_false_negative_count=2,
|
|
random_negative_population_count=0,
|
|
random_reviewed_count=0,
|
|
random_false_negative_count=0,
|
|
)
|
|
|
|
assert result.recall == 0.9
|
|
assert result.recall_lower_bound == 0.9
|
|
assert result.estimated_false_negative_count == 2.0
|
|
|
|
|
|
def test_recall_remains_unavailable_before_random_audit_evidence_exists() -> None:
|
|
result = estimate_release_recall(
|
|
true_positive_count=10,
|
|
disagreement_false_negative_count=0,
|
|
random_negative_population_count=25,
|
|
random_reviewed_count=0,
|
|
random_false_negative_count=0,
|
|
)
|
|
|
|
assert result.recall is None
|
|
assert result.recall_lower_bound is None
|
|
assert result.estimated_false_negative_count is None
|
|
|
|
|
|
def test_zero_true_positives_with_confirmed_miss_has_zero_recall() -> None:
|
|
result = estimate_release_recall(
|
|
true_positive_count=0,
|
|
disagreement_false_negative_count=1,
|
|
random_negative_population_count=0,
|
|
random_reviewed_count=0,
|
|
random_false_negative_count=0,
|
|
)
|
|
|
|
assert result.recall == 0.0
|
|
assert result.recall_lower_bound == 0.0
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"kwargs",
|
|
[
|
|
{"random_reviewed_count": 3, "random_negative_population_count": 2},
|
|
{"random_false_negative_count": 2, "random_reviewed_count": 1},
|
|
{"true_positive_count": -1},
|
|
{"confidence_level": 0.92},
|
|
],
|
|
)
|
|
def test_recall_rejects_inconsistent_evidence(kwargs: dict[str, int | float]) -> None:
|
|
values: dict[str, int | float] = {
|
|
"true_positive_count": 1,
|
|
"disagreement_false_negative_count": 0,
|
|
"random_negative_population_count": 2,
|
|
"random_reviewed_count": 1,
|
|
"random_false_negative_count": 0,
|
|
"confidence_level": 0.95,
|
|
}
|
|
values.update(kwargs)
|
|
|
|
with pytest.raises(ValueError):
|
|
estimate_release_recall(**values) # type: ignore[arg-type]
|
|
|
|
|
|
def test_release_gate_collects_without_blind_negative_ground_truth() -> None:
|
|
result = evaluate_release(
|
|
"shadow",
|
|
ReleaseEvaluationInput(
|
|
total=20,
|
|
failure_count=0,
|
|
precision=1.0,
|
|
details={
|
|
"metric_source": "release_runtime_telemetry",
|
|
"negative_ground_truth_status": "insufficient_random_negative_reviews",
|
|
"recall_lower_bound": None,
|
|
},
|
|
),
|
|
ReleaseGuardPolicy(shadow_min_samples=1).to_dict(),
|
|
)
|
|
|
|
assert result["status"] == "collecting"
|
|
assert result["reasons"] == ["insufficient_random_negative_reviews"]
|
|
|
|
|
|
def test_release_gate_uses_recall_confidence_lower_bound_not_point_estimate() -> None:
|
|
policy = ReleaseGuardPolicy(shadow_min_samples=1, min_recall=0.95).to_dict()
|
|
result = evaluate_release(
|
|
"shadow",
|
|
ReleaseEvaluationInput(
|
|
total=20,
|
|
failure_count=0,
|
|
precision=1.0,
|
|
details={
|
|
"metric_source": "release_runtime_telemetry",
|
|
"negative_ground_truth_status": "available_stratified_random_audit",
|
|
"recall": 0.98,
|
|
"recall_lower_bound": 0.9,
|
|
},
|
|
),
|
|
policy,
|
|
)
|
|
|
|
assert result["status"] == "failed"
|
|
assert "recall_lower_bound_below_threshold" in result["reasons"]
|
|
|
|
|
|
def test_release_gate_passes_when_precision_recall_and_samples_are_sufficient() -> None:
|
|
result = evaluate_release(
|
|
"shadow",
|
|
ReleaseEvaluationInput(
|
|
total=20,
|
|
failure_count=0,
|
|
precision=0.99,
|
|
details={
|
|
"metric_source": "release_runtime_telemetry",
|
|
"negative_ground_truth_status": "available_stratified_random_audit",
|
|
"recall": 0.98,
|
|
"recall_lower_bound": 0.96,
|
|
},
|
|
),
|
|
ReleaseGuardPolicy(shadow_min_samples=20, min_recall=0.95).to_dict(),
|
|
)
|
|
|
|
assert result["status"] == "passed"
|