Skip to content

test: add e2e tests for all commands + CI matrix Python 3.12/3.13/3.14#16

Merged
luongnv89 merged 7 commits intomainfrom
feat/e2e-tests-python-3.12-3.13-3.14
Apr 5, 2026
Merged

test: add e2e tests for all commands + CI matrix Python 3.12/3.13/3.14#16
luongnv89 merged 7 commits intomainfrom
feat/e2e-tests-python-3.12-3.13-3.14

Conversation

@luongnv89
Copy link
Copy Markdown
Owner

Summary

  • Add tests/test_e2e.py with 143 tests covering every command, subcommand, hidden alias, option flag, and global flag
  • Add pytest-e2e pre-commit hook that runs the fast e2e suite (~2s) on CLI source changes
  • Bump CI test matrix from 3.10/3.11/3.123.12/3.13/3.14 across macOS, Linux, and Windows

What's covered in test_e2e.py

Test class Coverage
TestGlobalFlags --version, --help/-h, --no-color, NO_COLOR env, unknown command
TestAllCommandHelp 44 parametrized — every command/subcommand/alias exits 0 on --help
TestPlayOptions --mode (7 values), --mood (8 moods), --auto, --duration, --index, --source
TestSimplePlaybackCommands stop/s, pause/pp, resume/r, next/n, status/st
TestVolCommand get vol, set valid (0/50/100), reject out-of-range (-1/101/200), volume alias
TestMoodCommand list moods, play each of 8 moods, moods alias
TestHistoryCommand list, --limit/-n, play N, h alias
TestRadioCommand list/play/remove/update, radios alias
TestYtCommand list/play/remove/clear, youtube + cached aliases
TestAiCommand list/play/replay/remove, ai model subgroup, models alias
TestDaemonCommand start/stop/restart/status with process calls mocked
TestConfigCommand exits 0, output contains path info
TestUpdateRadios hidden update-radios alias
TestNoColor --no-color flag, NO_COLOR=1, NO_COLOR= (empty string per spec)
TestBareInvocation bare mc triggers status, doesn't crash
TestPlatformSmoke Python ≥3.10, platform detection, CLI importable

All daemon I/O is mocked — no FFmpeg, no running daemon, no network required.

CI matrix changes

Before After
Python versions 3.10, 3.11, 3.12 3.12, 3.13, 3.14
OS ubuntu, macos, windows ubuntu, macos, windows
3.14 on Windows n/a excluded (pre-release wheels)
allow-prereleases not set true

Pre-commit hook

- id: pytest-e2e
  name: pytest e2e (fast, no daemon)
  entry: python -m pytest tests/test_e2e.py -x -q --no-header --tb=short
  files: ^(music_cli/cli\.py|music_cli/config\.py|tests/test_e2e\.py)$

Only triggers when CLI source or the e2e test file is modified.

Test plan

  • pytest tests/test_e2e.py — 143 passed, ~2s locally (Python 3.12 macOS)
  • CI green on Python 3.12/3.13/3.14 × ubuntu/macos (and windows for 3.12/3.13)
  • Pre-commit hook installs cleanly: pre-commit install && pre-commit run pytest-e2e

- Add tests/test_e2e.py with 143 tests covering every command, subcommand,
  hidden alias, option flag, and global flag (--version, --no-color, NO_COLOR)
- Cover play --mode (7 values), --mood (8 moods), vol range validation,
  history/radio/yt/ai/daemon subcommands, config, update-radios
- All daemon I/O mocked — suite runs in ~2s, suitable for pre-commit
- Add pytest-e2e local pre-commit hook triggered on cli.py/config.py changes
- Bump CI test matrix from 3.10/3.11/3.12 to 3.12/3.13/3.14 (macOS+Linux+Windows)
- Exclude windows × 3.14 until pre-release wheels are stable
- Remove continue-on-error: true so test failures actually fail CI
- Fix radio tests to mock get_radios_categorized/get_radios/remove_radio
  (CLI never calls get_stations/remove_station which don't exist)
- Fix test_radio_update to return [] from get_new_default_stations
  (tests "up to date" path instead of hitting click.prompt Abort)
- Fix mock_client.get_volume to return int (matches real client return type)
- Fix ruff C408: dict() literal, PTH110: Path.exists(), E741: ambiguous l,
  F841: unused result var; auto-format test_cli.py and music_cli/cli.py
Add targeted # type: ignore comments on lines that had pre-existing
mypy errors (hf_cache.py ImportError fallback assignments, cli.py
_register_alias calls where click decorators return Group not AliasedGroup).
These errors existed on main before this PR but were masked by the
ruff formatting failure that blocked mypy from running.
…nments

- Change _register_alias signature to accept click.Group (not AliasedGroup)
  with an internal cast, so all call sites type-check without type: ignore
- Use noqa: F811 + type: ignore on hf_cache.py ImportError fallbacks so
  the ignores suppress errors when hf_hub is installed without triggering
  unused-ignore when it is absent
- Add [[tool.mypy.overrides]] for music_cli.hf_cache to disable
  warn_unused_ignores for that module only
The pre-commit ruff-format hook was pinned to v0.3.4 while the CI lint
job installs the latest ruff (~0.14). This caused pre-commit to reformat
files in the CI pre-commit check even though the lint check passed, as the
two versions had different formatting opinions on assert-with-message style.
On Windows, NamedTemporaryFile holds a file lock until the with block
exits. Calling Path.unlink() inside the with block raises WinError 32
(file in use). Move the unlink to after the with block using try/finally.
The pytest-e2e hook uses language: system, so it runs with whatever
Python is on PATH. In the GitHub Actions pre-commit job, pytest is not
installed in that Python. Tests are already covered by the test matrix
job, so skip pytest-e2e in CI via SKIP env var.
@luongnv89 luongnv89 merged commit 52c6535 into main Apr 5, 2026
13 checks passed
@luongnv89 luongnv89 deleted the feat/e2e-tests-python-3.12-3.13-3.14 branch April 5, 2026 12:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant