From b61335eaf6ff4aa2e08a4aaed6ff300040c85fb7 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 2 Oct 2026 16:54:04 +0800 Subject: [PATCH] =?UTF-8?q?test:=20=E6=B5=8B=E8=AF=95=E6=8C=89=E8=A1=8C?= =?UTF-8?q?=E4=B8=BA=E5=91=BD=E5=90=8D=E3=80=81server=20=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=88=B0=20test/=E3=80=81PG=20=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=88=86=E7=BB=84=E5=B9=B6=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- CLAUDE.md | 8 ++++ .../background-loop.test.ts | 8 ++-- .../classification.test.ts | 10 ++--- .../download-board-plans.test.ts | 2 +- .../download-board.test.ts | 2 +- .../enqueue-recovery.test.ts | 4 +- .../acquisition-organize}/harness.ts | 22 +++++----- .../library-versions.test.ts | 2 +- .../manual-acquisition.test.ts | 6 +-- .../acquisition-organize}/naming-api.test.ts | 2 +- .../acquisition-organize}/organize-fs.test.ts | 2 +- .../organize-guard.test.ts | 4 +- .../organize-move.test.ts | 6 +-- .../organize-sources.test.ts | 6 +-- .../records-filter.test.ts | 0 .../acquisition-organize}/replacement.test.ts | 2 +- .../acquisition-organize}/rules-api.test.ts | 4 +- .../acquisition-organize}/scrape.test.ts | 4 +- .../subscription.test.ts | 8 ++-- .../acquisition-organize}/takeover.test.ts | 8 ++-- .../acquisition-organize}/vertical.test.ts | 8 ++-- .../browser}/browser-api.test.ts | 6 +-- .../browser}/browser-cloudflare.test.ts | 4 +- .../browser}/browser-credentials.test.ts | 2 +- .../browser}/browser-e2e.test.ts | 0 .../browser}/browser-identity.test.ts | 6 +-- .../browser}/browser-keepalive.test.ts | 4 +- .../browser}/browser-provider-config.test.ts | 4 +- .../browser}/browser-reconnect.test.ts | 0 .../browser}/browser-resource.test.ts | 2 +- .../__tests__ => test/browser}/harness.ts | 30 ++++++------- .../desktop}/desktop.test.ts | 6 +-- ...elegate.test.ts => agent-delegate.test.ts} | 0 ...test.ts => agent-prompt-eval.real.test.ts} | 2 +- ...eval.test.ts => agent-prompt-eval.test.ts} | 2 +- ...-quality.test.ts => agent-quality.test.ts} | 0 ...igate.test.ts => auto-investigate.test.ts} | 0 ...o-recover.test.ts => auto-recover.test.ts} | 0 ...s.test.ts => background-continues.test.ts} | 0 ...ries.test.ts => continuous-series.test.ts} | 0 ...ecovery.test.ts => crash-recovery.test.ts} | 0 ...nteraction.test.ts => interaction.test.ts} | 0 ...s => plan-and-inventory-endpoints.test.ts} | 0 ...library.test.ts => rss-to-library.test.ts} | 0 ...11-subagents.test.ts => subagents.test.ts} | 0 ...ag-removal.test.ts => tag-removal.test.ts} | 0 ...e7-tv-series.test.ts => tv-series.test.ts} | 0 .../{e14-vision.test.ts => vision.test.ts} | 0 ...13-websearch.test.ts => websearch.test.ts} | 0 ...test.ts => copy-dir-and-overrides.test.ts} | 0 apps/server/test/file-manager/harness.ts | 2 +- .../{v2.test.ts => organize-options.test.ts} | 0 .../test/inventory/media-servers.test.ts | 2 +- apps/server/test/rules/rule-groups.test.ts | 2 +- ...arch2.test.ts => two-stage-search.test.ts} | 0 ...giene-dh1.test.ts => data-hygiene.test.ts} | 0 ....ts => metadata-language-fallback.test.ts} | 0 ...-md2.test.ts => overview-fallback.test.ts} | 0 .../subscriptions/subscriptions-c1a.test.ts | 2 +- .../subscriptions/subscriptions-v2.test.ts | 2 +- apps/server/vitest.config.ts | 44 +++++++++++++++---- ...agui3.test.ts => agent-session-ui.test.ts} | 0 ...rch2.test.ts => search-results-ui.test.ts} | 0 docs/adr/ADR-004-deno-runtime.md | 7 +++ docs/design/agent-prompt-eval.md | 4 +- docs/plan/README.md | 3 ++ docs/porting/moviepilot-v3-comparison.md | 2 +- docs/runbook/agent.md | 4 +- docs/runbook/browser.md | 6 +-- docs/runbook/identity.md | 2 +- docs/runbook/search.md | 2 +- docs/runbook/sites.md | 2 +- docs/runbook/websearch.md | 2 +- ...-id2.test.ts => identity-evidence.test.ts} | 0 ...-id3.test.ts => identity-site-ids.test.ts} | 0 75 files changed, 159 insertions(+), 115 deletions(-) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/background-loop.test.ts (93%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/classification.test.ts (94%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/download-board-plans.test.ts (99%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/download-board.test.ts (98%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/enqueue-recovery.test.ts (98%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/harness.ts (94%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/library-versions.test.ts (99%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/manual-acquisition.test.ts (95%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/naming-api.test.ts (98%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/organize-fs.test.ts (98%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/organize-guard.test.ts (98%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/organize-move.test.ts (96%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/organize-sources.test.ts (97%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/records-filter.test.ts (100%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/replacement.test.ts (99%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/rules-api.test.ts (98%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/scrape.test.ts (98%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/subscription.test.ts (97%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/takeover.test.ts (97%) rename apps/server/{src/modules/acquisition-organize/__tests__ => test/acquisition-organize}/vertical.test.ts (98%) rename apps/server/{src/modules/browser/__tests__ => test/browser}/browser-api.test.ts (98%) rename apps/server/{src/modules/browser/__tests__ => test/browser}/browser-cloudflare.test.ts (98%) rename apps/server/{src/modules/browser/__tests__ => test/browser}/browser-credentials.test.ts (98%) rename apps/server/{src/modules/browser/__tests__ => test/browser}/browser-e2e.test.ts (100%) rename apps/server/{src/modules/browser/__tests__ => test/browser}/browser-identity.test.ts (92%) rename apps/server/{src/modules/browser/__tests__ => test/browser}/browser-keepalive.test.ts (98%) rename apps/server/{src/modules/browser/__tests__ => test/browser}/browser-provider-config.test.ts (97%) rename apps/server/{src/modules/browser/__tests__ => test/browser}/browser-reconnect.test.ts (100%) rename apps/server/{src/modules/browser/__tests__ => test/browser}/browser-resource.test.ts (97%) rename apps/server/{src/modules/browser/__tests__ => test/browser}/harness.ts (88%) rename apps/server/{src/modules/desktop/__tests__ => test/desktop}/desktop.test.ts (97%) rename apps/server/test/e2e/{e2-agent-delegate.test.ts => agent-delegate.test.ts} (100%) rename apps/server/test/e2e/{e16-agent-prompt-eval.real.test.ts => agent-prompt-eval.real.test.ts} (98%) rename apps/server/test/e2e/{e16-agent-prompt-eval.test.ts => agent-prompt-eval.test.ts} (98%) rename apps/server/test/e2e/{e15-agent-quality.test.ts => agent-quality.test.ts} (100%) rename apps/server/test/e2e/{e5-auto-investigate.test.ts => auto-investigate.test.ts} (100%) rename apps/server/test/e2e/{e6-auto-recover.test.ts => auto-recover.test.ts} (100%) rename apps/server/test/e2e/{e3-background-continues.test.ts => background-continues.test.ts} (100%) rename apps/server/test/e2e/{e9-continuous-series.test.ts => continuous-series.test.ts} (100%) rename apps/server/test/e2e/{e4-crash-recovery.test.ts => crash-recovery.test.ts} (100%) rename apps/server/test/e2e/{e12-interaction.test.ts => interaction.test.ts} (100%) rename apps/server/test/e2e/{e10-w4a-endpoints.test.ts => plan-and-inventory-endpoints.test.ts} (100%) rename apps/server/test/e2e/{e1-rss-to-library.test.ts => rss-to-library.test.ts} (100%) rename apps/server/test/e2e/{e11-subagents.test.ts => subagents.test.ts} (100%) rename apps/server/test/e2e/{e8-tag-removal.test.ts => tag-removal.test.ts} (100%) rename apps/server/test/e2e/{e7-tv-series.test.ts => tv-series.test.ts} (100%) rename apps/server/test/e2e/{e14-vision.test.ts => vision.test.ts} (100%) rename apps/server/test/e2e/{e13-websearch.test.ts => websearch.test.ts} (100%) rename apps/server/test/file-manager/{w5a2.test.ts => copy-dir-and-overrides.test.ts} (100%) rename apps/server/test/file-manager/{v2.test.ts => organize-options.test.ts} (100%) rename apps/server/test/search/{search2.test.ts => two-stage-search.test.ts} (100%) rename apps/server/test/sources-identity/{data-hygiene-dh1.test.ts => data-hygiene.test.ts} (100%) rename apps/server/test/sources-identity/{metadata-md1.test.ts => metadata-language-fallback.test.ts} (100%) rename apps/server/test/sources-identity/{overview-md2.test.ts => overview-fallback.test.ts} (100%) rename apps/web/app/components/agent/{agui3.test.ts => agent-session-ui.test.ts} (100%) rename apps/web/app/components/search/{search2.test.ts => search-results-ui.test.ts} (100%) rename packages/domain/test/{identity-id2.test.ts => identity-evidence.test.ts} (100%) rename packages/domain/test/{identity-id3.test.ts => identity-site-ids.test.ts} (100%) diff --git a/CLAUDE.md b/CLAUDE.md index 5c6ee1a7..e21e543d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,6 +27,14 @@ deno task release # 测试环境发布,见下 - 界面文案守 `docs/design/glossary.md` 的术语与标点(`deno task check:copy`);状态色只表示状态(`StatusBadge` 带圆点,别拿来显示数量)。 - 主工作区不留常驻 dev 进程(`--watch` 会改写 `deno.lock`);构建前确认 `deno.lock` 没被改。 +## 测试 + +- 位置:server 的测试统一放 `apps/server/test/<模块>/`(不再用 `src/**/__tests__`);web 与 adapters 测试和源码放同目录 `*.test.ts`;其它包放 `test/`。 +- 命名按测的行为(`crash-recovery.test.ts`、`two-stage-search.test.ts`),不要用任务编号(`e4-`、`w5a2`、`-md1`)。 +- server 分三组(`apps/server/vitest.config.ts`):`unit` 不碰 PG;`pg` 真实 PG + 假引擎,文件并行;`serial` 起真实引擎 / worker / 子进程的集成与 e2e,串行。新文件缺省进 `pg`,起真实 worker 的必须加进 `SERIAL`。 +- 每个 PG 测试文件用自己的 schema;等待异步结果用轮询到条件,不写固定 sleep。 +- 真机测试用 `*.real.test.ts`,靠环境变量开启,缺省跳过。 + ## 文档放哪 - 正在推进的计划与待办:`docs/plan/README.md`;做完的计划、验收、发布记录直接删除(git 历史可查),不留归档。 diff --git a/apps/server/src/modules/acquisition-organize/__tests__/background-loop.test.ts b/apps/server/test/acquisition-organize/background-loop.test.ts similarity index 93% rename from apps/server/src/modules/acquisition-organize/__tests__/background-loop.test.ts rename to apps/server/test/acquisition-organize/background-loop.test.ts index c3cc6efc..c274daae 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/background-loop.test.ts +++ b/apps/server/test/acquisition-organize/background-loop.test.ts @@ -1,10 +1,10 @@ import { sql } from "@umt/db"; import { createMockQb, createTempTree, type MockQb, makeTorrent, type TempTree } from "@umt/testkit"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { createSchedulesModule } from "../../schedules/index.ts"; -import { evaluateAcquisition } from "../services/acquisition.ts"; -import { submitDownload, syncDownloads } from "../services/downloads.ts"; -import { executeRecord, planImport } from "../services/organize.ts"; +import { createSchedulesModule } from "../../src/modules/schedules/index.ts"; +import { evaluateAcquisition } from "../../src/modules/acquisition-organize/services/acquisition.ts"; +import { submitDownload, syncDownloads } from "../../src/modules/acquisition-organize/services/downloads.ts"; +import { executeRecord, planImport } from "../../src/modules/acquisition-organize/services/organize.ts"; import { createHarness, fakeDisk, diff --git a/apps/server/src/modules/acquisition-organize/__tests__/classification.test.ts b/apps/server/test/acquisition-organize/classification.test.ts similarity index 94% rename from apps/server/src/modules/acquisition-organize/__tests__/classification.test.ts rename to apps/server/test/acquisition-organize/classification.test.ts index 80f60e17..dc5c3054 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/classification.test.ts +++ b/apps/server/test/acquisition-organize/classification.test.ts @@ -2,11 +2,11 @@ import { OpenAPIHono } from "@hono/zod-openapi"; import type { TmdbClient } from "@umt/adapters"; import { DEFAULT_CLASSIFICATION_YAML } from "@umt/domain"; import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest"; -import type { AuthEnv } from "../../../http/auth.ts"; -import { createSettingsReader } from "../../auth-settings/provided.ts"; -import { registerSettingsRoutes } from "../../auth-settings/settings-routes.ts"; -import { SourcesRuntime } from "../../sources-identity/runtime.ts"; -import { classifyEntity } from "../services/classification.ts"; +import type { AuthEnv } from "../../src/http/auth.ts"; +import { createSettingsReader } from "../../src/modules/auth-settings/provided.ts"; +import { registerSettingsRoutes } from "../../src/modules/auth-settings/settings-routes.ts"; +import { SourcesRuntime } from "../../src/modules/sources-identity/runtime.ts"; +import { classifyEntity } from "../../src/modules/acquisition-organize/services/classification.ts"; import { createHarness, type Harness, pgAvailable, seedEntity } from "./harness.ts"; const hasPg = await pgAvailable(); diff --git a/apps/server/src/modules/acquisition-organize/__tests__/download-board-plans.test.ts b/apps/server/test/acquisition-organize/download-board-plans.test.ts similarity index 99% rename from apps/server/src/modules/acquisition-organize/__tests__/download-board-plans.test.ts rename to apps/server/test/acquisition-organize/download-board-plans.test.ts index 3ec38436..97f8361b 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/download-board-plans.test.ts +++ b/apps/server/test/acquisition-organize/download-board-plans.test.ts @@ -294,7 +294,7 @@ describe.skipIf(!hasPg)("DL-3 下载计划并入下载面板(真实 PG)", () => }); it("审批被拒 / 过期:草案跟着结束,不再算进行中", async () => { - const { closeDraftPlan } = await import("../../agent/interaction.ts"); + const { closeDraftPlan } = await import("../../src/modules/agent/interaction.ts"); await closeDraftPlan(h.db, { plan_type: "acquisition_plan", plan_id: ids.draft }, "rejected"); await closeDraftPlan(h.db, { plan_type: "acquisition_plan", plan_id: ids.agentDraft }, "expired"); // 不是草案的不动 diff --git a/apps/server/src/modules/acquisition-organize/__tests__/download-board.test.ts b/apps/server/test/acquisition-organize/download-board.test.ts similarity index 98% rename from apps/server/src/modules/acquisition-organize/__tests__/download-board.test.ts rename to apps/server/test/acquisition-organize/download-board.test.ts index 4cfc19c1..7c090a4d 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/download-board.test.ts +++ b/apps/server/test/acquisition-organize/download-board.test.ts @@ -1,6 +1,6 @@ import { createMockQb, type MockQb } from "@umt/testkit"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { syncDownloads } from "../services/downloads.ts"; +import { syncDownloads } from "../../src/modules/acquisition-organize/services/downloads.ts"; import { createHarness, type Harness, pgAvailable, seedEntity } from "./harness.ts"; // DL-2 下载面板:进行中 / 已完成分组与计数、每个种子的实时速度 / 剩余时间 / 连接数、下载器总速度、 diff --git a/apps/server/src/modules/acquisition-organize/__tests__/enqueue-recovery.test.ts b/apps/server/test/acquisition-organize/enqueue-recovery.test.ts similarity index 98% rename from apps/server/src/modules/acquisition-organize/__tests__/enqueue-recovery.test.ts rename to apps/server/test/acquisition-organize/enqueue-recovery.test.ts index d91f3f40..2759fce7 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/enqueue-recovery.test.ts +++ b/apps/server/test/acquisition-organize/enqueue-recovery.test.ts @@ -2,8 +2,8 @@ import type { TaskRunView } from "@umt/tasks"; import { createTempTree, type TempTree } from "@umt/testkit"; import { sql } from "kysely"; import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest"; -import { enqueueSweep, enqueueTracked } from "../services/common.ts"; -import { type QueueScrapeInput, queueScrape, runScrapeRecord } from "../services/scrape.ts"; +import { enqueueSweep, enqueueTracked } from "../../src/modules/acquisition-organize/services/common.ts"; +import { type QueueScrapeInput, queueScrape, runScrapeRecord } from "../../src/modules/acquisition-organize/services/scrape.ts"; import { createHarness, type Harness, pgAvailable, seedBase, seedEntity } from "./harness.ts"; const hasPg = await pgAvailable(); diff --git a/apps/server/src/modules/acquisition-organize/__tests__/harness.ts b/apps/server/test/acquisition-organize/harness.ts similarity index 94% rename from apps/server/src/modules/acquisition-organize/__tests__/harness.ts rename to apps/server/test/acquisition-organize/harness.ts index c04b34fc..f95eb87f 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/harness.ts +++ b/apps/server/test/acquisition-organize/harness.ts @@ -7,17 +7,17 @@ import { type DB, migrateToLatest } from "@umt/db"; import type { TaskEngine } from "@umt/tasks"; import { Kysely, PostgresDialect, sql } from "kysely"; import pg from "pg"; -import { ConfigSchema } from "../../../config.ts"; -import { createOutboxPort } from "../../../context.ts"; -import { createApp } from "../../../http/app.ts"; -import { hashApiKey } from "../../../http/auth.ts"; -import { EventBus } from "../../../http/sse.ts"; -import { createLogger } from "../../../logger.ts"; -import { createCapabilityRegistry } from "../../agent/capability-registry.ts"; -import { createSourcesIdentityModule } from "../../sources-identity/index.ts"; -import type { ServerContext } from "../../types.ts"; -import { type AoOverrides, createAcquisitionOrganizeModule } from "../index.ts"; -import type { AoDeps } from "../services/common.ts"; +import { ConfigSchema } from "../../src/config.ts"; +import { createOutboxPort } from "../../src/context.ts"; +import { createApp } from "../../src/http/app.ts"; +import { hashApiKey } from "../../src/http/auth.ts"; +import { EventBus } from "../../src/http/sse.ts"; +import { createLogger } from "../../src/logger.ts"; +import { createCapabilityRegistry } from "../../src/modules/agent/capability-registry.ts"; +import { createSourcesIdentityModule } from "../../src/modules/sources-identity/index.ts"; +import type { ServerContext } from "../../src/modules/types.ts"; +import { type AoOverrides, createAcquisitionOrganizeModule } from "../../src/modules/acquisition-organize/index.ts"; +import type { AoDeps } from "../../src/modules/acquisition-organize/services/common.ts"; // 真实 PG(独立 schema)+ mock qB + 临时目录 的测试装配;引擎用记录入队的假实现,测试直接驱动服务函数。 diff --git a/apps/server/src/modules/acquisition-organize/__tests__/library-versions.test.ts b/apps/server/test/acquisition-organize/library-versions.test.ts similarity index 99% rename from apps/server/src/modules/acquisition-organize/__tests__/library-versions.test.ts rename to apps/server/test/acquisition-organize/library-versions.test.ts index 1c799f93..2dee71d2 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/library-versions.test.ts +++ b/apps/server/test/acquisition-organize/library-versions.test.ts @@ -1,7 +1,7 @@ import type { DB } from "@umt/db"; import type { Kysely } from "kysely"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { backfillVersionSpecs } from "../services/version-backfill.ts"; +import { backfillVersionSpecs } from "../../src/modules/acquisition-organize/services/version-backfill.ts"; import { createHarness, type Harness, pgAvailable, seedEntity, seedRelease, seedSlot } from "./harness.ts"; // LIBV-1:入库版本 API(每集多个版本、季汇总、单目标比对)与规格回填(dry-run / apply)。 diff --git a/apps/server/src/modules/acquisition-organize/__tests__/manual-acquisition.test.ts b/apps/server/test/acquisition-organize/manual-acquisition.test.ts similarity index 95% rename from apps/server/src/modules/acquisition-organize/__tests__/manual-acquisition.test.ts rename to apps/server/test/acquisition-organize/manual-acquisition.test.ts index bcbe23fc..ae031645 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/manual-acquisition.test.ts +++ b/apps/server/test/acquisition-organize/manual-acquisition.test.ts @@ -1,9 +1,9 @@ import { ManualAcquisitionRequest } from "@umt/contracts"; import { createMockQb, createTempTree, makeTorrent } from "@umt/testkit"; import { describe, expect, it } from "vitest"; -import { evaluateAcquisition } from "../services/acquisition.ts"; -import { submitDownload } from "../services/downloads.ts"; -import { planManualAcquisition } from "../services/manual-acquisition.ts"; +import { evaluateAcquisition } from "../../src/modules/acquisition-organize/services/acquisition.ts"; +import { submitDownload } from "../../src/modules/acquisition-organize/services/downloads.ts"; +import { planManualAcquisition } from "../../src/modules/acquisition-organize/services/manual-acquisition.ts"; import { createHarness, fakeDisk, pgAvailable, seedBase, seedEntity, seedRelease, seedSlot } from "./harness.ts"; const hasPg = await pgAvailable(); diff --git a/apps/server/src/modules/acquisition-organize/__tests__/naming-api.test.ts b/apps/server/test/acquisition-organize/naming-api.test.ts similarity index 98% rename from apps/server/src/modules/acquisition-organize/__tests__/naming-api.test.ts rename to apps/server/test/acquisition-organize/naming-api.test.ts index 40e06d31..89060d60 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/naming-api.test.ts +++ b/apps/server/test/acquisition-organize/naming-api.test.ts @@ -2,7 +2,7 @@ import { createHash } from "node:crypto"; import { DEFAULT_NAMING_TEMPLATES } from "@umt/contracts"; import { LEGACY_V1_NAMING, MP_DEFAULT_NAMING, namingCanonicalJson } from "@umt/domain"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { ensureNamingVersion, namingByVersion } from "../services/naming.ts"; +import { ensureNamingVersion, namingByVersion } from "../../src/modules/acquisition-organize/services/naming.ts"; import { createHarness, type Harness, pgAvailable, seedBase, seedEntity } from "./harness.ts"; // W9-C 命名模板:变量表、预览(样本/错误/路径绑定)、三级模板读写与保存前校验、模板版本登记。 diff --git a/apps/server/src/modules/acquisition-organize/__tests__/organize-fs.test.ts b/apps/server/test/acquisition-organize/organize-fs.test.ts similarity index 98% rename from apps/server/src/modules/acquisition-organize/__tests__/organize-fs.test.ts rename to apps/server/test/acquisition-organize/organize-fs.test.ts index 54d05575..cced2c61 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/organize-fs.test.ts +++ b/apps/server/test/acquisition-organize/organize-fs.test.ts @@ -4,7 +4,7 @@ import { readdir, readFile, rm, utimes, writeFile } from "node:fs/promises"; import { dirname, join } from "node:path"; import { createTempTree, type TempTree } from "@umt/testkit"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { executeRecord, type FrozenPlan, planImport } from "../services/organize.ts"; +import { executeRecord, type FrozenPlan, planImport } from "../../src/modules/acquisition-organize/services/organize.ts"; import { createHarness, type Harness, pgAvailable, seedBase, seedEntity } from "./harness.ts"; // W4-E4 执行侧:replace_with_verify 接线(写临时→校验→原子替换,旧文件进回收目录)、写前清 partial、 diff --git a/apps/server/src/modules/acquisition-organize/__tests__/organize-guard.test.ts b/apps/server/test/acquisition-organize/organize-guard.test.ts similarity index 98% rename from apps/server/src/modules/acquisition-organize/__tests__/organize-guard.test.ts rename to apps/server/test/acquisition-organize/organize-guard.test.ts index 55e57b28..10c44f48 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/organize-guard.test.ts +++ b/apps/server/test/acquisition-organize/organize-guard.test.ts @@ -4,8 +4,8 @@ import { join } from "node:path"; import { sql } from "@umt/db"; import { createMockQb, createTempTree, type MockQb, makeTorrent, type TempTree } from "@umt/testkit"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { evaluateAcquisition } from "../services/acquisition.ts"; -import { submitDownload, syncDownloads } from "../services/downloads.ts"; +import { evaluateAcquisition } from "../../src/modules/acquisition-organize/services/acquisition.ts"; +import { submitDownload, syncDownloads } from "../../src/modules/acquisition-organize/services/downloads.ts"; import { createHarness, EXEC_KEY, diff --git a/apps/server/src/modules/acquisition-organize/__tests__/organize-move.test.ts b/apps/server/test/acquisition-organize/organize-move.test.ts similarity index 96% rename from apps/server/src/modules/acquisition-organize/__tests__/organize-move.test.ts rename to apps/server/test/acquisition-organize/organize-move.test.ts index ce41163b..3193037d 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/organize-move.test.ts +++ b/apps/server/test/acquisition-organize/organize-move.test.ts @@ -12,9 +12,9 @@ import { type TempTree, } from "@umt/testkit"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { evaluateAcquisition } from "../services/acquisition.ts"; -import { submitDownload, syncDownloads } from "../services/downloads.ts"; -import { executeRecord, type FrozenPlan, planImport } from "../services/organize.ts"; +import { evaluateAcquisition } from "../../src/modules/acquisition-organize/services/acquisition.ts"; +import { submitDownload, syncDownloads } from "../../src/modules/acquisition-organize/services/downloads.ts"; +import { executeRecord, type FrozenPlan, planImport } from "../../src/modules/acquisition-organize/services/organize.ts"; import { createHarness, EXEC_KEY, diff --git a/apps/server/src/modules/acquisition-organize/__tests__/organize-sources.test.ts b/apps/server/test/acquisition-organize/organize-sources.test.ts similarity index 97% rename from apps/server/src/modules/acquisition-organize/__tests__/organize-sources.test.ts rename to apps/server/test/acquisition-organize/organize-sources.test.ts index 7adf1147..d5d7e2fc 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/organize-sources.test.ts +++ b/apps/server/test/acquisition-organize/organize-sources.test.ts @@ -4,9 +4,9 @@ import type { TmdbClient } from "@umt/adapters"; import { DEFAULT_SETTINGS, type OrganizeSourceConfig } from "@umt/contracts"; import { createMockQb, createTempTree, type MockQb, type TempTree } from "@umt/testkit"; import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest"; -import { SourcesRuntime } from "../../sources-identity/runtime.ts"; -import { syncDownloads } from "../services/downloads.ts"; -import { executeRecord, planImport } from "../services/organize.ts"; +import { SourcesRuntime } from "../../src/modules/sources-identity/runtime.ts"; +import { syncDownloads } from "../../src/modules/acquisition-organize/services/downloads.ts"; +import { executeRecord, planImport } from "../../src/modules/acquisition-organize/services/organize.ts"; import { createHarness, type Harness, pgAvailable, seedBase, seedEntity } from "./harness.ts"; const hasPg = await pgAvailable(); diff --git a/apps/server/src/modules/acquisition-organize/__tests__/records-filter.test.ts b/apps/server/test/acquisition-organize/records-filter.test.ts similarity index 100% rename from apps/server/src/modules/acquisition-organize/__tests__/records-filter.test.ts rename to apps/server/test/acquisition-organize/records-filter.test.ts diff --git a/apps/server/src/modules/acquisition-organize/__tests__/replacement.test.ts b/apps/server/test/acquisition-organize/replacement.test.ts similarity index 99% rename from apps/server/src/modules/acquisition-organize/__tests__/replacement.test.ts rename to apps/server/test/acquisition-organize/replacement.test.ts index 4f7d5566..25757db9 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/replacement.test.ts +++ b/apps/server/test/acquisition-organize/replacement.test.ts @@ -5,7 +5,7 @@ import { dirname } from "node:path"; import { DEFAULT_SETTINGS } from "@umt/contracts"; import { createMockQb, createTempTree, type MockQb, type TempTree } from "@umt/testkit"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; -import { executeRecord, planImport } from "../services/organize.ts"; +import { executeRecord, planImport } from "../../src/modules/acquisition-organize/services/organize.ts"; import { createHarness, type Harness, pgAvailable, seedBase, seedEntity } from "./harness.ts"; const hasPg = await pgAvailable(); diff --git a/apps/server/src/modules/acquisition-organize/__tests__/rules-api.test.ts b/apps/server/test/acquisition-organize/rules-api.test.ts similarity index 98% rename from apps/server/src/modules/acquisition-organize/__tests__/rules-api.test.ts rename to apps/server/test/acquisition-organize/rules-api.test.ts index bbc68605..0bbb8b4b 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/rules-api.test.ts +++ b/apps/server/test/acquisition-organize/rules-api.test.ts @@ -1,7 +1,7 @@ import { makeTorrent } from "@umt/testkit"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { createAcquisitionOrganizeModule } from "../index.ts"; -import { evaluateAcquisition } from "../services/acquisition.ts"; +import { createAcquisitionOrganizeModule } from "../../src/modules/acquisition-organize/index.ts"; +import { evaluateAcquisition } from "../../src/modules/acquisition-organize/services/acquisition.ts"; import { createHarness, fakeDisk, type Harness, pgAvailable, seedBase, seedEntity, seedRelease } from "./harness.ts"; // 规则 API:订阅/Slot/策略版本(发布=持续授权、回滚)/偏好标签抑制;新作品收录走已发布策略;outbox 路由。 diff --git a/apps/server/src/modules/acquisition-organize/__tests__/scrape.test.ts b/apps/server/test/acquisition-organize/scrape.test.ts similarity index 98% rename from apps/server/src/modules/acquisition-organize/__tests__/scrape.test.ts rename to apps/server/test/acquisition-organize/scrape.test.ts index 5d60f1ea..c1ad01de 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/scrape.test.ts +++ b/apps/server/test/acquisition-organize/scrape.test.ts @@ -2,8 +2,8 @@ import { chmod, readdir, readFile, stat, writeFile } from "node:fs/promises"; import { join, relative } from "node:path"; import { createTempTree, type TempTree } from "@umt/testkit"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { executeRecord, planImport } from "../services/organize.ts"; -import { entityScrapeLocations, runScrapeRecord, SCRAPE_TASK, scrapeEntity } from "../services/scrape.ts"; +import { executeRecord, planImport } from "../../src/modules/acquisition-organize/services/organize.ts"; +import { entityScrapeLocations, runScrapeRecord, SCRAPE_TASK, scrapeEntity } from "../../src/modules/acquisition-organize/services/scrape.ts"; import { createHarness, type Harness, pgAvailable, seedBase, seedEntity } from "./harness.ts"; // 刮削(SCR-1 / SCR-2):入库成功后自动对作品目录排 scrapeMedia → 写 NFO 与图片(临时库根);只补缺失 / 全部覆盖; diff --git a/apps/server/src/modules/acquisition-organize/__tests__/subscription.test.ts b/apps/server/test/acquisition-organize/subscription.test.ts similarity index 97% rename from apps/server/src/modules/acquisition-organize/__tests__/subscription.test.ts rename to apps/server/test/acquisition-organize/subscription.test.ts index 78277b5a..9a2630ce 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/subscription.test.ts +++ b/apps/server/test/acquisition-organize/subscription.test.ts @@ -1,10 +1,10 @@ import { createQbClient } from "@umt/adapters"; import { createMockQb, createTempTree, type MockQb, makeTorrent, type TempTree } from "@umt/testkit"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { evaluateAcquisition } from "../services/acquisition.ts"; -import { submitDownload, syncDownloads } from "../services/downloads.ts"; -import { executeRecord, planImport } from "../services/organize.ts"; -import { applyEpisodeTotal, subscriptionProgress, sweepDueSlots } from "../services/subscriptions.ts"; +import { evaluateAcquisition } from "../../src/modules/acquisition-organize/services/acquisition.ts"; +import { submitDownload, syncDownloads } from "../../src/modules/acquisition-organize/services/downloads.ts"; +import { executeRecord, planImport } from "../../src/modules/acquisition-organize/services/organize.ts"; +import { applyEpisodeTotal, subscriptionProgress, sweepDueSlots } from "../../src/modules/acquisition-organize/services/subscriptions.ts"; import { createHarness, fakeDisk, type Harness, pgAvailable, seedBase, seedEntity, seedRelease } from "./harness.ts"; // W5-C 订阅增强集成:Slot×单元多在途 + 忙时延后重评 + 移除释放;季包按缺集选文件(mock qB files/filePrio); diff --git a/apps/server/src/modules/acquisition-organize/__tests__/takeover.test.ts b/apps/server/test/acquisition-organize/takeover.test.ts similarity index 97% rename from apps/server/src/modules/acquisition-organize/__tests__/takeover.test.ts rename to apps/server/test/acquisition-organize/takeover.test.ts index 3a5f280f..f0ad8a66 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/takeover.test.ts +++ b/apps/server/test/acquisition-organize/takeover.test.ts @@ -2,10 +2,10 @@ import { existsSync, readdirSync, statSync } from "node:fs"; import { join } from "node:path"; import { createMockQb, createTempTree, type MockQb, type TempTree } from "@umt/testkit"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { addAliasIfAbsent } from "../../sources-identity/service/entities.ts"; -import { syncDownloads } from "../services/downloads.ts"; -import { executeRecord, planImport } from "../services/organize.ts"; -import { organizeDecisionHook, pickMainVideo, takeoverParsed } from "../services/takeover.ts"; +import { addAliasIfAbsent } from "../../src/modules/sources-identity/service/entities.ts"; +import { syncDownloads } from "../../src/modules/acquisition-organize/services/downloads.ts"; +import { executeRecord, planImport } from "../../src/modules/acquisition-organize/services/organize.ts"; +import { organizeDecisionHook, pickMainVideo, takeoverParsed } from "../../src/modules/acquisition-organize/services/takeover.ts"; import { createHarness, type Harness, pgAvailable, seedBase, seedEntity } from "./harness.ts"; // QB-1:接管任务(用户手动加进 qB、贴管理标签,没有站点资源)完成后按 种子名 + 主视频文件名 识别 → 直接整理; diff --git a/apps/server/src/modules/acquisition-organize/__tests__/vertical.test.ts b/apps/server/test/acquisition-organize/vertical.test.ts similarity index 98% rename from apps/server/src/modules/acquisition-organize/__tests__/vertical.test.ts rename to apps/server/test/acquisition-organize/vertical.test.ts index a3606902..e737b461 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/vertical.test.ts +++ b/apps/server/test/acquisition-organize/vertical.test.ts @@ -4,10 +4,10 @@ import { join } from "node:path"; import { sql } from "@umt/db"; import { createMockQb, createTempTree, type MockQb, makeTorrent, type TempTree } from "@umt/testkit"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { evaluateAcquisition } from "../services/acquisition.ts"; -import { cleanupDownloadCache, submitDownload, syncDownloads } from "../services/downloads.ts"; -import { executeRecord, planImport } from "../services/organize.ts"; -import { reconcileLibrary } from "../services/reconcile.ts"; +import { evaluateAcquisition } from "../../src/modules/acquisition-organize/services/acquisition.ts"; +import { cleanupDownloadCache, submitDownload, syncDownloads } from "../../src/modules/acquisition-organize/services/downloads.ts"; +import { executeRecord, planImport } from "../../src/modules/acquisition-organize/services/organize.ts"; +import { reconcileLibrary } from "../../src/modules/acquisition-organize/services/reconcile.ts"; import { createHarness, fakeDisk, diff --git a/apps/server/src/modules/browser/__tests__/browser-api.test.ts b/apps/server/test/browser/browser-api.test.ts similarity index 98% rename from apps/server/src/modules/browser/__tests__/browser-api.test.ts rename to apps/server/test/browser/browser-api.test.ts index 00d78ac5..1001cd37 100644 --- a/apps/server/src/modules/browser/__tests__/browser-api.test.ts +++ b/apps/server/test/browser/browser-api.test.ts @@ -3,8 +3,8 @@ import { readdir, readFile } from "node:fs/promises"; import { join } from "node:path"; import { MOCK_ROW_H, MOCK_ROW_Y0, MOCK_VIEWPORT } from "@umt/testkit"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { registerBrowserCapabilities } from "../capabilities.ts"; -import { createBrowserModule } from "../index.ts"; +import { registerBrowserCapabilities } from "../../src/modules/browser/capabilities.ts"; +import { createBrowserModule } from "../../src/modules/browser/index.ts"; import { ACCESS_KEY, type BrowserHarness, @@ -206,7 +206,7 @@ describe.skipIf(!up)("浏览器 HTTP 端点", () => { it("服务重启:按数据库恢复会话,旧引用失效;上游回收的标签页标 lost 并自动重开", async () => { // 新模块实例 = 进程重启(适配器内存为空) const fresh = createBrowserModule({ stagingDir: h.stagingDir }); - const reg = (await import("../../agent/capability-registry.ts")).createCapabilityRegistry({ + const reg = (await import("../../src/modules/agent/capability-registry.ts")).createCapabilityRegistry({ logger: h.ctx.logger, db: h.db, settings: h.ctx.settings, diff --git a/apps/server/src/modules/browser/__tests__/browser-cloudflare.test.ts b/apps/server/test/browser/browser-cloudflare.test.ts similarity index 98% rename from apps/server/src/modules/browser/__tests__/browser-cloudflare.test.ts rename to apps/server/test/browser/browser-cloudflare.test.ts index f8ac33ef..9d133824 100644 --- a/apps/server/src/modules/browser/__tests__/browser-cloudflare.test.ts +++ b/apps/server/test/browser/browser-cloudflare.test.ts @@ -1,8 +1,8 @@ import { siteCloudflareState } from "@umt/contracts"; import { MOCK_CAMOFOX_IDENTITY, type MockCamofoxSite } from "@umt/testkit"; import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { browserServiceFor } from "../index.ts"; -import { type BrowserService, registerChallengeVision } from "../service.ts"; +import { browserServiceFor } from "../../src/modules/browser/index.ts"; +import { type BrowserService, registerChallengeVision } from "../../src/modules/browser/service.ts"; import { type BrowserHarness, createBrowserHarness, type Loose, ORIGIN, pgAvailable } from "./harness.ts"; // CF-2 浏览器一侧(对 testkit mock camofox 的 Cloudflare 模式): diff --git a/apps/server/src/modules/browser/__tests__/browser-credentials.test.ts b/apps/server/test/browser/browser-credentials.test.ts similarity index 98% rename from apps/server/src/modules/browser/__tests__/browser-credentials.test.ts rename to apps/server/test/browser/browser-credentials.test.ts index 82fb02e2..e53df9f7 100644 --- a/apps/server/src/modules/browser/__tests__/browser-credentials.test.ts +++ b/apps/server/test/browser/browser-credentials.test.ts @@ -1,5 +1,5 @@ import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { readAccountCookie } from "../../sources-identity/service/credentials.ts"; +import { readAccountCookie } from "../../src/modules/sources-identity/service/credentials.ts"; import { type BrowserHarness, createBrowserHarness, type Loose, ORIGIN, pgAvailable } from "./harness.ts"; // BR-2 凭据统一(对 testkit mock camofox + mock NexusPHP 登录态判定): diff --git a/apps/server/src/modules/browser/__tests__/browser-e2e.test.ts b/apps/server/test/browser/browser-e2e.test.ts similarity index 100% rename from apps/server/src/modules/browser/__tests__/browser-e2e.test.ts rename to apps/server/test/browser/browser-e2e.test.ts diff --git a/apps/server/src/modules/browser/__tests__/browser-identity.test.ts b/apps/server/test/browser/browser-identity.test.ts similarity index 92% rename from apps/server/src/modules/browser/__tests__/browser-identity.test.ts rename to apps/server/test/browser/browser-identity.test.ts index 0e970c17..e5d1551b 100644 --- a/apps/server/src/modules/browser/__tests__/browser-identity.test.ts +++ b/apps/server/test/browser/browser-identity.test.ts @@ -1,8 +1,8 @@ import { MOCK_CAMOFOX_IDENTITY } from "@umt/testkit"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { sourcesIdentityModule } from "../../sources-identity/index.ts"; -import { readBrowserUserAgent } from "../../sources-identity/service/user-agent.ts"; -import { egressProxy } from "../service.ts"; +import { sourcesIdentityModule } from "../../src/modules/sources-identity/index.ts"; +import { readBrowserUserAgent } from "../../src/modules/sources-identity/service/user-agent.ts"; +import { egressProxy } from "../../src/modules/browser/service.ts"; import { type BrowserHarness, createBrowserHarness, type Loose, ORIGIN, pgAvailable } from "./harness.ts"; // BR-4:打开会话前按站点下发出口(站点开代理 = 与 HTTP 同一代理,否则直连);浏览器固定身份写进 browser_provider.identity, diff --git a/apps/server/src/modules/browser/__tests__/browser-keepalive.test.ts b/apps/server/test/browser/browser-keepalive.test.ts similarity index 98% rename from apps/server/src/modules/browser/__tests__/browser-keepalive.test.ts rename to apps/server/test/browser/browser-keepalive.test.ts index 61c92d4b..05f97c1f 100644 --- a/apps/server/src/modules/browser/__tests__/browser-keepalive.test.ts +++ b/apps/server/test/browser/browser-keepalive.test.ts @@ -1,6 +1,6 @@ import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { browserServiceFor } from "../index.ts"; -import { BrowserKeepalive } from "../keepalive.ts"; +import { browserServiceFor } from "../../src/modules/browser/index.ts"; +import { BrowserKeepalive } from "../../src/modules/browser/keepalive.ts"; import { type BrowserHarness, createBrowserHarness, type Loose, ORIGIN, pgAvailable } from "./harness.ts"; // AG-4 浏览器会话保活(对 testkit mock camofox):桌面连接 / 人工接管 / 智能体会话正在用的会话定期做一次 diff --git a/apps/server/src/modules/browser/__tests__/browser-provider-config.test.ts b/apps/server/test/browser/browser-provider-config.test.ts similarity index 97% rename from apps/server/src/modules/browser/__tests__/browser-provider-config.test.ts rename to apps/server/test/browser/browser-provider-config.test.ts index 6e8bb688..beeb5ab5 100644 --- a/apps/server/src/modules/browser/__tests__/browser-provider-config.test.ts +++ b/apps/server/test/browser/browser-provider-config.test.ts @@ -1,6 +1,6 @@ import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import type { ServerContext } from "../../types.ts"; -import { BrowserService, BrowserServiceError, ENV_PROVIDER_NAME } from "../service.ts"; +import type { ServerContext } from "../../src/modules/types.ts"; +import { BrowserService, BrowserServiceError, ENV_PROVIDER_NAME } from "../../src/modules/browser/service.ts"; import { ACCESS_KEY, type BrowserHarness, createBrowserHarness, type Loose, pgAvailable } from "./harness.ts"; // BR-1:浏览器服务登记 —— 环境变量(BROWSER_PROVIDER_URL / BROWSER_ACCESS_KEY / BROWSER_ENABLED → camofox-env) diff --git a/apps/server/src/modules/browser/__tests__/browser-reconnect.test.ts b/apps/server/test/browser/browser-reconnect.test.ts similarity index 100% rename from apps/server/src/modules/browser/__tests__/browser-reconnect.test.ts rename to apps/server/test/browser/browser-reconnect.test.ts diff --git a/apps/server/src/modules/browser/__tests__/browser-resource.test.ts b/apps/server/test/browser/browser-resource.test.ts similarity index 97% rename from apps/server/src/modules/browser/__tests__/browser-resource.test.ts rename to apps/server/test/browser/browser-resource.test.ts index 64fdc6e1..cfb967d7 100644 --- a/apps/server/src/modules/browser/__tests__/browser-resource.test.ts +++ b/apps/server/test/browser/browser-resource.test.ts @@ -1,6 +1,6 @@ import { getSiteDefinition } from "@umt/adapters"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { resourceIdFromUrl } from "../resource.ts"; +import { resourceIdFromUrl } from "../../src/modules/browser/resource.ts"; import { type BrowserHarness, createBrowserHarness, ORIGIN, pgAvailable, READ_KEY } from "./harness.ts"; const def = getSiteDefinition("audiences")!; diff --git a/apps/server/src/modules/browser/__tests__/harness.ts b/apps/server/test/browser/harness.ts similarity index 88% rename from apps/server/src/modules/browser/__tests__/harness.ts rename to apps/server/test/browser/harness.ts index 44396451..891d8cfd 100644 --- a/apps/server/src/modules/browser/__tests__/harness.ts +++ b/apps/server/test/browser/harness.ts @@ -12,24 +12,24 @@ import { } from "@umt/testkit"; import { Kysely as K, PostgresDialect } from "kysely"; import pg from "pg"; -import { withSchemaLock } from "../../../../test/auth-settings/harness.ts"; -import { DEV_KEY, memoryLogger, memoryStore } from "../../../../test/helpers.ts"; -import { fakeEngine, fakeSecrets, PG_URL, plainSettings } from "../../../../test/sources-identity/harness.ts"; -import { createOutboxPort } from "../../../context.ts"; -import { createApp } from "../../../http/app.ts"; -import { EventBus } from "../../../http/sse.ts"; -import { createAcquisitionOrganizeModule } from "../../acquisition-organize/index.ts"; -import { createCapabilityRegistry } from "../../agent/capability-registry.ts"; -import { createAgentModule } from "../../agent/index.ts"; -import { createSourcesIdentityModule } from "../../sources-identity/index.ts"; -import type { ServerContext } from "../../types.ts"; -import { registerBrowserCapabilities } from "../capabilities.ts"; -import { createBrowserModule } from "../index.ts"; -import type { BrowserModuleOptions } from "../service.ts"; +import { withSchemaLock } from "../auth-settings/harness.ts"; +import { DEV_KEY, memoryLogger, memoryStore } from "../helpers.ts"; +import { fakeEngine, fakeSecrets, PG_URL, plainSettings } from "../sources-identity/harness.ts"; +import { createOutboxPort } from "../../src/context.ts"; +import { createApp } from "../../src/http/app.ts"; +import { EventBus } from "../../src/http/sse.ts"; +import { createAcquisitionOrganizeModule } from "../../src/modules/acquisition-organize/index.ts"; +import { createCapabilityRegistry } from "../../src/modules/agent/capability-registry.ts"; +import { createAgentModule } from "../../src/modules/agent/index.ts"; +import { createSourcesIdentityModule } from "../../src/modules/sources-identity/index.ts"; +import type { ServerContext } from "../../src/modules/types.ts"; +import { registerBrowserCapabilities } from "../../src/modules/browser/capabilities.ts"; +import { createBrowserModule } from "../../src/modules/browser/index.ts"; +import type { BrowserModuleOptions } from "../../src/modules/browser/service.ts"; // C1-E 浏览器模块测试装配:独立 PG schema + 真实迁移 + 交互式 mock camofox(观众风格站点)+ 真实能力注册中心。 -export { pgAvailable } from "../../../../test/sources-identity/harness.ts"; +export { pgAvailable } from "../sources-identity/harness.ts"; export const ORIGIN = "https://audiences.example"; export const PASSWORD = "brw-s3cret-login-pw"; diff --git a/apps/server/src/modules/desktop/__tests__/desktop.test.ts b/apps/server/test/desktop/desktop.test.ts similarity index 97% rename from apps/server/src/modules/desktop/__tests__/desktop.test.ts rename to apps/server/test/desktop/desktop.test.ts index 19e3e83e..b25649b7 100644 --- a/apps/server/src/modules/desktop/__tests__/desktop.test.ts +++ b/apps/server/test/desktop/desktop.test.ts @@ -1,7 +1,7 @@ import { OpenAPIHono } from "@hono/zod-openapi"; import { describe, expect, it } from "vitest"; -import type { AuthEnv, Principal, SessionVar } from "../../../http/auth.ts"; -import { ApiException, errorBody } from "../../../http/errors.ts"; +import type { AuthEnv, Principal, SessionVar } from "../../src/http/auth.ts"; +import { ApiException, errorBody } from "../../src/http/errors.ts"; import { basicAuth, type DesktopDeps, @@ -10,7 +10,7 @@ import { desktopUpstream, registerDesktopRoutes, sameOrigin, -} from "../desktop.ts"; +} from "../../src/modules/desktop/desktop.ts"; // 桌面模式反代:口令派生与容器 init 脚本一致、角色 → 口令注入、只接受控制台会话、响应头与路径约束。 diff --git a/apps/server/test/e2e/e2-agent-delegate.test.ts b/apps/server/test/e2e/agent-delegate.test.ts similarity index 100% rename from apps/server/test/e2e/e2-agent-delegate.test.ts rename to apps/server/test/e2e/agent-delegate.test.ts diff --git a/apps/server/test/e2e/e16-agent-prompt-eval.real.test.ts b/apps/server/test/e2e/agent-prompt-eval.real.test.ts similarity index 98% rename from apps/server/test/e2e/e16-agent-prompt-eval.real.test.ts rename to apps/server/test/e2e/agent-prompt-eval.real.test.ts index 100883ef..ab1289f3 100644 --- a/apps/server/test/e2e/e16-agent-prompt-eval.real.test.ts +++ b/apps/server/test/e2e/agent-prompt-eval.real.test.ts @@ -14,7 +14,7 @@ import { createE2E, type E2E, pgAvailable, runEvents, runRow, waitFor } from "./ // 运行(凭证只经环境变量传入,不写进代码与报告): // UMT_EVAL_REAL=1 UMT_EVAL_API_KEY=… UMT_EVAL_PROVIDER=opencode-go UMT_EVAL_MODEL=deepseek-v4.1-flash \ // UMT_EVAL_LEGACY_PROMPT=/tmp/legacy-system-prompt.ts UMT_EVAL_CASES=health,acquire,why UMT_EVAL_OUT=/tmp/eval.json \ -// DATABASE_URL=… deno run -A npm:vitest run test/e2e/e16-agent-prompt-eval.real.test.ts +// DATABASE_URL=… deno run -A npm:vitest run test/e2e/agent-prompt-eval.real.test.ts // UMT_EVAL_LEGACY_PROMPT 用 `git show main:packages/agent/src/system-prompt.ts > /tmp/legacy-system-prompt.ts` 生成; // UMT_EVAL_PROMPTS=legacy,layered 控制跑哪几组(缺省两组都跑)。 diff --git a/apps/server/test/e2e/e16-agent-prompt-eval.test.ts b/apps/server/test/e2e/agent-prompt-eval.test.ts similarity index 98% rename from apps/server/test/e2e/e16-agent-prompt-eval.test.ts rename to apps/server/test/e2e/agent-prompt-eval.test.ts index 83f85e02..5bdeb35b 100644 --- a/apps/server/test/e2e/e16-agent-prompt-eval.test.ts +++ b/apps/server/test/e2e/agent-prompt-eval.test.ts @@ -9,7 +9,7 @@ import { createE2E, type E2E, pgAvailable, runEvents, runRow, waitFor } from "./ // - fake 模型按"合格模型"的路线调用能力:断言调用顺序、只读调用都成功、需要批准的被拒绝(不向用户要 ID: // download.inspect 的 ID 来自 download.list); // - 汇报格式约束:合格汇报(只用工具结果里的数据写)通过 lintReport(无 UUID / 原始字节 / 内部字段,≤8 行,结论先行)。 -// 真实模型的前后对比见 e16-agent-prompt-eval.real.test.ts 与 docs/design/agent-prompt-eval.md。 +// 真实模型的前后对比见 agent-prompt-eval.real.test.ts 与 docs/design/agent-prompt-eval.md。 const hasPg = await pgAvailable(); diff --git a/apps/server/test/e2e/e15-agent-quality.test.ts b/apps/server/test/e2e/agent-quality.test.ts similarity index 100% rename from apps/server/test/e2e/e15-agent-quality.test.ts rename to apps/server/test/e2e/agent-quality.test.ts diff --git a/apps/server/test/e2e/e5-auto-investigate.test.ts b/apps/server/test/e2e/auto-investigate.test.ts similarity index 100% rename from apps/server/test/e2e/e5-auto-investigate.test.ts rename to apps/server/test/e2e/auto-investigate.test.ts diff --git a/apps/server/test/e2e/e6-auto-recover.test.ts b/apps/server/test/e2e/auto-recover.test.ts similarity index 100% rename from apps/server/test/e2e/e6-auto-recover.test.ts rename to apps/server/test/e2e/auto-recover.test.ts diff --git a/apps/server/test/e2e/e3-background-continues.test.ts b/apps/server/test/e2e/background-continues.test.ts similarity index 100% rename from apps/server/test/e2e/e3-background-continues.test.ts rename to apps/server/test/e2e/background-continues.test.ts diff --git a/apps/server/test/e2e/e9-continuous-series.test.ts b/apps/server/test/e2e/continuous-series.test.ts similarity index 100% rename from apps/server/test/e2e/e9-continuous-series.test.ts rename to apps/server/test/e2e/continuous-series.test.ts diff --git a/apps/server/test/e2e/e4-crash-recovery.test.ts b/apps/server/test/e2e/crash-recovery.test.ts similarity index 100% rename from apps/server/test/e2e/e4-crash-recovery.test.ts rename to apps/server/test/e2e/crash-recovery.test.ts diff --git a/apps/server/test/e2e/e12-interaction.test.ts b/apps/server/test/e2e/interaction.test.ts similarity index 100% rename from apps/server/test/e2e/e12-interaction.test.ts rename to apps/server/test/e2e/interaction.test.ts diff --git a/apps/server/test/e2e/e10-w4a-endpoints.test.ts b/apps/server/test/e2e/plan-and-inventory-endpoints.test.ts similarity index 100% rename from apps/server/test/e2e/e10-w4a-endpoints.test.ts rename to apps/server/test/e2e/plan-and-inventory-endpoints.test.ts diff --git a/apps/server/test/e2e/e1-rss-to-library.test.ts b/apps/server/test/e2e/rss-to-library.test.ts similarity index 100% rename from apps/server/test/e2e/e1-rss-to-library.test.ts rename to apps/server/test/e2e/rss-to-library.test.ts diff --git a/apps/server/test/e2e/e11-subagents.test.ts b/apps/server/test/e2e/subagents.test.ts similarity index 100% rename from apps/server/test/e2e/e11-subagents.test.ts rename to apps/server/test/e2e/subagents.test.ts diff --git a/apps/server/test/e2e/e8-tag-removal.test.ts b/apps/server/test/e2e/tag-removal.test.ts similarity index 100% rename from apps/server/test/e2e/e8-tag-removal.test.ts rename to apps/server/test/e2e/tag-removal.test.ts diff --git a/apps/server/test/e2e/e7-tv-series.test.ts b/apps/server/test/e2e/tv-series.test.ts similarity index 100% rename from apps/server/test/e2e/e7-tv-series.test.ts rename to apps/server/test/e2e/tv-series.test.ts diff --git a/apps/server/test/e2e/e14-vision.test.ts b/apps/server/test/e2e/vision.test.ts similarity index 100% rename from apps/server/test/e2e/e14-vision.test.ts rename to apps/server/test/e2e/vision.test.ts diff --git a/apps/server/test/e2e/e13-websearch.test.ts b/apps/server/test/e2e/websearch.test.ts similarity index 100% rename from apps/server/test/e2e/e13-websearch.test.ts rename to apps/server/test/e2e/websearch.test.ts diff --git a/apps/server/test/file-manager/w5a2.test.ts b/apps/server/test/file-manager/copy-dir-and-overrides.test.ts similarity index 100% rename from apps/server/test/file-manager/w5a2.test.ts rename to apps/server/test/file-manager/copy-dir-and-overrides.test.ts diff --git a/apps/server/test/file-manager/harness.ts b/apps/server/test/file-manager/harness.ts index f1009226..5d97281d 100644 --- a/apps/server/test/file-manager/harness.ts +++ b/apps/server/test/file-manager/harness.ts @@ -20,7 +20,7 @@ import { createApp } from "../../src/http/app.ts"; import { deriveCsrfToken, hashApiKey, hashSessionToken } from "../../src/http/auth.ts"; import { EventBus } from "../../src/http/sse.ts"; import { createLogger } from "../../src/logger.ts"; -import { fakeEngine } from "../../src/modules/acquisition-organize/__tests__/harness.ts"; +import { fakeEngine } from "../acquisition-organize/harness.ts"; import { executeRecord, type FrozenPlan } from "../../src/modules/acquisition-organize/services/organize.ts"; import { runScrapeRecord } from "../../src/modules/acquisition-organize/services/scrape.ts"; import { createCapabilityRegistry } from "../../src/modules/agent/capability-registry.ts"; diff --git a/apps/server/test/file-manager/v2.test.ts b/apps/server/test/file-manager/organize-options.test.ts similarity index 100% rename from apps/server/test/file-manager/v2.test.ts rename to apps/server/test/file-manager/organize-options.test.ts diff --git a/apps/server/test/inventory/media-servers.test.ts b/apps/server/test/inventory/media-servers.test.ts index 3796c8da..49adc540 100644 --- a/apps/server/test/inventory/media-servers.test.ts +++ b/apps/server/test/inventory/media-servers.test.ts @@ -4,7 +4,7 @@ import { type Harness, pgAvailable, seedEntity, -} from "../../src/modules/acquisition-organize/__tests__/harness.ts"; +} from "../acquisition-organize/harness.ts"; import { MEDIA_SERVER_LIMITS } from "../../src/modules/acquisition-organize/routes/library.ts"; // C1-D:GET /inventory/media-servers 按实例并行(allSettled)+ 实例内并发上限 + 整实例超时;部分失败 partial=true。 diff --git a/apps/server/test/rules/rule-groups.test.ts b/apps/server/test/rules/rule-groups.test.ts index e39a3aad..924c8058 100644 --- a/apps/server/test/rules/rule-groups.test.ts +++ b/apps/server/test/rules/rule-groups.test.ts @@ -9,7 +9,7 @@ import { seedEntity, seedRelease, seedSlot, -} from "../../src/modules/acquisition-organize/__tests__/harness.ts"; +} from "../acquisition-organize/harness.ts"; import { evaluateAcquisition } from "../../src/modules/acquisition-organize/services/acquisition.ts"; // W9-D 规则组:关键字表、规则串解析/序列化、CRUD + 修订、抑制记录、试算、默认链、评估接入、旧端点别名。 diff --git a/apps/server/test/search/search2.test.ts b/apps/server/test/search/two-stage-search.test.ts similarity index 100% rename from apps/server/test/search/search2.test.ts rename to apps/server/test/search/two-stage-search.test.ts diff --git a/apps/server/test/sources-identity/data-hygiene-dh1.test.ts b/apps/server/test/sources-identity/data-hygiene.test.ts similarity index 100% rename from apps/server/test/sources-identity/data-hygiene-dh1.test.ts rename to apps/server/test/sources-identity/data-hygiene.test.ts diff --git a/apps/server/test/sources-identity/metadata-md1.test.ts b/apps/server/test/sources-identity/metadata-language-fallback.test.ts similarity index 100% rename from apps/server/test/sources-identity/metadata-md1.test.ts rename to apps/server/test/sources-identity/metadata-language-fallback.test.ts diff --git a/apps/server/test/sources-identity/overview-md2.test.ts b/apps/server/test/sources-identity/overview-fallback.test.ts similarity index 100% rename from apps/server/test/sources-identity/overview-md2.test.ts rename to apps/server/test/sources-identity/overview-fallback.test.ts diff --git a/apps/server/test/subscriptions/subscriptions-c1a.test.ts b/apps/server/test/subscriptions/subscriptions-c1a.test.ts index 5f3b94a4..375b237b 100644 --- a/apps/server/test/subscriptions/subscriptions-c1a.test.ts +++ b/apps/server/test/subscriptions/subscriptions-c1a.test.ts @@ -10,7 +10,7 @@ import { seedBase, seedEntity, seedRelease, -} from "../../src/modules/acquisition-organize/__tests__/harness.ts"; +} from "../acquisition-organize/harness.ts"; import { evaluateAcquisition, subscriptionSavePath, diff --git a/apps/server/test/subscriptions/subscriptions-v2.test.ts b/apps/server/test/subscriptions/subscriptions-v2.test.ts index 6f372ec8..f74a398c 100644 --- a/apps/server/test/subscriptions/subscriptions-v2.test.ts +++ b/apps/server/test/subscriptions/subscriptions-v2.test.ts @@ -10,7 +10,7 @@ import { seedBase, seedEntity, seedRelease, -} from "../../src/modules/acquisition-organize/__tests__/harness.ts"; +} from "../acquisition-organize/harness.ts"; import { evaluateAcquisition } from "../../src/modules/acquisition-organize/services/acquisition.ts"; // W9-E 订阅重做后端:总览分组/排序/计数、覆盖矩阵(单集/季包/多版本/在途/临时单元/电影版本)、 diff --git a/apps/server/vitest.config.ts b/apps/server/vitest.config.ts index eb79250c..cfc08ea7 100644 --- a/apps/server/vitest.config.ts +++ b/apps/server/vitest.config.ts @@ -1,11 +1,12 @@ import { defineConfig } from "vitest/config"; -// 两组测试: -// unit —— 纯内存、无 PG,文件并行; -// pg —— 真实 PG 的集成/e2e(每文件独立业务 schema + 引擎 schema,真实 worker/后台循环),文件级串行, -// 并排在 unit 之后(groupOrder)。并发全量跑时多个 e2e 的 worker/轮询/迁移互相抢 CPU 与连接, -// 会把"入库→状态推进"这类时序拉长到超时,也会放大竞态;串行让每个文件拿到稳定的时序预算。 -// 新测试文件缺省归入 pg(串行,安全);确认不碰 PG 的才加进 UNIT。 +// 三组测试,按 groupOrder 依次跑: +// unit —— 纯内存、无 PG,文件并行; +// pg —— 真实 PG、假引擎(每文件独立 schema),文件并行; +// serial —— 起真实引擎 / worker / 后台循环 / 子进程的集成与 e2e,文件级串行。并发跑时 worker、轮询、迁移 +// 互相抢 CPU 与连接,会把"入库→状态推进"这类时序拉长到超时,串行让每个文件拿到稳定的时序预算。 +// 新测试缺省进 pg;起真实 worker / 引擎的加进 SERIAL;确认不碰 PG 的加进 UNIT。 +// 并行文件数受开发库连接数约束(max_connections 100,每个 harness 连接池 5–10):MAX_WORKERS 别随手调大。 const UNIT = [ "test/api-registry.test.ts", "test/organize-in-place-verify.test.ts", @@ -22,23 +23,48 @@ const UNIT = [ "test/static.test.ts", ]; +const SERIAL = [ + "test/e2e/**/*.test.ts", + "test/auth-settings/admin-api.test.ts", + "test/auth-settings/engine-retry.test.ts", + "test/system/**/*.test.ts", + "test/search/search.test.ts", + "test/search/two-stage-search.test.ts", + "test/worker-lanes.test.ts", + "test/preflight.test.ts", + "test/bootstrap.test.ts", +]; + +const MAX_WORKERS = 6; + export default defineConfig({ test: { // 建/删 schema 在一把咨询锁上排队(withSchemaLock),迁移 + 引擎 schema 在负载下可能超过缺省 10s hookTimeout: 120_000, teardownTimeout: 60_000, + maxWorkers: MAX_WORKERS, projects: [ { extends: true, test: { name: "unit", include: UNIT } }, { extends: true, test: { name: "pg", - include: ["test/**/*.test.ts", "src/**/*.test.ts"], - exclude: UNIT, - fileParallelism: false, + include: ["test/**/*.test.ts"], + exclude: [...UNIT, ...SERIAL], + // 迁移类用例在用例体里建 schema + 迁移,并行时排队等同一把锁,缺省 5s 不够 + testTimeout: 60_000, sequence: { groupOrder: 1 }, }, }, + { + extends: true, + test: { + name: "serial", + include: SERIAL, + fileParallelism: false, + sequence: { groupOrder: 2 }, + }, + }, ], }, }); diff --git a/apps/web/app/components/agent/agui3.test.ts b/apps/web/app/components/agent/agent-session-ui.test.ts similarity index 100% rename from apps/web/app/components/agent/agui3.test.ts rename to apps/web/app/components/agent/agent-session-ui.test.ts diff --git a/apps/web/app/components/search/search2.test.ts b/apps/web/app/components/search/search-results-ui.test.ts similarity index 100% rename from apps/web/app/components/search/search2.test.ts rename to apps/web/app/components/search/search-results-ui.test.ts diff --git a/docs/adr/ADR-004-deno-runtime.md b/docs/adr/ADR-004-deno-runtime.md index b9162dd8..12d839d2 100644 --- a/docs/adr/ADR-004-deno-runtime.md +++ b/docs/adr/ADR-004-deno-runtime.md @@ -173,3 +173,10 @@ deno task ci # typecheck(tsc 7.0.2 + deno check)→ biome → vitest - 约 400 行相对 import 补扩展名,可自动修复。server `main.ts` 约 30 行。2 个崩溃恢复测试的 spawn 参数。db scripts 3 个。logger 约 15 行。web 的 tsconfig 和 package.json 各 1 到 2 行。 - 文档:README、plan §1、ADR-001 标记 superseded。 - 业务逻辑(domain/contracts/adapters/agent/tasks 的实现)**不用改**。 + +## 补充(2026-10-02):维持 Deno,不迁移 + +讨论过换 Bun / Node 24,结论是不换。 + +- 实际权限:生产编译参数为 `--allow-net --allow-env --allow-sys --allow-read --allow-write --allow-run=ffprobe,sha256sum,/app/umt-server`,只有 `--allow-run` 白名单真正起限制,上文"读路径白名单"没有落实;选 Deno 的理由以直接跑 TS、单二进制、集中的 workspace 配置为主。 +- 已知摩擦都有固定做法:web 测试用 Node 跑 vitest、编译产物自检兜底漏嵌、`deno.lock` 被常驻进程改写(见 `docs/plan/lessons.md`)。 diff --git a/docs/design/agent-prompt-eval.md b/docs/design/agent-prompt-eval.md index 2cd571ea..0a71f91b 100644 --- a/docs/design/agent-prompt-eval.md +++ b/docs/design/agent-prompt-eval.md @@ -31,7 +31,7 @@ ## 3. fake 模型评测(e16,CI) -`apps/server/test/e2e/e16-agent-prompt-eval.test.ts`,测试库数据仿反例现场:下载器「家里的 qB」(mock qB,3 个任务:下载中 / 文件缺失 / 已入库做种)、站点「憨憨」(上次检查正常,账号带原始用户数据)、没有媒体服务器、《沙丘2》(追更中订阅 + 1080p / 2160p 两个收录版本、一条 2160p 资源)、4 条已取消订阅、1 条待处理。 +`apps/server/test/e2e/agent-prompt-eval.test.ts`,测试库数据仿反例现场:下载器「家里的 qB」(mock qB,3 个任务:下载中 / 文件缺失 / 已入库做种)、站点「憨憨」(上次检查正常,账号带原始用户数据)、没有媒体服务器、《沙丘2》(追更中订阅 + 1080p / 2160p 两个收录版本、一条 2160p 资源)、4 条已取消订阅、1 条待处理。 | # | 提示 | 载入的 Skill | 能力调用顺序 | 结果 | |---|---|---|---|---| @@ -52,7 +52,7 @@ fake 模型只能证明"按提示的路线走得通、数据够写出合格汇 **本次没有跑,真跑 0 次。** 任务说明里的凭据文件 `.local/admin-credentials` 只有管理员、TMDB、Brave 搜索与 qB 的条目,没有 OpenCode Go 的 key;用户栈里配置的模型服务按约束不能碰。评测脚手架已就绪: -- `apps/server/test/e2e/e16-agent-prompt-eval.real.test.ts`:同一套测试库数据,"前"用 main 的 `buildSystemPrompt`(`git show main:packages/agent/src/system-prompt.ts`)+ 旧约束 + 不含 `download.list` 的能力集,"后"用分层提示;缺省跑反例那条 + 收录 + 入库排查共 3 条 × 2 组 = 6 次会话(上限 12)。 +- `apps/server/test/e2e/agent-prompt-eval.real.test.ts`:同一套测试库数据,"前"用 main 的 `buildSystemPrompt`(`git show main:packages/agent/src/system-prompt.ts`)+ 旧约束 + 不含 `download.list` 的能力集,"后"用分层提示;缺省跑反例那条 + 收录 + 入库排查共 3 条 × 2 组 = 6 次会话(上限 12)。 - 结果写 JSON:每次的能力调用序列(含是否成功)、汇报原文、`lintReport` 结果、行数、token / 费用。 - 跑法见 runbook;拿到 key 后执行一次,把结果按下表补进本节: diff --git a/docs/plan/README.md b/docs/plan/README.md index 0c763d5d..9393377d 100644 --- a/docs/plan/README.md +++ b/docs/plan/README.md @@ -28,9 +28,12 @@ - 任务恢复的三处边界:升级前未结束任务的版本处置(发布前查在途任务版本,没有在途旧任务就不保留旧处理器);业务结果提交、run 关联、事件派发三个时点分别注入失败确认最终状态;引擎查询异常时只记录并跳过本轮,仅在明确查无 run 或终态时补投。 - 生产模块装配的处理器来源断言(防止漏注册的任务静默回显 todo),下次改任务注册时补。 +- 测试装配去重:server 6 份 harness(约 1800 行)各自实现建 schema、迁移、假引擎、临时目录,抽公共部分到 `@umt/testkit`,各模块只留自己的装配;下次改到相关 harness 时做。 - 大文件拆分候选,改到相关功能时顺手拆,不单独重构:`packages/contracts/src/api.ts`(~2700 行)、`acquisition-organize/services/organize.ts`(~2600)、`browser/service.ts`(~2400)、`web/app/components/organize-wizard/organize-wizard.tsx`。 ### 业务 +- 疑似竞争:同一 Slot 两个版本几乎同时评估时,`evaluateForSlot`(`acquisition.ts`)读在途计划与写计划 / 延后之间没有锁,可能都不被延后。现象:`test/e2e/continuous-series.test.ts`「另一版本延后」在全量跑时偶发等待超时(2026-10-02 并行化后 3 次全量中 1 次,单跑 4 次均过)。先复现确认,再考虑按 Slot 加行锁。 + - 与 MP 对照里仍未定的项,见 [对照 §6.4/6.5](../porting/moviepilot-v3-comparison.md#64-用户反馈与后续讨论2026-10-02)。 - 需求里仍是"部分实现"的:R06 通用 HTTP 失败转浏览器、真实 Cloudflare 挑战验收;R21 缓存阈值里未纳管占用的专项验收;四站与真实下载、媒体服务器的完整验收。 diff --git a/docs/porting/moviepilot-v3-comparison.md b/docs/porting/moviepilot-v3-comparison.md index 1833c99c..415b4aff 100644 --- a/docs/porting/moviepilot-v3-comparison.md +++ b/docs/porting/moviepilot-v3-comparison.md @@ -68,7 +68,7 @@ MP 是优先采用的业务参考;用户已确认尽量复用其成熟业务 | 先持久接受,再安排执行 | [workflow.py](https://github.com/jxxghp/MoviePilot/blob/0aa857173f77de31c7c8d9d2e12052d99f37bcc1/app/application/transfer/workflow.py#L116) `TransferQueueService.put`;[queue.py](https://github.com/jxxghp/MoviePilot/blob/0aa857173f77de31c7c8d9d2e12052d99f37bcc1/app/chain/transfer/queue.py#L1194) `__record_enqueue_failure`;[test_transfer_queue_service.py](https://github.com/jxxghp/MoviePilot/blob/0aa857173f77de31c7c8d9d2e12052d99f37bcc1/tests/test_transfer_queue_service.py#L139) `test_transfer_queue_service_commits_admission_before_failed_enqueue` 使用 SQLite 仓储注入队列失败 | [common.ts](../../apps/server/src/modules/acquisition-organize/services/common.ts) `enqueueTracked` 第 134 行;[scrape.ts](../../apps/server/src/modules/acquisition-organize/services/scrape.ts) `queueScrape` 第 152 行 | | 查询异常不当作不存在 | [queue.py](https://github.com/jxxghp/MoviePilot/blob/0aa857173f77de31c7c8d9d2e12052d99f37bcc1/app/chain/transfer/queue.py#L754) `__replay_pending` 第 754 行;[admission.py](https://github.com/jxxghp/MoviePilot/blob/0aa857173f77de31c7c8d9d2e12052d99f37bcc1/app/db/adapters/transfer/admission.py#L296) `claim_recoverable`;[test_transfer_pending_replay.py](https://github.com/jxxghp/MoviePilot/blob/0aa857173f77de31c7c8d9d2e12052d99f37bcc1/tests/test_transfer_pending_replay.py#L423) `test_replay_survives_db_failure` 断言不执行恢复 | [common.ts](../../apps/server/src/modules/acquisition-organize/services/common.ts) `enqueueSweep` 第 159 行起 | | 旧执行迹象保守隔离 | [models.py](https://github.com/jxxghp/MoviePilot/blob/0aa857173f77de31c7c8d9d2e12052d99f37bcc1/app/application/transfer/models.py#L485) `TransferPlanCheckpoint.from_payload`;[迁移](https://github.com/jxxghp/MoviePilot/blob/0aa857173f77de31c7c8d9d2e12052d99f37bcc1/database/versions/e5c7a9b1d3f6_3_0_16.py#L404) `_backfill_pending`;[test_transfer_execution_migration.py](https://github.com/jxxghp/MoviePilot/blob/0aa857173f77de31c7c8d9d2e12052d99f37bcc1/tests/test_transfer_execution_migration.py#L282) `test_upgrade_is_conservative_and_repairs_interrupted_indexes` | [handlers.ts](../../apps/server/src/modules/acquisition-organize/handlers.ts) 第 193 行;[0058](../../packages/db/migrations/0058_scrape_record.ts);[引擎](../../packages/tasks/src/openworkflow-engine.ts) `startWorker` 第 221 行 | -| 步骤恢复先看已有结果和实际副作用 | [execution.py](https://github.com/jxxghp/MoviePilot/blob/0aa857173f77de31c7c8d9d2e12052d99f37bcc1/app/chain/transfer/execution.py#L193) `_DurableTransferStepRunner.run`;[test_transfer_execution_runner.py](https://github.com/jxxghp/MoviePilot/blob/0aa857173f77de31c7c8d9d2e12052d99f37bcc1/tests/test_transfer_execution_runner.py#L148) 成功步骤不重做、未知孤立步骤进入人工复核断言 | [op-ledger.ts](../../packages/tasks/src/op-ledger.ts);[organize.ts](../../apps/server/src/modules/acquisition-organize/services/organize.ts);[E4](../../apps/server/test/e2e/e4-crash-recovery.test.ts)、[tasks 崩溃恢复](../../packages/tasks/test/crash-recovery.test.ts) | +| 步骤恢复先看已有结果和实际副作用 | [execution.py](https://github.com/jxxghp/MoviePilot/blob/0aa857173f77de31c7c8d9d2e12052d99f37bcc1/app/chain/transfer/execution.py#L193) `_DurableTransferStepRunner.run`;[test_transfer_execution_runner.py](https://github.com/jxxghp/MoviePilot/blob/0aa857173f77de31c7c8d9d2e12052d99f37bcc1/tests/test_transfer_execution_runner.py#L148) 成功步骤不重做、未知孤立步骤进入人工复核断言 | [op-ledger.ts](../../packages/tasks/src/op-ledger.ts);[organize.ts](../../apps/server/src/modules/acquisition-organize/services/organize.ts);[E4](../../apps/server/test/e2e/crash-recovery.test.ts)、[tasks 崩溃恢复](../../packages/tasks/test/crash-recovery.test.ts) | | 结果和事件共同提交 | [chain.py](https://github.com/jxxghp/MoviePilot/blob/0aa857173f77de31c7c8d9d2e12052d99f37bcc1/app/db/adapters/chain.py#L219) `TransactionalChainDurableEventWriter.transfer_result` | [organize.ts](../../apps/server/src/modules/acquisition-organize/services/organize.ts) 核验事务第 1386 行、`emitRecord` 第 1710 行;[outbox-relay.ts](../../packages/tasks/src/outbox-relay.ts) 第 46 行起 | | handler 装配风险不可强行类比 | [runtime/tasks.py](https://github.com/jxxghp/MoviePilot/blob/0aa857173f77de31c7c8d9d2e12052d99f37bcc1/app/runtime/tasks.py) | [context.ts](../../apps/server/src/context.ts) 第 96 行;[modules.test.ts](../../apps/server/test/modules.test.ts) 第 86 行 | diff --git a/docs/runbook/agent.md b/docs/runbook/agent.md index 0fcd5bfb..09521cb1 100644 --- a/docs/runbook/agent.md +++ b/docs/runbook/agent.md @@ -364,7 +364,7 @@ curl -X POST localhost:4380/api/v1/agent/runs -H "Authorization: Bearer umt-dev- ### 评测 - 汇报核对 `lintReport(text)`(`packages/agent/src/prompt/report-lint.ts`):行数 >8、UUID、hash、7 位以上裸数字(原始字节数)、ISO 时间、内部字段(adapter、snake_case、camelCase)、能力名、Markdown 标题、开头不是结论("检查结果""以下…")、向用户要 ID、说教句。反例原文必须不过,期望样子必须过(`report-lint.test.ts`)。 -- fake 模型:`apps/server/test/e2e/e16-agent-prompt-eval.test.ts`,夹具 `fixtures/agent-eval.ts`(测试库里的一套用户环境 + 8 条固定提示 + 合格模型的调用路线与汇报),随 `deno task test` 跑。 +- fake 模型:`apps/server/test/e2e/agent-prompt-eval.test.ts`,夹具 `fixtures/agent-eval.ts`(测试库里的一套用户环境 + 8 条固定提示 + 合格模型的调用路线与汇报),随 `deno task test` 跑。 - 真实模型(手动,不进 CI,每次最多 12 次调用模型会话): ```sh @@ -373,7 +373,7 @@ curl -X POST localhost:4380/api/v1/agent/runs -H "Authorization: Bearer umt-dev- UMT_EVAL_PROVIDER=opencode-go UMT_EVAL_MODEL=deepseek-v4.1-flash \ UMT_EVAL_LEGACY_PROMPT=/tmp/legacy-system-prompt.ts UMT_EVAL_CASES=health,acquire,why \ UMT_EVAL_OUT=/tmp/agent-eval.json DATABASE_URL=postgres://… \ - deno run -A npm:vitest run test/e2e/e16-agent-prompt-eval.real.test.ts + deno run -A npm:vitest run test/e2e/agent-prompt-eval.real.test.ts ``` "前"用 main 的 `buildSystemPrompt` + 旧约束,能力集去掉 `download.list`;"后"用分层提示。结果 JSON 每条含能力调用序列、汇报原文、`lintReport` 结果、行数、用量;对比写进 `docs/design/agent-prompt-eval.md`。站点地址是本机关闭端口、下载器是 mock qB,智能体不会请求真实站点。key 只经环境变量传入,不写进代码与报告。 diff --git a/docs/runbook/browser.md b/docs/runbook/browser.md index 6e3588dd..3367291a 100644 --- a/docs/runbook/browser.md +++ b/docs/runbook/browser.md @@ -473,7 +473,7 @@ camofox 容器重启(`docker compose up -d` 重建、OOM、手动 restart)后, ### 10.4 核验 -- 自动化:`apps/server/src/modules/browser/__tests__/browser-reconnect.test.ts`(mock camofox `restart()`:标签页清空、旧 tabId 410、 +- 自动化:`apps/server/test/browser/browser-reconnect.test.ts`(mock camofox `restart()`:标签页清空、旧 tabId 410、 实例标识换新、可选丢 profile)——截图自愈并注入 Cookie、回到原地址;实例标识变化 + `reconnect` 幂等;接管中重连保持租约; 失败 503 与退避;前端冒烟"原站视图 → 浏览器重启后自动重连(mock)"。 - 真机:未对真实 camofox 容器重启核验。桌面镜像要重建后才有 `GET /umt/instance`(`docker compose build camofox && docker compose up -d camofox`); @@ -621,7 +621,7 @@ Camoufox 浏览器包的层都走构建缓存,**不会重新下载 Camoufox(约 仍从本地 `camofox-browser:1.17.0` 复制,不从网上下)。首次启动生成身份,之后 profile 卷里的登录状态照常可用。 核验:`apps/server/test/sources-identity/user-agent.test.ts`(每条出站请求的 UA、优先级、待切换、CF Cookie)、 -`migration-browser-identity.test.ts`、`apps/server/src/modules/browser/__tests__/browser-identity.test.ts`(出口下发、身份存库)、 +`migration-browser-identity.test.ts`、`apps/server/test/browser/browser-identity.test.ts`(出口下发、身份存库)、 `packages/adapters/src/browser/umt-plugin.test.ts`(插件纯函数、适配器接口);真机:本地临时容器(`--network none`,只开容器内本地 页面)核验了 §11.1 表格与 §11.5 出口行为,未对真实站点发请求。 @@ -658,7 +658,7 @@ camofox 按 **API 调用**判断活动,有三道空闲回收(`docker-compose.yml → 按 §10 自愈重连(注入 Cookie、同一会话新开标签页回到原地址)后在新标签页上再保活一次;重连也失败(浏览器服务不可用)只记 warn 日志 「浏览器会话保活失败」,会话照常标 `needsReconnect` / `reconnect.status = failed`,下一轮再试。 -核验:`apps/server/src/modules/browser/__tests__/browser-keepalive.test.ts`(登记 / 引用计数 / 复查;对 mock camofox:接管期间保活、 +核验:`apps/server/test/browser/browser-keepalive.test.ts`(登记 / 引用计数 / 复查;对 mock camofox:接管期间保活、 不记自动化活动、交还后停;桌面连接期间标签页被回收 → 保活时自愈重连;智能体会话结束后停、会话关闭撤销)。真机未核验。 ## 13. Cloudflare 挑战(CF-2) diff --git a/docs/runbook/identity.md b/docs/runbook/identity.md index 416441ec..ef34c1c7 100644 --- a/docs/runbook/identity.md +++ b/docs/runbook/identity.md @@ -96,7 +96,7 @@ select key, value->>'title' title, value->>'year' y, value->>'source' src from k 测试:`apps/server/test/sources-identity/id-escalation.test.ts`(观众 DET-2 / 憨憨 HH-2 详情样本经注入 fetch、TMDB mock,零真实请求); 差分组 `packages/domain/test/differential/identity/site-ids-*.json`(MP `search/result.py _match_source`:种子 IMDb 与作品一致即命中; -我们年份 / 类型冲突时只作候选,登记为 intentional,见 `site-ids-deviations.json`),`packages/domain/test/identity-id3.test.ts`。 +我们年份 / 类型冲突时只作候选,登记为 intentional,见 `site-ids-deviations.json`),`packages/domain/test/identity-site-ids.test.ts`。 ## 接管任务按文件名识别(QB-1) diff --git a/docs/runbook/search.md b/docs/runbook/search.md index 67036172..aa3b6013 100644 --- a/docs/runbook/search.md +++ b/docs/runbook/search.md @@ -1,6 +1,6 @@ # 全局搜索运行手册(W9-G、SEARCH-2) -实现:`apps/server/src/modules/search/`(`routes.ts` 端点、`releases.ts` 跨站搜索/缓存/筛选/叠加、`media.ts` 本地作品与 TMDB、`works.ts` 作品合并/库存对比/查询词、`actions.ts` 页面与操作清单、`dispatch.ts` 进程内转调);合同 `packages/contracts/src/search.ts`;测试 `apps/server/test/search/`(`search2.test.ts` 为两阶段)。 +实现:`apps/server/src/modules/search/`(`routes.ts` 端点、`releases.ts` 跨站搜索/缓存/筛选/叠加、`media.ts` 本地作品与 TMDB、`works.ts` 作品合并/库存对比/查询词、`actions.ts` 页面与操作清单、`dispatch.ts` 进程内转调);合同 `packages/contracts/src/search.ts`;测试 `apps/server/test/search/`(`search-results-ui.test.ts` 为两阶段)。 ## 两阶段流程(SEARCH-2) diff --git a/docs/runbook/sites.md b/docs/runbook/sites.md index 23e94a94..c23f8c40 100644 --- a/docs/runbook/sites.md +++ b/docs/runbook/sites.md @@ -197,7 +197,7 @@ **测试**:`packages/adapters/src/sites/challenge.test.ts`(分类)、`apps/server/test/sources-identity/egress.test.ts`(出口)、 `cloudflare.test.ts`(级联:自动通过 / 视觉 / 转人工 / 复用失败回退浏览器 / 节流单飞 / 同步条件 / 健康)、 `modules/browser/__tests__/browser-cloudflare.test.ts`(mock camofox:自动放行、插件真实鼠标点击、选图转人工、交还继续、代抓与 profile 锁)、 -`test/e2e/e14-vision.test.ts`(系统发起的视觉子流程)。真机:未对真实站点验证(CF-2 只用 mock);插件鼠标点击见 browser.md §13。 +`test/e2e/vision.test.ts`(系统发起的视觉子流程)。真机:未对真实站点验证(CF-2 只用 mock);插件鼠标点击见 browser.md §13。 **要人处理时**:站点页 Cloudflare 行显示「需要你验证」→ 原站视图接管 → 过验证 → 交还。一直过不去(站点绑定 TLS 指纹、代理 IP 被拉黑): 关掉站点代理或换出口;实在不行关闭该站点的定时快照,只保留 RSS。 diff --git a/docs/runbook/websearch.md b/docs/runbook/websearch.md index 8ca3df98..caa625e9 100644 --- a/docs/runbook/websearch.md +++ b/docs/runbook/websearch.md @@ -48,7 +48,7 @@ curl -X POST -H "authorization: Bearer $KEY" -H 'content-type: application/json' ## 验证 - 单元:`packages/adapters/src/websearch/websearch.test.ts`(参数与鉴权头、错误分类、归一化、地址守卫、截断、重定向到内网、超时)。 -- e2e:`apps/server/test/e2e/e13-websearch.test.ts`(见 [agent.md](agent.md#e2e))。 +- e2e:`apps/server/test/e2e/websearch.test.ts`(见 [agent.md](agent.md#e2e))。 - 真实 Key(2026-09-28,开发库,后端 4415):`POST /websearch/test` 3 条 / 914 ms;`web.search`「MoviePilot v3 订阅规则」5 条 / 736 ms,再查命中缓存;`web.fetch` 第一条(wiki.movie-pilot.org)200、13.6 KB → 1342 字正文 / 1.9 s;读白名单外链接、`127.0.0.1` 均被拒。 ## 已知限制 diff --git a/packages/domain/test/identity-id2.test.ts b/packages/domain/test/identity-evidence.test.ts similarity index 100% rename from packages/domain/test/identity-id2.test.ts rename to packages/domain/test/identity-evidence.test.ts diff --git a/packages/domain/test/identity-id3.test.ts b/packages/domain/test/identity-site-ids.test.ts similarity index 100% rename from packages/domain/test/identity-id3.test.ts rename to packages/domain/test/identity-site-ids.test.ts