Final acceptance: Консольная утилита поиска и скачивания изображений для вызова ИИ-агентом (#18)
Repository Validate / repository-checks (push) Successful in 21s

Co-authored-by: AI Tester <ai-tester@git.p2prod.ru>
This commit was merged in pull request #18.
This commit is contained in:
2026-11-03 02:51:14 +00:00
committed by ai-reviewer
parent 1b21e0ba82
commit 44473a1055
23 changed files with 6643 additions and 2 deletions
+92
View File
@@ -0,0 +1,92 @@
"""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