import assert from "node:assert/strict"; import test from "node:test"; import { readFileSync } from "node:fs"; const adminApp = readFileSync( new URL("../app/admin-app.tsx", import.meta.url), "utf8", ); const adminCss = readFileSync( new URL("../app/globals.css", import.meta.url), "utf8", ); const actionRoute = readFileSync( new URL("../app/api/action/route.ts", import.meta.url), "utf8", ); const taskService = readFileSync( new URL("../lib/task-service.ts", import.meta.url), "utf8", ); const projectService = readFileSync( new URL("../lib/project-service.ts", import.meta.url), "utf8", ); const migration = readFileSync( new URL("../mysql/0020_task_recovery_merge.sql", import.meta.url), "utf8", ); test("单任务模型:配对聚合与截图任务分支已删除", () => { assert.doesNotMatch(adminApp, /groupTaskMasters/); assert.doesNotMatch(adminApp, /PairableTask/); assert.doesNotMatch(adminApp, /companion/); assert.doesNotMatch(adminApp, /screenshot_collect/); assert.doesNotMatch(adminApp, /withScreenshotTask/); assert.doesNotMatch(adminApp, /仅回收/); assert.doesNotMatch(adminApp, /回收群/); assert.doesNotMatch(taskService, /createScreenshotTask/); assert.doesNotMatch(actionRoute, /create_screenshot_task/); assert.doesNotMatch(adminCss, /\.task-row-companion/); }); test("TaskRowCard 双面板:发布面板 + 回收实面板/ghost 开通面板", () => { assert.match(adminApp, /master-panel publish/); assert.match(adminApp, /master-panel recovery/); assert.match(adminApp, /master-panel ghost/); assert.match(adminApp, /未开通回收/); assert.match(adminApp, /开通回收 ›/); assert.match(adminApp, /已收取/); assert.match(adminApp, /recovery_submitted/); assert.match(adminApp, /回收已开通/); assert.match(adminCss, /\.master-panel\.ghost/); }); test("创建/编辑表单携带回收设置", () => { assert.match(adminApp, /开启截图回收(第二阶段)/); assert.match(adminApp, /recoveryEnabled/); assert.match(adminApp, /recoveryDueAt/); assert.match(adminApp, /recoveryInstructions/); assert.match(adminApp, /recoveryExampleImageKey/); assert.match(adminApp, /发布截止日期/); }); test("create_task / task_update / task_enable_recovery 三个 action 都落 recovery 字段", () => { const createBlock = actionRoute.slice( actionRoute.indexOf('body.action === "create_task"'), actionRoute.indexOf('body.action === "task_update"'), ); assert.match(createBlock, /recoveryEnabled/); assert.match(createBlock, /recoveryExampleImageKey/); const enableBlock = actionRoute.slice( actionRoute.indexOf('body.action === "task_enable_recovery"'), ); assert.match(enableBlock, /enableTaskRecovery/); assert.match(enableBlock, /recoveryDueAt/); assert.match(taskService, /export async function enableTaskRecovery/); assert.match(taskService, /recovery_enabled, recovery_due_at, recovery_instructions, recovery_example_images/); }); test("项目详情查询带回 recovery 字段(不再依赖 related_task_id)", () => { assert.match(projectService, /recovery_enabled, recovery_due_at/); assert.match(projectService, /recovery_submitted/); assert.doesNotMatch(projectService, /related_task_id/); }); test("存量迁移 SQL:配对并入、独立转正、删除老截图任务", () => { assert.match(migration, /ALTER TABLE tasks ADD COLUMN recovery_enabled/); assert.match(migration, /ADD COLUMN recovery_due_at/); assert.match(migration, /ADD COLUMN recovery_instructions/); assert.match(migration, /ADD COLUMN recovery_example_images/); assert.match(migration, /child\.related_task_id = parent\.id/); assert.match(migration, /target\.result_screenshot_key = legacy\.result_screenshot_key/); assert.match(migration, /DELETE FROM tasks WHERE task_type = 'screenshot_collect' AND related_task_id != ''/); assert.match(migration, /standalone\.task_type = 'content_publish'/); });