test: cover stt and comfyui boundaries

This commit is contained in:
mARTin-B78 2026-09-04 09:27:45 +02:00
parent a714ea0273
commit c6c9c53489
5 changed files with 60 additions and 3 deletions

View File

@ -44,6 +44,8 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
restart-safe status tracking cannot become an unbounded database growth path.
- **Frontend Quality Gate.** Added `npm run check:frontend` to validate every manifest module for syntax errors,
duplicate manifest entries, and top-level declaration collisions before bundling.
- **Provider Boundary Regression Coverage.** Added deterministic tests for STT backend normalization and ComfyUI
workflow submission, prompt injection, polling, seed derivation, and image retrieval.
- **STT Cancellation Safety.** Queued transcription cancels before consuming the single STT slot, and timed-out
requests keep the device slot held until the underlying worker actually exits.
- **Credential Boundary Review.** Provider credentials are now documented as server-only; only the explicitly

View File

@ -168,8 +168,8 @@ the first block before investing in architecture or polish.
- [ ] Voice-matching constraints.
- [ ] Profile-completeness counting.
- [x] Rating persistence.
- [ ] STT endpoint normalization.
- [ ] ComfyUI workflow submission.
- [x] STT endpoint normalization and backend-response shape handling.
- [x] ComfyUI workflow submission, prompt injection, polling, and image retrieval.
- [ ] API failure and timeout behavior.
### Playwright smoke suite

View File

@ -1 +1 @@
1.23.1
1.23.2

View File

@ -17,6 +17,8 @@ from routes.conversation import (
_consistent_image_cancel_events,
_consistent_image_jobs,
_fallback_attribute_response,
_bundled_comfyui_workflow,
_comfyui_run,
)
@ -63,6 +65,47 @@ class AttributionParserTests(unittest.TestCase):
)
self.assertTrue(all(segment["type"] == "dialogue" for segment in result["segments"][1:]))
def test_bundled_comfyui_workflows_have_required_prompt_and_save_nodes(self):
for image_kind in ("portrait", "concept_art"):
raw, prompt_node, prompt_field, save_node = _bundled_comfyui_workflow(image_kind)
graph = json.loads(raw)
self.assertIn(prompt_node, graph)
self.assertEqual(graph[prompt_node]["class_type"], "CLIPTextEncode")
self.assertIn(prompt_field, graph[prompt_node]["inputs"])
self.assertIn(save_node, graph)
self.assertIn("filename_prefix", graph[save_node]["inputs"])
def test_comfyui_submission_injects_prompt_polls_and_returns_image(self):
from unittest.mock import patch
class ResponseStub:
def __init__(self, payload=None, content=b"", headers=None):
self._payload, self.content, self.headers = payload, content, headers or {}
self.ok = True
self.status_code = 200
self.text = ""
def json(self):
return self._payload
def raise_for_status(self):
return None
graph = {
"2": {"class_type": "CLIPTextEncode", "inputs": {"text": "old"}},
"5": {"class_type": "KSampler", "inputs": {"seed": 0}},
"7": {"class_type": "SaveImage", "inputs": {"filename_prefix": "test"}},
}
post = ResponseStub({"prompt_id": "p1"})
history = ResponseStub({"p1": {"status": {"completed": True}, "outputs": {"7": {"images": [{"filename": "out.png"}]}}}})
view = ResponseStub(content=b"png", headers={"Content-Type": "image/png"})
with patch("routes.conversation.requests.post", return_value=post) as post_call, \
patch("routes.conversation.requests.get", side_effect=[history, view]), \
patch("routes.conversation.time.sleep"):
result = _comfyui_run("http://comfyui:8188", json.dumps(graph), "2", "text", "7", "new prompt", "stable")
self.assertTrue(result.startswith("data:image/png;base64,"))
submitted = post_call.call_args.kwargs["json"]["prompt"]
self.assertEqual(submitted["2"]["inputs"]["text"], "new prompt")
self.assertNotEqual(submitted["5"]["inputs"]["seed"], 0)
def test_consistent_image_job_can_be_cancelled(self):
from fastapi.testclient import TestClient
from server import app

View File

@ -47,6 +47,18 @@ class SecurityValidationTests(unittest.TestCase):
_validated_upload_suffix(filename, default=".wav", allowed={".wav"})
self.assertEqual(_validated_upload_suffix("VOICE.WAV", default=".wav", allowed={".wav"}), ".wav")
def test_stt_backend_aliases_and_response_shapes_are_normalized(self):
from routes.stt import _clean_stt_backend, _transcription_text_from_response
class ResponseStub:
def __init__(self, payload, text=""):
self._payload, self.text = payload, text
def json(self):
return self._payload
self.assertEqual(_clean_stt_backend("faster-whisper"), "faster_whisper")
self.assertEqual(_clean_stt_backend("groq"), "groq_whisper")
self.assertEqual(_transcription_text_from_response(ResponseStub({"segments": [{"text": " one "}, {"text": "two"}]})), "one two")
self.assertEqual(_transcription_text_from_response(ResponseStub(" plain text ")), "plain text")
def test_request_size_middleware_rejects_oversized_json(self):
sent = []