60 lines
2.6 KiB
JavaScript
60 lines
2.6 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { mkdtemp } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import test from 'node:test'
|
|
import { UpdateJournal } from '../../control-plane/modules/updater/lib/journal.mjs'
|
|
|
|
const transaction = {
|
|
transactionId: 'transaction-one',
|
|
mode: 'experimental',
|
|
from: {
|
|
dsh: '0.1.0-rc.7', environment: 'env-1', runtime: 'runtime-a', dataSnapshot: null, receiptTokens: [],
|
|
},
|
|
to: { dsh: '0.1.0-rc.8', environment: 'env-1', runtime: 'runtime-b' },
|
|
}
|
|
|
|
test('persists a complete update transaction across process restarts', async () => {
|
|
const root = await mkdtemp(join(tmpdir(), 'dsh-journal-'))
|
|
const path = join(root, 'state', 'update-transaction.json')
|
|
let tick = 0
|
|
const now = () => new Date(Date.UTC(2026, 7, 19, 0, 0, tick++))
|
|
const journal = new UpdateJournal(path, now)
|
|
await journal.begin(transaction)
|
|
await journal.transition('candidate-ready', { receiptTokens: ['receipt-one'] })
|
|
await journal.transition('suspended')
|
|
await journal.transition('snapshot-created', { snapshotId: 'snapshot-one' })
|
|
|
|
const resumed = await new UpdateJournal(path, now).read()
|
|
assert.equal(resumed.phase, 'snapshot-created')
|
|
assert.equal(resumed.from.runtime, 'runtime-a')
|
|
assert.equal(resumed.to.runtime, 'runtime-b')
|
|
assert.equal(resumed.snapshotId, 'snapshot-one')
|
|
assert.deepEqual(resumed.receiptTokens, ['receipt-one'])
|
|
})
|
|
|
|
test('rejects skipped phases and clears only terminal journals', async () => {
|
|
const root = await mkdtemp(join(tmpdir(), 'dsh-journal-transition-'))
|
|
const journal = new UpdateJournal(join(root, 'state', 'update-transaction.json'))
|
|
await journal.begin(transaction)
|
|
await assert.rejects(journal.transition('switched'), /cannot transition/)
|
|
await assert.rejects(journal.clear(), /nonterminal/)
|
|
await journal.transition('failed', { error: 'candidate build failed' })
|
|
await journal.clear()
|
|
assert.equal(await journal.read(), undefined)
|
|
})
|
|
|
|
test('records an idempotent data-restoration phase before rollback completion', async () => {
|
|
const root = await mkdtemp(join(tmpdir(), 'dsh-journal-restore-'))
|
|
const path = join(root, 'state', 'update-transaction.json')
|
|
const journal = new UpdateJournal(path)
|
|
await journal.begin(transaction)
|
|
await journal.transition('candidate-ready')
|
|
await journal.transition('suspended')
|
|
await journal.transition('snapshot-created', { snapshotId: 'snapshot-one' })
|
|
await journal.transition('switched')
|
|
await journal.transition('restoring-data')
|
|
assert.equal((await new UpdateJournal(path).read()).phase, 'restoring-data')
|
|
await journal.transition('rolled-back')
|
|
})
|