|
| 1 | +// planRelaySweep: categorization for the relay-directory data sweep. |
| 2 | +import test from 'node:test'; |
| 3 | +import assert from 'node:assert/strict'; |
| 4 | +import { planRelaySweep } from '../src/hoses/relays.js'; |
| 5 | + |
| 6 | +const ids = (arr) => arr.map((d) => d._id).sort(); |
| 7 | + |
| 8 | +test('bad URLs are dropped', () => { |
| 9 | + const docs = [ |
| 10 | + { _id: 1, relay: 'wss://relay.damus.io/' }, |
| 11 | + { _id: 2, relay: 'ws://127.0.0.1/' }, // SSRF |
| 12 | + { _id: 3, relay: 'garbage' }, // malformed |
| 13 | + { _id: 4, relay: 'wss://nostr.wine/inbox' }, |
| 14 | + ]; |
| 15 | + const p = planRelaySweep(docs, {}); |
| 16 | + assert.deepEqual(ids(p.bad), [2, 3]); |
| 17 | +}); |
| 18 | + |
| 19 | +test('dedup keeps the richest of a canonical group', () => { |
| 20 | + const docs = [ |
| 21 | + { _id: 1, relay: 'wss://nos.lol', checksTotal: 2 }, // canonicalizes to wss://nos.lol/ |
| 22 | + { _id: 2, relay: 'wss://nos.lol/', checksTotal: 9 }, // richest -> keep |
| 23 | + { _id: 3, relay: 'wss://nos.lol/', checksTotal: 0 }, |
| 24 | + ]; |
| 25 | + const p = planRelaySweep(docs, {}); |
| 26 | + assert.deepEqual(ids(p.dups), [1, 3]); // two dropped |
| 27 | + assert.equal(p.kept, 1); |
| 28 | +}); |
| 29 | + |
| 30 | +test('host-spam: trims path-variant flooding, keeps origin + richest few', () => { |
| 31 | + const docs = [ |
| 32 | + { _id: 0, relay: 'wss://nostr.wine/', checksTotal: 1 }, // origin — always kept |
| 33 | + { _id: 1, relay: 'wss://nostr.wine/echo-zulu', checksTotal: 5 }, |
| 34 | + { _id: 2, relay: 'wss://nostr.wine/flint-cipher', checksTotal: 4 }, |
| 35 | + { _id: 3, relay: 'wss://nostr.wine/raven-warden', checksTotal: 3 }, |
| 36 | + { _id: 4, relay: 'wss://nostr.wine/juliet-jade', checksTotal: 2 }, |
| 37 | + ]; |
| 38 | + const p = planRelaySweep(docs, { maxPerHost: 3 }); |
| 39 | + assert.equal(p.kept, 3); |
| 40 | + // origin (0) kept; then richest paths (1,2); spam = the rest (3,4) |
| 41 | + assert.deepEqual(ids(p.hostSpam), [3, 4]); |
| 42 | +}); |
| 43 | + |
| 44 | +test('a host under the cap is untouched', () => { |
| 45 | + const docs = [ |
| 46 | + { _id: 1, relay: 'wss://relay.example.com/inbox' }, |
| 47 | + { _id: 2, relay: 'wss://relay.example.com/outbox' }, |
| 48 | + ]; |
| 49 | + const p = planRelaySweep(docs, { maxPerHost: 3 }); |
| 50 | + assert.deepEqual(p.hostSpam, []); |
| 51 | + assert.equal(p.kept, 2); |
| 52 | +}); |
| 53 | + |
| 54 | +test('renorm flags survivors whose stored URL is not canonical', () => { |
| 55 | + const docs = [{ _id: 1, relay: 'wss://nos.lol' }]; // -> wss://nos.lol/ |
| 56 | + const p = planRelaySweep(docs, {}); |
| 57 | + assert.equal(p.renames.length, 1); |
| 58 | + assert.equal(p.renames[0].canonical, 'wss://nos.lol/'); |
| 59 | +}); |
| 60 | + |
| 61 | +test('stale prune by age (only when staleDays set)', () => { |
| 62 | + const now = 1_000_000_000_000; |
| 63 | + const docs = [ |
| 64 | + { _id: 1, relay: 'wss://a.example.com/', lastChecked: new Date(now - 2 * 864e5).toISOString() }, // 2d old |
| 65 | + { _id: 2, relay: 'wss://b.example.com/', lastChecked: new Date(now - 10 * 864e5).toISOString() }, // 10d old |
| 66 | + ]; |
| 67 | + assert.deepEqual(ids(planRelaySweep(docs, { staleDays: 7, now }).stale), [2]); |
| 68 | + assert.deepEqual(planRelaySweep(docs, { now }).stale, []); // no staleDays -> none |
| 69 | +}); |
0 commit comments