"use strict"; const test = require("node:test"); const assert = require("node:assert/strict"); const { mapToAppRequest, normalizeHeaders, createMutationPersister, createRuntime, } = require("../src/server"); function createRuntimeConfig() { return { nodeEnv: "test", port: 3000, logLevel: "info", appBaseUrl: "http://localhost:3000", betterAuthSecret: "test-better-auth-secret", betterAuthBasePath: "/api/auth", xOAuthClientId: "", xOAuthClientSecret: "", internalApiToken: "", convexDeploymentUrl: "", convexAuthToken: "", convexStateQuery: "state:getLatestSnapshot", convexStateMutation: "state:saveSnapshot", xWebhookSecret: "x-secret", xBearerToken: "", xBotUserId: "", polarWebhookSecret: "polar-secret", polarAccessToken: "", polarServer: "production", polarProductIds: [], qwenTtsApiKey: "", qwenTtsBaseUrl: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1", qwenTtsModel: "qwen-tts-latest", qwenTtsVoice: "Cherry", qwenTtsFormat: "mp3", minioEndPoint: "", minioPort: 443, minioUseSSL: true, minioBucket: "", minioRegion: "us-east-1", minioAccessKey: "", minioSecretKey: "", minioSignedUrlTtlSec: 3600, rateLimits: { webhookPerMinute: 120, authPerMinute: 30, actionPerMinute: 60, }, abuse: { maxJobsPerUserPerDay: 0, cooldownSec: 0, denyUserIds: [], }, credit: { baseCredits: 1, includedChars: 25000, stepChars: 10000, stepCredits: 1, maxCharsPerArticle: 120000, }, allowInMemoryStateFallback: true, }; } test("normalizeHeaders lowercases and joins array values", () => { const headers = normalizeHeaders({ "X-User-Id": "u1", "X-Forwarded-For": ["a", "b"], }); assert.equal(headers["x-user-id"], "u1"); assert.equal(headers["x-forwarded-for"], "a,b"); }); test("mapToAppRequest extracts method/path/headers/body correctly", () => { const request = mapToAppRequest({ req: { method: "POST", url: "/api/webhooks/x?debug=1&returnTo=%2Faudio%2F1", headers: { "X-Signature": "sha256=abc" }, }, rawBody: "{\"ok\":true}", }); assert.equal(request.method, "POST"); assert.equal(request.path, "/api/webhooks/x"); assert.equal(request.query.debug, "1"); assert.equal(request.query.returnTo, "/audio/1"); assert.equal(request.headers["x-signature"], "sha256=abc"); assert.equal(request.rawBody, "{\"ok\":true}"); }); test("createMutationPersister writes sequentially and flush waits", async () => { const saved = []; const stateStore = { async save(state) { await new Promise((resolve) => setTimeout(resolve, 5)); saved.push(state.id); }, }; const persister = createMutationPersister({ stateStore, logger: { error() {} } }); persister.enqueue({ id: "s1" }); persister.enqueue({ id: "s2" }); await persister.flush(); assert.deepEqual(saved, ["s1", "s2"]); }); test("createRuntime falls back to in-memory state when initial load fails", async () => { const warnings = []; const runtime = await createRuntime({ runtimeConfig: createRuntimeConfig(), logger: { warn(payload, message) { warnings.push({ payload, message }); }, info() {}, error() {}, }, stateStore: { async load() { throw new Error("state_load_failed"); }, async save() { throw new Error("state_save_should_not_run"); }, }, }); assert.equal(warnings.length, 1); assert.match(String(warnings[0].message), /falling back to in-memory state/); const signUp = await runtime.app.handleRequest({ method: "POST", path: "/auth/email/sign-up", headers: {}, rawBody: "name=User+One&email=u1%40example.com&password=password123&returnTo=%2Fapp", query: {}, }); assert.equal(signUp.status, 303); const response = await runtime.app.handleRequest({ method: "POST", path: "/app/actions/topup", headers: { cookie: signUp.headers["set-cookie"] }, rawBody: "amount=3", query: {}, }); assert.equal(response.status, 303); await runtime.persister.flush(); }); test("createRuntime fails startup when fallback is disabled", async () => { const runtimeConfig = createRuntimeConfig(); runtimeConfig.allowInMemoryStateFallback = false; await assert.rejects( createRuntime({ runtimeConfig, logger: { info() {}, warn() {}, error() {} }, stateStore: { async load() { throw new Error("state_load_failed"); }, async save() {}, }, }), /state_store_unavailable_without_fallback/, ); }); test("createMutationPersister surfaces save errors", async () => { const persister = createMutationPersister({ stateStore: { async save() { throw new Error("persist_failed"); }, }, logger: { error() {} }, }); await assert.rejects( persister.enqueue({}), /persist_failed/, ); assert.equal(persister.getLastError()?.message, "persist_failed"); });