"use strict"; const test = require("node:test"); const assert = require("node:assert/strict"); const { buildApp } = require("../src/app"); const { hmacSHA256Hex } = require("../src/lib/signature"); function createApp() { return buildApp({ config: { xWebhookSecret: "x-secret", polarWebhookSecret: "polar-secret", credit: { baseCredits: 1, includedChars: 25000, stepChars: 10000, stepCredits: 1, maxCharsPerArticle: 120000, }, }, }); } function postJSON(app, path, payload, secret) { const rawBody = JSON.stringify(payload); const sig = hmacSHA256Hex(rawBody, secret); return app.handleRequest({ method: "POST", path, headers: { "x-signature": `sha256=${sig}` }, rawBody, }); } test("rejects invalid X webhook signature", () => { const app = createApp(); const response = app.handleRequest({ method: "POST", path: "/api/webhooks/x", headers: { "x-signature": "sha256=deadbeef" }, rawBody: JSON.stringify({ mentionPostId: "m1", callerUserId: "u1", parentPost: {} }), }); assert.equal(response.status, 401); }); test("X webhook returns not_article response and no charge", () => { const app = createApp(); postJSON(app, "/api/webhooks/polar", { userId: "u1", credits: 5, eventId: "evt1" }, "polar-secret"); const response = postJSON( app, "/api/webhooks/x", { mentionPostId: "m1", callerUserId: "u1", parentPost: { id: "p1" } }, "x-secret", ); const body = JSON.parse(response.body); assert.equal(response.status, 200); assert.equal(body.status, "not_article"); assert.equal(app.engine.getWalletBalance("u1"), 5); }); test("X webhook processes article, charges caller, and exposes audio route", () => { const app = createApp(); postJSON(app, "/api/webhooks/polar", { userId: "u1", credits: 5, eventId: "evt2" }, "polar-secret"); const response = postJSON( app, "/api/webhooks/x", { mentionPostId: "m2", callerUserId: "u1", parentPost: { id: "p2", article: { id: "a2", title: "Article", body: "Hello from article", }, }, }, "x-secret", ); const body = JSON.parse(response.body); assert.equal(response.status, 200); assert.equal(body.status, "completed"); assert.equal(body.creditsCharged, 1); const audioPageUnauthed = app.handleRequest({ method: "GET", path: body.publicLink, headers: {}, rawBody: "", }); assert.equal(audioPageUnauthed.status, 200); assert.match(audioPageUnauthed.body, /Sign in required before playback/); }); test("non-owner can unlock with same credits then access", () => { const app = createApp(); postJSON(app, "/api/webhooks/polar", { userId: "u1", credits: 5, eventId: "evt3" }, "polar-secret"); postJSON(app, "/api/webhooks/polar", { userId: "u2", credits: 5, eventId: "evt4" }, "polar-secret"); const makeAudio = postJSON( app, "/api/webhooks/x", { mentionPostId: "m3", callerUserId: "u1", parentPost: { id: "p3", article: { id: "a3", title: "Article", body: "Hello from article", }, }, }, "x-secret", ); const { publicLink } = JSON.parse(makeAudio.body); const assetId = publicLink.replace("/audio/", ""); const unlock = app.handleRequest({ method: "POST", path: `/api/audio/${assetId}/unlock`, headers: { "x-user-id": "u2" }, rawBody: "", }); assert.equal(unlock.status, 200); assert.equal(app.engine.getWalletBalance("u2"), 4); const pageAfterUnlock = app.handleRequest({ method: "GET", path: publicLink, headers: { "x-user-id": "u2" }, rawBody: "", }); assert.match(pageAfterUnlock.body, /Access granted/); });