mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-16 03:42:20 +02:00
Rebuild PR C to ship the vendor/photocraft harness as a coherent unit (vendor implementation + vendor tests) instead of a per-file ahead/behind mix that produced broken imports and 6+ test failures. - Flip 36 harness impl/route/test files back to vendor (the per-file line-set policy misclassified user-rewritten files as 'behind'). - Flip core/models.py + src/tool_approval_scopes.py to vendor: vendor's harness (authority.py, turn_contract.py, clean_agent_preview.py) depends on vendor's older approval-scope API; upstream added CHAT_SESSION_APPROVAL_SIGNATURE_FIELD that vendor's core/models.py lacks. - Add src/shutdown.py (new vendor infra) required by routes/chat_routes.py. - Keep src/agent_tools/__init__.py at upstream (editor_tools registration lives in PR B). Upstream-only security divergence preserved in C_upstream_delta.patch and flagged for integration-time reconciliation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
224 lines
7.3 KiB
Python
224 lines
7.3 KiB
Python
"""Guards for the generated ODYSSEUS_* configuration reference.
|
|
|
|
`website/configuration-reference.md` is produced by
|
|
`scripts/generate_env_reference.py`. The point of these tests is that adding a
|
|
new `ODYSSEUS_*` read without documenting it fails the suite: the current state -
|
|
most of the configuration surface undiscoverable - happened because nothing
|
|
objected.
|
|
|
|
The generator's detection is exercised against a synthetic source tree rather
|
|
than against the real one, so a test failure names a behavior rather than a
|
|
count that drifted. Only the two whole-tree tests touch the repository, and they
|
|
compare generator output to the committed page instead of asserting on source
|
|
text.
|
|
"""
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from tests.helpers.cli_loader import load_script
|
|
|
|
REPO = Path(__file__).resolve().parent.parent
|
|
PAGE = REPO / "website" / "configuration-reference.md"
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def generator():
|
|
return load_script("generate_env_reference.py")
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def built(generator):
|
|
"""The generator run once against the real tree; reused by the slow tests."""
|
|
return generator.build()
|
|
|
|
|
|
def _write(root: Path, relative: str, body: str) -> None:
|
|
path = root / relative
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(body, encoding="utf-8")
|
|
|
|
|
|
@pytest.fixture
|
|
def fake_tree(tmp_path, generator, monkeypatch):
|
|
"""A miniature source tree covering every read pattern the generator claims."""
|
|
monkeypatch.setattr(generator, "SOURCE_ROOTS", ("app.py", "src"))
|
|
_write(tmp_path, "app.py", """
|
|
import os
|
|
|
|
DIRECT = os.environ.get("ODYSSEUS_DIRECT_GET", "on")
|
|
GETENV = os.getenv("ODYSSEUS_PLAIN_GETENV")
|
|
SUBSCRIPT = os.environ["ODYSSEUS_SUBSCRIPT"]
|
|
SPANNING = os.environ.get(
|
|
"ODYSSEUS_SPANS_TWO_LINES", "spanned"
|
|
)
|
|
os.environ["ODYSSEUS_WRITE_ONLY"] = "1"
|
|
""")
|
|
_write(tmp_path, "src/indirect.py", '''
|
|
import os
|
|
|
|
NAME_HELD_IN_CONSTANT = "ODYSSEUS_VIA_CONSTANT"
|
|
FALLBACK = 7
|
|
|
|
value = os.environ.get(NAME_HELD_IN_CONSTANT, FALLBACK)
|
|
|
|
|
|
def read_limit(name, default):
|
|
"""An env-reader helper: the generator should follow calls to this."""
|
|
raw = os.getenv(name)
|
|
return default if raw is None else int(raw)
|
|
|
|
|
|
LIMIT = read_limit("ODYSSEUS_VIA_HELPER", 5 * 1024)
|
|
|
|
|
|
def flag(environ=None):
|
|
source = os.environ if environ is None else environ
|
|
return source.get("ODYSSEUS_VIA_MAPPING_ARG", "1")
|
|
|
|
|
|
GENERATED = [
|
|
"import os",
|
|
"if os.environ.get('ODYSSEUS_INSIDE_A_STRING'): pass",
|
|
]
|
|
''')
|
|
return tmp_path
|
|
|
|
|
|
def test_finds_every_read_pattern_it_claims_to(generator, fake_tree):
|
|
found = generator.collect(fake_tree)
|
|
|
|
assert set(found) == {
|
|
"ODYSSEUS_DIRECT_GET",
|
|
"ODYSSEUS_PLAIN_GETENV",
|
|
"ODYSSEUS_SUBSCRIPT",
|
|
"ODYSSEUS_SPANS_TWO_LINES",
|
|
"ODYSSEUS_VIA_CONSTANT",
|
|
"ODYSSEUS_VIA_HELPER",
|
|
"ODYSSEUS_VIA_MAPPING_ARG",
|
|
"ODYSSEUS_INSIDE_A_STRING",
|
|
}
|
|
|
|
|
|
def test_a_plain_grep_would_miss_what_the_extra_passes_find(generator, fake_tree):
|
|
"""Pins why the generator is not a one-line grep."""
|
|
naive = generator.naive_line_scan(fake_tree)
|
|
found = set(generator.collect(fake_tree))
|
|
|
|
assert found - naive == {
|
|
"ODYSSEUS_SPANS_TWO_LINES",
|
|
"ODYSSEUS_VIA_CONSTANT",
|
|
"ODYSSEUS_VIA_HELPER",
|
|
"ODYSSEUS_VIA_MAPPING_ARG",
|
|
}
|
|
# And it over-counts in the other direction: a line-based scan cannot tell a
|
|
# write from a read, which is why the page reports the intersection.
|
|
assert naive - found == {"ODYSSEUS_WRITE_ONLY"}
|
|
|
|
|
|
def test_records_defaults_and_locations_from_the_source(generator, fake_tree):
|
|
found = generator.collect(fake_tree)
|
|
|
|
assert found["ODYSSEUS_DIRECT_GET"].primary.default == "'on'"
|
|
assert found["ODYSSEUS_SPANS_TWO_LINES"].primary.default == "'spanned'"
|
|
# One level of indirection is resolved: the name and the default both come
|
|
# from module-level constants.
|
|
assert found["ODYSSEUS_VIA_CONSTANT"].primary.default == "7"
|
|
assert found["ODYSSEUS_VIA_HELPER"].primary.default == "5 * 1024"
|
|
assert found["ODYSSEUS_PLAIN_GETENV"].primary.default is None
|
|
|
|
assert found["ODYSSEUS_DIRECT_GET"].primary.location == "app.py:4"
|
|
assert found["ODYSSEUS_VIA_HELPER"].primary.path == "src/indirect.py"
|
|
|
|
|
|
def test_environ_writes_are_not_reads(generator, fake_tree):
|
|
assert "ODYSSEUS_WRITE_ONLY" not in generator.collect(fake_tree)
|
|
|
|
|
|
def test_an_undocumented_variable_is_reported(generator, fake_tree):
|
|
"""The whole point: a new variable with no notes entry must fail loudly."""
|
|
problems = generator.check_notes(generator.collect(fake_tree))
|
|
|
|
assert problems
|
|
offender = "ODYSSEUS_VIA_HELPER"
|
|
assert any(offender in problem for problem in problems)
|
|
assert any("VARIABLE_NOTES" in problem for problem in problems)
|
|
assert any("src/indirect.py" in problem for problem in problems)
|
|
|
|
|
|
def test_a_stale_notes_entry_is_reported(generator):
|
|
problems = generator.check_notes({})
|
|
|
|
assert len(problems) == len(generator.VARIABLE_NOTES)
|
|
assert all("no longer read anywhere" in problem for problem in problems)
|
|
|
|
|
|
def test_renders_one_table_row_per_variable(generator, fake_tree, monkeypatch):
|
|
monkeypatch.setitem(
|
|
generator.VARIABLE_NOTES,
|
|
"ODYSSEUS_VIA_HELPER",
|
|
("Search", generator.USER, "A synthetic limit."),
|
|
)
|
|
found = {"ODYSSEUS_VIA_HELPER": generator.collect(fake_tree)["ODYSSEUS_VIA_HELPER"]}
|
|
|
|
page = generator.render(found, generator.naive_line_scan(fake_tree))
|
|
|
|
assert page.startswith("---\nlayout: default\n---\n")
|
|
assert "| `ODYSSEUS_VIA_HELPER` | `5 * 1024` | `src/indirect.py:16` | A synthetic limit. |" in page
|
|
assert "### Search" in page
|
|
|
|
|
|
def test_every_variable_read_in_the_repository_is_documented(built):
|
|
_, variables, problems = built
|
|
|
|
assert not problems, "\n".join(problems)
|
|
assert variables, "expected the generator to find ODYSSEUS_* reads"
|
|
|
|
|
|
def test_committed_page_matches_the_source(built):
|
|
page, _, _ = built
|
|
|
|
assert PAGE.read_text(encoding="utf-8") == page, (
|
|
"website/configuration-reference.md is stale - regenerate it with "
|
|
"`python3 scripts/generate_env_reference.py`"
|
|
)
|
|
|
|
|
|
def test_page_has_no_emoji_or_other_non_ascii(built):
|
|
page, _, _ = built
|
|
|
|
offenders = sorted({character for character in page if ord(character) > 126})
|
|
assert not offenders, f"non-ASCII in the generated page: {offenders}"
|
|
|
|
|
|
def test_check_mode_reports_a_stale_page(generator, tmp_path, monkeypatch):
|
|
stale = tmp_path / "configuration-reference.md"
|
|
stale.write_text("out of date\n", encoding="utf-8")
|
|
monkeypatch.setattr(generator, "OUTPUT_PATH", stale)
|
|
|
|
assert generator.main(["--check"]) == 1
|
|
assert generator.main([]) == 0
|
|
assert generator.main(["--check"]) == 0
|
|
|
|
|
|
def test_script_runs_as_a_subprocess_without_importing_the_app():
|
|
result = subprocess.run(
|
|
[sys.executable, "scripts/generate_env_reference.py", "--check"],
|
|
cwd=REPO, capture_output=True, text=True, timeout=180,
|
|
)
|
|
|
|
assert result.returncode == 0, result.stderr
|
|
assert re.search(r"\d+ variables", result.stdout), result.stdout
|
|
|
|
|
|
def test_page_is_linked_from_the_places_a_reader_starts():
|
|
assert "configuration-reference.md" in (REPO / "website" / "setup.md").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
assert "configuration-reference.md" in (REPO / ".env.example").read_text(
|
|
encoding="utf-8"
|
|
)
|