feat(planner): add bridge target version strategy for auto-stamping ledger to app versions
This commit is contained in:
@@ -8,6 +8,24 @@ import { SmartMigration, SmartMigrationError } from '../ts/index.js';
|
||||
let db: smartdata.SmartdataDb;
|
||||
let cleanup: () => Promise<void>;
|
||||
|
||||
async function setLedgerVersion(ledgerName: string, currentVersion: string): Promise<void> {
|
||||
await db.mongoDb.collection('SmartdataEasyStore').updateOne(
|
||||
{ nameId: `smartmigration:${ledgerName}` },
|
||||
{
|
||||
$set: {
|
||||
nameId: `smartmigration:${ledgerName}`,
|
||||
data: {
|
||||
currentVersion,
|
||||
steps: {},
|
||||
lock: { holder: null, acquiredAt: null, expiresAt: null },
|
||||
checkpoints: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ upsert: true },
|
||||
);
|
||||
}
|
||||
|
||||
tap.test('setup: connect shared db', async () => {
|
||||
const r = await makeTestDb('run_mongo');
|
||||
db = r.db;
|
||||
@@ -153,6 +171,116 @@ tap.test('run: lock heartbeat prevents concurrent execution of a slow step', asy
|
||||
expect(maxActiveSteps).toEqual(1);
|
||||
});
|
||||
|
||||
tap.test('run: strict target strategy rejects target beyond registered chain', async () => {
|
||||
await setLedgerVersion('strict_target_gap', '2.0.0');
|
||||
|
||||
const m = new SmartMigration({ targetVersion: '3.0.0', db, ledgerName: 'strict_target_gap' });
|
||||
m.step('a').from('1.0.0').to('2.0.0').up(async () => {});
|
||||
|
||||
let caught: SmartMigrationError | undefined;
|
||||
try {
|
||||
await m.run();
|
||||
} catch (err) {
|
||||
caught = err as SmartMigrationError;
|
||||
}
|
||||
|
||||
expect(caught).toBeInstanceOf(SmartMigrationError);
|
||||
expect(caught!.code).toEqual('TARGET_NOT_REACHABLE');
|
||||
});
|
||||
|
||||
tap.test('run: bridge target strategy stamps from past chain end to target', async () => {
|
||||
await setLedgerVersion('bridge_from_past_chain_end', '2.5.0');
|
||||
|
||||
let realStepCalled = false;
|
||||
const m = new SmartMigration({
|
||||
targetVersion: '3.0.0',
|
||||
db,
|
||||
ledgerName: 'bridge_from_past_chain_end',
|
||||
targetVersionStrategy: 'bridge',
|
||||
});
|
||||
m.step('a').from('1.0.0').to('2.0.0').up(async () => { realStepCalled = true; });
|
||||
|
||||
const r = await m.run();
|
||||
|
||||
expect(realStepCalled).toBeFalse();
|
||||
expect(r.stepsApplied).toHaveLength(1);
|
||||
expect(r.stepsApplied[0].id).toEqual('smartmigration-auto-bridge-to-3.0.0');
|
||||
expect(r.stepsApplied[0].fromVersion).toEqual('2.0.0');
|
||||
expect(r.currentVersionAfter).toEqual('3.0.0');
|
||||
expect(await m.getCurrentVersion()).toEqual('3.0.0');
|
||||
});
|
||||
|
||||
tap.test('run: bridge target strategy runs real migrations before bridge', async () => {
|
||||
await setLedgerVersion('bridge_after_real_steps', '1.0.0');
|
||||
|
||||
const log: string[] = [];
|
||||
const m = new SmartMigration({
|
||||
targetVersion: '3.0.0',
|
||||
db,
|
||||
ledgerName: 'bridge_after_real_steps',
|
||||
targetVersionStrategy: 'bridge',
|
||||
});
|
||||
m.step('a').from('1.0.0').to('2.0.0').up(async () => { log.push('a'); });
|
||||
|
||||
const r = await m.run();
|
||||
|
||||
expect(log).toEqual(['a']);
|
||||
expect(r.stepsApplied.map((step) => step.id)).toEqual([
|
||||
'a',
|
||||
'smartmigration-auto-bridge-to-3.0.0',
|
||||
]);
|
||||
expect(r.currentVersionAfter).toEqual('3.0.0');
|
||||
});
|
||||
|
||||
tap.test('plan: bridge target strategy is read-only', async () => {
|
||||
await setLedgerVersion('bridge_plan_readonly', '2.5.0');
|
||||
|
||||
const m = new SmartMigration({
|
||||
targetVersion: '3.0.0',
|
||||
db,
|
||||
ledgerName: 'bridge_plan_readonly',
|
||||
targetVersionStrategy: 'bridge',
|
||||
});
|
||||
m.step('a').from('1.0.0').to('2.0.0').up(async () => {});
|
||||
|
||||
const r = await m.plan();
|
||||
|
||||
expect(r.stepsSkipped).toHaveLength(1);
|
||||
expect(r.stepsSkipped[0].id).toEqual('smartmigration-auto-bridge-to-3.0.0');
|
||||
expect(r.currentVersionAfter).toEqual('3.0.0');
|
||||
expect(await m.getCurrentVersion()).toEqual('2.5.0');
|
||||
});
|
||||
|
||||
tap.test('run: future real migration applies after a bridged version stamp', async () => {
|
||||
await setLedgerVersion('bridge_future_migration', '2.5.0');
|
||||
|
||||
const bridgeRunner = new SmartMigration({
|
||||
targetVersion: '3.0.0',
|
||||
db,
|
||||
ledgerName: 'bridge_future_migration',
|
||||
targetVersionStrategy: 'bridge',
|
||||
});
|
||||
bridgeRunner.step('a').from('1.0.0').to('2.0.0').up(async () => {});
|
||||
await bridgeRunner.run();
|
||||
|
||||
let futureStepCalled = false;
|
||||
const futureRunner = new SmartMigration({
|
||||
targetVersion: '4.0.0',
|
||||
db,
|
||||
ledgerName: 'bridge_future_migration',
|
||||
targetVersionStrategy: 'bridge',
|
||||
});
|
||||
futureRunner
|
||||
.step('a').from('1.0.0').to('2.0.0').up(async () => {})
|
||||
.step('future').from('2.0.0').to('4.0.0').up(async () => { futureStepCalled = true; });
|
||||
|
||||
const r = await futureRunner.run();
|
||||
|
||||
expect(futureStepCalled).toBeTrue();
|
||||
expect(r.stepsApplied.map((step) => step.id)).toEqual(['future']);
|
||||
expect(r.currentVersionAfter).toEqual('4.0.0');
|
||||
});
|
||||
|
||||
tap.test('cleanup: close shared db', async () => {
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user