feat(smartmigration): add initial smartmigration package with MongoDB and S3 migration runner
This commit is contained in:
81
test/test.mongoledger.ts
Normal file
81
test/test.mongoledger.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import { makeTestDb } from './helpers/services.js';
|
||||
import type * as smartdata from '@push.rocks/smartdata';
|
||||
import { MongoLedger } from '../ts/ledgers/classes.mongoledger.js';
|
||||
|
||||
// Smartdata's CollectionFactory caches collections by class name globally
|
||||
// (see classes.collection.ts:22), so we MUST share a single db across all
|
||||
// tests in a file. We isolate tests via unique ledger names — the EasyStore
|
||||
// stores data keyed by `nameId`, so distinct ledger names live in the same
|
||||
// underlying mongo collection without interference.
|
||||
|
||||
let db: smartdata.SmartdataDb;
|
||||
let cleanup: () => Promise<void>;
|
||||
|
||||
tap.test('setup: connect shared db', async () => {
|
||||
const r = await makeTestDb('mongoledger');
|
||||
db = r.db;
|
||||
cleanup = r.cleanup;
|
||||
});
|
||||
|
||||
tap.test('MongoLedger: init creates an empty ledger', async () => {
|
||||
const ledger = new MongoLedger(db, 'init-test');
|
||||
await ledger.init();
|
||||
const data = await ledger.read();
|
||||
expect(data.currentVersion).toBeNull();
|
||||
expect(data.steps).toEqual({});
|
||||
expect(data.lock.holder).toBeNull();
|
||||
expect(data.checkpoints).toEqual({});
|
||||
});
|
||||
|
||||
tap.test('MongoLedger: write+read roundtrips', async () => {
|
||||
const ledger = new MongoLedger(db, 'rw-test');
|
||||
await ledger.init();
|
||||
await ledger.write({
|
||||
currentVersion: '1.5.0',
|
||||
steps: { foo: { id: 'foo', fromVersion: '1.0.0', toVersion: '1.5.0', status: 'applied', startedAt: 'a', finishedAt: 'b', durationMs: 12 } },
|
||||
lock: { holder: null, acquiredAt: null, expiresAt: null },
|
||||
checkpoints: { foo: { progress: 42 } },
|
||||
});
|
||||
const round = await ledger.read();
|
||||
expect(round.currentVersion).toEqual('1.5.0');
|
||||
expect(round.steps['foo'].durationMs).toEqual(12);
|
||||
expect((round.checkpoints['foo'] as any).progress).toEqual(42);
|
||||
});
|
||||
|
||||
tap.test('MongoLedger: lock acquire/release', async () => {
|
||||
const ledger = new MongoLedger(db, 'lock-test');
|
||||
await ledger.init();
|
||||
|
||||
const acquired = await ledger.acquireLock('holder-A', 60_000);
|
||||
expect(acquired).toBeTrue();
|
||||
|
||||
// A second acquire from a different holder should fail.
|
||||
const acquired2 = await ledger.acquireLock('holder-B', 60_000);
|
||||
expect(acquired2).toBeFalse();
|
||||
|
||||
// Release lets holder-B in.
|
||||
await ledger.releaseLock('holder-A');
|
||||
const acquired3 = await ledger.acquireLock('holder-B', 60_000);
|
||||
expect(acquired3).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('MongoLedger: expired lock can be stolen', async () => {
|
||||
const ledger = new MongoLedger(db, 'expire-test');
|
||||
await ledger.init();
|
||||
|
||||
// Acquire with a 1ms TTL so it's instantly expired.
|
||||
const got = await ledger.acquireLock('stale-holder', 1);
|
||||
expect(got).toBeTrue();
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
|
||||
// Now another holder should be able to take over (lock is expired).
|
||||
const got2 = await ledger.acquireLock('fresh-holder', 60_000);
|
||||
expect(got2).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('cleanup: close shared db', async () => {
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
Reference in New Issue
Block a user