claude-dev-env 2.13.0 → 2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +61 -0
- package/CLAUDE.md +1 -57
- package/_shared/AGENTS.md +15 -0
- package/_shared/CLAUDE.md +1 -15
- package/_shared/advisor/AGENTS.md +22 -0
- package/_shared/advisor/CLAUDE.md +1 -22
- package/_shared/pr-loop/AGENTS.md +27 -0
- package/_shared/pr-loop/CLAUDE.md +1 -27
- package/_shared/pr-loop/scripts/AGENTS.md +35 -0
- package/_shared/pr-loop/scripts/CLAUDE.md +1 -35
- package/_shared/pr-loop/scripts/code_rules_gate_parts/AGENTS.md +42 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +1 -42
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/AGENTS.md +26 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -26
- package/_shared/pr-loop/scripts/tests/AGENTS.md +44 -0
- package/_shared/pr-loop/scripts/tests/CLAUDE.md +1 -44
- package/_shared/process-tree/AGENTS.md +41 -0
- package/_shared/process-tree/CLAUDE.md +1 -41
- package/agents/AGENTS.md +31 -0
- package/agents/CLAUDE.md +1 -31
- package/agents/clean-coder.md +6 -6
- package/agents/test_agent_frontmatter.py +4 -4
- package/audit-rubrics/AGENTS.md +43 -0
- package/audit-rubrics/CLAUDE.md +1 -43
- package/audit-rubrics/category_rubrics/AGENTS.md +37 -0
- package/audit-rubrics/category_rubrics/CLAUDE.md +1 -37
- package/audit-rubrics/prompts/AGENTS.md +37 -0
- package/audit-rubrics/prompts/CLAUDE.md +1 -37
- package/bin/AGENTS.md +112 -0
- package/bin/CLAUDE.md +1 -112
- package/bin/ever-shipped-skills.mjs +1 -0
- package/bin/install-constants.mjs +6 -7
- package/bin/install.mjs +24 -5
- package/bin/install.test.mjs +34 -0
- package/commands/AGENTS.md +23 -0
- package/commands/CLAUDE.md +1 -23
- package/commands/hook-log-extract.md +3 -3
- package/docs/AGENTS.md +32 -0
- package/docs/CLAUDE.md +1 -32
- package/docs/CODE_RULES.md +1 -1
- package/docs/imagegen.md +22 -0
- package/docs/references/AGENTS.md +17 -0
- package/docs/references/CLAUDE.md +1 -17
- package/hooks/AGENTS.md +31 -0
- package/hooks/CLAUDE.md +1 -31
- package/hooks/_gh_pr_author_swap_utils.py +14 -0
- package/hooks/advisory/AGENTS.md +16 -0
- package/hooks/advisory/CLAUDE.md +1 -16
- package/hooks/blocking/AGENTS.md +123 -0
- package/hooks/blocking/CLAUDE.md +1 -123
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/AGENTS.md +28 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/CLAUDE.md +1 -28
- package/hooks/blocking/code_rules_banned_identifiers.py +1 -1
- package/hooks/blocking/code_rules_dead_module_constant.py +149 -37
- package/hooks/blocking/code_rules_probe_chains.py +6 -2
- package/hooks/blocking/config/AGENTS.md +10 -0
- package/hooks/blocking/config/CLAUDE.md +1 -10
- package/hooks/blocking/gh_pr_author_restore.py +7 -2
- package/hooks/blocking/intent_only_ending_blocker.py +11 -16
- package/hooks/blocking/inventory_intent_records/AGENTS.md +26 -0
- package/hooks/blocking/inventory_intent_records/CLAUDE.md +1 -26
- package/hooks/blocking/package_inventory_stale_blocker_parts/AGENTS.md +26 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/CLAUDE.md +1 -26
- package/hooks/blocking/pii_prevention_blocker_parts/AGENTS.md +24 -0
- package/hooks/blocking/pii_prevention_blocker_parts/CLAUDE.md +1 -24
- package/hooks/blocking/tdd_enforcer_parts/AGENTS.md +30 -0
- package/hooks/blocking/tdd_enforcer_parts/CLAUDE.md +1 -30
- package/hooks/blocking/test_code_rules_enforcer_banned_prefixes.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +171 -0
- package/hooks/blocking/test_code_rules_enforcer_magic_allowlist.py +1 -1
- package/hooks/blocking/test_gh_pr_author_restore.py +2 -1
- package/hooks/blocking/test_intent_only_ending_blocker.py +4 -0
- package/hooks/diagnostic/AGENTS.md +43 -0
- package/hooks/diagnostic/CLAUDE.md +1 -43
- package/hooks/diagnostic/hook_log_extractor.py +2 -2
- package/hooks/diagnostic/migrations/AGENTS.md +16 -0
- package/hooks/diagnostic/migrations/CLAUDE.md +1 -16
- package/hooks/diagnostic/queries/AGENTS.md +19 -0
- package/hooks/diagnostic/queries/CLAUDE.md +1 -19
- package/hooks/diagnostic/test_hook_log_extractor.py +2 -2
- package/hooks/git-hooks/AGENTS.md +31 -0
- package/hooks/git-hooks/CLAUDE.md +1 -31
- package/hooks/git-hooks/git_hooks_constants/AGENTS.md +21 -0
- package/hooks/git-hooks/git_hooks_constants/CLAUDE.md +1 -21
- package/hooks/git-hooks/git_hooks_constants/__init__.py +4 -6
- package/hooks/git-hooks/pre_push.py +47 -34
- package/hooks/git-hooks/test_pre_push.py +111 -7
- package/hooks/hooks_constants/AGENTS.md +104 -0
- package/hooks/hooks_constants/CLAUDE.md +1 -104
- package/hooks/hooks_constants/hook_log_extractor_constants.py +1 -2
- package/hooks/hooks_constants/task_list_loop_starter_constants.py +4 -3
- package/hooks/hooks_constants/working_style_prompt_constants.py +29 -20
- package/hooks/lifecycle/AGENTS.md +18 -0
- package/hooks/lifecycle/CLAUDE.md +1 -18
- package/hooks/observability/AGENTS.md +20 -0
- package/hooks/observability/CLAUDE.md +1 -20
- package/hooks/session/AGENTS.md +33 -0
- package/hooks/session/CLAUDE.md +1 -33
- package/hooks/session/gh_pr_author_session_cleanup.py +7 -2
- package/hooks/session/task_list_loop_starter.py +3 -3
- package/hooks/session/test_gh_pr_author_session_cleanup.py +2 -1
- package/hooks/session/test_task_list_loop_starter.py +10 -2
- package/hooks/session/test_working_style_prompt.py +9 -4
- package/hooks/validation/AGENTS.md +20 -0
- package/hooks/validation/CLAUDE.md +1 -20
- package/hooks/validators/AGENTS.md +51 -0
- package/hooks/validators/CLAUDE.md +1 -51
- package/hooks/validators/health_check.py +52 -19
- package/hooks/validators/python_style_checks.py +1 -1
- package/hooks/validators/test_health_check.py +112 -27
- package/hooks/workflow/AGENTS.md +16 -0
- package/hooks/workflow/CLAUDE.md +1 -16
- package/installable-surfaces.manifest.json +1 -0
- package/output-styles/AGENTS.md +15 -0
- package/output-styles/CLAUDE.md +1 -15
- package/package.json +2 -1
- package/rules/AGENTS.md +61 -0
- package/rules/CLAUDE.md +1 -61
- package/rules/code-standards.md +5 -9
- package/rules/doc-prose-cuts.md +1 -1
- package/scripts/AGENTS.md +62 -0
- package/scripts/CLAUDE.md +1 -62
- package/scripts/codex_compat_materializer.py +4 -0
- package/scripts/dev_env_scripts_constants/AGENTS.md +21 -0
- package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -21
- package/scripts/sync_to_cursor/AGENTS.md +23 -0
- package/scripts/sync_to_cursor/CLAUDE.md +1 -23
- package/scripts/test_validate_instruction_pairs.py +90 -0
- package/scripts/test_verify_installable_package.py +5 -1
- package/scripts/tests/AGENTS.md +33 -0
- package/scripts/tests/CLAUDE.md +1 -33
- package/scripts/tests/test_codex_compat_materializer.py +36 -1
- package/scripts/validate_instruction_pairs.py +183 -0
- package/skills/AGENTS.md +72 -0
- package/skills/CLAUDE.md +1 -72
- package/skills/_shared/AGENTS.md +44 -0
- package/skills/_shared/CLAUDE.md +1 -44
- package/skills/_shared/advisor/AGENTS.md +9 -0
- package/skills/_shared/advisor/CLAUDE.md +1 -9
- package/skills/_shared/pr-loop/AGENTS.md +50 -0
- package/skills/_shared/pr-loop/CLAUDE.md +1 -50
- package/skills/_shared/pr-loop/prompts/AGENTS.md +9 -0
- package/skills/_shared/pr-loop/prompts/CLAUDE.md +1 -9
- package/skills/_shared/pr-loop/scripts/AGENTS.md +35 -0
- package/skills/_shared/pr-loop/scripts/CLAUDE.md +1 -35
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +24 -0
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +1 -24
- package/skills/anthropic-plan/AGENTS.md +34 -0
- package/skills/anthropic-plan/CLAUDE.md +1 -34
- package/skills/anthropic-plan/scripts/AGENTS.md +11 -0
- package/skills/anthropic-plan/scripts/CLAUDE.md +1 -11
- package/skills/anthropic-plan/scripts/anthropic_plan_scripts_constants/AGENTS.md +16 -0
- package/skills/anthropic-plan/scripts/anthropic_plan_scripts_constants/CLAUDE.md +1 -16
- package/skills/anthropic-plan/templates/AGENTS.md +13 -0
- package/skills/anthropic-plan/templates/CLAUDE.md +1 -13
- package/skills/anthropic-plan/workflow/AGENTS.md +14 -0
- package/skills/anthropic-plan/workflow/CLAUDE.md +1 -14
- package/skills/auditing-claude-config/AGENTS.md +21 -0
- package/skills/auditing-claude-config/CLAUDE.md +1 -21
- package/skills/autoconverge/AGENTS.md +36 -0
- package/skills/autoconverge/CLAUDE.md +1 -36
- package/skills/autoconverge/reference/AGENTS.md +16 -0
- package/skills/autoconverge/reference/CLAUDE.md +1 -16
- package/skills/autoconverge/workflow/AGENTS.md +26 -0
- package/skills/autoconverge/workflow/CLAUDE.md +1 -26
- package/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +16 -0
- package/skills/autoconverge/workflow/autoconverge_report_constants/CLAUDE.md +1 -16
- package/skills/bugteam/AGENTS.md +30 -0
- package/skills/bugteam/CLAUDE.md +1 -30
- package/skills/bugteam/reference/AGENTS.md +20 -0
- package/skills/bugteam/reference/CLAUDE.md +1 -20
- package/skills/bugteam/reference/obstacles/AGENTS.md +24 -0
- package/skills/bugteam/reference/obstacles/CLAUDE.md +1 -24
- package/skills/bugteam/scripts/AGENTS.md +30 -0
- package/skills/bugteam/scripts/CLAUDE.md +1 -30
- package/skills/bugteam/scripts/bugteam_scripts_constants/AGENTS.md +18 -0
- package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +1 -18
- package/skills/closeout/reference/handoff-prompt-template.md +1 -1
- package/skills/codex-review/AGENTS.md +46 -0
- package/skills/codex-review/CLAUDE.md +1 -46
- package/skills/codex-review/reference/AGENTS.md +15 -0
- package/skills/codex-review/reference/CLAUDE.md +1 -15
- package/skills/codex-review/scripts/codex_review_scripts_constants/AGENTS.md +18 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/CLAUDE.md +1 -18
- package/skills/codex-review/test_skill_scaffold.py +3 -3
- package/skills/copilot-review/AGENTS.md +18 -0
- package/skills/copilot-review/CLAUDE.md +1 -18
- package/skills/everything-search/AGENTS.md +17 -0
- package/skills/everything-search/CLAUDE.md +1 -17
- package/skills/fresh-branch/AGENTS.md +14 -0
- package/skills/fresh-branch/CLAUDE.md +1 -14
- package/skills/grok-spawn/AGENTS.md +28 -0
- package/skills/grok-spawn/CLAUDE.md +1 -28
- package/skills/pr-converge/AGENTS.md +32 -0
- package/skills/pr-converge/CLAUDE.md +1 -32
- package/skills/pr-converge/pr_converge_skill_constants/AGENTS.md +26 -0
- package/skills/pr-converge/pr_converge_skill_constants/CLAUDE.md +1 -26
- package/skills/pr-converge/reference/AGENTS.md +28 -0
- package/skills/pr-converge/reference/CLAUDE.md +1 -28
- package/skills/pr-converge/reference/obstacles/AGENTS.md +23 -0
- package/skills/pr-converge/reference/obstacles/CLAUDE.md +1 -23
- package/skills/pr-converge/scripts/AGENTS.md +46 -0
- package/skills/pr-converge/scripts/CLAUDE.md +1 -46
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/AGENTS.md +18 -0
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -18
- package/skills/pr-converge/workflows/AGENTS.md +16 -0
- package/skills/pr-converge/workflows/CLAUDE.md +1 -16
- package/skills/prototype/reference/honest-limitations.md +1 -1
- package/skills/rebase/AGENTS.md +32 -0
- package/skills/rebase/CLAUDE.md +1 -32
- package/skills/recall/AGENTS.md +30 -0
- package/skills/recall/CLAUDE.md +1 -30
- package/skills/remember/AGENTS.md +31 -0
- package/skills/remember/CLAUDE.md +1 -31
- package/skills/reviews/SKILL.md +1 -1
- package/skills/session-log/AGENTS.md +32 -0
- package/skills/session-log/CLAUDE.md +1 -32
- package/skills/session-tidy/AGENTS.md +36 -0
- package/skills/session-tidy/CLAUDE.md +1 -36
- package/skills/skill-builder/AGENTS.md +49 -0
- package/skills/skill-builder/CLAUDE.md +1 -49
- package/skills/skill-builder/references/AGENTS.md +25 -0
- package/skills/skill-builder/references/CLAUDE.md +1 -25
- package/skills/skill-builder/references/deterministic-elements.md +1 -1
- package/skills/skill-builder/templates/AGENTS.md +14 -0
- package/skills/skill-builder/templates/CLAUDE.md +1 -14
- package/skills/skill-builder/workflows/AGENTS.md +19 -0
- package/skills/skill-builder/workflows/CLAUDE.md +1 -19
- package/skills/task-build/AGENTS.md +29 -0
- package/skills/task-build/CLAUDE.md +1 -29
- package/skills/update/AGENTS.md +38 -0
- package/skills/update/CLAUDE.md +1 -38
- package/system-prompts/AGENTS.md +17 -0
- package/system-prompts/CLAUDE.md +1 -17
- package/system-prompts/software-engineer.xml +3 -3
- package/skills/imagegen/SKILL.md +0 -21
- package/skills/imagegen/scripts/config/__init__.py +0 -1
- package/skills/imagegen/scripts/config/constants.py +0 -28
- package/skills/imagegen/scripts/imagegen.py +0 -45
- package/skills/imagegen/scripts/imagegen_core.py +0 -528
- package/skills/imagegen/scripts/test_imagegen.py +0 -204
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
"""Focused tests for exact-resolution image generation."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import base64
|
|
6
|
-
import hashlib
|
|
7
|
-
import json
|
|
8
|
-
import sys
|
|
9
|
-
from collections.abc import Mapping, Sequence
|
|
10
|
-
from io import BytesIO
|
|
11
|
-
from pathlib import Path
|
|
12
|
-
|
|
13
|
-
import pytest
|
|
14
|
-
from PIL import Image
|
|
15
|
-
|
|
16
|
-
sys.path.insert(0, str(Path(__file__).parent))
|
|
17
|
-
|
|
18
|
-
import imagegen_core
|
|
19
|
-
from imagegen_core import (
|
|
20
|
-
ImagegenError,
|
|
21
|
-
ImageSize,
|
|
22
|
-
build_oauth_environment,
|
|
23
|
-
decode_image,
|
|
24
|
-
download_https_image,
|
|
25
|
-
generate_image,
|
|
26
|
-
parse_size,
|
|
27
|
-
publish_artifact,
|
|
28
|
-
resize_image,
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def make_png(image_size: tuple[int, int]) -> bytes:
|
|
33
|
-
"""Create real PNG bytes for provider-boundary tests."""
|
|
34
|
-
destination = BytesIO()
|
|
35
|
-
Image.new("RGB", image_size, "purple").save(destination, format="PNG")
|
|
36
|
-
return destination.getvalue()
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def test_native_output_preserves_source_bytes_and_receipt(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
40
|
-
source_bytes = make_png((2880, 2880))
|
|
41
|
-
monkeypatch.setenv("OPENAI_API_KEY", "present")
|
|
42
|
-
provider_payload = b'{"data":[{"b64_json":"' + base64.b64encode(source_bytes) + b'"}]}'
|
|
43
|
-
receipt = generate_image("native", "openai-api", ImageSize(2880, 2880), tmp_path / "native.png", "forbid", False, transport=lambda _url, _body, _headers: provider_payload)
|
|
44
|
-
assert (tmp_path / "native.png").read_bytes() == source_bytes
|
|
45
|
-
assert receipt["transformation"] == "native"
|
|
46
|
-
assert receipt["source_sha256"] == hashlib.sha256(source_bytes).hexdigest()
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def test_oauth_output_resizes_and_strips_api_key(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
50
|
-
monkeypatch.setenv("OPENAI_API_KEY", "secret")
|
|
51
|
-
captured_environment: dict[str, str] = {}
|
|
52
|
-
|
|
53
|
-
def runner(_arguments: Sequence[str], work_directory: Path, environment: Mapping[str, str]) -> None:
|
|
54
|
-
captured_environment.update(environment)
|
|
55
|
-
(work_directory / "generated.png").write_bytes(make_png((1254, 1254)))
|
|
56
|
-
|
|
57
|
-
receipt = generate_image("resize", "codex-oauth", ImageSize(2880, 2880), tmp_path / "resized.png", "allow", False, runner=runner)
|
|
58
|
-
assert decode_image((tmp_path / "resized.png").read_bytes()) == ImageSize(2880, 2880)
|
|
59
|
-
assert receipt["transformation"] == "resized"
|
|
60
|
-
assert "OPENAI_API_KEY" not in captured_environment
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
@pytest.mark.parametrize("invalid_size", ("2880", "2880x1440", "2881x2881", "3841x3841", "3840x3840"))
|
|
64
|
-
def test_invalid_sizes_fail(invalid_size: str) -> None:
|
|
65
|
-
with pytest.raises(ImagegenError):
|
|
66
|
-
parse_size(invalid_size)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
@pytest.mark.parametrize("valid_size", ("256x256", "512x512", "1028x1028", "2880x2880"))
|
|
70
|
-
def test_final_sizes_are_independent_from_native_api_sizes(valid_size: str) -> None:
|
|
71
|
-
assert parse_size(valid_size).as_text() == valid_size
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def test_openai_unsupported_size_fails_before_authentication(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
75
|
-
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
76
|
-
|
|
77
|
-
with pytest.raises(ImagegenError, match="resize-policy"):
|
|
78
|
-
generate_image("prompt", "openai-api", ImageSize(1028, 1028), tmp_path / "artifact.png", "forbid", False)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def test_openai_native_size_requires_api_key(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
82
|
-
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
83
|
-
|
|
84
|
-
with pytest.raises(ImagegenError, match="OPENAI_API_KEY"):
|
|
85
|
-
generate_image("prompt", "openai-api", ImageSize(2880, 2880), tmp_path / "artifact.png", "forbid", False)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def test_openai_unsupported_size_requests_valid_provider_size_before_resize(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
89
|
-
monkeypatch.setenv("OPENAI_API_KEY", "present")
|
|
90
|
-
request_sizes: list[str] = []
|
|
91
|
-
request_formats: list[str] = []
|
|
92
|
-
source_bytes = make_png((2048, 2048))
|
|
93
|
-
provider_payload = b'{"data":[{"b64_json":"' + base64.b64encode(source_bytes) + b'"}]}'
|
|
94
|
-
|
|
95
|
-
def transport(_url: str, body: bytes, _headers: Mapping[str, str]) -> bytes:
|
|
96
|
-
request_payload = json.loads(body)
|
|
97
|
-
request_sizes.append(request_payload["size"])
|
|
98
|
-
request_formats.append(request_payload["output_format"])
|
|
99
|
-
return provider_payload
|
|
100
|
-
|
|
101
|
-
receipt = generate_image("prompt", "openai-api", ImageSize(1028, 1028), tmp_path / "artifact.png", "allow", False, transport=transport)
|
|
102
|
-
|
|
103
|
-
assert request_sizes == ["2048x2048"]
|
|
104
|
-
assert request_formats == ["png"]
|
|
105
|
-
assert receipt["source_size"] == "2048x2048"
|
|
106
|
-
assert receipt["final_size"] == "1028x1028"
|
|
107
|
-
assert receipt["transformation"] == "resized"
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def test_oauth_environment_strips_api_key(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
111
|
-
monkeypatch.setenv("OPENAI_API_KEY", "secret")
|
|
112
|
-
assert "OPENAI_API_KEY" not in build_oauth_environment()
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def test_library_validates_square_size_and_png_destination(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
116
|
-
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
117
|
-
|
|
118
|
-
with pytest.raises(ImagegenError, match="size exceeds"):
|
|
119
|
-
generate_image("prompt", "openai-api", ImageSize(1028, 1024), tmp_path / "artifact.png", "forbid", False)
|
|
120
|
-
with pytest.raises(ImagegenError, match=".png"):
|
|
121
|
-
generate_image("prompt", "openai-api", ImageSize(2880, 2880), tmp_path / "artifact.jpg", "forbid", False)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def test_http_provider_image_url_is_rejected() -> None:
|
|
125
|
-
with pytest.raises(ImagegenError, match="HTTPS"):
|
|
126
|
-
download_https_image("http://example.invalid/image.png")
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
def test_provider_image_url_redirects_are_rejected() -> None:
|
|
130
|
-
handler = imagegen_core._RejectRedirectHandler()
|
|
131
|
-
with pytest.raises(ImagegenError, match="redirects"):
|
|
132
|
-
handler.redirect_request(None, None, 302, "Found", {}, "https://example.invalid/other.png")
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
def test_non_png_provider_bytes_are_rejected() -> None:
|
|
136
|
-
destination = BytesIO()
|
|
137
|
-
Image.new("RGB", (16, 16), "purple").save(destination, format="JPEG")
|
|
138
|
-
|
|
139
|
-
with pytest.raises(ImagegenError, match="PNG"):
|
|
140
|
-
decode_image(destination.getvalue())
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
@pytest.mark.parametrize("artifact_names", ((), ("one.png", "two.png")))
|
|
144
|
-
def test_missing_or_multiple_artifacts_fail(tmp_path: Path, artifact_names: tuple[str, ...]) -> None:
|
|
145
|
-
def runner(_arguments: Sequence[str], work_directory: Path, _environment: Mapping[str, str]) -> None:
|
|
146
|
-
for each_artifact_name in artifact_names:
|
|
147
|
-
(work_directory / each_artifact_name).write_bytes(make_png((1254, 1254)))
|
|
148
|
-
|
|
149
|
-
with pytest.raises(ImagegenError, match="exactly one"):
|
|
150
|
-
generate_image("prompt", "codex-oauth", ImageSize(2880, 2880), tmp_path / "artifact.png", "allow", False, runner=runner)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def test_mismatched_aspect_ratio_is_rejected(tmp_path: Path) -> None:
|
|
154
|
-
def runner(_arguments: Sequence[str], work_directory: Path, _environment: Mapping[str, str]) -> None:
|
|
155
|
-
(work_directory / "generated.png").write_bytes(make_png((1254, 1000)))
|
|
156
|
-
|
|
157
|
-
with pytest.raises(ImagegenError, match="square"):
|
|
158
|
-
generate_image("prompt", "codex-oauth", ImageSize(2880, 2880), tmp_path / "artifact.png", "allow", False, runner=runner)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
def test_transparency_survives_native_publication(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
162
|
-
destination = BytesIO()
|
|
163
|
-
Image.new("RGBA", (16, 16), (10, 20, 30, 0)).save(destination, format="PNG")
|
|
164
|
-
monkeypatch.setenv("OPENAI_API_KEY", "present")
|
|
165
|
-
provider_payload = b'{"data":[{"b64_json":"' + base64.b64encode(destination.getvalue()) + b'"}]}'
|
|
166
|
-
generate_image("transparent", "openai-api", ImageSize(16, 16), tmp_path / "transparent.png", "allow", False, transport=lambda _url, _body, _headers: provider_payload)
|
|
167
|
-
with Image.open(tmp_path / "transparent.png") as image:
|
|
168
|
-
assert image.mode == "RGBA"
|
|
169
|
-
assert image.getpixel((0, 0)) == (10, 20, 30, 0)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
def test_overwrite_refusal_and_cleanup(tmp_path: Path) -> None:
|
|
173
|
-
output_path = tmp_path / "existing.png"
|
|
174
|
-
output_path.write_bytes(make_png((16, 16)))
|
|
175
|
-
with pytest.raises(ImagegenError, match="already exists"):
|
|
176
|
-
publish_artifact(output_path, output_path.read_bytes(), {"transformation": "native"}, False)
|
|
177
|
-
assert not list(tmp_path.glob("*.tmp"))
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
def test_publication_rolls_back_when_receipt_replace_fails(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
181
|
-
destination_path = tmp_path / "artifact.png"
|
|
182
|
-
original_replace = imagegen_core.os.replace
|
|
183
|
-
replacement_count = 0
|
|
184
|
-
|
|
185
|
-
def replace_with_failure(source: Path, target: Path) -> None:
|
|
186
|
-
nonlocal replacement_count
|
|
187
|
-
replacement_count += 1
|
|
188
|
-
if replacement_count == 2:
|
|
189
|
-
raise OSError("receipt replacement failed")
|
|
190
|
-
original_replace(source, target)
|
|
191
|
-
|
|
192
|
-
monkeypatch.setattr(imagegen_core.os, "replace", replace_with_failure)
|
|
193
|
-
with pytest.raises(ImagegenError, match="publication failed"):
|
|
194
|
-
publish_artifact(destination_path, make_png((16, 16)), {"transformation": "native"}, False)
|
|
195
|
-
assert not destination_path.exists()
|
|
196
|
-
assert not destination_path.with_suffix(".json").exists()
|
|
197
|
-
assert not list(tmp_path.glob("*.tmp"))
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
def test_resize_and_decode_have_expected_hashes() -> None:
|
|
201
|
-
source_bytes = make_png((1254, 1254))
|
|
202
|
-
final_bytes = resize_image(source_bytes, ImageSize(2880, 2880))
|
|
203
|
-
assert decode_image(final_bytes) == ImageSize(2880, 2880)
|
|
204
|
-
assert hashlib.sha256(source_bytes).hexdigest() != hashlib.sha256(final_bytes).hexdigest()
|