"""`--run-id` navngir en kjøring, ikke en sti — og de sju utboks-dørene må håndheve det. Hver skriver bygger filnavnet sitt som ``/-.json``. Utkatalogen er kallerens å navngi (det er en egen regel), men run-id-en gikk urørt inn i den sammensetningen: ``--outbox-dir /inni --run-id ../../ESCAPE`` skrev artefaktet TO nivåer OVER katalogen kalleren navnga, og svarte 0. Vakten sto på den ene halvdelen av stien og manglet på den andre. Klassen måles her, ikke ett tilfelle: alle SJU dørene mot fire former for stibærende run-id — relativ oppover, absolutt, med skilletegn, og den bare ``..``. Hver arm asserterer to ting, fordi én av dem alene ville vært tilfreds med feil oppførsel: at exit-koden er ikke-null, OG at filtreet under `tmp_path` er BYTE-uendret etterpå. En dør som avviste med rett kode etter å ha lagt igjen fila ville passert på den første alene. rc-0-kontrollen er obligatorisk: samme argv med en ren run-id må svare 0 og skrive. Uten den måler armene ingenting — en dør som avviste ALT ville vært like grønn som den riktige. """ from __future__ import annotations import json import subprocess import sys from collections.abc import Callable from pathlib import Path from typing import Any import pytest _REPO = Path(__file__).resolve().parents[1] _IR = _REPO / "shared" / "examples" / "bygg-energi-mikro" / "validator-input.json" #: En run-id uten stibærende tegn. Den ENE verdien rc-0-kontrollen står på. _REN = "probe-containment-01" def _toolbox(*args: str) -> subprocess.CompletedProcess[str]: """Døren som subprosess, `-m`-formen — den virker i en ren klone uten sync.""" return subprocess.run( [sys.executable, "-m", "portfolio_optimiser.toolbox", *args], cwd=_REPO, capture_output=True, text=True, ) def _json_file(path: Path, payload: Any) -> str: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(payload), encoding="utf-8") return str(path) def _stamp(path: Path) -> str: return _json_file( path, { "citations": [ { "file": "index.md", "locator": {"start_index": 0, "end_index": 12}, "snippet": "energiprofil", } ], "model": "ingen-modell", "role": "proposer", "validator_decision": "validated", "token_usage": 0, "cost_baseline_anchored": False, "bundle_id_source": None, "code_forms": {"ENERGI-TOTAL-EL": "identifier"}, "external_calls": [], }, ) #: Dør -> (flagget som navngir utkatalogen, de øvrige argumentene bygget mot en fikstur-mappe). #: Argumentene er de MINIMALE som ellers ville gitt 0 — det er dét som gjør nekten målbar. _DOERER: dict[str, tuple[str, Callable[[Path], list[str]]]] = { "write-run-config": ( "--out-dir", lambda f: [ "--profile", "local", "--resolved-models", _json_file(f / "modeller.json", {"proposer": "gpt-x"}), "--max-rounds", "5", "--max-tokens", "600000", "--top-k", "3", ], ), "write-coverage": ( "--outbox-dir", lambda f: [ "--rows", _json_file(f / "rader.json", []), "--stop-reason", "tokens", ], ), "write-outbox": ( "--outbox-dir", lambda f: [ "--proposal", str(_IR), "--provenance", _stamp(f / "stempel.json"), "--verdict-id", "v-probe", ], ), "write-prepass": ( "--outbox-dir", lambda f: [ "--declaration", _json_file(f / "kutt.json", {"bundle_id": "x", "considered": 1, "delivered": 1}), ], ), "write-parse-failures": ( "--outbox-dir", lambda f: ["--failures", _json_file(f / "feil.json", [])], ), "write-proposal-reviews": ( "--outbox-dir", lambda f: ["--payload", _json_file(f / "vurderinger.json", {"reviews": []})], ), "write-debate-tools": ( "--outbox-dir", lambda f: ["--tool-calls", _json_file(f / "kall.json", [])], ), } def _tree(root: Path) -> dict[str, bytes]: """Hele filtreet som innhold, ikke bare navn — en dør som overskrev en fil den ikke skulle røre ville hatt samme navneliste.""" return { str(p.relative_to(root)): p.read_bytes() for p in sorted(root.rglob("*")) if p.is_file() } @pytest.mark.parametrize("door", sorted(_DOERER)) @pytest.mark.parametrize( "form", ["relativ-opp", "med-skilletegn", "bare-punktum", "absolutt"], ) def test_a_run_id_that_carries_a_path_is_refused_and_writes_nothing( tmp_path: Path, door: str, form: str ) -> None: """Fire former, sju dører. Utkatalogen ligger TO nivåer under `tmp_path`, så en rømning oppover lander et sted armen kan se — den ville vært usynlig om utkatalogen lå på toppen.""" fikstur = tmp_path / "fikstur" ut = tmp_path / "inni" / "dypere" flagg, oevrige = _DOERER[door] argv = oevrige(fikstur) run_id = { "relativ-opp": "../../ESCAPE", "med-skilletegn": "under/ESCAPE", "bare-punktum": "..", "absolutt": str(tmp_path / "ABSOLUTT"), }[form] foer = _tree(tmp_path) proc = _toolbox(door, flagg, str(ut), "--run-id", run_id, *argv) assert proc.returncode != 0, f"{door} godtok en stibærende run-id: {proc.stdout}" assert _tree(tmp_path) == foer, f"{door} la igjen en fil for run-id {run_id!r}" assert not ut.exists(), f"{door} opprettet utkatalogen for en avvist run-id" @pytest.mark.parametrize("door", sorted(_DOERER)) def test_the_same_call_with_a_clean_run_id_writes(tmp_path: Path, door: str) -> None: """rc-0-kontrollen. Samme dør, samme argv, eneste forskjell er run-id-en — så nekten over handler om run-id-en og ikke om at kallet var mangelfullt.""" fikstur = tmp_path / "fikstur" ut = tmp_path / "inni" / "dypere" flagg, oevrige = _DOERER[door] proc = _toolbox(door, flagg, str(ut), "--run-id", _REN, *oevrige(fikstur)) assert proc.returncode == 0, proc.stdout + proc.stderr skrevet = json.loads(proc.stdout) stier = [skrevet["path"]] if "path" in skrevet else [skrevet["proposal_path"]] for sti in stier: assert Path(sti).is_file() assert Path(sti).resolve().parent == ut.resolve(), "artefaktet havnet utenfor utkatalogen" def test_write_coverage_refuses_to_invent_a_stop_reason(tmp_path: Path) -> None: """«Kjøringen ble ferdig» og «vi fikk aldri vite» må ikke være samme verdi — derfor er `--stop-reason` PÅKREVD og defaulter aldri til tom streng. Regelen er kalt bærende i hovedboken, men ingen arm felte den: en `required=True` byttet til `default=""` passerte hele suiten. Denne armen er vakten — uten flagget skal kallet være en usage-feil, ikke en kjøring som stille kaller en avbrutt runde fullført.""" ut = tmp_path / "ut" rader = _json_file(tmp_path / "rader.json", []) uten = _toolbox("write-coverage", "--outbox-dir", str(ut), "--run-id", _REN, "--rows", rader) assert uten.returncode == 2, uten.stdout + uten.stderr assert "--stop-reason" in uten.stderr, "nekten må NAVNGI flagget som mangler" assert not ut.exists(), "et kall uten stoppgrunn skal ikke ha lagt igjen noe" # rc-0-kontroll: samme argv PLUSS flagget, med den tomme strengen som er en gyldig verdi — # det er nettopp verdien mutanten ville funnet på selv, og den må kalleren kunne oppgi. med = _toolbox( "write-coverage", "--outbox-dir", str(ut), "--run-id", _REN, "--rows", rader, "--stop-reason", "", ) assert med.returncode == 0, med.stdout + med.stderr paa_disk = json.loads(Path(json.loads(med.stdout)["path"]).read_text(encoding="utf-8")) assert paa_disk["stop_reason"] == ""