test: 测试按行为命名、server 测试统一到 test/、PG 测试分组并行

- server 的 src/modules/*/__tests__ 挪到 test/<模块>/;e2e 去掉 eN- 前缀,
  w5a2 / v2 / search2 / *-dh1 / *-md1 / *-md2、web 的 agui3 / search2、domain 的 id2 / id3 改为按行为命名
- vitest 分三组:unit、pg(真实 PG + 假引擎,文件并行,最多 6 个)、serial(真实引擎 / worker / e2e,串行);
  pg 组用例超时放宽到 60s(迁移类用例并行时排队等 schema 锁)。server 测试 615s → 约 350s
- CLAUDE.md 补测试约定;待办补 harness 去重与 continuous-series 偶发超时(疑似 Slot 评估竞争)
- ADR-004 补充:维持 Deno,记录实际只用到 --allow-run 白名单

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-02 16:54:04 +08:00
1 parent b343dddbf6
commit b61335eaf6
75 files changed
+159 -115

No files matched your search

+147
View File
@@ -0,0 +1,147 @@
import { OpenAPIHono } from "@hono/zod-openapi";
import { describe, expect, it } from "vitest";
import type { AuthEnv, Principal, SessionVar } from "../../src/http/auth.ts";
import { ApiException, errorBody } from "../../src/http/errors.ts";
import {
basicAuth,
type DesktopDeps,
type DesktopRole,
deriveDesktopPasswords,
desktopUpstream,
registerDesktopRoutes,
sameOrigin,
} from "../../src/modules/desktop/desktop.ts";
// 桌面模式反代:口令派生与容器 init 脚本一致、角色 → 口令注入、只接受控制台会话、响应头与路径约束。
const SID = "7d3c1a52-9b0e-4b7a-8a51-0c2f7f7f0a11";
const KEY = "p0key";
function harness(
opts: { principal?: Partial<Principal>; role?: DesktopRole; check?: DesktopDeps["checkSession"] } = {},
) {
const calls: { url: string; headers: Headers; method: string }[] = [];
const app = new OpenAPIHono<AuthEnv>();
app.onError((err, c) => {
if (err instanceof ApiException) return c.json(errorBody(err.code, err.message), err.status as 400);
throw err;
});
app.use("*", async (c, next) => {
const p: Principal = { kind: "admin_session", id: "u1", name: "admin", scopes: ["admin"], ...opts.principal };
c.set("principal", p);
c.set("session", p.kind === "admin_session" ? ({ id: "s1" } as SessionVar) : undefined);
await next();
});
registerDesktopRoutes(app, {
upstream: "http://camofox:3000",
accessKey: KEY,
checkSession:
opts.check ??
(async (_id, holder) => {
expect(holder).toBe("admin_session:admin");
return { ok: true, role: opts.role ?? "viewer", leaseExpiresAt: null };
}),
consoleSessionValid: async () => true,
probeTtlMs: 0,
fetch: (async (input: string | URL | Request, init?: RequestInit) => {
const url = String(input);
calls.push({ url, headers: new Headers(init?.headers), method: init?.method ?? "GET" });
if (url.endsWith("/api/secret-401"))
return new Response("no", { status: 401, headers: { "www-authenticate": "Basic" } });
return new Response("ok", {
status: 200,
headers: { "content-type": "text/html", "www-authenticate": "Basic", "x-frame-options": "DENY" },
});
}) as typeof fetch,
});
const root = new OpenAPIHono<AuthEnv>();
root.onError((err, c) => {
if (err instanceof ApiException) return c.json(errorBody(err.code, err.message), err.status as 400);
throw err;
});
root.route("/api/v1", app);
return { app: root, calls };
}
describe("桌面口令与上游地址", () => {
it("口令与容器 init-umt-desktop 的 openssl HMAC 派生一致", () => {
// printf '%s' umt-desktop/control | openssl dgst -sha256 -hmac p0key -r | cut -c1-48
const p = deriveDesktopPasswords(KEY);
expect(p.controller).toBe("0b29251f95456b1e4612e3f4c45c2e353fd24e5ecbdf4959");
expect(p.viewer).toHaveLength(48);
expect(p.viewer).not.toBe(p.controller);
});
it("缺省由浏览器服务地址推出(端口换 3000),可显式覆盖", () => {
expect(desktopUpstream({ providerUrl: "http://camofox:9377" })).toBe("http://camofox:3000");
expect(desktopUpstream({ providerUrl: "http://camofox:9377", desktopUrl: "http://desk:3000/" })).toBe(
"http://desk:3000",
);
expect(desktopUpstream({})).toBeNull();
});
it("WebSocket 只接受同源页面", () => {
const req = (origin: string | null, host = "media.example.com") =>
new Request("https://media.example.com/x", { headers: { host, ...(origin ? { origin } : {}) } });
expect(sameOrigin(req("https://media.example.com"))).toBe(true);
expect(sameOrigin(req("https://evil.example"))).toBe(false);
expect(sameOrigin(req(null))).toBe(false);
});
});
describe("桌面反代", () => {
it("外部 API Key 不能用桌面模式", async () => {
const { app } = harness({ principal: { kind: "api_key" } });
const r = await app.request(`/api/v1/browser/desktop/${SID}/`);
expect(r.status).toBe(403);
});
it("状态:按租约给角色,并返回 iframe 地址", async () => {
const { app } = harness({ role: "controller" });
const r = await app.request(`/api/v1/browser/desktop/${SID}/_status`);
const body = (await r.json()) as { data: { available: boolean; role: string; path: string } };
expect(body.data).toMatchObject({ available: true, role: "controller", path: `/api/v1/browser/desktop/${SID}/` });
});
it("静态资源:不带口令、不转发 cookie;响应只允许本站嵌入,不外露认证质询", async () => {
const { app, calls } = harness();
const r = await app.request(`/api/v1/browser/desktop/${SID}/assets/index.js?v=1`, {
headers: { cookie: "umt_session=secret" },
});
expect(r.status).toBe(200);
expect(calls.at(-1)?.url).toBe("http://camofox:3000/assets/index.js?v=1");
expect(calls.at(-1)?.headers.get("authorization")).toBeNull();
expect(calls.at(-1)?.headers.get("cookie")).toBeNull();
expect(r.headers.get("content-security-policy")).toBe("frame-ancestors 'self'");
expect(r.headers.get("x-frame-options")).toBe("SAMEORIGIN");
expect(r.headers.get("www-authenticate")).toBeNull();
});
it("Selkies 接口:只读 / 控制口令按角色注入", async () => {
const pw = deriveDesktopPasswords(KEY);
for (const role of ["viewer", "controller"] as const) {
const { app, calls } = harness({ role });
await app.request(`/api/v1/browser/desktop/${SID}/api/websockets`);
expect(calls.at(-1)?.headers.get("authorization")).toBe(basicAuth(pw[role]));
}
});
it("上游 401 不透传质询,报口令不匹配", async () => {
const { app } = harness();
const r = await app.request(`/api/v1/browser/desktop/${SID}/api/secret-401`);
expect(r.status).toBe(502);
expect(r.headers.get("www-authenticate")).toBeNull();
});
it("会话不属于自带浏览器服务 → 409;非法会话 id → 404;WebSocket 跨源 → 403", async () => {
const { app } = harness({
check: async () => ({ ok: false, code: "unsupported", message: "只有部署自带的浏览器服务支持桌面模式" }),
});
expect((await app.request(`/api/v1/browser/desktop/${SID}/api/websockets`)).status).toBe(409);
expect((await app.request("/api/v1/browser/desktop/not-a-uuid/")).status).toBe(404);
const ws = await harness().app.request(`/api/v1/browser/desktop/${SID}/api/websockets`, {
headers: { upgrade: "websocket", origin: "https://evil.example", host: "media.example.com" },
});
expect(ws.status).toBe(403);
});
});