// Lease: single active-signer election. Injected `now` makes the wall-clock TTL deterministic.
// Mock KV log (last-write-wins models the autobase's deterministic linearized winner).
import '../lib/bare-globals.js'
import test from 'brittle'
import { Lease } from '../lib/lease.js'
function mockSB () {
const m = new Map()
return { m, async set (k, v) { m.set(k, v) }, async get (k) { return m.has(k) ? m.get(k) : null }, async del (k) { m.delete(k) }, async list (p) { const o = {}; for (const [k, v] of m) if (k.startsWith(p)) o[k] = v; return o } }
}
test('claim → single holder; standby takes over ONLY after TTL lapse (no live-lease steal)', async (t) => {
const sb = mockSB()
const A = new Lease(sb, { self: 'A', ttlMs: 1000 })
const B = new Lease(sb, { self: 'B', ttlMs: 1000 })
const t0 = 1000
await A.claim(t0)
t.ok(await A.holds(t0), 'A holds after claiming')
t.absent(await B.holds(t0), 'B does not hold')
t.is(await B.claim(t0), null, 'B CANNOT steal A\'s LIVE lease (split-brain fix)')
t.ok(await A.holds(t0), 'A still holds — no split-brain')
// A goes silent; once its lease TTL-lapses, B may take over (failover).
t.is(await B.claim(t0 + 1001), 2, 'epoch monotonic (B claims epoch 2 after A lapsed)')
t.ok(await B.holds(t0 + 1001), 'B now holds')
t.absent(await A.holds(t0 + 1001), 'A lost the lease')
t.is((await A.current()).holder, 'B', 'current holder = B (linearized winner)')
})
test('expiry + renew (out-of-apply wall-clock TTL)', async (t) => {
const sb = mockSB()
const A = new Lease(sb, { self: 'A', ttlMs: 1000 })
const B = new Lease(sb, { self: 'B', ttlMs: 1000 })
await A.claim(1000)
t.ok(await A.holds(1500), 'holds within ttl')
t.absent(await A.holds(2001), 'expired past ttl')
t.ok(await A.isFree(2001), 'free to reclaim after expiry')
t.absent(await B.renew(1500), 'B cannot renew a lease it does not hold')
t.ok(await A.renew(1900), 'A renews (bumps ts)')
t.ok(await A.holds(2100), 'renewed → live again')
})
test('guards', (t) => {
t.exception(() => new Lease(null, { self: 'A' }), /statebase/)
t.exception(() => new Lease(mockSB(), {}), /self/)
})