refactor(data): 收口关系表运行时数据源
Some checks failed
Deploy Production / Build, push, deploy, verify (push) Has been cancelled
Some checks failed
Deploy Production / Build, push, deploy, verify (push) Has been cancelled
- 移除已迁移业务 AppData 运行时 fallback,改走领域 API 和关系表快读 - 补齐需求产品负责人、版本计划任务 JSON 和成员 username 回填迁移 - 统一治理字典入口,并补充 AI provider、数据源契约和领域服务测试 Co-Authored-By: Codex GPT-5 <codex@openai.com>
This commit is contained in:
89
apps/web/lib/no-broad-business-fetch-source.test.ts
Normal file
89
apps/web/lib/no-broad-business-fetch-source.test.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
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');
|
||||
}
|
||||
|
||||
function assertNoBroadBusinessFetches(path: string, allowedPatterns: RegExp[] = []) {
|
||||
const text = allowedPatterns.reduce(
|
||||
(current, pattern) => current.replace(pattern, ''),
|
||||
source(path),
|
||||
);
|
||||
|
||||
for (const method of [
|
||||
'fetchRequirements',
|
||||
'fetchPlans',
|
||||
'fetchTasks',
|
||||
'fetchDevTasks',
|
||||
'fetchTestCases',
|
||||
'fetchBugs',
|
||||
]) {
|
||||
assert.doesNotMatch(
|
||||
text,
|
||||
new RegExp(`\\b${method}\\(\\s*\\)`),
|
||||
`${path} must not call ${method}() without a relation scope`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
test('version detail runtime loads requirements and work data by relation scope', () => {
|
||||
const text = source('app/versions/[id]/page.tsx');
|
||||
|
||||
assert.match(text, /fetchRequirements\(\{ productId: version\.productId, versionId \}\)/);
|
||||
assert.match(text, /fetchRequirements\(\{ productId: version\.productId, versionId, force: true \}\)/);
|
||||
assertNoBroadBusinessFetches('app/versions/[id]/page.tsx');
|
||||
});
|
||||
|
||||
test('version list hydrates visible versions through scoped relation partitions', () => {
|
||||
const text = source('app/versions/page.tsx');
|
||||
|
||||
assert.match(text, /fetchRequirements\(\{ productId: version\.productId, versionId: version\.id \}\)/);
|
||||
assert.match(text, /fetchPlans\(\{ versionId: version\.id \}\)/);
|
||||
assert.match(text, /fetchDevTasks\(\{ versionId: version\.id \}\)/);
|
||||
assert.match(text, /fetchTestCases\(\{ versionId: version\.id \}\)/);
|
||||
assertNoBroadBusinessFetches('app/versions/page.tsx');
|
||||
});
|
||||
|
||||
test('workspace fallback and drawer hydration remain version scoped', () => {
|
||||
const hook = source('hooks/useWorkspaceWorkItems.ts');
|
||||
const page = source('app/workspace/page.tsx');
|
||||
|
||||
assert.match(hook, /hydrateWorkspaceStoreFallback/);
|
||||
assert.match(page, /hydrateWorkspaceStoreFallback/);
|
||||
assert.match(page, /fetchRequirements\(\{ productId: versionContext\.productId, versionId: item\.versionId \}\)/);
|
||||
assertNoBroadBusinessFetches('hooks/useWorkspaceWorkItems.ts');
|
||||
assertNoBroadBusinessFetches('app/workspace/page.tsx');
|
||||
});
|
||||
|
||||
test('xiaobao fallback hydrates visible versions by relation scope only', () => {
|
||||
const text = source('hooks/useXiaobaoWarningRisks.ts');
|
||||
|
||||
assert.match(text, /hydrateXiaobaoStoreFallback/);
|
||||
assert.match(text, /fetchRequirements\(\{ productId: version\.productId, versionId: version\.id \}\)/);
|
||||
assert.match(text, /fetchPlans\(\{ versionId: version\.id \}\)/);
|
||||
assert.match(text, /fetchTasks\(\{ versionId: version\.id \}\)/);
|
||||
assert.match(text, /fetchTestCases\(\{ versionId: version\.id \}\)/);
|
||||
assert.match(text, /fetchBugs\(\{ versionId: version\.id \}\)/);
|
||||
assertNoBroadBusinessFetches('hooks/useXiaobaoWarningRisks.ts');
|
||||
});
|
||||
|
||||
test('overtime modal loads selected version requirements through relation scope', () => {
|
||||
const text = source('app/overtime/page.tsx');
|
||||
|
||||
assert.match(text, /onVersionChange=\{\(version\) => fetchRequirements\(\{ productId: version\.productId, versionId: version\.id \}\)\}/);
|
||||
assertNoBroadBusinessFetches('app/overtime/page.tsx');
|
||||
});
|
||||
|
||||
test('requirement pool fallback hydrates the selected relation scope', () => {
|
||||
const text = source('app/requirements/page.tsx');
|
||||
|
||||
assert.match(text, /hydrateRequirementStoreFallback/);
|
||||
assert.match(text, /fetchRequirements\(\{ productId: selectedScope\.productId \}\)/);
|
||||
assert.match(text, /fetchRequirements\(\{ productId: project\.productId, projectId: selectedScope\.projectId \}\)/);
|
||||
assert.match(text, /fetchRequirements\(\{ productId: product\.id \}\)/);
|
||||
assert.match(text, /fetchDevTasks\(\{ versionId: version\.id \}\)/);
|
||||
assertNoBroadBusinessFetches('app/requirements/page.tsx');
|
||||
});
|
||||
Reference in New Issue
Block a user