tests: Abnahmetest fuer Phase A der Fassungs-Meldung
Phase A des Umstiegs auf MCP 2026-07-28 ist "observe-only": erst
feststellen, welche Protokollfassungen die Gegenstellen ueberhaupt
verlangen, bevor irgendetwas durchgesetzt wird. Der Vertrag dafuer
steht im Kopf der Datei; dies ist der getrennte Test dazu.
GETRENNT ist hier die Bedingung, nicht die Beschreibung. An derselben
Aufgabe war der Defekt an vier aufeinanderfolgenden Laeufen der
SELBSTTEST, nie der Code - wer implementiert, schreibt den Test nicht.
Deshalb ist er hier im Repo und nicht in der Werkstatt der Erzeuger.
Nachgewiesen unterscheidungsfaehig, in beide Richtungen:
* Rueckgabe 1 und zwei Fehlschlaege gegen eine absichtlich kaputte
Fassung,
* Rueckgabe 0 gegen eine korrekte.
Ein Test, der nur besteht, misst nichts.
Zehn Pruefungen, darunter die zwei, die in Handfassungen zuerst
fallen: der Deckel bei 50 darf nicht zu frueh zuschlagen, und das
Modul darf keine neue globale Variable hinterlassen.
Aufruf: lua5.4 tests/phase_a_acceptance.lua <impl.lua>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
-- Abnahmetest fuer lmcp Phase A (nur beobachten, nie ablehnen).
|
||||
--
|
||||
-- Vom Vertrag geschrieben, NICHT von der Implementierung: der Autor dieses
|
||||
-- Tests hat den zu pruefenden Code nicht gesehen. Genau daran sind die
|
||||
-- letzten vier Runden gescheitert - der Implementierer hat seine eigenen
|
||||
-- Hausaufgaben korrigiert, und der Test prueft dann verlaesslich das, was
|
||||
-- der Code ohnehin tut.
|
||||
--
|
||||
-- VERTRAG
|
||||
-- Eine einzelne, in sich geschlossene Lua-5.4-Datei stellt eine Tabelle M
|
||||
-- bereit mit:
|
||||
-- M.report(version, peer, ua) -- Produktionseinstieg
|
||||
-- M.sink(version, peer, ua) -- ueberschreibbar, wird beim ERSTEN
|
||||
-- -- Auftreten eines Tripels gerufen
|
||||
-- * jedes verschiedene Tripel (version, peer, ua) genau EINMAL
|
||||
-- * version nil oder "" -> nichts
|
||||
-- * der Entprellungszustand ist ein privates Upvalue: KEINE globale
|
||||
-- Variable, KEIN Parameter, den der Aufrufer mitgeben muss
|
||||
-- * gedeckelt bei 50 verschiedenen Tripeln; danach nichts mehr
|
||||
-- * M.report lehnt NIE etwas ab und liefert immer nil
|
||||
--
|
||||
-- Aufruf: lua5.4 phase_a_acceptance.lua <zu-pruefende-datei.lua>
|
||||
|
||||
local pfad = arg and arg[1]
|
||||
if not pfad then
|
||||
io.stderr:write("usage: lua5.4 phase_a_acceptance.lua <impl.lua>\n")
|
||||
os.exit(2)
|
||||
end
|
||||
|
||||
local vorher_global = {}
|
||||
for k in pairs(_G) do vorher_global[k] = true end
|
||||
|
||||
local lade = assert(loadfile(pfad))
|
||||
local M = lade()
|
||||
if type(M) ~= "table" then
|
||||
io.stderr:write("FAIL: die Datei liefert keine Tabelle zurueck\n")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local fehler = 0
|
||||
local function pruefe(name, bedingung, zusatz)
|
||||
if bedingung then
|
||||
print((" [ok ] %s"):format(name))
|
||||
else
|
||||
fehler = fehler + 1
|
||||
print((" [FAIL ] %s%s"):format(name, zusatz and (" — " .. zusatz) or ""))
|
||||
end
|
||||
end
|
||||
|
||||
-- Faenger einhaengen
|
||||
local gesehen = {}
|
||||
M.sink = function(v, p, u)
|
||||
gesehen[#gesehen + 1] = { v = v, p = p, u = u }
|
||||
end
|
||||
local function zuruecksetzen() gesehen = {} end
|
||||
|
||||
pruefe("M.report existiert und ist aufrufbar", type(M.report) == "function")
|
||||
if type(M.report) ~= "function" then os.exit(1) end
|
||||
|
||||
-- 1. nil und Leerstring melden nichts
|
||||
zuruecksetzen()
|
||||
M.report(nil, "peerA", "uaA")
|
||||
M.report("", "peerA", "uaA")
|
||||
pruefe("nil und \"\" melden nichts", #gesehen == 0,
|
||||
("es kamen %d Meldungen"):format(#gesehen))
|
||||
|
||||
-- 2. erstes Tripel meldet genau einmal
|
||||
zuruecksetzen()
|
||||
M.report("2025-06-18", "peerA", "uaA")
|
||||
pruefe("erstes Tripel meldet einmal", #gesehen == 1)
|
||||
|
||||
-- 3. DER FALL, DER DREI RUNDEN LANG DURCHRUTSCHTE:
|
||||
-- Entprellung am ECHTEN Einstieg, ohne dass der Aufrufer Zustand mitgibt.
|
||||
zuruecksetzen()
|
||||
for _ = 1, 5 do M.report("2025-06-18", "peerA", "uaA") end
|
||||
pruefe("fuenf gleiche Tripel -> keine weitere Meldung", #gesehen == 0,
|
||||
("es kamen %d Meldungen; Zustand ueberlebt den Aufruf nicht")
|
||||
:format(#gesehen))
|
||||
|
||||
-- 4. gleiche Fassung, andere Gegenstelle -> meldet wieder
|
||||
zuruecksetzen()
|
||||
M.report("2025-06-18", "peerB", "uaA")
|
||||
pruefe("gleiche Fassung, andere Gegenstelle -> Meldung", #gesehen == 1,
|
||||
"Entprellung nur auf die Fassung wuerde andere Klienten maskieren")
|
||||
|
||||
-- 5. gleiche Fassung und Gegenstelle, anderer User-Agent -> meldet wieder
|
||||
zuruecksetzen()
|
||||
M.report("2025-06-18", "peerB", "uaZ")
|
||||
pruefe("anderer User-Agent -> Meldung", #gesehen == 1)
|
||||
|
||||
-- 6. liefert immer nil, lehnt nie ab
|
||||
local r1 = M.report("2026-07-28", "peerC", "uaC")
|
||||
local r2 = M.report("2026-07-28", "peerC", "uaC")
|
||||
pruefe("M.report liefert nil (lehnt nie ab)", r1 == nil and r2 == nil)
|
||||
|
||||
-- 7. Deckel bei 50
|
||||
zuruecksetzen()
|
||||
for i = 1, 80 do M.report("v" .. i, "peerD", "uaD") end
|
||||
pruefe("Deckel greift bei 50", #gesehen <= 50,
|
||||
("es kamen %d Meldungen"):format(#gesehen))
|
||||
pruefe("Deckel wirft nicht zu frueh", #gesehen >= 40,
|
||||
("nur %d Meldungen vor dem Deckel"):format(#gesehen))
|
||||
|
||||
-- 8. kein globaler Zustand
|
||||
local neue = {}
|
||||
for k in pairs(_G) do
|
||||
if not vorher_global[k] then neue[#neue + 1] = k end
|
||||
end
|
||||
pruefe("keine neuen globalen Variablen", #neue == 0,
|
||||
"hinzugekommen: " .. table.concat(neue, ", "))
|
||||
|
||||
print((" %d Pruefungen fehlgeschlagen"):format(fehler))
|
||||
os.exit(fehler == 0 and 0 or 1)
|
||||
Reference in New Issue
Block a user