156 lines
4.0 KiB
JavaScript
156 lines
4.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 {
|
|
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,
|
|
},
|
|
};
|
|
}
|
|
|
|
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();
|
|
});
|