Files
xarticleaudio/test/server.test.js

194 lines
5.0 KiB
JavaScript

"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");
});