diff --git a/tests/phase_a_acceptance.lua b/tests/phase_a_acceptance.lua new file mode 100644 index 0000000..1faa4ff --- /dev/null +++ b/tests/phase_a_acceptance.lua @@ -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 + +local pfad = arg and arg[1] +if not pfad then + io.stderr:write("usage: lua5.4 phase_a_acceptance.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)