test: harden pocketbase dry run readiness

This commit is contained in:
Codex
2026-06-30 02:23:17 +08:00
parent 7eaf26fa40
commit 52cef9f13d
7 changed files with 223 additions and 15 deletions

View File

@@ -44,7 +44,7 @@ function createSafeExport() {
{ id: 'q1', subjectId: 's1', categoryId: 'c1', type: 'choice', content: '题干', answer: 'A' },
]);
writeJson(path.join(dir, 'orders.json'), [
{ id: 'o1', userId: 'u1', amount: 99, status: 'paid' },
{ id: 'o1', userId: 'u1', planId: 'p1', amount: 99, status: 'paid' },
]);
writeJson(path.join(dir, 'svip_plans.json'), [
{ id: 'p1', name: '题库会员', price: 99 },
@@ -105,12 +105,36 @@ function createBrokenRelationExport() {
return dir;
}
function createProductionIncompleteExport() {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'tiku-pb-dry-prod-incomplete-'));
writeJson(path.join(dir, 'users.json'), [
{ id: 'u1', role: 'student', nickname: '缺手机号学生' },
]);
writeJson(path.join(dir, 'questions.json'), [
{ id: 'q1', categoryId: 'c1', type: 'choice', content: '缺科目题' },
]);
return dir;
}
const safeDir = createSafeExport();
const safe = runDryRun(safeDir);
assert.equal(safe.status, 0, `safe dry-run should pass: ${safe.stdout} ${safe.stderr}`);
assert.equal(safe.payload.summary?.blockers, 0, 'safe dry-run should have no blockers');
assert.ok(safe.payload.summary?.records >= 12, 'safe dry-run should count fixture records');
assert.equal(safe.payload.businessCounts?.questions, 1, 'safe dry-run should report question count');
assert.equal(safe.payload.migrationProfile, 'development', 'default dry-run profile should be development');
assert.ok(Array.isArray(safe.payload.migrationReadiness?.requiredCollections), 'dry-run should report migration readiness');
const safeProduction = runDryRun(safeDir, { args: ['--profile=production'] });
assert.equal(safeProduction.status, 0, `complete production dry-run should pass: ${safeProduction.stdout} ${safeProduction.stderr}`);
assert.equal(safeProduction.payload.summary?.blockers, 0, 'complete production dry-run should have no blockers');
const invalidProfile = runDryRun(safeDir, { args: ['--profile=prodution'] });
assert.notEqual(invalidProfile.status, 0, 'invalid migration profile should fail closed');
assert.ok(
invalidProfile.payload.issues?.some(item => item.code === 'migration_profile_invalid'),
'invalid migration profile should be reported as a blocker',
);
const unsafeDir = createUnsafeExport();
const unsafe = runDryRun(unsafeDir);
@@ -152,9 +176,23 @@ assert.ok(
'broken relation dry-run should detect unresolved question subject relation',
);
const productionIncompleteDir = createProductionIncompleteExport();
const productionIncomplete = runDryRun(productionIncompleteDir, { args: ['--profile=production'] });
assert.notEqual(productionIncomplete.status, 0, 'production profile should fail incomplete export');
assert.ok(
productionIncomplete.payload.issues?.some(item => item.code === 'production_required_collection_missing'),
'production profile should block missing required collections',
);
assert.ok(
productionIncomplete.payload.issues?.some(item => item.code === 'critical_field_coverage_low' && item.collection === 'questions'),
'production profile should block critical field coverage gaps',
);
assert.equal(productionIncomplete.payload.migrationProfile, 'production', 'production profile should be reflected in report');
fs.rmSync(safeDir, { recursive: true, force: true });
fs.rmSync(unsafeDir, { recursive: true, force: true });
fs.rmSync(warningOnlyDir, { recursive: true, force: true });
fs.rmSync(brokenRelationDir, { recursive: true, force: true });
fs.rmSync(productionIncompleteDir, { recursive: true, force: true });
console.log('[PASS] PocketBase dry-run report');