Refactor ZMQ DB backups: add property coverage and constructor test

By refactorer.
This commit is contained in:
Chris Troutner
2026-08-28 10:10:30 -07:00
parent 2d193c6bd2
commit 89731964de
2 changed files with 72 additions and 0 deletions
@@ -0,0 +1,66 @@
/*
Property tests for the indexer's DB backup decision.
BackupDb.maybeBackupDb decides when the indexer should ask psf-memo-db to
back up its LevelDB. The decision is a pure function of the block height and
the configured epoch: a backup is requested exactly when the height is a
positive multiple of the epoch. These properties pin that invariant across a
broad range of heights and epochs, and confirm the decision is deterministic
(idempotent) for repeated calls.
*/
import test from 'node:test'
import { seededRandom, forAll, intGen } from './harness.js'
import BackupDb from '../../src/use-cases/backup-db.js'
const rng = seededRandom(20260828)
function makeBackupDb () {
const calls = []
const adapters = {
dbCtrl: {
backupDb: async (height, epoch) => {
calls.push({ height, epoch })
return true
}
}
}
const uut = new BackupDb({ adapters })
return { uut, calls }
}
test('maybeBackupDb requests a backup exactly at positive multiples of the epoch', async () => {
const heightGen = intGen(rng, 0, 1000000)
const epochGen = intGen(rng, 1, 10000)
await forAll(
(i) => ({ height: heightGen(), epoch: epochGen() }),
async ({ height, epoch }) => {
const { uut, calls } = makeBackupDb()
const result = await uut.maybeBackupDb(height, epoch)
const expected = height > 0 && height % epoch === 0
const requested = calls.length === 1 &&
calls[0].height === height &&
calls[0].epoch === epoch
return result === expected && requested === expected
},
{ label: 'backup decision matches height % epoch invariant' }
)
})
test('maybeBackupDb is deterministic across repeated calls', async () => {
const heightGen = intGen(rng, 0, 1000000)
const epochGen = intGen(rng, 1, 10000)
await forAll(
(i) => ({ height: heightGen(), epoch: epochGen() }),
async ({ height, epoch }) => {
const { uut, calls } = makeBackupDb()
const first = await uut.maybeBackupDb(height, epoch)
const second = await uut.maybeBackupDb(height, epoch)
return first === second && calls.length === (first ? 2 : 0)
},
{ label: 'backup decision idempotence' }
)
})
@@ -19,6 +19,12 @@ describe('#BackupDb', () => {
afterEach(() => sandbox.restore())
describe('constructor', () => {
it('should throw when adapters are missing', () => {
assert.throws(() => new BackupDb(), /Adapters required/)
})
})
describe('#maybeBackupDb', () => {
it('should request a backup at an epoch boundary', async () => {
const result = await uut.maybeBackupDb(1000, 1000)