from __future__ import annotations import io import json import tempfile import unittest from contextlib import redirect_stdout from pathlib import Path from unittest.mock import patch from owner_voice_pet.cli import main from owner_voice_pet.speech_models import REQUIRED_MODEL_FILES, write_default_manifest class CliAcceptanceTests(unittest.TestCase): def call(self, *args: str) -> tuple[int, dict[str, object]]: buffer = io.StringIO() with redirect_stdout(buffer): code = main(list(args)) return code, json.loads(buffer.getvalue()) def test_acceptance_command_runs_end_to_end(self) -> None: code, data = self.call("acceptance") self.assertEqual(code, 0) self.assertTrue(data["success"]) self.assertEqual(data["transcript"], "你是谁") self.assertGreaterEqual(data["played_segments"], 1) def test_validate_assets_command(self) -> None: code, data = self.call("validate-assets") self.assertEqual(code, 0) self.assertTrue(data["valid"]) def test_show_config_reads_env_file_without_printing_secret(self) -> None: import tempfile with tempfile.TemporaryDirectory() as tmp: path = f"{tmp}/.env" with open(path, "w", encoding="utf-8") as handle: handle.write("OWNER_LLM_API_KEY=secret-value\nOWNER_LLM_MODEL=file-model\n") code, data = self.call("--env-file", path, "--show-config") self.assertEqual(code, 0) self.assertTrue(data["llm_api_key_present"]) self.assertEqual(data["llm_model"], "file-model") self.assertNotIn("secret-value", str(data)) def test_security_check_command_has_no_leaks(self) -> None: code, data = self.call("security-check") self.assertEqual(code, 0) self.assertEqual(data["secret_leaks"], []) def test_model_check_reports_manifest_and_files(self) -> None: with tempfile.TemporaryDirectory() as tmp: root = Path(tmp) write_default_manifest(root) for relative in REQUIRED_MODEL_FILES: path = root / relative path.parent.mkdir(parents=True, exist_ok=True) path.write_bytes(b"placeholder") with patch("importlib.util.find_spec", return_value=object()): code, data = self.call("model-check", "--models-dir", str(root)) self.assertEqual(code, 0) self.assertTrue(data["ok"]) self.assertEqual(data["missing_files"], []) def test_model_check_reports_missing_files(self) -> None: with tempfile.TemporaryDirectory() as tmp: with patch("importlib.util.find_spec", return_value=object()): code, data = self.call("model-check", "--models-dir", tmp) self.assertEqual(code, 1) self.assertFalse(data["ok"]) self.assertTrue(data["missing_files"]) if __name__ == "__main__": unittest.main()