feat: modernize project with uv, add pytest test suite

- Add pyproject.toml with modern Python packaging (PEP 517/518)
- Add uv-based dependency management replacing inline venv bootstrap
- Add requirements.txt and requirements-dev.txt for compatibility
- Add mise.toml with dev tasks (test, lint, run, sync, ci)
- Add .python-version pinned to Python 3.12
- Add comprehensive pytest test suite (73 tests) for:
  - engine/config, filter, terminal, sources, mic, ntfy modules
- Configure pytest with coverage reporting (16% total, 100% on tested modules)
- Configure ruff for linting with Python 3.10+ target
- Remove redundant venv bootstrap code from mainline.py
- Update .gitignore for uv/venv artifacts

Run 'uv sync' to install dependencies, 'uv run pytest' to test.
This commit is contained in:
2026-03-15 14:12:38 -07:00
parent d758541156
commit 9201117096
14 changed files with 774 additions and 34 deletions

124
tests/test_terminal.py Normal file
View File

@@ -0,0 +1,124 @@
"""
Tests for engine.terminal module.
"""
import io
import sys
from unittest.mock import patch
from engine import terminal
class TestTerminalDimensions:
"""Tests for terminal width/height functions."""
def test_tw_returns_columns(self):
"""tw() returns terminal width."""
with patch.object(sys.stdout, "isatty", return_value=True):
with patch("os.get_terminal_size") as mock_size:
mock_size.return_value = io.StringIO("columns=120")
mock_size.columns = 120
result = terminal.tw()
assert isinstance(result, int)
def test_th_returns_lines(self):
"""th() returns terminal height."""
with patch.object(sys.stdout, "isatty", return_value=True):
with patch("os.get_terminal_size") as mock_size:
mock_size.return_value = io.StringIO("lines=30")
mock_size.lines = 30
result = terminal.th()
assert isinstance(result, int)
def test_tw_fallback_on_error(self):
"""tw() falls back to 80 on error."""
with patch("os.get_terminal_size", side_effect=OSError):
result = terminal.tw()
assert result == 80
def test_th_fallback_on_error(self):
"""th() falls back to 24 on error."""
with patch("os.get_terminal_size", side_effect=OSError):
result = terminal.th()
assert result == 24
class TestANSICodes:
"""Tests for ANSI escape code constants."""
def test_ansi_constants_exist(self):
"""All ANSI constants are defined."""
assert terminal.RST == "\033[0m"
assert terminal.BOLD == "\033[1m"
assert terminal.DIM == "\033[2m"
def test_green_shades_defined(self):
"""Green gradient colors are defined."""
assert terminal.G_HI == "\033[38;5;46m"
assert terminal.G_MID == "\033[38;5;34m"
assert terminal.G_LO == "\033[38;5;22m"
def test_white_shades_defined(self):
"""White/gray tones are defined."""
assert terminal.W_COOL == "\033[38;5;250m"
assert terminal.W_DIM == "\033[2;38;5;245m"
def test_cursor_controls_defined(self):
"""Cursor control codes are defined."""
assert "?" in terminal.CURSOR_OFF
assert "?" in terminal.CURSOR_ON
class TestTypeOut:
"""Tests for type_out function."""
@patch("sys.stdout", new_callable=io.StringIO)
@patch("time.sleep")
def test_type_out_writes_text(self, mock_sleep, mock_stdout):
"""type_out writes text to stdout."""
with patch("random.random", return_value=0.5):
terminal.type_out("Hi", color=terminal.G_HI)
output = mock_stdout.getvalue()
assert len(output) > 0
@patch("time.sleep")
def test_type_out_uses_color(self, mock_sleep):
"""type_out applies color codes."""
with patch("sys.stdout", new_callable=io.StringIO):
with patch("random.random", return_value=0.5):
terminal.type_out("Test", color=terminal.G_HI)
class TestSlowPrint:
"""Tests for slow_print function."""
@patch("sys.stdout", new_callable=io.StringIO)
@patch("time.sleep")
def test_slow_print_writes_text(self, mock_sleep, mock_stdout):
"""slow_print writes text to stdout."""
terminal.slow_print("Hi", color=terminal.G_DIM, delay=0)
output = mock_stdout.getvalue()
assert len(output) > 0
class TestBootLn:
"""Tests for boot_ln function."""
@patch("sys.stdout", new_callable=io.StringIO)
@patch("time.sleep")
def test_boot_ln_writes_label_and_status(self, mock_sleep, mock_stdout):
"""boot_ln shows label and status."""
with patch("random.uniform", return_value=0):
terminal.boot_ln("Loading", "OK", ok=True)
output = mock_stdout.getvalue()
assert "Loading" in output
assert "OK" in output
@patch("sys.stdout", new_callable=io.StringIO)
@patch("time.sleep")
def test_boot_ln_error_status(self, mock_sleep, mock_stdout):
"""boot_ln shows red for error status."""
with patch("random.uniform", return_value=0):
terminal.boot_ln("Loading", "FAIL", ok=False)
output = mock_stdout.getvalue()
assert "FAIL" in output