aboutsummaryrefslogtreecommitdiff
path: root/app/tests/test_hub.py
diff options
context:
space:
mode:
Diffstat (limited to 'app/tests/test_hub.py')
-rw-r--r--app/tests/test_hub.py64
1 files changed, 64 insertions, 0 deletions
diff --git a/app/tests/test_hub.py b/app/tests/test_hub.py
index b1a91ed..80a97d0 100644
--- a/app/tests/test_hub.py
+++ b/app/tests/test_hub.py
@@ -1218,6 +1218,52 @@ class ConvertFlowTests(unittest.TestCase):
"beta": [("book.txt", "planned")]})
self.assertNotIn("planned", kwargs)
+ def test_all_pick_skips_non_narrating_families_with_a_notice(self):
+ # Speech-to-speech-only families (PersonaPlex) cannot narrate text:
+ # every request would fail, so the All pick drops them, records a
+ # run notice naming what was skipped, and keeps them single-pickable.
+ spec_cache = audiocpp_client._FAMILY_SPECS
+ spec_cache["personaplex"] = {"tasks": ["s2s"]}
+ self.addCleanup(spec_cache.pop, "personaplex", None)
+ self._patch_remote(
+ [{"id": "alpha", "family": "higgs_audio_tts", "task": "tts"},
+ {"id": "plex", "family": "personaplex", "task": "tts"}],
+ voices=["narrator"])
+ self._mock_preflight()
+ self._answer_form(backend="audiocpp-remote",
+ model_id=hub.AUDIOCPP_MODEL_ALL,
+ audiocpp_voice="narrator", instructions="")
+ cmd = self._convert(None, [self._remote("audiocpp", "audio.cpp")])
+ kwargs = cmd[2]
+ self.assertEqual(kwargs["model_ids"], ["alpha"])
+ self.assertEqual(kwargs["model_voices"], {"alpha": "narrator"})
+ self.assertEqual(kwargs["run_notice"],
+ "skipped non-TTS model(s): plex")
+
+ def test_all_pick_refuses_when_every_model_is_non_narrating(self):
+ # With nothing left to generate with after the skip, the All pick
+ # is refused up front instead of starting a doomed run.
+ spec_cache = audiocpp_client._FAMILY_SPECS
+ spec_cache["personaplex"] = {"tasks": ["s2s"]}
+ self.addCleanup(spec_cache.pop, "personaplex", None)
+ self._patch_remote(
+ [{"id": "plex-1", "family": "personaplex", "task": "tts"},
+ {"id": "plex-2", "family": "personaplex", "task": "tts"}],
+ voices=["narrator"])
+ self._mock_preflight()
+ self._answer_form(backend="audiocpp-remote",
+ model_id=hub.AUDIOCPP_MODEL_ALL,
+ audiocpp_voice="narrator", instructions="")
+ self._convert(None, [self._remote("audiocpp", "audio.cpp")])
+ fields = self.tui.forms_seen[0][1]
+ voice_field = self._field("audiocpp_voice")
+ self._field("model_id")["value"] = hub.AUDIOCPP_MODEL_ALL
+ error = voice_field["validate"]("narrator")
+ self.assertIsNotNone(error)
+ self.assertIn("plex-1", error)
+ self.assertIn("plex-2", error)
+ self.assertIn("synthesize text", error)
+
def test_all_voice_falls_back_per_capability(self):
# A CustomVoice entry cannot clone: it synthesizes with a built-in
# speaker (the pick when it names one, the first speaker when it
@@ -2448,6 +2494,24 @@ class PrepareRunConfigTests(unittest.TestCase):
self.assertIn("clon", cfg.notice)
self.assertNotIn("restarted", cfg.notice)
+ def test_run_notice_is_popped_and_joined_with_the_server_notice(self):
+ # The form's pre-flight warning (e.g. the All run's skipped
+ # non-narrating models) rides to the run view's notice line, and
+ # multiple notices accumulate instead of overwriting each other.
+ spec = self._spec("audiocpp", "http://127.0.0.1:8080")
+ status = BackendStatus("audiocpp", "audio.cpp", installed=True,
+ configured=True, servers=[spec])
+ kwargs = {"run_notice": "skipped non-TTS model(s): plex",
+ "restart_server": "audiocpp", "audiocpp_rehost": True}
+ with patch.object(hub, "detect_all", return_value=[status]), \
+ patch("backends.common.server_running",
+ return_value=True), \
+ patch.object(hub.servers, "alive", return_value=True):
+ cfg = hub._prepare_run_config("audiocpp", kwargs)
+ self.assertNotIn("run_notice", kwargs)
+ self.assertIn("skipped non-TTS model(s): plex", cfg.notice)
+ self.assertIn("re-hosted clone-only", cfg.notice)
+
def test_stop_and_exit_travels_on_the_config_not_the_kwargs(self):
# The run-view toggle is not a converter kwarg: it moves onto the
# config (and defaults to off when the form did not send it).