"use strict"; const assert = require("node:assert/strict"); const fs = require("node:fs"); const os = require("node:os"); const path = require("node:path"); const { after, describe, it } = require("node:test"); const testDataDir = fs.mkdtempSync(path.join(os.tmpdir(), "toolctrl-leads-test-")); process.env.DATA_DIR = testDataDir; process.env.CLAMAV_ENABLED = "false"; const { bindSubmissionId, createLeadFingerprint } = require("./public/submission-id"); const { PlatformLeadConfigurationError, PlatformLeadSubmissionError, createServer, forwardLeadToPlatform } = require("./server"); const ENV = { PLATFORM_LEADS_API_URL: "https://api.hivegrid.de", PLATFORM_LEADS_API_KEY: "t".repeat(32) }; const LEAD = { submissionId: "334955b4-269d-4f18-b51b-7b1d93ed2103", firstName: "Erika", lastName: "Musterfrau", email: "erika@example.com", phone: "+49 123 456", company: "Beispiel GmbH", message: "Bitte melden." }; function successResponse() { return new Response(JSON.stringify({ id: "lead_789", status: "new", createdAt: "2026-07-16T12:00:00.000Z" }), { status: 201, headers: { "Content-Type": "application/json" } }); } async function withServer(submitLead, callback) { const server = createServer({ submitLead: submitLead }); await new Promise((resolve, reject) => { server.once("error", reject); server.listen(0, "127.0.0.1", resolve); }); const address = server.address(); try { return await callback("http://127.0.0.1:" + address.port); } finally { await new Promise((resolve) => server.close(resolve)); } } after(() => { fs.rmSync(testDataDir, { recursive: true, force: true }); }); describe("forwardLeadToPlatform", () => { it("sends the strict native contract without a source field", async () => { const calls = []; const fetchImpl = async (input, init) => { calls.push({ input: String(input), init: init }); return successResponse(); }; const result = await forwardLeadToPlatform(LEAD, { env: ENV, fetchImpl: fetchImpl }); assert.equal(result.id, "lead_789"); assert.equal(calls.length, 1); assert.equal(calls[0].input, "https://api.hivegrid.de/v1/platform/leads"); assert.deepEqual(calls[0].init.headers, { "Content-Type": "application/json", Authorization: "Bearer " + "t".repeat(32), "Idempotency-Key": LEAD.submissionId }); const body = JSON.parse(calls[0].init.body); assert.deepEqual(body, { externalId: LEAD.submissionId, sourceDetail: "Messe — Landing Page", firstName: "Erika", lastName: "Musterfrau", email: "erika@example.com", phone: "+49 123 456", company: "Beispiel GmbH", message: "Bitte melden.", utmCampaign: "messe-landing-page", metadata: { consent: true, formId: "contact-form", formName: "ToolCTRL Kontaktformular", locale: "de-DE", submissionId: LEAD.submissionId } }); assert.equal(Object.hasOwn(body, "source"), false); }); it("reuses the exact key and body across a retry", async () => { const calls = []; const fetchImpl = async (_input, init) => { calls.push(init); return calls.length === 1 ? new Response(null, { status: 503 }) : successResponse(); }; await forwardLeadToPlatform(LEAD, { env: ENV, fetchImpl: fetchImpl }); assert.equal(calls.length, 2); assert.equal(calls[0].body, calls[1].body); assert.deepEqual(calls[0].headers, calls[1].headers); }); it("rejects an API error instead of reporting success", async () => { await assert.rejects( forwardLeadToPlatform(LEAD, { env: ENV, fetchImpl: async () => new Response(null, { status: 401 }) }), (error) => error instanceof PlatformLeadSubmissionError && error.code === "upstream" && error.upstreamStatus === 401 ); }); it("fails closed without runtime credentials", async () => { await assert.rejects( forwardLeadToPlatform(LEAD, { env: {} }), PlatformLeadConfigurationError ); }); }); describe("browser submission id binding", () => { it("keeps the id for an equivalent retry and renews it after a semantic edit", () => { let sequence = 0; const createId = () => "submission-" + (++sequence); const original = { name: " Erika Musterfrau ", firma: " Beispiel GmbH ", email: " ERIKA@example.com ", telefon: " +49 123 456 ", nachricht: " Bitte melden. " }; const first = bindSubmissionId(null, createLeadFingerprint(original), createId); const retry = bindSubmissionId(first, createLeadFingerprint({ ...original, name: "Erika Musterfrau", email: "erika@EXAMPLE.com" }), createId); const edited = bindSubmissionId(retry, createLeadFingerprint({ ...original, nachricht: "Bitte morgen melden." }), createId); assert.equal(retry, first); assert.equal(retry.id, "submission-1"); assert.equal(edited.id, "submission-2"); }); it("loads the binding helper before the form client", () => { const index = fs.readFileSync(path.join(__dirname, "public/index.html"), "utf8"); const helperPosition = index.indexOf(''); const clientPosition = index.indexOf(''); assert.notEqual(helperPosition, -1); assert.equal(helperPosition < clientPosition, true); }); }); describe("runtime readiness", () => { it("fails without platform credentials and succeeds with valid runtime configuration", async () => { const previousUrl = process.env.PLATFORM_LEADS_API_URL; const previousKey = process.env.PLATFORM_LEADS_API_KEY; try { delete process.env.PLATFORM_LEADS_API_URL; delete process.env.PLATFORM_LEADS_API_KEY; await withServer(async () => ({ id: "unused" }), async (baseUrl) => { const response = await fetch(baseUrl + "/readyz"); assert.equal(response.status, 503); assert.equal((await response.json()).checks.platformLeadsConfigured, false); }); process.env.PLATFORM_LEADS_API_URL = ENV.PLATFORM_LEADS_API_URL; process.env.PLATFORM_LEADS_API_KEY = ENV.PLATFORM_LEADS_API_KEY; await withServer(async () => ({ id: "unused" }), async (baseUrl) => { const response = await fetch(baseUrl + "/readyz"); assert.equal(response.status, 200); assert.equal((await response.json()).checks.platformLeadsConfigured, true); }); } finally { if (previousUrl == null) delete process.env.PLATFORM_LEADS_API_URL; else process.env.PLATFORM_LEADS_API_URL = previousUrl; if (previousKey == null) delete process.env.PLATFORM_LEADS_API_KEY; else process.env.PLATFORM_LEADS_API_KEY = previousKey; } }); it("uses readiness rather than liveness for the container healthcheck", () => { const dockerfile = fs.readFileSync(path.join(__dirname, "Dockerfile"), "utf8"); assert.match(dockerfile, /HEALTHCHECK[^]*\/readyz/); }); }); describe("deployment contract", () => { it("uses Coolify pull-deploy and verifies the ready revision", () => { const workflow = fs.readFileSync( path.join(__dirname, ".gitea/workflows/deploy.yml"), "utf8" ); assert.equal( workflow.includes( "/api/v1/deploy?uuid=${COOLIFY_RESOURCE_UUID}&force=true" ), true ); assert.doesNotMatch( workflow, /\/api\/v1\/applications\/[^\s"']+\/(?:start|restart)/ ); assert.equal(workflow.includes('-t "$IMAGE_LC:$SHA"'), true); assert.equal(workflow.includes("EXPECTED_REVISION: ${{ github.sha }}"), true); assert.equal(workflow.includes("READY_URL: https://tool-ctrl.de/readyz"), true); assert.equal( workflow.includes( 'if [ "$DEPLOYED_REVISION" = "$EXPECTED_REVISION" ]; then' ), true ); }); }); describe("POST /api/lead", () => { const formBody = { name: " Erika Musterfrau ", firma: " Beispiel GmbH ", email: " ERIKA@example.com ", telefon: " +49 123 456 ", nachricht: " Bitte melden. ", website: "", submissionId: LEAD.submissionId }; it("returns success only after the platform accepted the normalized lead", async () => { const captured = []; await withServer(async (lead) => { captured.push(lead); return { id: "lead_route", status: "new", createdAt: "now" }; }, async (baseUrl) => { const response = await fetch(baseUrl + "/api/lead", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(formBody) }); assert.equal(response.status, 200); assert.deepEqual(await response.json(), { ok: true, id: "lead_route" }); }); assert.deepEqual(captured, [LEAD]); assert.equal(fs.existsSync(path.join(testDataDir, "leads")), false); }); it("returns an error and does not write NDJSON/CSV when the platform fails", async () => { await withServer(async () => { throw new PlatformLeadSubmissionError("network"); }, async (baseUrl) => { const response = await fetch(baseUrl + "/api/lead", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(formBody) }); assert.equal(response.status, 502); assert.equal((await response.json()).error.includes("erneut"), true); }); assert.equal(fs.existsSync(path.join(testDataDir, "leads")), false); }); it("requires a stable submission id before calling the platform", async () => { let calls = 0; await withServer(async () => { calls += 1; return { id: "unexpected" }; }, async (baseUrl) => { const response = await fetch(baseUrl + "/api/lead", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...formBody, submissionId: undefined }) }); assert.equal(response.status, 400); }); assert.equal(calls, 0); }); it("keeps the honeypot response silent without creating a lead", async () => { let calls = 0; await withServer(async () => { calls += 1; return { id: "unexpected" }; }, async (baseUrl) => { const response = await fetch(baseUrl + "/api/lead", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ website: "bot.example" }) }); assert.equal(response.status, 200); assert.deepEqual(await response.json(), { ok: true }); }); assert.equal(calls, 0); }); });