82 lines
2.9 KiB
TypeScript
82 lines
2.9 KiB
TypeScript
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();
|