Files
UHD_Media_Tool/apps/server/test/auth-settings/harness.ts
T
rootandClaude Opus 5.5 c9e8120fc6 test: server 测试共用建库与假引擎,sse-pg 归入串行组
- 新增 apps/server/test/support/{pg,engine}.ts:测试库地址、可用性探测、schema 咨询锁、
  建 schema + 加锁迁移 + 删除、可选去重的假引擎;6 份 harness 改用它(acquisition-organize 与
  file-manager 原先迁移不加锁,并行下有冲突隐患,一并修正)
- sse-pg 起子进程,并行下子进程启动超时,归入 serial
- 修正 b61335e 挪动 / 改名测试后遗留的 import 排序与格式(当时 lint 检查经管道被吞掉退出码)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 17:40:43 +08:00

129 lines
4.4 KiB
TypeScript

import type { DB } from "@umt/db";
import { migrateEngineSchema } from "@umt/tasks";
import type { Kysely } from "kysely";
import { vi } from "vitest";
import { ConfigSchema } from "../../src/config.ts";
import { createOutboxPort } from "../../src/context.ts";
import { createApp } from "../../src/http/app.ts";
import { EventBus } from "../../src/http/sse.ts";
import { createCapabilityRegistry } from "../../src/modules/agent/capability-registry.ts";
import { createAuthSettingsModule } from "../../src/modules/auth-settings/index.ts";
import { createSecretStore, createSettingsReader } from "../../src/modules/auth-settings/provided.ts";
import type { ServerContext, ServerModule } from "../../src/modules/types.ts";
import { DEV_KEY, memoryLogger } from "../helpers.ts";
// 真实 PG 的 W2-A 测试夹具:独立业务 schema + 独立引擎 schema;任务引擎用假对象(只测 cancel 透传)。
import { createTestSchema, PG_URL, uniqueSchema, withSchemaLock } from "../support/pg.ts";
export { PG_URL, pgAvailable, withSchemaLock } from "../support/pg.ts";
export const MASTER_KEY = "test-master-key-0123456789";
export async function createHarness(opts: { engine?: boolean; extraModules?: ServerModule[] } = {}) {
const ts = await createTestSchema(uniqueSchema("w2a_test"), { max: 5 });
const { schema } = ts;
const db: Kysely<DB> = ts.db;
const engineSchema = `${schema}_ow`;
if (opts.engine) await withSchemaLock(() => migrateEngineSchema(PG_URL, engineSchema));
const { logger, lines } = memoryLogger();
const bus = new EventBus(100);
const config = ConfigSchema.parse({
DATABASE_URL: PG_URL,
SECRET_MASTER_KEY: MASTER_KEY,
NODE_ENV: "development",
DEV_ADMIN_KEY: DEV_KEY,
});
const cancel = vi.fn(async (_runId: string, _reason?: string) => true);
const ctx = {
role: "all",
db,
config,
logger,
events: bus,
engine: { cancel } as unknown as ServerContext["engine"],
outbox: createOutboxPort(),
secrets: createSecretStore({ db, masterKey: MASTER_KEY }),
settings: createSettingsReader({ db, logger, events: bus }),
capabilities: createCapabilityRegistry({ logger }),
} as ServerContext;
const mod = createAuthSettingsModule({ setupTokenOnStart: false, engineSchema });
const app = createApp({
config,
logger,
bus,
db,
role: "all",
version: "0.0.0-test",
modules: [mod, ...(opts.extraModules ?? [])],
ctx,
});
return {
db,
app,
ctx,
bus,
lines,
cancel,
schema,
engineSchema,
async close() {
await ts.drop(engineSchema);
},
};
}
export type Harness = Awaited<ReturnType<typeof createHarness>>;
/** 极简 cookie jar:记住 Set-Cookie,请求时带上;写方法自动带 CSRF 头 */
export class Browser {
cookies = new Map<string, string>();
constructor(
private readonly app: Harness["app"],
readonly ip = "203.0.113.10",
) {}
absorb(res: Response) {
for (const sc of res.headers.getSetCookie()) {
const [pair] = sc.split(";");
const i = pair!.indexOf("=");
const k = pair!.slice(0, i).trim();
const v = pair!.slice(i + 1).trim();
if (!v || /max-age=0/i.test(sc)) this.cookies.delete(k);
else this.cookies.set(k, decodeURIComponent(v));
}
}
async req(
method: string,
path: string,
body?: unknown,
opts: { csrf?: boolean; headers?: Record<string, string> } = {},
) {
const headers: Record<string, string> = { "x-real-ip": this.ip, ...opts.headers };
if (this.cookies.size)
headers.cookie = [...this.cookies].map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join("; ");
if (body !== undefined) headers["content-type"] = "application/json";
const csrf = this.cookies.get("umt_csrf");
if (method !== "GET" && opts.csrf !== false && csrf) headers["x-csrf-token"] = csrf;
const res = await this.app.request(`/api/v1${path}`, {
method,
headers,
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
});
this.absorb(res);
return res;
}
}
/** Bearer 调用 */
export function bearer(app: Harness["app"], key: string) {
return (method: string, path: string, body?: unknown) =>
app.request(`/api/v1${path}`, {
method,
headers: {
authorization: `Bearer ${key}`,
...(body !== undefined ? { "content-type": "application/json" } : {}),
},
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
});
}