Files
ftb-project-management/apps/web/lib/no-business-appdata-runtime-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

67 lines
3.4 KiB
TypeScript

import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import test from 'node:test';
function source(path: string) {
return readFileSync(join(process.cwd(), path), 'utf8');
}
const migratedBusinessStores = [
['stores/useProductStore.ts', 'products-overview'],
['stores/useRequirementStore.ts', 'requirements'],
['stores/useVersionPlanStore.ts', 'version-plans'],
['stores/useDevTaskStore.ts', 'dev-tasks'],
['stores/useTestCaseStore.ts', 'test-cases'],
['stores/useBugStore.ts', 'bugs'],
['stores/useTaskCategoryStore.ts', 'task-categories'],
['stores/useTaskWorklogStore.ts', 'task-worklogs'],
['stores/useWorkActivityStore.ts', 'work-activities'],
] as const;
test('migrated business stores do not read or write AppData documents at runtime', () => {
for (const [file, key] of migratedBusinessStores) {
const text = source(file);
assert.doesNotMatch(text, new RegExp(`loadServerData<[^>]*>\\('${key}'`), `${file} must not read ${key}`);
assert.doesNotMatch(text, new RegExp(`loadServerData\\([^)]*'${key}'`), `${file} must not read ${key}`);
assert.doesNotMatch(text, new RegExp(`saveServerData\\('${key}'`), `${file} must not write ${key}`);
assert.doesNotMatch(text, /\.catch\(loadStored\)/, `${file} must not fall back to legacy AppData reads`);
}
});
test('only explicit legacy config stores keep AppData access', () => {
assert.match(source('stores/useMemberStore.ts'), /saveServerData\('members'/);
assert.match(source('stores/useMemberStore.ts'), /loadServerData<[^>]+>\('members'/);
assert.match(source('stores/useOvertimeStore.ts'), /loadServerData<[^>]+>\('overtime'/);
});
test('read-only Xiaobao AppData archive keys are not used by runtime stores', () => {
for (const file of ['stores/useXiaobaoRiskStore.ts', 'stores/useXiaobaoWarningReadStore.ts']) {
const text = source(file);
assert.doesNotMatch(text, /loadServerData<[^>]+>\('xiaobao-risk-snapshots'/, `${file} must not read Xiaobao snapshots AppData`);
assert.doesNotMatch(text, /loadServerData<[^>]+>\('xiaobao-risk-insights'/, `${file} must not read Xiaobao insights AppData`);
assert.doesNotMatch(text, /loadServerData<[^>]+>\('xiaobao-warning-views'/, `${file} must not read Xiaobao warning read-state AppData`);
assert.doesNotMatch(text, /saveServerData\('xiaobao-risk-snapshots'/, `${file} must not write Xiaobao snapshots AppData`);
assert.doesNotMatch(text, /saveServerData\('xiaobao-risk-insights'/, `${file} must not write Xiaobao insights AppData`);
assert.doesNotMatch(text, /saveServerData\('xiaobao-warning-views'/, `${file} must not write Xiaobao warning read-state AppData`);
}
});
test('auth reads member identities from the member domain API instead of members AppData', () => {
const text = source('stores/useAuthStore.ts');
assert.match(text, /listMembersDomain/);
assert.doesNotMatch(text, /loadServerData<[^>]+>\('members'/);
assert.doesNotMatch(text, /from '@\/lib\/server-data'/);
});
test('member store keeps members AppData as config only, not member identity fallback', () => {
const text = source('stores/useMemberStore.ts');
assert.doesNotMatch(text, /cached\.members/);
assert.doesNotMatch(text, /members: get\(\)\.members,/);
assert.match(text, /saveServerData\('members', \{ departments: state\.departments, members: \[\], roles: state\.roles, passwordRule: state\.passwordRule \}\)/);
});