feat: harden pocketbase migration readiness checks

This commit is contained in:
Codex
2026-06-30 00:51:20 +08:00
parent db86fa3705
commit 889a53c90f
7 changed files with 348 additions and 7 deletions

View File

@@ -11,13 +11,14 @@ function writeJson(filePath, value) {
fs.writeFileSync(filePath, JSON.stringify(value, null, 2), 'utf8');
}
function runDryRun(exportDir) {
const result = spawnSync(process.execPath, ['--import', 'tsx', dryRunScript, '--json'], {
function runDryRun(exportDir, options = {}) {
const result = spawnSync(process.execPath, ['--import', 'tsx', dryRunScript, '--json', ...(options.args || [])], {
cwd: repoRoot,
encoding: 'utf8',
env: {
...process.env,
PB_EXPORT_DIR: exportDir,
...(options.env || {}),
},
});
@@ -88,6 +89,22 @@ function createUnsafeExport() {
return dir;
}
function createWarningOnlyExport() {
const dir = createSafeExport();
writeJson(path.join(dir, 'unknown_business.json'), [
{ id: 'x1', name: '尚未映射集合' },
]);
return dir;
}
function createBrokenRelationExport() {
const dir = createSafeExport();
writeJson(path.join(dir, 'questions.json'), [
{ id: 'q1', subjectId: 'missing-subject', categoryId: 'c1', type: 'choice', content: '题干', answer: 'A' },
]);
return dir;
}
const safeDir = createSafeExport();
const safe = runDryRun(safeDir);
assert.equal(safe.status, 0, `safe dry-run should pass: ${safe.stdout} ${safe.stderr}`);
@@ -115,8 +132,29 @@ assert.ok(
unsafe.payload.issues?.some(item => item.code === 'unsupported_collection'),
'unsafe dry-run should warn about unsupported collections',
);
assert.ok(
unsafe.payload.issues?.some(item => item.code === 'relation_target_export_missing' && item.collection === 'questions'),
'unsafe dry-run should block when question relation target export is missing',
);
const warningOnlyDir = createWarningOnlyExport();
const warningOnly = runDryRun(warningOnlyDir);
assert.equal(warningOnly.status, 0, 'warning-only dry-run should pass without fail-on-warnings');
assert.ok(warningOnly.payload.summary?.warnings > 0, 'warning-only dry-run should report warnings');
const warningAsFailure = runDryRun(warningOnlyDir, { args: ['--fail-on-warnings'] });
assert.notEqual(warningAsFailure.status, 0, 'warning-only dry-run should fail with --fail-on-warnings');
const brokenRelationDir = createBrokenRelationExport();
const brokenRelation = runDryRun(brokenRelationDir);
assert.notEqual(brokenRelation.status, 0, 'broken relation dry-run should fail');
assert.ok(
brokenRelation.payload.issues?.some(item => item.code === 'relation_unresolved' && item.collection === 'questions'),
'broken relation dry-run should detect unresolved question subject relation',
);
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 });
console.log('[PASS] PocketBase dry-run report');