MomsVoiceAI / test_modules /test_qa_flow.py
minhahwang's picture
feat: bounded pre-gen, cancellation, disk cache pruning, GPU lock
994d3d9
Raw
History Blame Contribute Delete
3.85 kB
"""Dependency-light tests for the Ask/Q&A server flow."""
import logging
import tempfile
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
logging.disable(logging.CRITICAL)
from qa_flow import build_qa_response, claim_audio_submission, release_audio_submission
def check(label, condition):
if not condition:
raise AssertionError(label)
print(f"[OK] {label}")
def fake_answer_fn(**kwargs):
check("story context forwarded", "Three Little Pigs" in kwargs["story_context"])
check("max_new_tokens forwarded", kwargs["max_new_tokens"] == 12)
if kwargs["question_audio_path"]:
return "The wolf huffed and puffed.", object(), 24000
return "The pig built a house.", None, 24000
def fake_writer(path: Path, waveform, sample_rate: int):
check("audio writer receives sample rate", sample_rate == 24000)
path.write_bytes(b"RIFF fake wav")
def failing_answer_fn(**kwargs):
raise RuntimeError("model unavailable")
def test_empty_question():
result = build_qa_response(
question_text="",
question_audio_path=None,
paragraphs=[],
output_dir=Path(tempfile.gettempdir()),
answer_fn=fake_answer_fn,
)
check("empty question is rejected", result["ok"] is False)
check("empty question has no audio", result["audio_path"] is None)
def test_text_question():
result = build_qa_response(
question_text="What did the pig build?",
question_audio_path=None,
paragraphs=["The Three Little Pigs built houses."],
output_dir=Path(tempfile.gettempdir()),
answer_fn=fake_answer_fn,
max_new_tokens=12,
)
check("text question succeeds", result["ok"] is True)
check("text answer returned", result["answer_text"] == "The pig built a house.")
check("text question does not write audio when model returns none", result["audio_path"] is None)
def test_audio_question_writes_answer():
with tempfile.TemporaryDirectory() as tmp:
result = build_qa_response(
question_text="",
question_audio_path=Path(tmp) / "question.wav",
paragraphs=["The Three Little Pigs met a wolf."],
output_dir=Path(tmp),
answer_fn=fake_answer_fn,
audio_writer=fake_writer,
max_new_tokens=12,
)
check("audio question succeeds", result["ok"] is True)
check("audio question display fallback", result["display_question"] == "(audio question)")
check("audio answer path created", result["audio_path"] is not None)
check("audio answer file exists", Path(result["audio_path"]).exists())
def test_duplicate_audio_claim():
with tempfile.TemporaryDirectory() as tmp:
audio = Path(tmp) / "same-question.wav"
check("first audio claim accepted", claim_audio_submission(audio) is True)
check("duplicate audio claim rejected", claim_audio_submission(audio) is False)
release_audio_submission(audio)
check("released audio claim can retry", claim_audio_submission(audio) is True)
def test_failed_answer_marks_error():
result = build_qa_response(
question_text="",
question_audio_path="question.wav",
paragraphs=["The Three Little Pigs met a wolf."],
output_dir=Path(tempfile.gettempdir()),
answer_fn=failing_answer_fn,
)
check("failed answer still returns display result", result["ok"] is True)
check("failed answer records error", result["error"] == "RuntimeError")
check("failed answer has no audio", result["audio_path"] is None)
if __name__ == "__main__":
test_empty_question()
test_text_question()
test_audio_question_writes_answer()
test_duplicate_audio_claim()
test_failed_answer_marks_error()
print("Q&A flow tests passed")