From 2dbafbf332dd8c9a3b90c6d4724eaccc73d78321 Mon Sep 17 00:00:00 2001 From: Nana Dev Date: Fri, 7 Aug 2026 10:16:04 +0700 Subject: [PATCH] Add cross-platform CLI smoke tests --- .github/workflows/python-ci.yml | 18 ++++++++ CHANGELOG.md | 1 + README.md | 4 ++ docs/platform-support.md | 28 ++++++++++++ pyproject.toml | 1 + tests/test_cli_smoke.py | 76 +++++++++++++++++++++++++++++++++ 6 files changed, 128 insertions(+) create mode 100644 docs/platform-support.md create mode 100644 tests/test_cli_smoke.py diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 5d04382..aac9852 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -47,3 +47,21 @@ jobs: run: python -m pip install --upgrade pip && python -m pip install -e ".[dev]" - name: Run tests run: pytest + + smoke: + name: smoke (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12" + cache: pip + - name: Install package and smoke-test tools + run: python -m pip install --upgrade pip && python -m pip install . pytest pytest-cov + - name: Run installed CLI smoke tests + run: pytest -q tests/test_cli_smoke.py --no-cov diff --git a/CHANGELOG.md b/CHANGELOG.md index e53996e..4e0cfa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project are documented in this file. The format is b - Scriptable `title`, `tags`, and `rename` subcommands. - Safe rename previews, confirmation, recovery manifests, and an `undo` command. - Machine-readable JSON output for every scriptable subcommand. +- Installed CLI smoke coverage on Windows, macOS, and Linux. - Python 3.11–3.13 test matrix, linting, coverage, and distribution checks. ### Changed diff --git a/README.md b/README.md index be05acf..c90f469 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,10 @@ or normalize a folder of image names from an interactive menu or scriptable subc ## Requirements - Python 3.11+ +- A current Windows, macOS, or Linux system + +See the [platform support policy](https://github.com/HoungDev/creator-toolkit-cli/blob/main/docs/platform-support.md) +for the tested OS/Python matrix and support expectations. ## Installation diff --git a/docs/platform-support.md b/docs/platform-support.md new file mode 100644 index 0000000..e070edf --- /dev/null +++ b/docs/platform-support.md @@ -0,0 +1,28 @@ +# Platform support + +Creator Toolkit CLI supports Python 3.11–3.13 on current Windows, macOS, and Linux systems. + +## Continuous integration policy + +The CI suite uses two complementary matrices: + +- Unit, coverage, lint, audit, and distribution checks run on Ubuntu across Python 3.11, 3.12, + and 3.13. +- Installed CLI smoke tests run on the current GitHub-hosted Ubuntu, macOS, and Windows images + with Python 3.12. + +The cross-platform jobs install the package with `pip install .` and invoke the generated +`creator-toolkit` console entrypoint. They cover command help, title generation, tag generation, +non-mutating image rename previews, structured diagnostics, and failure exit codes. + +## Support expectations + +- Bugs reproduced on the tested matrix are treated as supported-platform defects. +- Other Python 3 implementations, end-of-life Python releases, and older operating system + versions may work but are not continuously verified. +- Filesystem behavior can differ by platform. Rename safety and rollback tests therefore run in + both the unit suite and installed CLI smoke suite. + +When reporting a platform-specific issue, include the operating system, Python version, install +method, command, exit code, and complete stdout/stderr output. Do not include sensitive paths or +file contents. diff --git a/pyproject.toml b/pyproject.toml index 5ec4b09..cfed180 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ classifiers = [ "Development Status :: 3 - Alpha", "Environment :: Console", "Intended Audience :: Developers", + "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/tests/test_cli_smoke.py b/tests/test_cli_smoke.py new file mode 100644 index 0000000..e0e553d --- /dev/null +++ b/tests/test_cli_smoke.py @@ -0,0 +1,76 @@ +import json +import shutil +import subprocess +import sys +from pathlib import Path + + +def installed_cli() -> str: + executable = shutil.which("creator-toolkit") + if executable is not None: + return executable + + script_name = "creator-toolkit.exe" if sys.platform == "win32" else "creator-toolkit" + candidate = Path(sys.executable).with_name(script_name) + assert candidate.is_file(), f"Installed CLI not found at {candidate}" + return str(candidate) + + +def run_cli(*arguments: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [installed_cli(), *arguments], + check=False, + capture_output=True, + text=True, + ) + + +def assert_success(result: subprocess.CompletedProcess[str]) -> None: + assert result.returncode == 0, f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}" + + +def test_installed_cli_help_lists_commands(): + result = run_cli("--help") + + assert_success(result) + assert result.stderr == "" + for command in ("title", "tags", "rename", "undo"): + assert command in result.stdout + + +def test_installed_cli_generates_title_and_tags_json(): + title_result = run_cli("title", "creator workflow", "--json") + assert_success(title_result) + title = json.loads(title_result.stdout) + assert title["command"] == "title" + assert "Creator Workflow" in title["result"]["title"] + + tags_result = run_cli("tags", "--count", "3", "--json") + assert_success(tags_result) + tags = json.loads(tags_result.stdout) + assert tags["command"] == "tags" + assert tags["result"]["count"] == 3 + + +def test_installed_cli_rename_preview_does_not_change_files(tmp_path): + image = tmp_path / "photo.jpg" + image.write_bytes(b"photo") + + result = run_cli("rename", str(tmp_path), "--dry-run", "--json") + + assert_success(result) + assert result.stderr == "" + payload = json.loads(result.stdout) + assert payload["status"] == "preview" + assert image.read_bytes() == b"photo" + assert not (tmp_path / "image_1.jpg").exists() + + +def test_installed_cli_failure_preserves_exit_code_and_diagnostics(tmp_path): + result = run_cli("rename", str(tmp_path / "missing"), "--dry-run", "--json") + + assert result.returncode == 1 + assert result.stdout == "" + payload = json.loads(result.stderr) + assert payload["ok"] is False + assert payload["error"]["type"] == "FileNotFoundError"