232 lines
7.7 KiB
Python
232 lines
7.7 KiB
Python
"""Die Planung: eine Arbeitskopie, die den Projektstand nicht anfasst.
|
|
|
|
Geprüft wird die eine Zusage — solange die Planung läuft, bleibt die Übersicht,
|
|
was sie war. Und dass sie einen Neustart übersteht, weil sie auf der Platte liegt.
|
|
"""
|
|
|
|
import features
|
|
import parser
|
|
import planung
|
|
import projekte
|
|
import pytest
|
|
|
|
|
|
def karte(projekt: str, titel: str, *funktionen: str) -> str:
|
|
roh = f"<!-- titel -->\n{titel}\n" + "".join(
|
|
f"{'-' * 40}\n<!-- funktion -->\n{f}\n<!-- regeln -->\n- Etwas gilt\n"
|
|
for f in funktionen)
|
|
return features.anlegen(projekt, roh)["id"]
|
|
|
|
|
|
def titel(projekt: str) -> list:
|
|
return sorted(f["sektionen"]["titel"] for f in features.liste(projekt))
|
|
|
|
|
|
@pytest.fixture
|
|
def projekt(datenordner):
|
|
projekte.anlegen("P")
|
|
karte("P", "Galerie", "Bild wählen")
|
|
karte("P", "Shop", "Karte kaufen")
|
|
return "P"
|
|
|
|
|
|
def test_ohne_planung_arbeitet_alles_im_projektordner(projekt):
|
|
assert not planung.laeuft(projekt)
|
|
assert projekte.wurzel(projekt) == projekte.stand(projekt)
|
|
assert planung.aenderungen(projekt) == {}
|
|
|
|
|
|
def test_planung_leitet_das_schreiben_um(projekt):
|
|
planung.starte(projekt)
|
|
assert projekte.wurzel(projekt) != projekte.stand(projekt)
|
|
|
|
karte(projekt, "Neu", "Etwas tun")
|
|
# Die Karte steht in der Kopie …
|
|
assert "Neu" in titel(projekt)
|
|
# … und nicht im Projektstand
|
|
assert not (projekte.stand(projekt) / "neu.md").exists()
|
|
|
|
|
|
def test_zweite_planung_wird_abgelehnt(projekt):
|
|
planung.starte(projekt)
|
|
with pytest.raises(ValueError):
|
|
planung.starte(projekt)
|
|
|
|
|
|
def test_planung_ueberlebt_einen_neustart(projekt):
|
|
planung.starte(projekt)
|
|
karte(projekt, "Neu", "Etwas tun")
|
|
# Nichts im Speicher — der nächste Prozess sieht nur die Platte
|
|
assert planung.laeuft(projekt)
|
|
assert "Neu" in planung.aenderungen(projekt)
|
|
|
|
|
|
def test_abbruch_laesst_den_projektstand_unberuehrt(projekt):
|
|
vorher = titel(projekt)
|
|
planung.starte(projekt)
|
|
karte(projekt, "Neu", "Etwas tun")
|
|
planung.brich_ab(projekt)
|
|
assert not planung.laeuft(projekt)
|
|
assert titel(projekt) == vorher
|
|
|
|
|
|
def test_umsetzen_macht_die_kopie_zum_stand(projekt):
|
|
planung.starte(projekt)
|
|
karte(projekt, "Neu", "Etwas tun")
|
|
planung.setze_um(projekt)
|
|
assert not planung.laeuft(projekt)
|
|
assert "Neu" in titel(projekt)
|
|
assert not (projekte.stand(projekt) / projekte.PLANUNG).exists()
|
|
|
|
|
|
def test_umsetzen_ohne_planung_ist_ein_fehler(projekt):
|
|
with pytest.raises(ValueError):
|
|
planung.setze_um(projekt)
|
|
|
|
|
|
# ---------------------------------------------------------------- Änderungen
|
|
|
|
def test_neue_karte_ist_neu(projekt):
|
|
planung.starte(projekt)
|
|
karte(projekt, "Neu", "Etwas tun")
|
|
a = planung.aenderungen(projekt)
|
|
assert a["Neu"]["art"] == "neu"
|
|
assert a["Neu"]["funktionen"] == {"Etwas tun": "neu"}
|
|
assert "Galerie" not in a
|
|
|
|
|
|
def test_entfernte_karte_ist_weg(projekt):
|
|
planung.starte(projekt)
|
|
features.loeschen(projekt, "shop")
|
|
a = planung.aenderungen(projekt)
|
|
assert a["Shop"]["art"] == "weg"
|
|
assert a["Shop"]["funktionen"] == {"Karte kaufen": "weg"}
|
|
|
|
|
|
def test_neue_funktion_wird_markiert(projekt):
|
|
planung.starte(projekt)
|
|
f = next(x for x in features.liste(projekt) if x["sektionen"]["titel"] == "Galerie")
|
|
features.speichern(projekt, f["id"],
|
|
f["roh"] + f"{'-' * 40}\n<!-- funktion -->\nBild löschen\n")
|
|
a = planung.aenderungen(projekt)
|
|
assert a["Galerie"]["art"] == "geaendert"
|
|
assert a["Galerie"]["funktionen"] == {"Bild löschen": "neu"}
|
|
|
|
|
|
def test_geaenderte_regel_wird_markiert(projekt):
|
|
planung.starte(projekt)
|
|
f = next(x for x in features.liste(projekt) if x["sektionen"]["titel"] == "Galerie")
|
|
features.speichern(projekt, f["id"], f["roh"].replace("Etwas gilt", "Anderes gilt"))
|
|
assert planung.aenderungen(projekt)["Galerie"]["funktionen"] == {
|
|
"Bild wählen": "geaendert"}
|
|
|
|
|
|
def test_umbenannte_karte_ist_neu_und_weg(projekt):
|
|
"""Verglichen wird über den Titel. Eine Umbenennung ist zwei Änderungen."""
|
|
planung.starte(projekt)
|
|
f = next(x for x in features.liste(projekt) if x["sektionen"]["titel"] == "Shop")
|
|
features.speichern(projekt, f["id"], f["roh"].replace("Shop", "Laden", 1))
|
|
a = planung.aenderungen(projekt)
|
|
assert a["Shop"]["art"] == "weg" and a["Laden"]["art"] == "neu"
|
|
|
|
|
|
def test_gleicher_inhalt_ist_keine_aenderung(projekt):
|
|
planung.starte(projekt)
|
|
f = features.liste(projekt)[0]
|
|
features.speichern(projekt, f["id"], f["roh"])
|
|
assert planung.aenderungen(projekt) == {}
|
|
|
|
|
|
# ---------------------------------------------------------------- Zurücksetzen
|
|
|
|
def test_zuruecksetzen_loescht_eine_neue_karte(projekt):
|
|
planung.starte(projekt)
|
|
karte(projekt, "Neu", "Etwas tun")
|
|
planung.zuruecksetzen(projekt, "Neu")
|
|
assert planung.aenderungen(projekt) == {}
|
|
assert "Neu" not in titel(projekt)
|
|
|
|
|
|
def test_zuruecksetzen_holt_eine_geloeschte_karte_zurueck(projekt):
|
|
planung.starte(projekt)
|
|
features.loeschen(projekt, "shop")
|
|
planung.zuruecksetzen(projekt, "Shop")
|
|
assert planung.aenderungen(projekt) == {}
|
|
assert "Shop" in titel(projekt)
|
|
|
|
|
|
def test_zuruecksetzen_stellt_den_alten_inhalt_her(projekt):
|
|
planung.starte(projekt)
|
|
f = next(x for x in features.liste(projekt) if x["sektionen"]["titel"] == "Galerie")
|
|
features.speichern(projekt, f["id"], f["roh"].replace("Etwas gilt", "Anderes gilt"))
|
|
planung.zuruecksetzen(projekt, "Galerie")
|
|
assert planung.aenderungen(projekt) == {}
|
|
|
|
|
|
def test_zuruecksetzen_laesst_die_uebrigen_stehen(projekt):
|
|
planung.starte(projekt)
|
|
karte(projekt, "Eins", "A tun")
|
|
karte(projekt, "Zwei", "B tun")
|
|
planung.zuruecksetzen(projekt, "Eins")
|
|
assert set(planung.aenderungen(projekt)) == {"Zwei"}
|
|
|
|
|
|
def test_zuruecksetzen_ohne_planung_ist_ein_fehler(projekt):
|
|
with pytest.raises(ValueError):
|
|
planung.zuruecksetzen(projekt, "Galerie")
|
|
|
|
|
|
def test_unbekannte_karte_zuruecksetzen_meldet_es(projekt):
|
|
planung.starte(projekt)
|
|
with pytest.raises(FileNotFoundError):
|
|
planung.zuruecksetzen(projekt, "Gibt es nicht")
|
|
|
|
|
|
# ---------------------------------------------------------------- Nachbarschaft
|
|
|
|
def test_die_kopie_ist_kein_eigenes_projekt(projekt):
|
|
planung.starte(projekt)
|
|
assert [p["name"] for p in projekte.liste()] == ["P"]
|
|
|
|
|
|
def test_gespraech_und_gedaechtnis_wandern_mit(projekt):
|
|
"""Was während des Planens beurteilt wurde, gilt für den geplanten Stand."""
|
|
(projekte.stand(projekt) / ".pruefung.json").write_text('{"stil": {}}')
|
|
planung.starte(projekt)
|
|
assert (projekte.wurzel(projekt) / ".pruefung.json").is_file()
|
|
|
|
|
|
def test_die_kopie_kopiert_sich_nicht_selbst(projekt):
|
|
planung.starte(projekt)
|
|
assert not (projekte.wurzel(projekt) / projekte.PLANUNG).exists()
|
|
|
|
|
|
def test_arbeitsverzeichnis_ueberlebt_das_umsetzen(projekt, tmp_path):
|
|
"""Erst nach dem Planungsstart festgelegt — dann fehlt es in der Kopie.
|
|
|
|
Vorher räumte das Umsetzen es mit dem alten Stand weg, und der nächste Lauf
|
|
fand kein Verzeichnis mehr.
|
|
"""
|
|
import verzeichnis
|
|
planung.starte(projekt)
|
|
verzeichnis.setze(projekt, str(tmp_path))
|
|
assert not (planung._kopie(projekt) / verzeichnis.DATEI).exists()
|
|
|
|
planung.setze_um(projekt)
|
|
assert verzeichnis.lies(projekt) == str(tmp_path)
|
|
|
|
|
|
def test_geplantes_verzeichnis_gewinnt(projekt, tmp_path):
|
|
"""Steht es in beiden, gilt die Kopie — sie ist der neuere Stand."""
|
|
import verzeichnis
|
|
alt = tmp_path / "alt"
|
|
alt.mkdir()
|
|
verzeichnis.setze(projekt, str(alt))
|
|
planung.starte(projekt)
|
|
neu = tmp_path / "neu"
|
|
neu.mkdir()
|
|
(planung._kopie(projekt) / verzeichnis.DATEI).write_text(str(neu))
|
|
|
|
planung.setze_um(projekt)
|
|
assert verzeichnis.lies(projekt) == str(neu)
|