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:
1 parent
b343dddbf6
commit
b61335eaf6
75 files changed
+159
-115
No files matched your search
@@ -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 历史可查),不留归档。
|
||||
|
||||
+4
-4
@@ -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,
|
||||
+5
-5
@@ -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();
|
||||
+1
-1
@@ -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");
|
||||
// 不是草案的不动
|
||||
+1
-1
@@ -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 下载面板:进行中 / 已完成分组与计数、每个种子的实时速度 / 剩余时间 / 连接数、下载器总速度、
|
||||
+2
-2
@@ -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();
|
||||
+11
-11
@@ -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 + 临时目录 的测试装配;引擎用记录入队的假实现,测试直接驱动服务函数。
|
||||
|
||||
+1
-1
@@ -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)。
|
||||
+3
-3
@@ -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();
|
||||
+1
-1
@@ -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 命名模板:变量表、预览(样本/错误/路径绑定)、三级模板读写与保存前校验、模板版本登记。
|
||||
+1
-1
@@ -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、
|
||||
+2
-2
@@ -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,
|
||||
+3
-3
@@ -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,
|
||||
+3
-3
@@ -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();
|
||||
File renamed without changes.
+1
-1
@@ -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();
|
||||
+2
-2
@@ -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 路由。
|
||||
+2
-2
@@ -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 与图片(临时库根);只补缺失 / 全部覆盖;
|
||||
+4
-4
@@ -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);
|
||||
+4
-4
@@ -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、贴管理标签,没有站点资源)完成后按 种子名 + 主视频文件名 识别 → 直接整理;
|
||||
+4
-4
@@ -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,
|
||||
+3
-3
@@ -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,
|
||||
+2
-2
@@ -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 模式):
|
||||
+1
-1
@@ -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 登录态判定):
|
||||
File renamed without changes.
+3
-3
@@ -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,
|
||||
+2
-2
@@ -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):桌面连接 / 人工接管 / 智能体会话正在用的会话定期做一次
|
||||
+2
-2
@@ -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)
|
||||
File renamed without changes.
+1
-1
@@ -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")!;
|
||||
+15
-15
@@ -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";
|
||||
+3
-3
@@ -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 脚本一致、角色 → 口令注入、只接受控制台会话、响应头与路径约束。
|
||||
|
||||
File renamed without changes.
+1
-1
@@ -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 控制跑哪几组(缺省两组都跑)。
|
||||
|
||||
+1
-1
@@ -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();
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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";
|
||||
|
||||
File renamed without changes.
@@ -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。
|
||||
|
||||
@@ -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 + 修订、抑制记录、试算、默认链、评估接入、旧端点别名。
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -10,7 +10,7 @@ import {
|
||||
seedBase,
|
||||
seedEntity,
|
||||
seedRelease,
|
||||
} from "../../src/modules/acquisition-organize/__tests__/harness.ts";
|
||||
} from "../acquisition-organize/harness.ts";
|
||||
import {
|
||||
evaluateAcquisition,
|
||||
subscriptionSavePath,
|
||||
|
||||
@@ -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 订阅重做后端:总览分组/排序/计数、覆盖矩阵(单集/季包/多版本/在途/临时单元/电影版本)、
|
||||
|
||||
@@ -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 },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
File renamed without changes.
File renamed without changes.
@@ -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`)。
|
||||
@@ -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 后执行一次,把结果按下表补进本节:
|
||||
|
||||
|
||||
@@ -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 缓存阈值里未纳管占用的专项验收;四站与真实下载、媒体服务器的完整验收。
|
||||
@@ -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 行 |
|
||||
|
||||
|
||||
@@ -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 只经环境变量传入,不写进代码与报告。
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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。
|
||||
|
||||
@@ -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` 均被拒。
|
||||
|
||||
## 已知限制
|
||||
|
||||
File renamed without changes.
File renamed without changes.
Reference in new issue
Block a user