Completes the consumer half of bitspire-#70: a paired machine gets its LNbits
transport relay(s) + server pubkey from the pairing, so a blank-.env unit reaches
the backend after scanning a seed — no VITE_RELAY_URL / VITE_LNBITS_SERVER_PUBKEY
provisioning.
- resolveSigner now returns { signer, transport }. transport (relays +
lnbitsServerPubkey) comes from the seed on a fresh pair / seeded resume, and
from the binding on a seedless resume. It's threaded out of resolveSigner
rather than re-parsed in loadLightningConfig because the seed arrives over the
one-shot get-atm-secrets IPC — a second consumer would break that contract.
- bunker_binding persists relays + lnbits_server_pubkey (state.db v11→v12,
nullable so pre-#70 bindings resume and fall back to env). Mirrored into
BunkerBindingRecord (preload + electron.d.ts).
- initializeLightningServices resolves effective transport with env-wins
precedence (explicit env override for dev, else pairing, else a dev-only
localhost relay), mutating CONFIG to a single source of truth and building the
Nostr/LNbits/CLINK clients from the full relay list. Strict + required-config
validation now run on the resolved values.
state.db round-trip test covers the new columns + their absence on a pre-#70
binding. Renderer + electron typechecks and all 38 machine tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
69 lines
2.1 KiB
TypeScript
69 lines
2.1 KiB
TypeScript
/**
|
|
* Tests for bunker-binding persistence in state-store (aiolabs/bitspire#52,
|
|
* transport config added in #70).
|
|
*
|
|
* Validates the round-trip of the binding singleton, including the v11→v12
|
|
* transport columns (relays JSON + lnbits_server_pubkey) and their absence on
|
|
* a pre-#70 binding.
|
|
*
|
|
* Uses an in-memory SQLite database — fresh per test, no on-disk artifacts.
|
|
*/
|
|
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
import {
|
|
clearBunkerBinding,
|
|
closeDatabase,
|
|
getBunkerBinding,
|
|
initDatabase,
|
|
saveBunkerBinding,
|
|
type StoredBunkerBinding,
|
|
} from '../state-store.js'
|
|
|
|
const BASE: StoredBunkerBinding = {
|
|
clientSecretHex: 'aa'.repeat(32),
|
|
spirePubkey: 'bb'.repeat(32),
|
|
bunkerUrl: 'bunker://bb?relay=wss%3A%2F%2Fr%2F&secret=deadbeef',
|
|
seedFingerprint: 'cc'.repeat(32),
|
|
pairedAt: 1_780_000_000,
|
|
}
|
|
|
|
beforeEach(() => {
|
|
initDatabase(':memory:')
|
|
})
|
|
afterEach(() => {
|
|
closeDatabase()
|
|
})
|
|
|
|
describe('bunker binding persistence', () => {
|
|
it('round-trips a binding carrying transport config (#70)', () => {
|
|
const binding: StoredBunkerBinding = {
|
|
...BASE,
|
|
relays: ['wss://one.relay/', 'wss://two.relay/'],
|
|
lnbitsServerPubkey: 'dd'.repeat(32),
|
|
}
|
|
saveBunkerBinding(binding)
|
|
expect(getBunkerBinding()).toEqual(binding)
|
|
})
|
|
|
|
it('round-trips a pre-#70 binding (no transport config) as undefined fields', () => {
|
|
saveBunkerBinding(BASE)
|
|
const got = getBunkerBinding()
|
|
expect(got).toEqual(BASE)
|
|
expect(got?.relays).toBeUndefined()
|
|
expect(got?.lnbitsServerPubkey).toBeUndefined()
|
|
})
|
|
|
|
it('upserts transport config in place (re-pair overwrites)', () => {
|
|
saveBunkerBinding({ ...BASE, relays: ['wss://old/'], lnbitsServerPubkey: 'ee'.repeat(32) })
|
|
saveBunkerBinding({ ...BASE, relays: ['wss://new/'], lnbitsServerPubkey: 'ff'.repeat(32) })
|
|
const got = getBunkerBinding()
|
|
expect(got?.relays).toEqual(['wss://new/'])
|
|
expect(got?.lnbitsServerPubkey).toBe('ff'.repeat(32))
|
|
})
|
|
|
|
it('returns null after clear', () => {
|
|
saveBunkerBinding(BASE)
|
|
clearBunkerBinding()
|
|
expect(getBunkerBinding()).toBeNull()
|
|
})
|
|
})
|