Files
VoiceStudio/tests/conftest.py
T
Palash DebnathandClaude Opus 4.7 93aa66ab0a Phase 3 Plan 03-01: Supertonic-3 engine on SubprocessBackend (#101)
* Phase 3 Plan 03-01: Supertonic-3 engine on SubprocessBackend

Adds Supertonic-3 as a 7th opt-in TTS engine on the Phase 2
SubprocessBackend primitive. Closes TTS-01..06 (REQUIREMENTS.md):

  * TTS-01 — _REGISTRY["supertonic3"] resolves to Supertonic3Backend,
             a SubprocessBackend subclass.
  * TTS-02 — `supertonic==1.3.1` lives under [project.optional-dependencies];
             default `uv sync --no-dev` does NOT install it. Exactly one
             `onnxruntime` row in `uv pip list` after `--extra supertonic`.
  * TTS-03 — Model revision pinned by 40-char commit SHA
             (724fb5abbf5502583fb520898d45929e62f02c0b — the "Initial
             Supertonic 3 release" SHA, same as the SDK's own pin).
             Resolver script for intentional bumps:
             scripts/resolve_supertonic3_sha.py.
  * TTS-04 — Honest CPU-only reporting. `is_available()` message says
             "ready (CPU-only via onnxruntime)" and never mentions
             "cuda" or "mps". `gpu_compat = ("cpu",)`.
  * TTS-05 — License gate via settings_store helpers
             (get/set_license_accepted) + Loopback-only
             /api/settings/license endpoint + SupertonicLicenseDialog
             frontend modal showing MIT (code) and OpenRAIL-M (model).
             Wired into EngineCompatibilityMatrix as an "Accept license"
             button on rows whose `reason` mentions "license not
             accepted".
  * TTS-06 — 3 langs (en/ja/ru) × 3 sec smoke test in
             tests/test_supertonic3.py::test_smoke_3langs_3sec
             (OMNIVOICE_SMOKE-gated; asserts no onnxruntime-gpu row
             post-synthesize).

Package legitimacy gate (Task 1 in plan): supertonic on PyPI verified
to be published by Supertone Inc. (ato@supertone.ai), repo
github.com/supertone-inc/supertonic, wheel is pure-Python with no
postinstall scripts. Same publisher ships supertonic-js on npm under
the same maintainer email.

Test results:
  * tests/test_supertonic3.py — 10 passed, 3 skipped (network-gated).
  * tests/smoke/ — 4 passed.
  * tests/ (full, --ignore=tests/manual) — 412 passed, 0 failed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci(tests): uv sync --all-extras so optional-engine tests can import their package

Phase 3 added `supertonic` as an optional dependency. The CI Tests job
runs `uv sync` (no extras), so `test_cpu_only_honest` and `test_license_gate`
in tests/test_supertonic3.py hit the "supertonic package not installed"
fallback instead of the real import path, and fail.

Bare `uv sync` is the right default for users (engines are opt-in), but
the test environment should exercise the full surface. `--all-extras`
keeps the smoke job lean (still bare `uv sync`) while letting Tests
verify the integrated behavior of every optional engine.

Future-proofs against the same failure mode in Phase 4 (GGUF) and any
later optional engines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:09:48 +05:30

48 lines
1.8 KiB
Python

import os
import sys
# Backend runs with `--app-dir backend`, so tests must do the same.
_BACKEND = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "backend"))
if _BACKEND not in sys.path:
sys.path.insert(0, _BACKEND)
# ── Test fixtures ──────────────────────────────────────────────────────────
import pytest
@pytest.fixture
def mock_settings_store(monkeypatch):
"""In-memory replacement for ``services.settings_store`` license helpers.
Phase 3 Plan 03-01 / Wave 0 gap: the real settings_store talks to
SQLite via ``core.db.db_conn()``; that opens the project SQLite
file as a side effect of the import. Tests that exercise
``Supertonic3Backend.is_available()`` shouldn't need the SQLite
plumbing online ‑‑ they just need a controllable
``get_license_accepted`` / ``set_license_accepted`` pair.
Yields a dict ``{engine_id: bool}`` so tests can pre-seed
acceptance state or assert on what got written. The dict is
re-bound to the monkeypatched helpers on every read/write so a
test can mutate it directly to simulate "user clicked Accept".
"""
state: dict[str, bool] = {}
def fake_get(engine_id: str) -> bool:
return bool(state.get(engine_id, False))
def fake_set(engine_id: str, accepted: bool) -> None:
state[engine_id] = bool(accepted)
# Patch the canonical module so any importer (Supertonic3Backend,
# api.routers.settings, etc.) sees the fakes. Using setattr+
# monkeypatch lets pytest restore the originals between tests.
from services import settings_store as _ss
monkeypatch.setattr(_ss, "get_license_accepted", fake_get)
monkeypatch.setattr(_ss, "set_license_accepted", fake_set)
return state