fix: findings from the independent review
Scenario runner: stops when overridden base parameters leave the start out of balance (new field startlikevekt: krev, kalibrer, ubalanse); stops on non-finite results; says that models with a court queue drift slowly even without a shock and prints the drift before the shock; queue in months of inflow; never deletes files it did not write; stricter limits. Check: anchored on the note's own checksums for core and reference files, runs the scenario files too, exact comparison for the diagnosis, honest wording when parts were skipped or an extension is broken. Launcher: unknown field names in input are rejected with a suggestion; unwritable folders give a message, not a traceback. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
parent
2669759f5a
commit
776325c680
14 changed files with 1524 additions and 199 deletions
|
|
@ -3,16 +3,21 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import kjor as kjor_modul
|
||||
import pytest
|
||||
from conftest import DOMSTOL, EKSEMPLER, FASIT, MODELL, NORGE, ROT, fasit, kjor, lag_venv_omvei
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
|
||||
UTEN_SITE = [sys.executable, "-S"] # samme Python uten site-packages: numpy og scipy mangler
|
||||
|
||||
|
||||
|
|
@ -119,6 +124,74 @@ def test_ingen_pycache_i_kjernen(kopi: Path) -> None:
|
|||
assert not (kopi / "modell" / "kjerne" / "__pycache__").exists()
|
||||
|
||||
|
||||
# ---------------- ukjente feltnavn (funn 5) ----------------
|
||||
|
||||
def skriv_variant(tmp_path: Path, endre: Callable[[dict[str, Any]], None], navn: str = "variant.json") -> Path:
|
||||
"""En kopi av eksempel_domstol.json med en endring."""
|
||||
data = json.loads(DOMSTOL.read_text(encoding="utf-8"))
|
||||
endre(data)
|
||||
fil = tmp_path / navn
|
||||
fil.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
|
||||
return fil
|
||||
|
||||
|
||||
def skrivefeil_i_ledd(data: dict[str, Any]) -> None:
|
||||
domstol = data["ledd"][2]
|
||||
domstol["prognose_innstrømning"] = domstol.pop("prognose_innstromning")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("kommando", ["diagnose", "forlop"])
|
||||
def test_skrivefeil_i_ledd_avvises(kommando: str, tmp_path: Path) -> None:
|
||||
fil = skriv_variant(tmp_path, skrivefeil_i_ledd)
|
||||
prosess = kjor(kommando, str(fil))
|
||||
assert prosess.returncode == 2
|
||||
assert prosess.stdout == ""
|
||||
assert prosess.stderr.startswith(
|
||||
'Feil i inndata, ledd 3 (Domstol): feltet "prognose_innstrømning" finnes ikke. '
|
||||
'Mente du "prognose_innstromning"? Gyldige felt: navn, innstromning, kapasitet, restanse, maal_tid,')
|
||||
|
||||
|
||||
def test_skrivefeil_i_styring_avvises(tmp_path: Path) -> None:
|
||||
def endre(data: dict[str, Any]) -> None:
|
||||
data["styring"]["reaktivitett"] = data["styring"].pop("reaktivitet")
|
||||
|
||||
prosess = kjor("forlop", str(skriv_variant(tmp_path, endre)))
|
||||
assert prosess.returncode == 2
|
||||
assert prosess.stderr == ('Feil i inndata, styring: feltet "reaktivitett" finnes ikke. Mente du "reaktivitet"? '
|
||||
"Gyldige felt: forsinkelse, reaktivitet, merknad, kilde, kilder.\n")
|
||||
|
||||
|
||||
def test_merknadsfelt_og_ukjente_toppfelt_er_tillatt(tmp_path: Path) -> None:
|
||||
def endre(data: dict[str, Any]) -> None:
|
||||
data["ledd"][2].update(merknad="fri tekst", kilde="SSB", kilder=["a", "b"])
|
||||
data["styring"]["merknad"] = "regime A"
|
||||
data["noe_annet"] = {"fritt": True}
|
||||
|
||||
prosess = kjor("diagnose", str(skriv_variant(tmp_path, endre)))
|
||||
assert prosess.returncode == 0, prosess.stderr
|
||||
assert prosess.stdout == fasit("diagnose_eksempel_domstol.txt")
|
||||
|
||||
|
||||
def test_feltene_leses_fra_kjernen_i_modellen(kopi: Path, tmp_path: Path) -> None:
|
||||
diagnose = kopi / "modell" / "kjerne" / "diagnose.py"
|
||||
tekst = diagnose.read_text(encoding="utf-8")
|
||||
assert '"innstromning_for_okning")' in tekst
|
||||
diagnose.write_text(tekst.replace('"innstromning_for_okning")', '"innstromning_for_okning", "nytt_felt")', 1),
|
||||
encoding="utf-8")
|
||||
fil = skriv_variant(tmp_path, lambda data: data["ledd"][0].update(nytt_felt=1))
|
||||
assert kjor("diagnose", str(fil)).returncode == 2
|
||||
prosess = kjor("--modell", str(kopi), "diagnose", str(fil))
|
||||
assert prosess.returncode == 0, prosess.stderr
|
||||
|
||||
|
||||
def test_feil_form_overlates_til_kjernen(tmp_path: Path) -> None:
|
||||
fil = tmp_path / "feil.json"
|
||||
fil.write_text('{"ledd": [1, {"navn": "A"}]}', encoding="utf-8")
|
||||
prosess = kjor("diagnose", str(fil))
|
||||
assert prosess.returncode == 2
|
||||
assert prosess.stderr == "Feil i inndata, ledd 1: hvert ledd må være et objekt med felt og verdier.\n"
|
||||
|
||||
|
||||
# ---------------- --data og valg av Python ----------------
|
||||
|
||||
@pytest.mark.parametrize("verdi", [None, "", " ", "${CLAUDE_PLUGIN_DATA}", "/a/${CLAUDE_PLUGIN_DATA}/b"])
|
||||
|
|
@ -229,6 +302,18 @@ def test_velg_tolk(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|||
assert kjor_modul.velg_tolk(tmp_path).hvor == "mangler"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("system", "start", "sitert"), [
|
||||
("nt", "python ", '"/data med mellomrom"'), ("posix", "python3 ", "'/data med mellomrom'"),
|
||||
])
|
||||
def test_foreslatt_kommando_per_system(system: str, start: str, sitert: str) -> None:
|
||||
valg = kjor_modul.Valg(argv=[], data=Path("/data med mellomrom"), data_oppgitt=True, modell=MODELL,
|
||||
kommando="forlop", args=[])
|
||||
tekst = kjor_modul.kommando_tekst(valg, "miljo", "--opprett", system=system)
|
||||
assert tekst.startswith(start)
|
||||
assert f"--data {sitert} miljo --opprett" in tekst
|
||||
assert kjor_modul.python_navn(system) == start.strip()
|
||||
|
||||
|
||||
# ---------------- miljo --opprett (uten å installere) ----------------
|
||||
|
||||
def test_opprett_proever_laaste_versjoner_forst(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
|
|
@ -302,6 +387,7 @@ def test_modell_i_arbeidskopi(kopi: Path, undermappe: str) -> None:
|
|||
|
||||
@pytest.mark.parametrize("kommando", ["scenario", "arbeidskopi"])
|
||||
def test_modul_som_ikke_finnes_ennaa(kopi: Path, kommando: str) -> None:
|
||||
(kopi / "modell" / f"{kommando}.py").unlink()
|
||||
prosess = kjor("--modell", str(kopi), kommando, "x")
|
||||
assert prosess.returncode == 2
|
||||
assert f"Kommandoen «{kommando}» er ikke på plass i denne modellen ennå" in prosess.stderr
|
||||
|
|
@ -359,6 +445,29 @@ def test_notatet_nekter_ut_mappe_i_modellen() -> None:
|
|||
assert not (MODELL / "kjerne" / "data").exists()
|
||||
|
||||
|
||||
@pytest.mark.skipif(os.name == "nt" or os.geteuid() == 0, reason="skrivebeskyttelse virker ikke for root")
|
||||
def test_notatet_i_mappe_som_ikke_kan_skrives(tmp_path: Path) -> None:
|
||||
sperret = tmp_path / "sperret"
|
||||
sperret.mkdir()
|
||||
sperret.chmod(0o555)
|
||||
try:
|
||||
prosess = kjor("notatet", "--ut", str(sperret / "ut"))
|
||||
finally:
|
||||
sperret.chmod(0o755)
|
||||
assert prosess.returncode == 2
|
||||
assert prosess.stderr.startswith(f"Kan ikke skrive i {(sperret / 'ut').resolve()}: ")
|
||||
assert prosess.stderr.endswith(". Velg en annen mappe med --ut.\n")
|
||||
assert "Traceback" not in prosess.stderr
|
||||
|
||||
|
||||
def test_notatet_naar_sim_stopper(kopi: Path, tmp_path: Path) -> None:
|
||||
(kopi / "modell" / "kjerne" / "sim.py").write_text("print('start')\nraise SystemExit(5)\n", encoding="utf-8")
|
||||
prosess = kjor("--modell", str(kopi), "notatet", "--ut", str(tmp_path / "ut"))
|
||||
assert prosess.returncode == 2
|
||||
assert "Kjøringen av sim.py stoppet underveis (exit-kode 5)" in prosess.stderr
|
||||
assert "start\n" in prosess.stdout
|
||||
|
||||
|
||||
@pytest.mark.treg
|
||||
def test_notatet_gjenskaper_datafilene(tmp_path: Path) -> None:
|
||||
ut = tmp_path / "ut"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue