test: cover attribution and timeout safety

This commit is contained in:
mARTin-B78 2026-09-04 09:34:46 +02:00
parent 0fd39d4acb
commit 2608c953ee
4 changed files with 27 additions and 3 deletions

View File

@ -48,6 +48,8 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
workflow submission, prompt injection, polling, seed derivation, and image retrieval.
- **Casting Constraint Coverage.** Added deterministic regression coverage proving casting filters invented voice IDs
and avoids unnecessary duplicate assignments when distinct catalogue voices are available.
- **Attribution and Timeout Safety Coverage.** Added regressions preventing phantom narrator speakers, preserving source
prose after malformed LLM JSON, clamping caller timeouts, and bounding upstream error text.
- **4 GiB Container Validation.** Verified the current image serves the application under a hard 4 GiB cgroup limit
with no OOM kill; the probe recorded approximately 50.6 MiB idle memory and 0.09% CPU.
- **Profile Completeness Coverage.** Added browser regression coverage for empty, partial, and fully populated

View File

@ -165,13 +165,13 @@ the first block before investing in architecture or polish.
### Automated tests
- [ ] Character parsing and phantom-speaker prevention.
- [x] Character parsing and phantom-speaker prevention, including malformed-response prose preservation.
- [x] Voice-matching constraints reject invented voice IDs and avoid unnecessary duplicate assignments.
- [x] Profile-completeness counting for empty, partial, and fully populated sheets.
- [x] Rating persistence.
- [x] STT endpoint normalization and backend-response shape handling.
- [x] ComfyUI workflow submission, prompt injection, polling, and image retrieval.
- [ ] API failure and timeout behavior.
- [x] API timeout clamping and bounded upstream error text; live-provider failure drills remain environment-specific.
### Playwright smoke suite

View File

@ -1 +1 @@
1.23.5
1.23.6

View File

@ -21,6 +21,8 @@ from routes.conversation import (
_comfyui_run,
_CastJobRequest,
match_characters_voices,
_request_timeout_seconds,
_response_error_text,
)
@ -43,6 +45,26 @@ class AttributionParserTests(unittest.TestCase):
self.assertEqual([segment["type"] for segment in result["segments"]], ["narration", "dialogue", "narration"])
self.assertIn("Ein langer Satz endet hier.", result["segments"][1]["text"])
def test_attribution_normalization_prevents_phantom_narrator_and_preserves_text_on_bad_json(self):
raw = json.dumps({"segments": [
{"speaker": "Invented Narrator", "type": "narration", "text": "A road disappeared.", "emotion": "dramatic"},
{"speaker": "Mira", "type": "dialogue", "text": "We keep walking.", "emotion": ""},
]})
result = _attribution_parse(raw, "unused")
self.assertEqual(result["segments"][0]["speaker"], "Narrator")
self.assertEqual(result["characters"], ["Mira"])
fallback = _attribution_parse("not valid JSON", "Original prose must survive")
self.assertEqual(fallback["segments"], [{"speaker": "Narrator", "type": "narration", "text": "Original prose must survive", "emotion": ""}])
def test_api_timeout_and_error_text_are_bounded(self):
self.assertEqual(_request_timeout_seconds(-10), 5.0)
self.assertEqual(_request_timeout_seconds(99999), 600.0)
self.assertEqual(_request_timeout_seconds("not-a-number", default=12), 12.0)
class ResponseStub:
text = "x" * 5000
reason = "Bad Gateway"
self.assertEqual(len(_response_error_text(ResponseStub())), 2000)
def test_repairs_epigraph_split_before_mixed_guillemet_close(self):
raw = '{"segments":[' \
'{"speaker":"Schwester Larissa","type":"dialogue","text":"»Von dem Windhager Felsgesteyn bis zur Kueste von Mendena ziehet es sich hin, von Ifirns Eisgebirg bis zu dem dampfenden Waldt der Mohas, und"},' \