/* ===================================================================== test-kontingent-routen.mjs — die Anfragebremse an den echten Routen Prüft nicht den Baustein selbst (das tut pruef-kontingent.mjs im Projektstamm), sondern dass die beiden bisher ungebremsten Routen submitApplication und submitTestimonial ihn WIRKLICH benutzen: ab der Grenze kommt 429, und verschiedene IPs stören sich nicht. Läuft gegen eine Wegwerf-Datenbank, ruft die Handler direkt mit Attrappen auf -- gleiches Muster wie test-webdesign-anfragen.mjs. Aufruf (aus server-internal/): node test-kontingent-routen.mjs ===================================================================== */ import { mkdtempSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; process.env.DB_PATH = join(mkdtempSync(join(tmpdir(), "kont-test-")), "test.db"); const { initDb } = await import("./db.js"); initDb(); const { submitApplication } = await import("./routes/applications.js"); const { submitTestimonial } = await import("./routes/testimonials.js"); let ok = 0, schlecht = 0; const pruefe = (name, bedingung, zusatz = "") => { if (bedingung) { ok++; console.log(" ok " + name); } else { schlecht++; console.log(" FEHLT " + name + " " + zusatz); } }; function antwortAttrappe() { const a = { code: 200, daten: null }; a.status = (c) => { a.code = c; return a; }; a.json = (d) => { a.daten = d; return a; }; return a; } function req(koerper, ip) { return { body: koerper, params: {}, query: {}, headers: {}, ip, socket: { remoteAddress: ip } }; } /* Eine gültige Bewerbung bzw. Stimme, damit NICHT eine Inhaltsprüfung (Pflichtfeld o.ä.) fälschlich die 429-Messung verfälscht. */ const bewerbung = () => ({ type: "creator", data: { name: "Test Person", warum: "Ich will dabei sein und mithelfen." } }); const stimme = () => ({ name: "Test Person", tiktokUsername: "testi", nachricht: "Das Team ist wirklich super, ich bin gern dabei!" }); async function feuere(handler, koerperFn, ip, anzahl) { let durch = 0, gesperrt = 0; for (let i = 0; i < anzahl; i++) { const res = antwortAttrappe(); await handler(req(koerperFn(), ip), res); if (res.code === 429) gesperrt++; else durch++; } return { durch, gesperrt }; } console.log("\n=== Bewerbungen: 20 pro Stunde, dann 429 ==="); { const r = await feuere(submitApplication, bewerbung, "198.51.100.1", 25); pruefe("genau 20 kommen durch, der Rest wird gebremst", r.durch === 20 && r.gesperrt === 5, `durch=${r.durch}, gesperrt=${r.gesperrt}`); } console.log("\n=== Stimmen: 20 pro Stunde, dann 429 ==="); { const r = await feuere(submitTestimonial, stimme, "198.51.100.2", 25); pruefe("genau 20 kommen durch, der Rest wird gebremst", r.durch === 20 && r.gesperrt === 5, `durch=${r.durch}, gesperrt=${r.gesperrt}`); } console.log("\n=== Getrennte Töpfe: erschöpfte Bewerbung sperrt keine Stimme ==="); { await feuere(submitApplication, bewerbung, "198.51.100.3", 25); // Bewerbung erschöpfen const r = await feuere(submitTestimonial, stimme, "198.51.100.3", 1); // Stimme derselben IP pruefe("dieselbe IP darf noch eine Stimme abgeben", r.durch === 1 && r.gesperrt === 0); } console.log("\n=== Verschiedene IPs stören sich nicht ==="); { await feuere(submitApplication, bewerbung, "198.51.100.4", 25); // eine IP erschöpfen const r = await feuere(submitApplication, bewerbung, "198.51.100.5", 1); // andere IP pruefe("eine frische IP kommt durch", r.durch === 1 && r.gesperrt === 0); } console.log(`\n===== ${ok} bestanden, ${schlecht} fehlgeschlagen =====`); process.exitCode = schlecht ? 1 : 0;