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
+60
View File
@@ -24,6 +24,21 @@ tap.test('constructor: rejects invalid targetVersion', async () => {
expect(caught!.code).toEqual('INVALID_VERSION');
});
tap.test('constructor: rejects invalid freshInstallVersion', async () => {
let caught: SmartMigrationError | undefined;
try {
new SmartMigration({
targetVersion: '1.0.0',
freshInstallVersion: 'nope',
db: {} as any,
});
} catch (err) {
caught = err as SmartMigrationError;
}
expect(caught).toBeInstanceOf(SmartMigrationError);
expect(caught!.code).toEqual('INVALID_VERSION');
});
tap.test('constructor: rejects when neither db nor bucket given', async () => {
let caught: SmartMigrationError | undefined;
try {
@@ -70,6 +85,51 @@ tap.test('constructor: rejects mongo backend without db', async () => {
expect(caught!.code).toEqual('LEDGER_BACKEND_MISMATCH');
});
tap.test('constructor: rejects blank ledgerName', async () => {
let caught: SmartMigrationError | undefined;
try {
new SmartMigration({
targetVersion: '1.0.0',
db: {} as any,
ledgerName: ' ',
});
} catch (err) {
caught = err as SmartMigrationError;
}
expect(caught).toBeInstanceOf(SmartMigrationError);
expect(caught!.code).toEqual('INVALID_LEDGER_NAME');
});
tap.test('constructor: rejects negative lockWaitMs', async () => {
let caught: SmartMigrationError | undefined;
try {
new SmartMigration({
targetVersion: '1.0.0',
db: {} as any,
lockWaitMs: -1,
});
} catch (err) {
caught = err as SmartMigrationError;
}
expect(caught).toBeInstanceOf(SmartMigrationError);
expect(caught!.code).toEqual('INVALID_LOCK_WAIT_MS');
});
tap.test('constructor: rejects non-positive lockTtlMs', async () => {
let caught: SmartMigrationError | undefined;
try {
new SmartMigration({
targetVersion: '1.0.0',
db: {} as any,
lockTtlMs: 0,
});
} catch (err) {
caught = err as SmartMigrationError;
}
expect(caught).toBeInstanceOf(SmartMigrationError);
expect(caught!.code).toEqual('INVALID_LOCK_TTL_MS');
});
tap.test('constructor: rejects s3 backend without bucket', async () => {
let caught: SmartMigrationError | undefined;
try {