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

@@ -0,0 +1,74 @@
import { getSiteDefinition } from "@umt/adapters";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { resourceIdFromUrl } from "../../src/modules/browser/resource.ts";
import { type BrowserHarness, createBrowserHarness, ORIGIN, pgAvailable, READ_KEY } from "./harness.ts";
const def = getSiteDefinition("audiences")!;
describe("原站资源详情定位", () => {
it("仅按本站详情路径定位,不把列表、登录或别站同 id 当作资源", () => {
expect(resourceIdFromUrl(`${ORIGIN}/details.php?id=73`, `${ORIGIN}/`, def)).toBe("73");
expect(resourceIdFromUrl(`${ORIGIN}/torrents.php?id=73`, `${ORIGIN}/`, def)).toBeNull();
expect(resourceIdFromUrl(`${ORIGIN}/login.php?id=73`, `${ORIGIN}/`, def)).toBeNull();
expect(resourceIdFromUrl("https://else.example/details.php?id=73", `${ORIGIN}/`, def)).toBeNull();
});
});
const up = await pgAvailable();
describe.skipIf(!up)("原站详情页交接(隔离 PG 与浏览器 fixture)", () => {
let h: BrowserHarness;
let sid: string;
beforeAll(async () => {
h = await createBrowserHarness("resource", {
html: () => `<!doctype html><html><head><title>资源详情</title></head><body>
<h1 id="top">Example.Movie.2024.2160p.WEB-DL.DV</h1>
<table><tr><td class="rowhead">副标题</td><td class="rowfollow">测试译名与简介</td></tr>
<tr><td class="rowhead">基本信息</td><td class="rowfollow">大小: 2 GB 类型: 电影</td></tr></table>
<div id="kdescr">详情页提供的影片简介 <a href="https://www.imdb.com/title/tt1234567/">IMDb</a></div>
</body></html>`,
});
h.mock.loggedIn.add(`umt-site-${h.siteId}-acct-${h.accountId}`);
const session = await h.req("/browser/sessions", {
body: { siteId: h.siteId, url: `${ORIGIN}/details.php?id=73` },
});
expect(session.status).toBe(200);
sid = session.body.data.id;
}, 120_000);
afterAll(async () => {
await h?.close();
});
it("未在快照/搜索中的详情可直接登记,缓存PT内容供元数据兜底,不点击原站下载按钮", async () => {
const before = h.mock.actions.length;
const r = await h.req(`/browser/sessions/${sid}/resource`, { body: {} });
expect(r.status, JSON.stringify(r.body)).toBe(200);
expect(r.body.data.release).toMatchObject({
siteId: h.siteId,
siteReleaseId: "73",
title: "Example.Movie.2024.2160p.WEB-DL.DV",
});
expect(r.body.data.detailUrl).toBe(`${ORIGIN}/details.php?id=73`);
const row = await h.db
.selectFrom("source_release")
.selectAll()
.where("id", "=", r.body.data.release.id)
.executeTakeFirstOrThrow();
expect(row.detail_fetched_at).not.toBeNull();
expect(row.raw).toMatchObject({ _detail: { imdbId: "tt1234567", sourceUrl: `${ORIGIN}/details.php?id=73` } });
expect(h.mock.actions.slice(before).some((a) => a.action === "click")).toBe(false);
const again = await h.req(`/browser/sessions/${sid}/resource`, { body: {} });
expect(again.body.data.release.id).toBe(row.id);
});
it("使用当前页面而非旧会话地址;列表页明确提示进入详情", async () => {
const tab = [...h.mock.tabs.values()][0]!;
tab.url = `${ORIGIN}/torrents.php`;
const r = await h.req(`/browser/sessions/${sid}/resource`, { body: {} });
expect(r.status).toBe(400);
expect(r.body.error.message).toContain("详情页");
});
it("只读账号不能通过该入口登记下载资源", async () => {
const r = await h.req(`/browser/sessions/${sid}/resource`, { body: {}, key: READ_KEY });
expect(r.status).toBe(403);
});
});