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>
128 lines
5.5 KiB
Python
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
|