Trading-Plattform-Pruefung: Host nicht erreichbar gibt eine Zeile, --tag frueh geprueft (Paket NT, Task 729)
- abendkontrolle.py: Exit 255 oder Zeitlimit bei Summe oder Bestaetigung beendet die Sicherung mit genau einer Zeile in der Notiz von heute und Exit 1; ein Abbruch im Archiv laesst nur die Instanz scheitern. --tag ausser der Form endet gleich mit Exit 2, ohne Notiz und ssh. - abendkontrolle_dockerhost_angriffe.py: sieben neue Faelle, zwei angepasste, Mutanten E729-01 bis E729-15 und E729-25 (38 Faelle, 56 Mutanten, keiner ueberlebt). - abendkontrolle_wsl.py: Kopf nennt die neuen fehler-Gruende. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_0126RDfXiECscXipzf67uypJ
This commit is contained in:
1 parent
02ff0e878d
commit
88ec4fd40f
3 files changed
+355
-28
No files matched your search
@@ -15,6 +15,9 @@ gegen eine Host-Ablage im Wegwerf-Ordner; ANGRIFF_SICHERUNG stoert sie gezielt.
|
||||
lauf() setzt SICHERUNG_ERSTER_TAG auf None und SICHERUNG_ZIEL in den Wegwerf-Ordner;
|
||||
sicherungslauf() lenkt mit --sicherung-ziel um, und das Ziel der Konstante muss dort leer bleiben.
|
||||
Nichts landet unter D:\\Trading-Sicherung.
|
||||
Seit Paket NT (Quorum 4): ANGRIFF_HOST_WEG=<n> laesst die Attrappe jeden Aufruf nach den ersten n mit
|
||||
Exit 255 enden wie ssh ohne Host; ANGRIFF_SICHERUNG=langsam haelt jeden Sicherungsbefehl 5 s auf,
|
||||
ANGRIFF_SICHERUNG=archiv_unlesbar endet beim Archiv wie abendkontrolle-lesen.sh ohne Leserecht.
|
||||
Aufruf: python abendkontrolle_dockerhost_angriffe.py [--mutanten]
|
||||
Exit 0 ohne Abweichung, 2 mit Abweichung. --mutanten faehrt MUTANTEN auf Kopien von
|
||||
abendkontrolle.py und MUTANTEN_AUFGABE auf Kopien von abendkontrolle_einrichten.ps1: Jeder genannte
|
||||
@@ -47,6 +50,13 @@ import sys
|
||||
|
||||
with open(os.environ["ANGRIFF_PROTOKOLL"], "a", encoding="utf-8") as datei:
|
||||
datei.write(json.dumps(sys.argv[1:]) + "\n")
|
||||
# Seit Paket NT: Nach ANGRIFF_HOST_WEG Aufrufen ist der Host weg; ssh selbst endet dann mit 255.
|
||||
weg = os.environ.get("ANGRIFF_HOST_WEG", "")
|
||||
if weg:
|
||||
with open(os.environ["ANGRIFF_PROTOKOLL"], encoding="utf-8") as datei:
|
||||
if len(datei.read().splitlines()) > int(weg):
|
||||
sys.stderr.write("ssh: connect to host docker-host port 2498: Connection timed out\n")
|
||||
sys.exit(255)
|
||||
befehl = sys.argv[-1]
|
||||
if befehl.startswith("sicherung"):
|
||||
# Seit Paket E9: die Lesebefehle der Sicherung, so streng wie abendkontrolle-lesen.sh (Task 724),
|
||||
@@ -68,6 +78,11 @@ if befehl.startswith("sicherung"):
|
||||
sys.exit(2)
|
||||
name = lesen.group(2) if lesen else bestaetigen.group(1)
|
||||
gestoert = stoerung if not nur or name.endswith("-" + nur) else ""
|
||||
if gestoert == "langsam":
|
||||
# Seit Paket NT: laenger als das Zeitlimit, das der Fall in seiner Kopie setzt.
|
||||
import time
|
||||
|
||||
time.sleep(5)
|
||||
archiv = os.path.join(host, name + ".tar")
|
||||
summendatei = archiv + ".sha256"
|
||||
gemeint = archiv if lesen and lesen.group(1) == "sicherung" else summendatei
|
||||
@@ -84,6 +99,10 @@ if befehl.startswith("sicherung"):
|
||||
sys.stdout.buffer.write(inhalt)
|
||||
sys.exit(0)
|
||||
if lesen:
|
||||
if gestoert == "archiv_unlesbar":
|
||||
# Seit Paket NT: wie abendkontrolle-lesen.sh bei einem Archiv ohne Leserecht (Exit 1).
|
||||
sys.stderr.write("abendkontrolle-lesen: " + name + ".tar liegt auf dem Docker-Host, laesst sich aber nicht lesen\n")
|
||||
sys.exit(1)
|
||||
if gestoert == "kaputt":
|
||||
inhalt = inhalt[:-1] + bytes([inhalt[-1] ^ 1])
|
||||
if gestoert == "abbruch":
|
||||
@@ -168,6 +187,7 @@ def lauf(art, *argumente, modul=None):
|
||||
modul.SICHERUNG_ZIEL = wurzel / "sicherung-ziel"
|
||||
os.environ.pop("ANGRIFF_HOST", None)
|
||||
os.environ.pop("ANGRIFF_SICHERUNG", None)
|
||||
os.environ.pop("ANGRIFF_HOST_WEG", None)
|
||||
unerwartet = []
|
||||
if not getattr(modul.fahre_wsl, "attrappe", False):
|
||||
|
||||
@@ -410,7 +430,7 @@ def namen(ordner):
|
||||
return sorted(p.name for p in ordner.iterdir())
|
||||
|
||||
|
||||
def sicherungslauf(wurzel, *argumente, modul=None, stoerung="", erster_tag=date(2026, 10, 5), schalter=True):
|
||||
def sicherungslauf(wurzel, *argumente, modul=None, stoerung="", erster_tag=date(2026, 10, 5), schalter=True, host_weg=""):
|
||||
"""Ein Lauf von main() mit Quelle dockerhost und Sicherung, alles unter wurzel.
|
||||
|
||||
Vault, Host-Ablage (host_ablage) und Ziel bleiben stehen, damit ein zweiter Lauf daran
|
||||
@@ -446,6 +466,8 @@ def sicherungslauf(wurzel, *argumente, modul=None, stoerung="", erster_tag=date(
|
||||
os.environ["ANGRIFF_ART"] = "gut"
|
||||
os.environ["ANGRIFF_HOST"] = str(wurzel / "host")
|
||||
os.environ["ANGRIFF_SICHERUNG"] = stoerung
|
||||
# Seit Paket NT: "" heisst, der Host bleibt erreichbar.
|
||||
os.environ["ANGRIFF_HOST_WEG"] = host_weg
|
||||
vorne = ["--vault", str(vault), "--quelle", "dockerhost"]
|
||||
if schalter:
|
||||
vorne += ["--sicherung-ziel", str(wurzel / "ziel")]
|
||||
@@ -529,6 +551,9 @@ def eine_falsche_summe_wird_nie_abgelegt_und_nie_bestaetigt():
|
||||
|
||||
@fall
|
||||
def eine_abgebrochene_uebertragung_hinterlaesst_nichts():
|
||||
# Seit Paket NT (Plankritik, Befund 1): Exit 255 mitten im Archiv heisst nicht, dass der Host fehlt;
|
||||
# er hat die Summe gerade beantwortet. Nur die kaputte Instanz scheitert, mit dem Befehl im Grund,
|
||||
# die andere wird geholt. Keine Nachbardatei bleibt, die kaputte Instanz ist nicht bestaetigt.
|
||||
for kaputt, heil in (("alpaca", "ibkr"), ("ibkr", "alpaca")):
|
||||
with tempfile.TemporaryDirectory() as ordner:
|
||||
wurzel = Path(ordner)
|
||||
@@ -541,8 +566,13 @@ def eine_abgebrochene_uebertragung_hinterlaesst_nichts():
|
||||
assert namen(wurzel / "ziel" / TAG) == [f"{heil}.tar", f"{heil}.tar.sha256"], namen(wurzel / "ziel" / TAG)
|
||||
assert namen(host / "bestaetigt") == [f"{TAG}-{heil}"], namen(host / "bestaetigt")
|
||||
assert not any(b.startswith(f"sicherung-bestaetigt {TAG}-{kaputt} ") for b in befehle), befehle
|
||||
assert f"{kaputt} gescheitert (RuntimeError: Archiv nicht geholt (Exit 255;" in zeile, zeile
|
||||
assert "#needs-review" in zeile, zeile
|
||||
assert (
|
||||
f"{kaputt} gescheitert (RuntimeError: Archiv nicht geholt (sicherung {TAG}-{kaputt}: Exit 255; "
|
||||
"Connection reset by peer))"
|
||||
) in zeile, zeile
|
||||
assert f"{heil} abgelegt und bestätigt" in zeile, zeile
|
||||
assert "**gescheitert** #needs-review" in zeile, zeile
|
||||
assert "Docker-Host nicht erreichbar" not in zeile, zeile
|
||||
|
||||
|
||||
@fall
|
||||
@@ -929,16 +959,23 @@ def ohne_schalter_gilt_das_ziel_der_konstante():
|
||||
|
||||
|
||||
@fall
|
||||
def ein_tag_ausser_der_form_holt_keine_sicherung():
|
||||
for tag in ("2026-10-5", "2026-13-05", "20261005", "../2026-10-05"):
|
||||
def ein_tag_ausser_der_form_endet_mit_zwei_ohne_notiz_und_ohne_ssh():
|
||||
# Seit Paket NT (Quorum 4): main prueft --tag gleich nach dem Zerlegen. Vorher lief die
|
||||
# Abendkontrolle mit dem falschen Tag los und legte Inbox/<tag>.md an, mit ../ auch ausserhalb von
|
||||
# Inbox/; seit E9 hielt nur die Sicherung an (Exit 1). Jetzt endet der ganze Lauf vorher mit Exit 2.
|
||||
for tag in ("2026-10-5", "2026-13-05", "2026-02-30", "20261005", "x026-10-05", "2026-10-0x", "../2026-10-05", ""):
|
||||
with tempfile.TemporaryDirectory() as ordner:
|
||||
wurzel = Path(ordner)
|
||||
host_ablage(wurzel)
|
||||
code, notiz, befehle, _ = sicherungslauf(wurzel, "--tag", tag, "--jetzt", "2026-10-05T23:30")
|
||||
assert code == 1, (tag, code)
|
||||
assert befehle == [], (tag, befehle)
|
||||
fehler = io.StringIO()
|
||||
with contextlib.redirect_stderr(fehler):
|
||||
code, notiz, _, _ = sicherungslauf(wurzel, "--tag", tag, "--jetzt", "2026-10-05T23:30")
|
||||
assert code == 2, (tag, code)
|
||||
assert fehler.getvalue() == f"abendkontrolle: --tag {tag!a} ist kein Datum JJJJ-MM-TT\n", (tag, fehler.getvalue())
|
||||
assert namen(wurzel / "vault") == ["Inbox"], (tag, namen(wurzel / "vault"))
|
||||
assert namen(wurzel / "vault" / "Inbox") == [], (tag, notiz)
|
||||
assert not (wurzel / "ssh.jsonl").exists(), tag
|
||||
assert not (wurzel / "ziel").exists(), tag
|
||||
assert sicherungszeilen(notiz) == [], (tag, notiz)
|
||||
|
||||
|
||||
@fall
|
||||
@@ -958,6 +995,176 @@ def der_abendausloeser_steht_in_ortszeit_ohne_versatz():
|
||||
assert ausloeser[0] < zeilen.index(zeile) < eintragen[0], (ausloeser, zeilen.index(zeile), eintragen)
|
||||
|
||||
|
||||
# --- Seit Paket NT (Quorum 4): Host nicht erreichbar, --tag frueh, unlesbare Dateien --------------
|
||||
|
||||
HOST_WEG = "ssh: connect to host docker-host port 2498: Connection timed out"
|
||||
|
||||
|
||||
def ergebnisse_liegen(modul, wurzel):
|
||||
"""Die Ergebnisse beider Teile liegen fuer 2026-09-20 bis 2026-10-12: Der Lauf faehrt nur die Sicherung."""
|
||||
ablage = wurzel / "vault" / modul.ABLAGE_TEIL
|
||||
ablage.mkdir(parents=True, exist_ok=True)
|
||||
for tag in [f"2026-09-{t}" for t in range(20, 31)] + [f"2026-10-{t:02d}" for t in range(1, 13)]:
|
||||
(ablage / f"{tag}.json").write_text("{}", encoding="utf-8")
|
||||
(ablage / f"{tag}-ibkr.json").write_text("{}", encoding="utf-8")
|
||||
return ablage
|
||||
|
||||
|
||||
@fall
|
||||
def ein_unlesbares_archiv_auf_dem_host_scheitert_nur_fuer_seine_instanz():
|
||||
# Paket NT (Quorum 4, G2): abendkontrolle-lesen.sh endet bei einem Archiv ohne Leserecht mit Exit 1
|
||||
# und eigener Zeile. Das ist kein Ausfall des Hosts: nur diese Instanz scheitert, die andere wird
|
||||
# geholt. Seit ssh_lauf Exit 255 als HostNichtErreichbar meldet, erreicht ein Abbruch mitten im
|
||||
# Archiv die Pruefung des Exit-Codes nicht mehr; den Mutanten "Exit des Archivs egal" traegt
|
||||
# deshalb dieser Fall (vorher eine_abgebrochene_uebertragung_hinterlaesst_nichts).
|
||||
for kaputt, heil in (("alpaca", "ibkr"), ("ibkr", "alpaca")):
|
||||
with tempfile.TemporaryDirectory() as ordner:
|
||||
wurzel = Path(ordner)
|
||||
host = host_ablage(wurzel)
|
||||
code, notiz, _, _ = sicherungslauf(
|
||||
wurzel, "--tag", TAG, "--jetzt", "2026-10-05T23:30", stoerung=f"archiv_unlesbar:{kaputt}"
|
||||
)
|
||||
zeile = einzige_zeile(notiz)
|
||||
assert code == 1, code
|
||||
assert namen(wurzel / "ziel" / TAG) == [f"{heil}.tar", f"{heil}.tar.sha256"], namen(wurzel / "ziel" / TAG)
|
||||
assert namen(host / "bestaetigt") == [f"{TAG}-{heil}"], namen(host / "bestaetigt")
|
||||
assert (
|
||||
f"{kaputt} gescheitert (RuntimeError: Archiv nicht geholt (Exit 1; abendkontrolle-lesen: "
|
||||
f"{TAG}-{kaputt}.tar liegt auf dem Docker-Host, laesst sich aber nicht lesen)"
|
||||
) in zeile, zeile
|
||||
assert f"{heil} abgelegt und bestätigt" in zeile, zeile
|
||||
assert "Docker-Host nicht erreichbar" not in zeile, zeile
|
||||
|
||||
|
||||
@fall
|
||||
def ein_unerreichbarer_host_gibt_eine_zeile_von_heute_und_keine_weitere_notiz():
|
||||
# Paket NT (Quorum 4, G1): Vorher scheiterte bei einem Host ohne Verbindung jeder Tag des Fensters
|
||||
# mit eigener Zeile, und schreibe_notiz legte die fehlenden Notizen an, auch fuer Samstag und
|
||||
# Sonntag. Jetzt endet die Sicherung beim ersten Exit 255 mit genau einer Zeile von heute.
|
||||
with tempfile.TemporaryDirectory() as ordner:
|
||||
wurzel = Path(ordner)
|
||||
modul = lade()
|
||||
ablage = ergebnisse_liegen(modul, wurzel)
|
||||
code, notiz, befehle, _ = sicherungslauf(
|
||||
wurzel, "--jetzt", "2026-10-12T23:30", modul=modul, erster_tag=date(2026, 9, 1), host_weg="0"
|
||||
)
|
||||
assert code == 1, (code, notiz)
|
||||
assert namen(wurzel / "vault" / "Inbox") == ["2026-10-12.md"], namen(wurzel / "vault" / "Inbox")
|
||||
assert einzige_zeile(notiz) == (
|
||||
f"- 23:30 Uhr — {KOPF}: **gescheitert** #needs-review. Docker-Host nicht erreichbar "
|
||||
f"(sicherung-summe 2026-10-02-alpaca: Exit 255; {HOST_WEG}), Sicherung abgebrochen. "
|
||||
"Der nächste Lauf versucht es erneut."
|
||||
), notiz
|
||||
assert befehle == ["sicherung-summe 2026-10-02-alpaca"], befehle
|
||||
assert not (wurzel / "ziel").exists()
|
||||
log = (ablage / "lauf.log").read_text(encoding="utf-8")
|
||||
assert "sicherung 2026-10-02-alpaca: Docker-Host nicht erreichbar (" in log, log
|
||||
|
||||
|
||||
@fall
|
||||
def ein_host_der_mitten_im_lauf_wegfaellt_laesst_die_zeilen_davor_stehen():
|
||||
# Paket NT (Quorum 4, G1): Fertige Tage behalten ihre Zeilen in ihren Notizen; was ein angefangener
|
||||
# Tag schon hat, nennt die eine Zeile von heute. Je Tag mit Sicherung sechs Befehle (Summe, Archiv,
|
||||
# Bestaetigung je Instanz); nach weg Befehlen ist der Host weg. Am Samstag 2026-10-03 liegt keine
|
||||
# Sicherung, gefragt wird trotzdem.
|
||||
for weg, notizen, zuletzt, bis_dahin in (
|
||||
("12", ["2026-10-01.md", "2026-10-02.md", "2026-10-05.md"], "sicherung-summe 2026-10-03-alpaca", ""),
|
||||
("9", ["2026-10-01.md", "2026-10-05.md"], "sicherung-summe 2026-10-02-ibkr",
|
||||
" Bis dahin am 2026-10-02: alpaca abgelegt und bestätigt ("),
|
||||
):
|
||||
with tempfile.TemporaryDirectory() as ordner:
|
||||
wurzel = Path(ordner)
|
||||
host_ablage(wurzel, tage=("2026-10-01", "2026-10-02", "2026-10-05"))
|
||||
modul = lade()
|
||||
ergebnisse_liegen(modul, wurzel)
|
||||
code, notiz, befehle, _ = sicherungslauf(
|
||||
wurzel, "--jetzt", "2026-10-05T23:30", modul=modul, erster_tag=date(2026, 10, 1), host_weg=weg
|
||||
)
|
||||
zeilen = sicherungszeilen(notiz)
|
||||
assert code == 1, (weg, code)
|
||||
assert namen(wurzel / "vault" / "Inbox") == notizen, (weg, namen(wurzel / "vault" / "Inbox"))
|
||||
assert befehle[-1] == zuletzt and len(befehle) == int(weg) + 1, (weg, befehle)
|
||||
assert namen(wurzel / "ziel") == ["2026-10-01", "2026-10-02"], (weg, namen(wurzel / "ziel"))
|
||||
assert len(zeilen) == len(notizen), (weg, zeilen)
|
||||
for zeile in zeilen[:-1]:
|
||||
assert "alpaca abgelegt und bestätigt" in zeile and "ibkr abgelegt und bestätigt" in zeile, zeile
|
||||
assert zeilen[-1].startswith(
|
||||
f"- 23:30 Uhr — {KOPF}: **gescheitert** #needs-review. Docker-Host nicht erreichbar "
|
||||
f"({zuletzt}: Exit 255; {HOST_WEG}), Sicherung abgebrochen."
|
||||
), zeilen[-1]
|
||||
if bis_dahin:
|
||||
assert bis_dahin in zeilen[-1], zeilen[-1]
|
||||
else:
|
||||
assert "Bis dahin" not in zeilen[-1], zeilen[-1]
|
||||
assert zeilen[-1].endswith(" Der nächste Lauf versucht es erneut."), zeilen[-1]
|
||||
|
||||
|
||||
@fall
|
||||
def ein_zeitlimit_beim_host_zaehlt_wie_ein_unerreichbarer_host():
|
||||
# Paket NT (Quorum 4, G1): Laeuft die Frage nach der Summe in ihr Zeitlimit (SSH_TIMEOUT_S), gilt der
|
||||
# Host als nicht erreichbar. In der Kopie ist das Zeitlimit 1 s, die Attrappe braucht je
|
||||
# Sicherungsbefehl 5 s. Das Zeitlimit beim Archiv selbst: naechster Fall.
|
||||
with tempfile.TemporaryDirectory() as ordner:
|
||||
wurzel = Path(ordner)
|
||||
host_ablage(wurzel)
|
||||
modul = lade()
|
||||
ergebnisse_liegen(modul, wurzel)
|
||||
modul.SSH_TIMEOUT_S = 1
|
||||
code, notiz, befehle, _ = sicherungslauf(wurzel, "--jetzt", "2026-10-05T23:30", modul=modul, stoerung="langsam")
|
||||
assert code == 1, (code, notiz)
|
||||
assert namen(wurzel / "vault" / "Inbox") == ["2026-10-05.md"], namen(wurzel / "vault" / "Inbox")
|
||||
assert befehle == ["sicherung-summe 2026-10-05-alpaca"], befehle
|
||||
assert einzige_zeile(notiz) == (
|
||||
f"- 23:30 Uhr — {KOPF}: **gescheitert** #needs-review. Docker-Host nicht erreichbar "
|
||||
"(sicherung-summe 2026-10-05-alpaca: Zeitlimit 1 s abgelaufen), Sicherung abgebrochen. "
|
||||
"Der nächste Lauf versucht es erneut."
|
||||
), notiz
|
||||
assert not (wurzel / "ziel").exists()
|
||||
|
||||
|
||||
@fall
|
||||
def ein_zeitlimit_beim_archiv_laesst_nur_diese_instanz_scheitern():
|
||||
# Plankritik NT, Befund 1: Laeuft nur das Archiv in sein Zeitlimit (SICHERUNG_TIMEOUT_S), ist der Host
|
||||
# erreichbar; er hat die Summe gerade beantwortet. Die Instanz scheitert mit eigenem Grund, die
|
||||
# andere wird geholt, der naechste Tag auch. In der Kopie ist das Zeitlimit des Archivs 1 s, die
|
||||
# Attrappe braucht fuer jeden Befehl von alpaca 5 s; die Summe hat weiter SSH_TIMEOUT_S.
|
||||
tage = ("2026-10-01", "2026-10-02")
|
||||
with tempfile.TemporaryDirectory() as ordner:
|
||||
wurzel = Path(ordner)
|
||||
host = host_ablage(wurzel, tage=tage)
|
||||
modul = lade()
|
||||
ergebnisse_liegen(modul, wurzel)
|
||||
modul.SICHERUNG_TIMEOUT_S = 1
|
||||
code, notiz, befehle, _ = sicherungslauf(
|
||||
wurzel, "--jetzt", "2026-10-02T23:30", modul=modul, erster_tag=date(2026, 10, 1), stoerung="langsam:alpaca"
|
||||
)
|
||||
zeilen = sicherungszeilen(notiz)
|
||||
assert code == 1, (code, notiz)
|
||||
assert namen(wurzel / "vault" / "Inbox") == ["2026-10-01.md", "2026-10-02.md"], namen(wurzel / "vault" / "Inbox")
|
||||
assert namen(host / "bestaetigt") == [f"{t}-ibkr" for t in tage], namen(host / "bestaetigt")
|
||||
assert [b for b in befehle if b.startswith("sicherung ")] == [
|
||||
f"sicherung {t}-{i}" for t in tage for i in ("alpaca", "ibkr")
|
||||
], befehle
|
||||
assert len(zeilen) == 2, zeilen
|
||||
for tag, zeile in zip(tage, zeilen):
|
||||
assert namen(wurzel / "ziel" / tag) == ["ibkr.tar", "ibkr.tar.sha256"], (tag, namen(wurzel / "ziel" / tag))
|
||||
assert (
|
||||
"**gescheitert** #needs-review. alpaca gescheitert (RuntimeError: Archiv nicht geholt "
|
||||
f"(sicherung {tag}-alpaca: Zeitlimit 1 s abgelaufen)); ibkr abgelegt und bestätigt ("
|
||||
) in zeile, zeile
|
||||
assert "Docker-Host nicht erreichbar" not in zeile, zeile
|
||||
|
||||
|
||||
@fall
|
||||
def kein_kommentar_beginnt_mit_s3():
|
||||
# Paket NT (Quorum 4): Die Kommentarzeile ueber SICHERUNG_NACHHOLEN_TAGE begann mit "(S3).", einem
|
||||
# Verweis ohne Ziel. Erst die neue Zeile zusichern, dann die alte ausschliessen.
|
||||
zeilen = QUELLE.read_text(encoding="utf-8").splitlines()
|
||||
neu = "# Die Sicherung entsteht 16:45 New Yorker Zeit, in Berlin meist 22:45, vom 25.10. bis 31.10.2026"
|
||||
assert zeilen.count(neu) == 1, f"neue Kommentarzeile {zeilen.count(neu)}-mal"
|
||||
assert [z for z in zeilen if z.startswith("# (S3)")] == [], "Kommentar beginnt mit (S3)."
|
||||
|
||||
|
||||
# Mutanten fuer --mutanten: Name, Text in abendkontrolle.py (genau einmal), Ersatz und die Faelle,
|
||||
# die davon rot werden muessen. MUTANTEN_AUFGABE (unten) tut dasselbe fuer abendkontrolle_einrichten.ps1.
|
||||
# Ein Mutant, der das Verhalten nicht aendert, gehoert nicht hierher
|
||||
@@ -968,7 +1175,7 @@ MUTANTEN = [
|
||||
("Exit des Archivs egal",
|
||||
' if lauf.returncode != 0:\n raise RuntimeError(f"Archiv nicht geholt',
|
||||
' if False:\n raise RuntimeError(f"Archiv nicht geholt',
|
||||
["eine_abgebrochene_uebertragung_hinterlaesst_nichts"]),
|
||||
["ein_unlesbares_archiv_auf_dem_host_scheitert_nur_fuer_seine_instanz"]),
|
||||
("Nachbardatei bleibt", " teil.unlink(missing_ok=True)", " pass",
|
||||
["eine_falsche_summe_wird_nie_abgelegt_und_nie_bestaetigt", "eine_abgebrochene_uebertragung_hinterlaesst_nichts"]),
|
||||
("Bestaetigung ungeprueft",
|
||||
@@ -1032,11 +1239,62 @@ MUTANTEN = [
|
||||
["ohne_schalter_gilt_das_ziel_der_konstante"]),
|
||||
("Ziel der Konstante", r'SICHERUNG_ZIEL = Path(r"D:\Trading-Sicherung")',
|
||||
r'SICHERUNG_ZIEL = Path(r"D:\Trading-Sicherung-alt")', ["ohne_schalter_gilt_das_ziel_der_konstante"]),
|
||||
("Tag ungeprueft",
|
||||
' if not TAGFORM.fullmatch(tag):\n raise ValueError(f"--tag {tag!r} ist kein Datum JJJJ-MM-TT")\n'
|
||||
" date.fromisoformat(tag)",
|
||||
" pass", ["ein_tag_ausser_der_form_holt_keine_sicherung"]),
|
||||
("Datum ungeprueft", " date.fromisoformat(tag)", " pass", ["ein_tag_ausser_der_form_holt_keine_sicherung"]),
|
||||
# Seit Paket NT (Quorum 4): Host nicht erreichbar, --tag frueh geprueft, Kommentar ohne (S3).
|
||||
("E729-01 Host weg: Exit 255 wie jeder andere", " if lauf.returncode == 255:", " if False:",
|
||||
["ein_unerreichbarer_host_gibt_eine_zeile_von_heute_und_keine_weitere_notiz",
|
||||
"eine_abgebrochene_uebertragung_hinterlaesst_nichts"]),
|
||||
("E729-02 Host weg: Zeitlimit wie jeder Fehler", " except subprocess.TimeoutExpired:",
|
||||
" except ZeroDivisionError:", ["ein_zeitlimit_beim_host_zaehlt_wie_ein_unerreichbarer_host"]),
|
||||
("E729-03 Host weg reisst nur die Instanz ab", " except HostNichtErreichbar as ausnahme:",
|
||||
" except ZeroDivisionError as ausnahme:",
|
||||
["ein_unerreichbarer_host_gibt_eine_zeile_von_heute_und_keine_weitere_notiz",
|
||||
"ein_host_der_mitten_im_lauf_wegfaellt_laesst_die_zeilen_davor_stehen"]),
|
||||
("E729-04 Host weg: Zeile in der Notiz des Tags",
|
||||
" schreibe_notiz(vault, jetzt.date().isoformat(), zeile)",
|
||||
" schreibe_notiz(vault, kandidat, zeile)",
|
||||
["ein_unerreichbarer_host_gibt_eine_zeile_von_heute_und_keine_weitere_notiz"]),
|
||||
("E729-05 Host weg mit Exit 0",
|
||||
' logge(ablage, f"sicherung: Notiz nicht geschrieben {zweite!r}")\n return 1',
|
||||
' logge(ablage, f"sicherung: Notiz nicht geschrieben {zweite!r}")\n return 0',
|
||||
["ein_unerreichbarer_host_gibt_eine_zeile_von_heute_und_keine_weitere_notiz"]),
|
||||
("E729-06 Host weg: weiter mit der naechsten Instanz",
|
||||
' logge(ablage, f"sicherung: Notiz nicht geschrieben {zweite!r}")\n return 1',
|
||||
' logge(ablage, f"sicherung: Notiz nicht geschrieben {zweite!r}")\n continue',
|
||||
["ein_unerreichbarer_host_gibt_eine_zeile_von_heute_und_keine_weitere_notiz"]),
|
||||
("E729-07 Host weg: nichts vom angefangenen Tag", " if stuecke:\n zeile +=",
|
||||
" if False:\n zeile +=",
|
||||
["ein_host_der_mitten_im_lauf_wegfaellt_laesst_die_zeilen_davor_stehen"]),
|
||||
("E729-08 Host weg ohne lauf.log",
|
||||
' logge(ablage, f"sicherung {kandidat}-{instanz}: Docker-Host nicht erreichbar ({ausnahme}), '
|
||||
'Sicherung abgebrochen")',
|
||||
" pass", ["ein_unerreichbarer_host_gibt_eine_zeile_von_heute_und_keine_weitere_notiz"]),
|
||||
("E729-09 Tag spaet geprueft", " if argumente.tag is not None and not tag_gueltig(argumente.tag):",
|
||||
" if False:", ["ein_tag_ausser_der_form_endet_mit_zwei_ohne_notiz_und_ohne_ssh"]),
|
||||
("E729-10 leerer Tag gilt als keiner", " if argumente.tag is not None and not tag_gueltig(argumente.tag):",
|
||||
" if argumente.tag and not tag_gueltig(argumente.tag):",
|
||||
["ein_tag_ausser_der_form_endet_mit_zwei_ohne_notiz_und_ohne_ssh"]),
|
||||
("E729-11 falscher Tag mit Exit 1",
|
||||
' print(f"abendkontrolle: --tag {argumente.tag!a} ist kein Datum JJJJ-MM-TT", file=sys.stderr)\n'
|
||||
" return 2",
|
||||
' print(f"abendkontrolle: --tag {argumente.tag!a} ist kein Datum JJJJ-MM-TT", file=sys.stderr)\n'
|
||||
" return 1",
|
||||
["ein_tag_ausser_der_form_endet_mit_zwei_ohne_notiz_und_ohne_ssh"]),
|
||||
("E729-12 falscher Tag ohne Meldung",
|
||||
' print(f"abendkontrolle: --tag {argumente.tag!a} ist kein Datum JJJJ-MM-TT", file=sys.stderr)',
|
||||
" pass", ["ein_tag_ausser_der_form_endet_mit_zwei_ohne_notiz_und_ohne_ssh"]),
|
||||
("E729-13 Form des Tags ungeprueft", " if not TAGFORM.fullmatch(tag):\n return False",
|
||||
" if False:\n return False", ["ein_tag_ausser_der_form_endet_mit_zwei_ohne_notiz_und_ohne_ssh"]),
|
||||
("E729-14 Datum des Tags ungeprueft", " date.fromisoformat(tag)\n except ValueError:",
|
||||
" pass\n except ValueError:", ["ein_tag_ausser_der_form_endet_mit_zwei_ohne_notiz_und_ohne_ssh"]),
|
||||
("E729-15 Verweis S3 zurueck",
|
||||
"# Die Sicherung entsteht 16:45 New Yorker Zeit, in Berlin meist 22:45, vom 25.10. bis 31.10.2026",
|
||||
"# (S3). Die Sicherung entsteht 16:45 New Yorker Zeit, in Berlin meist 22:45, vom 25.10. bis 31.10.2026",
|
||||
["kein_kommentar_beginnt_mit_s3"]),
|
||||
# Seit der Plankritik NT, Befund 1: Ein Abbruch im Archiv laesst nur die Instanz scheitern.
|
||||
("E729-25 Abbruch im Archiv beendet die Sicherung", " except HostNichtErreichbar as abbruch:",
|
||||
" except ZeroDivisionError as abbruch:",
|
||||
["ein_zeitlimit_beim_archiv_laesst_nur_diese_instanz_scheitern",
|
||||
"eine_abgebrochene_uebertragung_hinterlaesst_nichts"]),
|
||||
("Name der Summendatei beim Host egal", ' if treffer is None or treffer.group(2) != f"{name}.tar":',
|
||||
" if treffer is None:", ["eine_summendatei_des_hosts_in_falscher_form_holt_kein_archiv"]),
|
||||
("Grossbuchstaben in der Summe", r'SUMMENZEILE = re.compile(r"([0-9a-f]{64}) (\S+)\n")',
|
||||
|
||||
Reference in new issue
Block a user