dynamisk-rettsstat/tests/test_arbeidskopi.py
Kjell Tore Guttormsen 776325c680
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>
2026-11-11 15:27:14 +01:00

128 lines
5.5 KiB
Python

# Copyright (c) 2026 Kjell Tore Guttormsen. MIT License, see LICENSE.
"""arbeidskopi.py: kopien lages, nekter en mappe som ikke er tom, og kan kjøres med --modell."""
from __future__ import annotations
from typing import TYPE_CHECKING
import arbeidskopi as arbeidskopi_modul
import pytest
from conftest import EKSEMPLER, FASIT, ROT, kjor
if TYPE_CHECKING:
from pathlib import Path
KJEDE_S2 = EKSEMPLER / "scenarioer" / "notatet" / "kjede_s2.json"
@pytest.fixture
def arbeidskopi(tmp_path: Path) -> Path:
"""En arbeidskopi laget med kjor.py arbeidskopi."""
mal = tmp_path / "rettsstat-modell"
prosess = kjor("arbeidskopi", str(mal))
assert prosess.returncode == 0, prosess.stderr
assert f"Arbeidskopien er laget i {mal}" in prosess.stdout
return mal
def test_kopien_har_mappene_og_les_meg(arbeidskopi: Path) -> None:
for navn in ("modell", "eksempler", "fasit", "tests"):
assert (arbeidskopi / navn).is_dir(), navn
for navn in ("modell/kjor.py", "modell/scenario.py", "modell/kjerne/sim.py", "modell/kjerne/KILDE.json",
"modell/utvidelser/paadrag_tidsserie.py", "fasit/data/p3.dat",
"eksempler/scenarioer/notatet/p3.json"):
assert (arbeidskopi / navn).is_file(), navn
for navn in ("modell/kjerne/sim.py", "fasit/data/endo_ko.dat"):
assert (arbeidskopi / navn).read_bytes() == (ROT / navn).read_bytes()
if (ROT / "docs").is_dir():
assert sorted(p.name for p in (arbeidskopi / "docs").iterdir()) == \
sorted(p.name for p in (ROT / "docs").iterdir() if p.is_file())
les_meg = (arbeidskopi / "LES-MEG.md").read_text(encoding="utf-8")
for tekst in ("Alt i mappa kan endres, også kjernen", f"python3 {arbeidskopi / 'modell' / 'kjor.py'} scenario",
"kontroll", "«bit for bit som notatet»", "«endret: ...»", "docs/", "BIDRA.md"):
assert tekst in les_meg, tekst
def test_kopien_har_ikke_mellomlagre_eller_resultater(arbeidskopi: Path) -> None:
uonsket = {".venv", "venv", "__pycache__", ".pytest_cache", ".mypy_cache", ".ruff_cache", "resultater"}
funnet = [p for p in arbeidskopi.rglob("*") if p.name in uonsket or p.suffix == ".pyc"]
assert funnet == []
def test_nekter_mappe_som_ikke_er_tom(tmp_path: Path) -> None:
mal = tmp_path / "opptatt"
mal.mkdir()
(mal / "notat.txt").write_text("mitt", encoding="utf-8")
prosess = kjor("arbeidskopi", str(mal))
assert prosess.returncode == 2
assert "finnes og er ikke tom" in prosess.stderr
assert sorted(p.name for p in mal.iterdir()) == ["notat.txt"]
def test_tom_mappe_er_greit(tmp_path: Path) -> None:
mal = tmp_path / "tom"
mal.mkdir()
assert kjor("arbeidskopi", str(mal)).returncode == 0
assert (mal / "LES-MEG.md").is_file()
@pytest.mark.parametrize("args", [[], ["a", "b"]])
def test_feil_bruk(args: list[str]) -> None:
prosess = kjor("arbeidskopi", *args)
assert prosess.returncode == 2
assert "arbeidskopi <mappe>" in prosess.stderr
def test_nekter_mappe_inne_i_det_som_kopieres(tmp_path: Path, arbeidskopi: Path) -> None:
mal = arbeidskopi / "eksempler" / "ny"
prosess = kjor("--modell", str(arbeidskopi), "arbeidskopi", str(mal))
assert prosess.returncode == 2, prosess.stdout
assert "ligger inne i" in prosess.stderr
assert not mal.exists()
assert not (tmp_path / "ny").exists()
def test_scenario_kjorer_fra_kopien_med_modell(arbeidskopi: Path, tmp_path: Path) -> None:
ut = tmp_path / "ut"
prosess = kjor("--modell", str(arbeidskopi), "scenario", str(KJEDE_S2), "--ut", str(ut))
assert prosess.returncode == 0, prosess.stderr
assert (ut / "kjede_s2.dat").read_bytes() == (FASIT / "data" / "kjede_s2.dat").read_bytes()
def test_endring_i_kopiens_kjerne_brukes(arbeidskopi: Path, tmp_path: Path) -> None:
sim = arbeidskopi / "modell" / "kjerne" / "sim.py"
tekst = sim.read_text(encoding="utf-8")
assert "shock=1.30," in tekst
sim.write_text(tekst.replace("shock=1.30,", "shock=1.40,", 1), encoding="utf-8")
ut = tmp_path / "ut"
prosess = kjor("--modell", str(arbeidskopi), "scenario", str(KJEDE_S2), "--ut", str(ut))
assert prosess.returncode == 0, prosess.stderr
assert (ut / "kjede_s2.dat").read_bytes() != (FASIT / "data" / "kjede_s2.dat").read_bytes()
plugin = kjor("scenario", str(KJEDE_S2), "--ut", str(tmp_path / "plugin"))
assert plugin.returncode == 0, plugin.stderr
assert (tmp_path / "plugin" / "kjede_s2.dat").read_bytes() == (FASIT / "data" / "kjede_s2.dat").read_bytes()
def test_nekter_mappe_inne_i_pluginen(tmp_path: Path) -> None:
mal = ROT / "nyttsted-test-arbeidskopi"
assert not mal.exists()
prosess = kjor("arbeidskopi", str(mal))
assert prosess.returncode == 2, prosess.stdout
assert "ligger inne i" in prosess.stderr
assert not mal.exists()
assert not (tmp_path / "x").exists()
def test_lisenssetningen(arbeidskopi: Path) -> None:
assert "Alt i pluginen er under MIT-lisensen (se `LICENSE`)." in (arbeidskopi / "LES-MEG.md").read_text(
encoding="utf-8")
def test_kommandoene_skrives_for_windows(tmp_path: Path) -> None:
mal = tmp_path / "Kari Nordmann" / "rettsstat-modell"
tekst = arbeidskopi_modul.les_meg(mal, [], windows=True)
assert f'python "{mal / "modell" / "kjor.py"}" scenario' in tekst
assert "python3" not in tekst
assert "'" not in tekst.split("## Slik kjører du kopien", 1)[1].split("## Slik kontrollerer", 1)[0]
posix = arbeidskopi_modul.les_meg(mal, [], windows=False)
assert f"python3 '{mal / 'modell' / 'kjor.py'}' scenario" in posix