"""Unit tests for seat structured output + one-shot repair.""" from __future__ import annotations import pytest from app.maf_roundtable.seat_output import ( build_repair_prompt, parse_seat_output, validate_or_repair_seat_output, ) def test_parse_valid_seat_output(): text = '{"summary":"ok","findings":["f1"],"proposed_actions":["do x"]}' model, errors = parse_seat_output(text) assert errors == [] assert model is not None assert model.summary == "ok" assert model.findings == ["f1"] def test_parse_fenced_json(): text = '说明\n```json\n{"summary":"s"}\n```\n' model, errors = parse_seat_output(text) assert model is not None assert errors == [] def test_parse_invalid_missing_summary(): model, errors = parse_seat_output('{"findings":[]}') assert model is None assert errors @pytest.mark.asyncio async def test_validate_ok_no_repair(): result = await validate_or_repair_seat_output( raw_content='{"summary":"fine"}', repair_runner=None, ) assert result["schema_status"] == "ok" assert result["repair_attempted"] is False @pytest.mark.asyncio async def test_one_shot_repair_success(): calls = {"n": 0} async def _repair(prompt: str) -> str: calls["n"] += 1 assert "SeatTurnOutput" in prompt or "校验" in prompt return '{"summary":"repaired","findings":["x"]}' result = await validate_or_repair_seat_output( raw_content="not json at all", repair_runner=_repair, ) assert calls["n"] == 1 assert result["schema_status"] == "repaired" assert result["output"]["summary"] == "repaired" @pytest.mark.asyncio async def test_repair_still_invalid_stops(): calls = {"n": 0} async def _repair(prompt: str) -> str: calls["n"] += 1 return "still not json" result = await validate_or_repair_seat_output( raw_content="garbage", repair_runner=_repair, ) assert calls["n"] == 1 assert result["schema_status"] == "schema_invalid" assert result["repair_attempted"] is True def test_build_repair_prompt_includes_errors(): prompt = build_repair_prompt("raw", ["summary: Field required"]) assert "summary: Field required" in prompt assert "raw" in prompt