Files
ftb-project-management/apps/web/lib/version-plan-owner-source.test.ts
2e595c7e72
Some checks failed
Deploy Production / Build, push, deploy, verify (push) Has been cancelled
refactor(data): 收口关系表运行时数据源
- 移除已迁移业务 AppData 运行时 fallback,改走领域 API 和关系表快读
- 补齐需求产品负责人、版本计划任务 JSON 和成员 username 回填迁移
- 统一治理字典入口,并补充 AI provider、数据源契约和领域服务测试

Co-Authored-By: Codex GPT-5 <codex@openai.com>
2026-07-09 14:59:49 +08:00

51 lines
2.1 KiB
TypeScript

import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import test from 'node:test';
const rootFile = (path: string) => readFileSync(join(process.cwd(), path), 'utf8');
test('plan auto-start effect only persists the same pending plan once', () => {
const text = rootFile('components/version/PlanTab.tsx');
assert.match(text, /useRef/);
assert.match(text, /autoStartedPlanIds/);
assert.match(text, /!autoStartedPlanIds\.current\.has\(plan\.id\)/);
assert.match(text, /autoStartedPlanIds\.current\.add\(plan\.id\)/);
});
test('plan owner UI resolves stored member ids to display names and filters transfer choices by member reference', () => {
const text = rootFile('components/version/PlanTab.tsx');
assert.match(text, /resolveMemberDisplayName/);
assert.match(text, /isMemberReference/);
assert.match(text, /ownerLabel/);
assert.doesNotMatch(text, />\{plan\.owner\}<\/span>/);
assert.doesNotMatch(text, />\{plan\.owner\}<\/dd>/);
assert.doesNotMatch(text, /member\.name !== plan\.owner/);
});
test('version detail passes relation-backed member candidates to plan tabs for id display resolution', () => {
const text = rootFile('app/versions/[id]/page.tsx');
assert.match(text, /username: member\.username/);
assert.match(text, /currentUserReference=\{user\?\.username/);
assert.match(text, /allMembers=\{memberCandidates\}/);
});
test('plan create response merge preserves optimistic auto-start fields from the current local row', () => {
const text = rootFile('stores/useVersionPlanStore.ts');
assert.match(text, /mergeCreatedVersionPlan/);
assert.doesNotMatch(text, /\{\s*\.\.\.plan,\s*\.\.\.result\.item,\s*tasks: plan\.tasks\s*\}/);
});
test('requirement product owner is sent to the relation API instead of staying as a local-only field', () => {
const requirementStore = rootFile('stores/useRequirementStore.ts');
const domainApi = rootFile('lib/domain-api.ts');
assert.doesNotMatch(requirementStore, /delete domainPatch\.productOwner/);
assert.match(domainApi, /productOwnerId/);
assert.match(domainApi, /data\.productOwner/);
});