| … | … | @@ -0,0 +1,83 @@ |
| 1 | // Byte-resolution failure diagnostics. |
| 2 | // |
| 3 | // `overlay.fetchFile` returns null for two ROOT-DISTINCT reasons, and the old log line asserted only |
| 4 | // one of them ("no connected peer served this oid"). That single misleading string cost days: a |
| 5 | // transport-level fault (the overlay channel never attached) read as a content fault (nobody holds the |
| 6 | // bytes), sending the investigation at the holder's index instead of at the wiring. |
| 7 | // |
| 8 | // It also deduped per-oid-per-PROCESS, so the line appeared once at boot and never again — a stale line |
| 9 | // was indistinguishable from a live failure, and could not be used to tell whether a repair had worked. |
| 10 | // |
| 11 | // These tests pin both properties, because this log line IS the diagnostic surface for a path that |
| 12 | // gates signing. |
| 13 | |
| 14 | import test from 'brittle' |
| 15 | import { mkdtempSync, rmSync } from 'node:fs' |
| 16 | import { join } from 'node:path' |
| 17 | import { tmpdir } from 'node:os' |
| 18 | import { OverlayRepoStore } from '../lib/overlay-repo-store.js' |
| 19 | |
| 20 | function tmp (t) { const d = mkdtempSync(join(tmpdir(), 'fmd-')); t.teardown(() => rmSync(d, { recursive: true, force: true })); return d } |
| 21 | |
| 22 | // An overlay that always fails to fetch, with a settable peer count. |
| 23 | function mkOverlay (peerCount) { |
| 24 | return { |
| 25 | peerCount, |
| 26 | async registerFile () { return { contentHash: 'deadbeef' } }, |
| 27 | async fetchFile () { return null } // the failure under test |
| 28 | } |
| 29 | } |
| 30 | |
| 31 | // Capture console.error for the duration of a block. |
| 32 | async function capture (t, fn) { |
| 33 | const lines = [] |
| 34 | const orig = console.error |
| 35 | console.error = (s) => lines.push(String(s)) |
| 36 | t.teardown(() => { console.error = orig }) |
| 37 | try { await fn() } finally { console.error = orig } |
| 38 | return lines.map((l) => { try { return JSON.parse(l) } catch { return { raw: l } } }) |
| 39 | } |
| 40 | |
| 41 | test('ZERO peers is reported as a transport/wiring fault, not a content fault', async (t) => { |
| 42 | const store = await new OverlayRepoStore(tmp(t), { overlay: mkOverlay(0), did: 'did:plc:x' }).ready() |
| 43 | |
| 44 | const logs = await capture(t, async () => { |
| 45 | const r = await store.ensureLocal('app.bsky.feed.post', '3kaaa', 'abc123') |
| 46 | t.is(r, null, 'unresolvable record still returns null') |
| 47 | }) |
| 48 | |
| 49 | t.is(logs.length, 1, 'exactly one diagnostic emitted') |
| 50 | t.is(logs[0].overlayPeers, 0, 'the peer count is reported') |
| 51 | t.ok(/NO overlay peers attached/.test(logs[0].msg), 'classified as a transport/wiring fault') |
| 52 | t.absent(/none served this oid/.test(logs[0].msg), 'does NOT assert the content-fault cause') |
| 53 | }) |
| 54 | |
| 55 | test('peers attached but content missing is reported as a distribution fault', async (t) => { |
| 56 | const store = await new OverlayRepoStore(tmp(t), { overlay: mkOverlay(2), did: 'did:plc:x' }).ready() |
| 57 | |
| 58 | const logs = await capture(t, async () => { |
| 59 | await store.ensureLocal('app.bsky.feed.post', '3kaaa', 'abc123') |
| 60 | }) |
| 61 | |
| 62 | t.is(logs.length, 1, 'exactly one diagnostic emitted') |
| 63 | t.is(logs[0].overlayPeers, 2, 'the peer count is reported') |
| 64 | t.ok(/none served this oid/.test(logs[0].msg), 'classified as a distribution fault') |
| 65 | }) |
| 66 | |
| 67 | test('dedup is keyed on peer count, so connectivity changes re-emit (staleness fix)', async (t) => { |
| 68 | const overlay = mkOverlay(0) |
| 69 | const store = await new OverlayRepoStore(tmp(t), { overlay, did: 'did:plc:x' }).ready() |
| 70 | |
| 71 | const logs = await capture(t, async () => { |
| 72 | await store.ensureLocal('app.bsky.feed.post', '3kaaa', 'abc123') |
| 73 | await store.ensureLocal('app.bsky.feed.post', '3kaaa', 'abc123') // same oid, same peers → quiet |
| 74 | t.is(overlay.peerCount, 0, 'still no peers') |
| 75 | overlay.peerCount = 1 // a peer attaches |
| 76 | await store.ensureLocal('app.bsky.feed.post', '3kaaa', 'abc123') // → must re-emit |
| 77 | }) |
| 78 | |
| 79 | t.is(logs.length, 2, 'quiet on repeat, re-emits when the peer count changes') |
| 80 | t.is(logs[0].overlayPeers, 0, 'first line: no peers') |
| 81 | t.is(logs[1].overlayPeers, 1, 'second line: a peer attached and it STILL failed — the useful signal') |
| 82 | t.ok(/none served this oid/.test(logs[1].msg), 'and the classification flipped with the evidence') |
| 83 | }) |