VoiceLedger / tests /test_modal_api.py
Sagar Patel
Add local AI mode and insight coach
445ae7d
Raw
History Blame Contribute Delete
8.17 kB
from pathlib import Path
from backend import modal_api
from voiceledger.parser.rules import parse_transaction as local_parse_transaction
class FakeResponse:
def __init__(self, payload: dict) -> None:
self.payload = payload
def raise_for_status(self) -> None:
return None
def json(self) -> dict:
return self.payload
def test_parse_transaction_uses_local_fallback_when_url_missing(monkeypatch) -> None:
monkeypatch.delenv(modal_api.MODAL_PARSE_URL_ENV, raising=False)
transaction = modal_api.parse_transaction("Amit owes 100", fallback=local_parse_transaction)
assert transaction.transaction_type == "customer_credit"
assert transaction.customer == "Amit"
def test_parse_transaction_uses_modal_response(monkeypatch) -> None:
monkeypatch.setenv(modal_api.MODAL_PARSE_URL_ENV, "https://modal.example/parse")
def fake_post(*args, **kwargs) -> FakeResponse:
return FakeResponse(
{
"transaction": {
"transaction_type": "expense",
"item": "rent",
"quantity": None,
"unit_price": None,
"amount": 300,
"customer": None,
"payment_status": "paid",
"notes": "rent 300",
"confidence": 0.99,
}
}
)
monkeypatch.setattr(modal_api.requests, "post", fake_post)
transaction = modal_api.parse_transaction("rent 300", fallback=local_parse_transaction)
assert transaction.transaction_type == "expense"
assert transaction.item == "rent"
assert transaction.amount == 300
def test_parse_transaction_result_reports_modal_source(monkeypatch) -> None:
monkeypatch.setenv(modal_api.MODAL_PARSE_URL_ENV, "https://modal.example/parse")
def fake_post(*args, **kwargs) -> FakeResponse:
return FakeResponse(
{
"transaction": {
"transaction_type": "sale",
"item": "mangoes",
"quantity": 12,
"unit_price": 20,
"amount": 240,
"customer": None,
"payment_status": "paid",
"notes": "Sold 12 mangoes, 20 each",
"confidence": 0.97,
}
}
)
monkeypatch.setattr(modal_api.requests, "post", fake_post)
result = modal_api.parse_transaction_result("Sold 12 mangoes, 20 each", fallback=local_parse_transaction)
assert result.source == "modal"
assert "NVIDIA Nemotron" in result.message
assert result.fallback_reason is None
assert result.transaction.amount == 240
def test_parse_transaction_falls_back_when_modal_errors(monkeypatch) -> None:
monkeypatch.setenv(modal_api.MODAL_PARSE_URL_ENV, "https://modal.example/parse")
def fake_post(*args, **kwargs) -> None:
raise RuntimeError("network down")
monkeypatch.setattr(modal_api.requests, "post", fake_post)
transaction = modal_api.parse_transaction("mango 12 x 20", fallback=local_parse_transaction)
assert transaction.transaction_type == "sale"
assert transaction.amount == 240
def test_parse_transaction_result_reports_fallback_reason(monkeypatch) -> None:
monkeypatch.setenv(modal_api.MODAL_PARSE_URL_ENV, "https://modal.example/parse")
def fake_post(*args, **kwargs) -> None:
raise RuntimeError("network down")
monkeypatch.setattr(modal_api.requests, "post", fake_post)
result = modal_api.parse_transaction_result("mango 12 x 20", fallback=local_parse_transaction)
assert result.source == "local"
assert "after Modal failed" in result.message
assert "network down" in result.fallback_reason
assert result.transaction.amount == 240
def test_parse_transaction_result_force_local_skips_modal(monkeypatch) -> None:
monkeypatch.setenv(modal_api.MODAL_PARSE_URL_ENV, "https://modal.example/parse")
def fake_post(*args, **kwargs) -> None:
raise AssertionError("Modal should not be called in local-first mode")
monkeypatch.setattr(modal_api.requests, "post", fake_post)
result = modal_api.parse_transaction_result(
"Amit owes 100",
fallback=local_parse_transaction,
force_local=True,
)
assert result.source == "local"
assert result.transaction.transaction_type == "customer_credit"
assert "local-first mode" in result.fallback_reason
def test_transcribe_audio_uses_modal_response(tmp_path: Path, monkeypatch) -> None:
audio_path = tmp_path / "audio.wav"
audio_path.write_bytes(b"audio")
monkeypatch.setenv(modal_api.MODAL_TRANSCRIBE_URL_ENV, "https://modal.example/transcribe")
def fake_post(*args, **kwargs) -> FakeResponse:
return FakeResponse({"transcript": "Sold 12 mangoes"})
monkeypatch.setattr(modal_api.requests, "post", fake_post)
transcript = modal_api.transcribe_audio(audio_path, fallback=lambda _: "fallback")
assert transcript == "Sold 12 mangoes"
def test_transcribe_audio_result_reports_modal_source(tmp_path: Path, monkeypatch) -> None:
audio_path = tmp_path / "audio.wav"
audio_path.write_bytes(b"audio")
monkeypatch.setenv(modal_api.MODAL_TRANSCRIBE_URL_ENV, "https://modal.example/transcribe")
def fake_post(*args, **kwargs) -> FakeResponse:
return FakeResponse({"transcript": "Sold 12 mangoes"})
monkeypatch.setattr(modal_api.requests, "post", fake_post)
result = modal_api.transcribe_audio_result(audio_path, fallback=lambda _: "fallback")
assert result.source == "modal"
assert result.transcript == "Sold 12 mangoes"
assert result.fallback_reason is None
def test_transcribe_audio_result_force_local_skips_modal(tmp_path: Path, monkeypatch) -> None:
audio_path = tmp_path / "audio.wav"
audio_path.write_bytes(b"audio")
monkeypatch.setenv(modal_api.MODAL_TRANSCRIBE_URL_ENV, "https://modal.example/transcribe")
def fake_post(*args, **kwargs) -> None:
raise AssertionError("Modal should not be called in local-first mode")
monkeypatch.setattr(modal_api.requests, "post", fake_post)
result = modal_api.transcribe_audio_result(
audio_path,
fallback=lambda _: "local transcript",
force_local=True,
)
assert result.source == "local"
assert result.transcript == "local transcript"
assert "local-first mode" in result.fallback_reason
def test_transcribe_audio_accepts_gradio_dict_payload(tmp_path: Path, monkeypatch) -> None:
audio_path = tmp_path / "audio.wav"
audio_path.write_bytes(b"audio")
monkeypatch.setenv(modal_api.MODAL_TRANSCRIBE_URL_ENV, "https://modal.example/transcribe")
def fake_post(*args, **kwargs) -> FakeResponse:
return FakeResponse({"transcript": "Paid 500 for supplies"})
monkeypatch.setattr(modal_api.requests, "post", fake_post)
transcript = modal_api.transcribe_audio({"path": str(audio_path)}, fallback=lambda _: "fallback")
assert transcript == "Paid 500 for supplies"
def test_transcribe_audio_falls_back_when_url_missing(tmp_path: Path, monkeypatch) -> None:
audio_path = tmp_path / "audio.wav"
audio_path.write_bytes(b"audio")
monkeypatch.delenv(modal_api.MODAL_TRANSCRIBE_URL_ENV, raising=False)
transcript = modal_api.transcribe_audio(audio_path, fallback=lambda _: "fallback transcript")
assert transcript == "fallback transcript"
def test_get_modal_health_reports_version(monkeypatch) -> None:
monkeypatch.setenv(modal_api.MODAL_PARSE_URL_ENV, "https://modal.example/parse")
def fake_get(url: str, *args, **kwargs) -> FakeResponse:
if url.endswith("/health"):
return FakeResponse({"status": "ok"})
if url.endswith("/version"):
return FakeResponse({"version": "parse-starlette-route-v1"})
raise AssertionError(f"unexpected url: {url}")
monkeypatch.setattr(modal_api.requests, "get", fake_get)
health = modal_api.get_modal_health()
assert health["status"] == "ok"
assert health["version"] == "parse-starlette-route-v1"