chore: import vocalinux project
CI / security (pull_request) Failing after 1m25s
CI / lint (pull_request) Failing after 34s
CI / ai-review (pull_request) Successful in 25s

Import the vocalinux source tree (tray app, Next.js site in web/,
packaging, installers, docs and tests) on top of the Gitea template
commit. The project README replaces the template README, and the
template CI files (.gitea/workflows/ci.yml, .jscpd.json, CI.md) are kept.
This commit is contained in:
2026-11-06 11:51:44 +05:30
parent 4a90b559ca
commit 80a3792576
597 changed files with 206764 additions and 11 deletions
+111
View File
@@ -0,0 +1,111 @@
"""Tests for PostProcessor and apply_post_processing."""
import subprocess
import unittest
from unittest.mock import MagicMock, patch
from vocalinux.post_processor import PostProcessor, apply_post_processing
from vocalinux.utils.host_process import host_env
def _make_run_result(returncode: int = 0, stdout: str = "", stderr: str = "") -> MagicMock:
result = MagicMock()
result.returncode = returncode
result.stdout = stdout
result.stderr = stderr
return result
class TestPostProcessor(unittest.TestCase):
def setUp(self) -> None:
self.patch_run = patch("vocalinux.post_processor.subprocess.run")
self.mock_run = self.patch_run.start()
def tearDown(self) -> None:
self.patch_run.stop()
def test_convenience_newline_stripped_when_input_had_none(self) -> None:
self.mock_run.return_value = _make_run_result(stdout="hello\n")
result = PostProcessor("/script.sh").process("hello")
self.assertEqual(result, "hello")
def test_internal_newlines_preserved(self) -> None:
self.mock_run.return_value = _make_run_result(stdout="a\nb\n")
result = PostProcessor("/script.sh").process("a\nb")
self.assertEqual(result, "a\nb")
def test_paragraph_break_preserved_verbatim(self) -> None:
"""A passthrough script must not eat the "\n\n" from "new paragraph"."""
self.mock_run.return_value = _make_run_result(stdout="end of paragraph.\n\n")
result = PostProcessor("/script.sh").process("end of paragraph.\n\n")
self.assertEqual(result, "end of paragraph.\n\n")
def test_script_added_paragraph_survives_convenience_strip(self) -> None:
"""Only one trailing newline is treated as a line terminator."""
self.mock_run.return_value = _make_run_result(stdout="a\n\n")
result = PostProcessor("/script.sh").process("a")
self.assertEqual(result, "a\n")
def test_empty_stdout_returns_empty_string(self) -> None:
self.mock_run.return_value = _make_run_result(stdout="")
result = PostProcessor("/script.sh").process("original")
self.assertEqual(result, "")
def test_nonzero_exit_returns_original(self) -> None:
self.mock_run.return_value = _make_run_result(returncode=1, stderr="oops")
result = PostProcessor("/script.sh").process("original")
self.assertEqual(result, "original")
def test_timeout_returns_original(self) -> None:
self.mock_run.side_effect = subprocess.TimeoutExpired(cmd="/script.sh", timeout=10)
result = PostProcessor("/script.sh").process("original")
self.assertEqual(result, "original")
def test_os_error_returns_original(self) -> None:
self.mock_run.side_effect = OSError("not found")
result = PostProcessor("/script.sh").process("original")
self.assertEqual(result, "original")
def test_subprocess_called_with_correct_args(self) -> None:
self.mock_run.return_value = _make_run_result(stdout="out")
PostProcessor("/my/script.sh").process("input text")
self.mock_run.assert_called_once_with(
["/my/script.sh"],
input="input text",
capture_output=True,
text=True,
timeout=10,
env=host_env(),
)
class TestApplyPostProcessing(unittest.TestCase):
def setUp(self) -> None:
self.patch_processor = patch("vocalinux.post_processor.PostProcessor")
self.mock_processor_cls = self.patch_processor.start()
self.mock_processor = MagicMock()
self.mock_processor_cls.return_value = self.mock_processor
def tearDown(self) -> None:
self.patch_processor.stop()
def _config(self, script_path: str) -> MagicMock:
config_manager = MagicMock()
config_manager.get_str.return_value = script_path
return config_manager
def test_no_script_returns_original_text(self) -> None:
config_manager = self._config("")
result = apply_post_processing("hello", config_manager)
self.assertEqual(result, "hello")
self.mock_processor_cls.assert_not_called()
def test_script_configured_returns_processed_text(self) -> None:
self.mock_processor.process.return_value = "modified"
result = apply_post_processing("hello", self._config("/s.sh"))
self.assertEqual(result, "modified")
def test_script_returns_empty_string_gives_none(self) -> None:
self.mock_processor.process.return_value = ""
result = apply_post_processing("hello", self._config("/s.sh"))
self.assertIsNone(result)