feat(migration): add lock heartbeats, predictive dry-run planning, and stricter ledger option validation

This commit is contained in:
2026-04-14 12:31:34 +00:00
parent 19ebdee31a
commit 1b4358aca5
17 changed files with 695 additions and 180 deletions
+27 -1
View File
@@ -1,5 +1,5 @@
import { tap, expect } from '@git.zone/tstest/tapbundle';
import { makeTestBucket } from './helpers/services.js';
import { makeIsolatedTestBucket, makeTestBucket } from './helpers/services.js';
import type * as smartbucket from '@push.rocks/smartbucket';
import { SmartMigration } from '../ts/index.js';
@@ -94,6 +94,32 @@ tap.test('s3 ctx exposes bucket and s3 client', async () => {
expect(observed!.hasS3).toBeTrue();
});
tap.test('s3 dryRun: freshInstallVersion skips steps without creating a sidecar', async () => {
const isolated = await makeIsolatedTestBucket('fresh-plan');
try {
const ledgerName = 'fresh-plan';
const m = new SmartMigration({
targetVersion: '2.0.0',
bucket: isolated.bucket,
ledgerName,
freshInstallVersion: '2.0.0',
dryRun: true,
});
let stepCalled = false;
m.step('move-uploads').from('1.0.0').to('2.0.0').up(async () => { stepCalled = true; });
const r = await m.run();
expect(stepCalled).toBeFalse();
expect(r.wasFreshInstall).toBeTrue();
expect(r.stepsSkipped).toHaveLength(0);
expect(r.currentVersionAfter).toEqual('2.0.0');
expect(await isolated.bucket.fastExists({ path: `.smartmigration/${ledgerName}.json` })).toBeFalse();
} finally {
await isolated.cleanup();
}
});
tap.test('cleanup: wipe smartmigration sidecars', async () => {
for await (const key of bucket.listAllObjects('.smartmigration/')) {
await bucket.fastRemove({ path: key });