Files
peardock/test/peer-cache.test.js
T
Raven Scott ecafa4f22a
Release rolling / release (push) Has been cancelled
updates
2026-07-14 20:04:49 -04:00

153 lines
4.5 KiB
JavaScript

import test from 'brittle'
import fs from 'fs'
import os from 'os'
import path from 'path'
import {
parsePeersPayload,
buildPeersPayload,
normalizePeerEntry,
writePeersToFile,
readPeersFromFile,
loadPeers,
savePeers,
clearPeers,
getPeersCachePath,
listSavedPeers,
} from '../client/peerCache.js'
const KEY =
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
const KEY2 =
'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
test('normalizePeerEntry accepts publicKeyHex', (t) => {
const e = normalizePeerEntry({ publicKeyHex: KEY, alias: 'prod' })
t.is(e.publicKeyHex, KEY)
t.is(e.id, KEY.slice(0, 12))
t.is(e.alias, 'prod')
})
test('normalizePeerEntry rejects invalid keys', (t) => {
t.absent(normalizePeerEntry({ publicKeyHex: 'nope' }))
t.absent(normalizePeerEntry(null))
})
test('parsePeersPayload supports versioned and legacy flat maps', (t) => {
const versioned = parsePeersPayload({
version: 1,
peers: {
[KEY.slice(0, 12)]: { publicKeyHex: KEY, alias: 'a' },
},
})
t.is(versioned[KEY.slice(0, 12)].publicKeyHex, KEY)
const flat = parsePeersPayload({
[KEY.slice(0, 12)]: { publicKeyHex: KEY, alias: 'b' },
})
t.is(flat[KEY.slice(0, 12)].alias, 'b')
})
test('buildPeersPayload wraps peers with version', (t) => {
const payload = buildPeersPayload({
[KEY.slice(0, 12)]: { publicKeyHex: KEY, alias: 'x' },
})
t.is(payload.version, 1)
t.ok(payload.updatedAt)
t.is(payload.peers[KEY.slice(0, 12)].publicKeyHex, KEY)
})
test('file cache round-trip under temp PEARDOCK_HOME', (t) => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'peardock-peers-cache-'))
const prev = process.env.PEARDOCK_HOME
process.env.PEARDOCK_HOME = tmp
t.teardown(() => {
if (prev === undefined) delete process.env.PEARDOCK_HOME
else process.env.PEARDOCK_HOME = prev
try {
fs.rmSync(tmp, { recursive: true, force: true })
} catch {
// ignore
}
})
const expectedPath = path.join(tmp, '.config', 'peardock', 'cache', 'peers.json')
t.is(getPeersCachePath(), expectedPath)
const seed = 'cd'.repeat(32)
const peers = {
[KEY.slice(0, 12)]: {
publicKeyHex: KEY,
alias: 'alpha',
inviteToken: 'tok',
adminSeed: seed,
},
[KEY2.slice(0, 12)]: { publicKeyHex: KEY2, alias: null },
}
const result = savePeers(peers)
t.ok(result.ok)
t.is(result.count, 2)
t.is(result.path, expectedPath)
t.ok(fs.existsSync(expectedPath))
const loaded = loadPeers()
t.is(loaded[KEY.slice(0, 12)].alias, 'alpha')
t.is(loaded[KEY.slice(0, 12)].inviteToken, 'tok')
t.is(loaded[KEY.slice(0, 12)].adminSeed, seed)
t.is(loaded[KEY2.slice(0, 12)].publicKeyHex, KEY2)
t.absent(loaded[KEY2.slice(0, 12)].adminSeed)
const list = listSavedPeers()
t.is(list.length, 2)
t.is(list.find((p) => p.publicKeyHex === KEY)?.adminSeed, seed)
clearPeers()
t.absent(fs.existsSync(expectedPath))
t.is(Object.keys(readPeersFromFile()).length, 0)
})
test('writePeersToFile is atomic enough to read back', (t) => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'peardock-peers-atomic-'))
const prev = process.env.PEARDOCK_HOME
process.env.PEARDOCK_HOME = tmp
t.teardown(() => {
if (prev === undefined) delete process.env.PEARDOCK_HOME
else process.env.PEARDOCK_HOME = prev
try {
fs.rmSync(tmp, { recursive: true, force: true })
} catch {
// ignore
}
})
t.ok(writePeersToFile({ [KEY.slice(0, 12)]: { publicKeyHex: KEY } }))
const again = readPeersFromFile()
t.is(again[KEY.slice(0, 12)].publicKeyHex, KEY)
})
test('savePeers empty map overwrites (explicit) but load survives merge pattern', (t) => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'peardock-peers-merge-'))
const prev = process.env.PEARDOCK_HOME
process.env.PEARDOCK_HOME = tmp
t.teardown(() => {
if (prev === undefined) delete process.env.PEARDOCK_HOME
else process.env.PEARDOCK_HOME = prev
try {
fs.rmSync(tmp, { recursive: true, force: true })
} catch {
// ignore
}
})
savePeers({ [KEY.slice(0, 12)]: { publicKeyHex: KEY, alias: 'Main' } })
t.is(loadPeers()[KEY.slice(0, 12)].alias, 'Main')
// Simulate safe merge used by manager.persist / saveConnections:
// never replace disk with bare empty without removeId intent
const existing = loadPeers()
const merged = { ...existing }
// no removeId → peers remain
savePeers(merged)
t.is(Object.keys(loadPeers()).length, 1)
t.is(loadPeers()[KEY.slice(0, 12)].alias, 'Main')
})