Files
tyx_AI_xhs/tests/api/postv1-local-test-session.test.ts
T
suyx 92434aec17
Dada P0-A isolated Windows CI / validate-and-package (push) Waiting to run
feat(POSTV1-03): 增加本机测试直达入口
2026-08-05 14:53:20 +08:00

96 lines
4.1 KiB
TypeScript

import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { createApp } from "../../apps/api/src/app.js";
import { RegistrationService } from "../../apps/api/src/registration.js";
import { MockResendAdapter } from "../../apps/api/src/resend-adapter.js";
const fixedNow = Date.parse("2026-08-05T06:00:00.000Z");
const writeHeaders = { host: "127.0.0.1:43121", origin: "http://127.0.0.1:43121" };
const roots: string[] = [];
const services: RegistrationService[] = [];
function createRegistrationService() {
const root = mkdtempSync(join(tmpdir(), "dada-local-test-session-"));
roots.push(root);
const registration = new RegistrationService({
challengePepper: Buffer.alloc(32, 0x51),
clock: () => fixedNow,
currentPrivacyNoticeVersion: "p0a-notice-v1",
databasePath: join(root, "dada.sqlite3"),
invitePepper: Buffer.alloc(32, 0x52),
resend: new MockResendAdapter(),
sessionPepper: Buffer.alloc(32, 0x53),
});
services.push(registration);
return registration;
}
afterEach(() => {
for (const service of services.splice(0)) {
try { service.close(); } catch { /* already closed by the test */ }
}
for (const root of roots.splice(0)) rmSync(root, { force: true, recursive: true });
});
describe("POSTV1-03 local test session", () => {
it("does not expose the local test route unless explicitly enabled", async () => {
const registration = createRegistrationService();
const app = await createApp({ browserGate: false, networkBoundary: { allowTestPort: true }, registration });
const status = await app.inject({ headers: writeHeaders, method: "GET", url: "/api/v1/auth/local-test" });
const created = await app.inject({ headers: writeHeaders, method: "POST", url: "/api/v1/auth/local-test" });
expect(status.statusCode).toBe(404);
expect(created.statusCode).toBe(404);
await app.close();
});
it("creates one isolated fixture account and restores it without duplicate credits", async () => {
const registration = createRegistrationService();
const app = await createApp({
browserGate: false,
localTestAuth: true,
networkBoundary: { allowTestPort: true },
registration,
});
const status = await app.inject({ headers: writeHeaders, method: "GET", url: "/api/v1/auth/local-test" });
expect(status.statusCode).toBe(200);
expect(status.json()).toEqual({ available: true });
const first = await app.inject({ headers: writeHeaders, method: "POST", url: "/api/v1/auth/local-test" });
expect(first.statusCode).toBe(200);
expect(first.json()).toMatchObject({
audience: "user",
credits: { available_balance: 10, reserved_balance: 0 },
status: "authenticated",
user: { creator_name: "本机测试用户", role: "user", social_id: "@dada_local_test", status: "active" },
});
expect(first.headers["set-cookie"]).toContain("dada_session=");
const session = await app.inject({
headers: { cookie: first.headers["set-cookie"], host: "127.0.0.1:43121" },
method: "GET",
url: "/api/v1/auth/session",
});
expect(session.statusCode).toBe(200);
expect(session.json()).toMatchObject({ authenticated: true, credits: { available_balance: 10 } });
const second = await app.inject({ headers: writeHeaders, method: "POST", url: "/api/v1/auth/local-test" });
expect(second.statusCode).toBe(200);
expect(second.json().user.user_id).toBe(first.json().user.user_id);
expect(registration.database.prepare("SELECT COUNT(*) AS count FROM users").get()).toEqual({ count: 1 });
expect(registration.database.prepare("SELECT COUNT(*) AS count FROM credit_ledger").get()).toEqual({ count: 1 });
expect(registration.database.prepare("SELECT counts_toward_stage_limit FROM users").get()).toEqual({ counts_toward_stage_limit: 0 });
const openapi = JSON.stringify(app.swagger());
expect(openapi).not.toContain("/api/v1/auth/local-test");
expect(openapi).not.toContain("local-test-user");
await app.close();
});
});