test: cover stt and comfyui boundaries
This commit is contained in:
parent
a714ea0273
commit
c6c9c53489
@ -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
|
||||
|
||||
4
TODO.md
4
TODO.md
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 = []
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user