런타임 계약과 학습자 흐름 보강

This commit is contained in:
Yun Chan 2026-06-29 08:12:14 +09:00
parent f456b8997a
commit 206018b088
56 changed files with 4306 additions and 1008 deletions

View file

@ -0,0 +1,110 @@
"""Regression tests for persona draft generation contract helpers."""
from __future__ import annotations
import unittest
from .contracts.engine_gateway import GenerateResponse
from . import persona_generation_contract as contract
from .persona_read_model import PersonaDraftGenerateRequest
class PersonaGenerationContractTest(unittest.TestCase):
def test_persona_generation_schema_pins_required_fields(self) -> None:
schema = contract.persona_generation_schema()
draft_schema = schema["properties"]["draft"]
self.assertFalse(schema["additionalProperties"])
self.assertEqual(schema["required"], ["draft", "source_summary", "warnings"])
self.assertFalse(draft_schema["additionalProperties"])
self.assertIn("source_provenance", draft_schema["required"])
self.assertEqual(draft_schema["properties"]["difficulty"]["enum"], ["easy", "moderate", "hard"])
def test_persona_draft_prompt_bundle_is_stable(self) -> None:
bundle = contract.persona_draft_prompt_bundle()
self.assertEqual(bundle["id"], "persona-draft-rag")
self.assertEqual(bundle["version"], "2026-06-28.1")
self.assertRegex(bundle["hash"], r"^[0-9a-f]{12}$")
def test_persona_generation_payload_from_response_uses_legacy_json_fallback(self) -> None:
response = GenerateResponse(
text='```json\n{"source_summary":"요약","warnings":["검수 필요"]}\n```',
model="test-model",
provider="test-provider",
structured=None,
)
payload = contract.persona_generation_payload_from_response(response)
self.assertEqual(payload["source_summary"], "요약")
self.assertEqual(payload["warnings"], ["검수 필요"])
def test_persona_generation_payload_from_response_returns_empty_dict_for_invalid_output(self) -> None:
response = GenerateResponse(text="[1, 2, 3]", model="test-model", provider="test-provider")
self.assertEqual(contract.persona_generation_payload_from_response(response), {})
def test_coerce_persona_generated_draft_normalizes_nested_draft(self) -> None:
request = PersonaDraftGenerateRequest(
source_ids=["persona_authoring_test"],
source_kind="client_record",
code_hint="p9",
display_name_hint="힌트 페르소나",
difficulty="hard",
theory_target=["CBT"],
)
draft = contract.coerce_persona_generated_draft(
{
"draft": {
"difficulty": "unsupported",
"theory_target": [" Humanistic ", ""],
"big5": {"O": 0.8, "C": "ignored"},
"resistance": {},
"affect_baseline": {},
"is_synthetic": False,
}
},
request,
)
self.assertEqual(draft.code, "P9")
self.assertEqual(draft.display_name, "힌트 페르소나")
self.assertEqual(draft.difficulty, "hard")
self.assertEqual(draft.theory_target, ["humanistic"])
self.assertEqual(draft.big5, {"O": 0.8})
self.assertEqual(draft.resistance["base_resistance"], 0.5)
self.assertEqual(draft.source_provenance, "masked client_record")
self.assertFalse(draft.is_synthetic)
self.assertFalse(draft.submit_for_review)
def test_coerce_persona_generated_draft_accepts_top_level_payload_without_draft(self) -> None:
request = PersonaDraftGenerateRequest(
source_ids=["persona_authoring_test"],
source_kind="mixed_notes",
difficulty="moderate",
)
draft = contract.coerce_persona_generated_draft(
{
"code": "p10",
"display_name": "상위 페르소나",
"difficulty": "easy",
"theory_target": ["CBT"],
"demographics": "not-a-dict",
"presenting": {"summary": "불안"},
},
request,
)
self.assertEqual(draft.code, "P10")
self.assertEqual(draft.display_name, "상위 페르소나")
self.assertEqual(draft.difficulty, "easy")
self.assertEqual(draft.theory_target, ["cbt"])
self.assertEqual(draft.demographics, {})
self.assertEqual(draft.presenting, {"summary": "불안"})
if __name__ == "__main__":
unittest.main()