Files
product-54354138-1/tests/test_example.py
T
Tool Backend f20e58e85c
Repository Validate / repository-checks (pull_request) Successful in 3s
change: Implement 3: Complete the native-process CLI, JSON request handling, status vocabulary and exit codes
Completed the native-process CLI for `python -m tools.example`. Added `tools/envelope.py` with the fixed status vocabulary (`ok`, `dry_run`, `invalid_request`, `no_format`, `no_size`, `access_error`, `partial_download`) mapped to exit codes 0/0/2/3/4/5/6 and the `Response`/`ResponseBuilder` JSON envelope (keys: status, source, request, items, error). Extended `tools/errors.py` with request errors (`RequestError`, `RequestDocumentError`, `RequestFieldError`, `UnsupportedFormatError`) and `ACCEPTED_FORMATS`. Rewrote `tools/example.py` as a plain executable CLI accepting either a JSON request document or flags (--topic, --width, --height, --format png|ico, --source, --count, --download, --output-dir, --resize, --dry-run), with a custom parser that turns usage errors into `invalid_request` envelopes, unknown sources reported together with the available source ids, unsupported formats rejected with the accepted values, and format/size filtering, download and partial-download handling mapped onto the shared status table. `examples/input.json` is now the canonical offline dry_run request, and `tests/test_cli.py` (new) plus a rewritten `tests/test_example.py` cover the status/exit-code table, single-JSON-document output, help text, unknown source/unsupported format rejection and the download branches.

Tests:
Facade checks run from /workspace with the prepared .venv: `python -m compileall tools tests` (tools.validate) OK; `pytest` (tools.test) 76 passed; `python -m tools.example examples/input.json` (tools.smoke) exit 0 with a single `dry_run` JSON document; `python -m tools.example --help` exits 0 and documents every parameter plus the JSON document contract. Origin target branch `origin/task/1-task` (1f929c5) is an ancestor of the work branch, so the target is integrated and the checks above ran on the integrated revision. No dependency changes were needed, so no prepare_environment update was required.

Progress:
Implemented envelope status/exit-code table and response builder, request errors, the full flag/JSON-document CLI in tools/example.py, canonical offline examples/input.json, and CLI tests; facade validate/test/smoke and --help all pass (76 tests).

Published by developer-agent through Tool Backend.

Closes #5
2026-11-03 01:48:00 +00:00

133 lines
4.2 KiB
Python

"""End-to-end tests for the example image-search CLI and its request document."""
from __future__ import annotations
import json
import subprocess
import sys
from pathlib import Path
import pytest
from tools import envelope, example
REPO_ROOT = Path(__file__).resolve().parents[1]
EXAMPLE_REQUEST = REPO_ROOT / "examples" / "input.json"
def run_cli(*args: str) -> subprocess.CompletedProcess[str]:
"""Run the CLI in a subprocess so stdout/exit codes are asserted end to end."""
return subprocess.run(
[sys.executable, "-m", "tools.example", *args],
cwd=REPO_ROOT,
capture_output=True,
text=True,
check=False,
)
def single_document(stdout: str) -> dict:
lines = [line for line in stdout.splitlines() if line.strip()]
assert len(lines) == 1, stdout
document = json.loads(lines[0])
assert isinstance(document, dict)
return document
def test_example_request_document_is_canonical_dry_run_request() -> None:
document = json.loads(EXAMPLE_REQUEST.read_text(encoding="utf-8"))
assert isinstance(document, dict)
assert set(example.REQUEST_FIELDS) <= set(document)
assert document["dry_run"] is True
assert document["format"] in example.ACCEPTED_FORMATS
assert isinstance(document["topic"], str) and document["topic"].strip()
assert document["source"] in example.default_registry().available_sources()
def test_example_request_document_passes_normalization() -> None:
document = json.loads(EXAMPLE_REQUEST.read_text(encoding="utf-8"))
request = example.normalize_request(document)
assert request["dry_run"] is True
assert request["format"] == "png"
assert request["source"] == "wikimedia_commons"
assert request["count"] == document["count"]
def test_cli_prints_single_json_envelope_and_exits_zero() -> None:
result = run_cli("examples/input.json")
assert result.returncode == envelope.EXIT_OK, result.stderr
assert result.stderr == ""
response = single_document(result.stdout)
assert response["status"] == envelope.STATUS_DRY_RUN
assert response["source"] == "wikimedia_commons"
assert response["items"] == []
assert response["request"]["dry_run"] is True
assert response["error"] is None
def test_cli_envelope_is_deterministic() -> None:
first = run_cli("examples/input.json")
second = run_cli("examples/input.json")
assert first.returncode == second.returncode == envelope.EXIT_OK
assert first.stdout == second.stdout
def test_cli_help_exits_zero_and_documents_parameters() -> None:
result = run_cli("--help")
assert result.returncode == 0
assert "REQUEST" in result.stdout
def test_cli_reports_missing_request_document(tmp_path: Path) -> None:
missing = tmp_path / "missing.json"
result = run_cli(str(missing))
assert result.returncode == envelope.EXIT_INVALID_REQUEST
response = single_document(result.stdout)
assert response["status"] == envelope.STATUS_INVALID_REQUEST
assert response["error"]["code"] == "request_document"
def test_cli_reports_malformed_json(tmp_path: Path) -> None:
malformed = tmp_path / "malformed.json"
malformed.write_text("{not json", encoding="utf-8")
result = run_cli(str(malformed))
assert result.returncode == envelope.EXIT_INVALID_REQUEST
response = single_document(result.stdout)
assert response["status"] == envelope.STATUS_INVALID_REQUEST
assert response["error"]["code"] == "request_document"
def test_cli_reports_non_object_document(tmp_path: Path) -> None:
payload = tmp_path / "list.json"
payload.write_text("[]", encoding="utf-8")
result = run_cli(str(payload))
assert result.returncode == envelope.EXIT_INVALID_REQUEST
assert single_document(result.stdout)["status"] == envelope.STATUS_INVALID_REQUEST
@pytest.mark.parametrize(
"document",
[
{},
{"topic": "red fox"},
{"topic": "", "format": "png"},
{"topic": "red fox", "format": "gif"},
{"topic": "red fox", "format": "png", "count": 0},
],
)
def test_normalize_request_rejects_invalid_documents(document: object) -> None:
with pytest.raises(example.RequestError):
example.normalize_request(document) # type: ignore[arg-type]