"""Keep README.md synchronized with the delivered CLI contract. The documentation is part of the product: the calling agent relies on the parameter list, the status vocabulary and the exit codes stated in README.md. These checks fail when the implementation moves on and the documentation is left describing something the tool no longer does. """ from __future__ import annotations from pathlib import Path from tools.envelope import STATUS_EXIT_CODES from tools.errors import ACCEPTED_FORMATS from tools.example import build_parser from tools.sources import default_registry README = Path(__file__).resolve().parent.parent / "README.md" def readme_text() -> str: assert README.is_file(), "README.md is missing" text = README.read_text(encoding="utf-8") assert text.strip(), "README.md is empty" return text def long_options() -> list[str]: """Return every ``--long`` option the real parser accepts.""" options: list[str] = [] for action in build_parser()._actions: for option in action.option_strings: if option.startswith("--"): options.append(option) return sorted(set(options)) def test_readme_documents_every_cli_parameter() -> None: text = readme_text() options = long_options() assert {"--topic", "--width", "--height", "--format", "--source", "--count", "--download", "--output-dir", "--resize", "--dry-run"} <= set(options) missing = [option for option in options if option == "--help" and option not in text] missing += [ option for option in options if option != "--help" and f"`{option}" not in text ] assert not missing, f"README.md does not document parameters: {missing}" # The positional request document and both invocation styles are documented. assert "REQUEST.json" in text assert "python -m tools.example examples/input.json" in text # Format vocabulary in the README matches the accepted formats. for image_format in ACCEPTED_FORMATS: assert image_format in text def test_readme_status_table_matches_exit_codes() -> None: text = readme_text() for status, exit_code in STATUS_EXIT_CODES.items(): assert f"| `{status}` | {exit_code} |" in text, ( f"README.md status table disagrees for {status!r} -> {exit_code}" ) def test_readme_documents_response_schema_and_sources() -> None: text = readme_text() for key in ('"status"', '"source"', '"request"', '"items"', '"error"'): assert key in text, f"README.md does not document response key {key}" for source_id in default_registry().available_sources(): assert source_id in text, f"README.md does not document source {source_id!r}" assert "SourceAdapter" in text and "SourceRegistry" in text assert "API key" in text def test_readme_documents_product_decisions() -> None: text = readme_text() # --download without --output-dir is an invalid request. assert "--download" in text and "without `--output-dir` is an invalid request" in text # Partial saves have their own status. assert "partial_download" in text assert "invalid_request" in text # Native process only: no Docker or container packaging. assert "Docker" in text and "container" in text def test_readme_documents_the_real_test_entrypoint() -> None: text = readme_text() assert "\npytest\n" in text, "README.md must document the real pytest entrypoint" assert "python -m compileall tools tests" in text assert "python -m tools.example --help" in text