multi-agent-system / tests /test_quality_engine.py
firepenguindisopanda
feat(03): add QualityEngine, DomainTermWhitelist, calibrated budgets, and quality schemas
a746fba
Raw
History Blame Contribute Delete
5.35 kB
"""Tests for QualityEngine, DomainTermWhitelist, and quality schemas."""
import pytest
from app.core.quality_engine import QualityEngine, DomainTermWhitelist
from app.core.schemas import QualityReport, ARIResult, RePromptAttempt
# Verify textstat is installed
textstat = pytest.importorskip("textstat")
# ── QualityReport schema ──
def test_quality_report_defaults():
"""QualityReport has sensible defaults and validates correctly."""
report = QualityReport(
role="test", agent_output="Hello world", ari_before_any=5.0, final_ari=5.0
)
assert report.role == "test"
assert report.re_prompt_count == 0
assert report.final_disposition == "passed"
assert report.is_budget_met() # No dimensions = vacuously true
def test_quality_report_with_dimensions():
"""is_budget_met checks budget-named dimensions."""
report = QualityReport(
role="test",
agent_output="x",
ari_before_any=1,
final_ari=1,
dimensions=[
{
"name": "ari_budget_check",
"score": 16,
"threshold": 14,
"passed": False,
"details": "",
},
],
)
assert not report.is_budget_met()
def test_ari_result_passed():
"""ARIResult.passed correctly reflects score <= budget."""
r = ARIResult(raw_score=12, whitelist_score=11, budget=14, passed=True)
assert r.passed
r2 = ARIResult(raw_score=18, whitelist_score=17, budget=14, passed=False)
assert not r2.passed
def test_reprompt_attempt_stagnation():
"""RePromptAttempt.stagnant correctly identifies delta < 0.5."""
a = RePromptAttempt(
attempt_number=1,
strategy="simplify",
ari_before=15.0,
ari_after=15.3,
ari_delta=0.3,
stagnant=True,
)
assert a.stagnant
b = RePromptAttempt(
attempt_number=2,
strategy="restructure",
ari_before=15.0,
ari_after=13.2,
ari_delta=-1.8,
stagnant=False,
)
assert not b.stagnant
# ── DomainTermWhitelist ──
def test_whitelist_strips_known_terms():
"""Whitelisted terms are replaced with placeholders."""
wl = DomainTermWhitelist()
text = "Use dependency injection and bounded context patterns."
cleaned = wl.strip_known_terms(text)
assert "dependency injection" not in cleaned
assert "bounded context" not in cleaned
assert "word" in cleaned.lower() # placeholder
assert "patterns" in cleaned # non-whitelisted preserved
def test_whitelist_matched_terms():
"""get_matched_terms returns correct matches."""
wl = DomainTermWhitelist()
text = "Use microservices with event-driven architecture and ACID compliance."
matched = wl.get_matched_terms(text)
assert "microservices" in matched
assert "event-driven" in matched
assert len(matched) >= 3 # microservices, event-driven, ACID
def test_whitelist_multi_word_preserves_surrounding():
"""Multi-word replacement preserves surrounding text."""
wl = DomainTermWhitelist()
text = "The blue-green deployment strategy works."
cleaned = wl.strip_known_terms(text)
assert "blue-green deployment" not in cleaned
assert "strategy" in cleaned # surrounding text preserved
assert "works" in cleaned
# ── QualityEngine ──
def test_engine_score_output_returns_report():
"""score_output returns a valid QualityReport with expected fields."""
engine = QualityEngine()
report = engine.score_output(
"The system shall authenticate users via JWT tokens.", "product_owner"
)
assert isinstance(report, QualityReport)
assert report.role == "product_owner"
assert report.word_count > 0
assert report.sentence_count > 0
assert report.final_ari > 0
assert report.ari_result.budget == 12 # PO budget
def test_engine_score_output_async():
"""Async wrapper runs without error (basic smoke test)."""
import asyncio
engine = QualityEngine()
report = asyncio.run(
engine.score_output_async("Test output for async scoring.", "solution_architect")
)
assert isinstance(report, QualityReport)
assert report.final_ari > 0
def test_engine_role_specific_budgets():
"""Different roles have different budgets enforced."""
engine = QualityEngine()
long_text = (
"Asynchronous bidirectional synchronous replication infrastructure requires "
"containerization orchestration implementation across heterogeneous environments. "
"Microservices event-driven domain-driven architecture decomposition patterns."
)
po_report = engine.score_output(long_text, "product_owner")
sa_report = engine.score_output(long_text, "solution_architect")
# PO has tighter budget; SA has looser
assert po_report.ari_result.budget == 12
assert sa_report.ari_result.budget == 20
def test_engine_detect_stagnation():
"""Stagnation detected when delta < 0.5 between last two scores."""
engine = QualityEngine()
assert not engine.detect_stagnation([15.0]) # Only 1 score
assert not engine.detect_stagnation([15.0, 14.2]) # Delta = 0.8 >= 0.5
assert engine.detect_stagnation([15.0, 15.3]) # Delta = 0.3 < 0.5
assert engine.detect_stagnation([18.0, 18.1]) # Delta = 0.1 < 0.5