OverviewFilesCommitsIssuesDocsReleasesPeers

favela-android / test / roster.test.mjs

82 lines · javascript ref:
// Which DIDs the phone's in-process favela-web subscribes to.
//
// ⛔ THE CONTROL BELOW IS THE BUG. favela-web reads its roster at MODULE TOP LEVEL:
//
//     const ROSTER = (process.env.FAVELA_ROSTER || '').split(',').map((s) => s.trim()).filter(Boolean)
//
// (bin/favela-web.js:152). `prepareEnv()` never set FAVELA_ROSTER, so on the phone that expression
// evaluated to `[]`, `rosterDids` was empty, `addPeer()` was never called, and the boot logged
//
//     [favela] joined 0 DID topic(s); indexing over P2P...
//
// Nothing was wired wrong and nothing threw. The phone simply subscribed to no one, so every record
// authored anywhere else — including on another co-owner of ITS OWN DID — reached its overlay store and
// never reached the index every screen reads. Measured 2026-08-18: three posts, pointers converged
// (checked 183→184→185, bytes fetched), still 404 on the phone two and a half hours and two boots later.
//
// So the first test replicates favela-web's parser VERBATIM against the value the phone used to hand it,
// pinning the defect; the rest assert the roster that replaces it.
//
// ⛔ AND THE ROSTER IS NOT THE SIGNING SET. A roster is what this node INDEXES (many DIDs, read-only,
// verified over the firehose). What it SIGNS for is one control socket (lib/writer.js:283). Conflating
// them is how a write lands in someone else's repo, so `hosted` DIDs are in the roster because we index
// them, not because we sign for them.
import test from 'brittle'
import { rosterFor, rosterEnv } from '../lib/roster.js'

const WJ62 = 'did:plc:wj62ebbusln6gxo5azo6ogzo'
const E2JEV = 'did:plc:e2jevhuurrhlm36ufk7fklpg'
const WQNW = 'did:plc:wqnwulql7ndcexample00000'

// favela-web/bin/favela-web.js:152, copied byte-for-byte. If favela-web ever changes how it parses the
// env, this test fails and tells us — which is the point of pinning the CONSUMER's parser rather than
// asserting our own idea of the format.
const parseAsFavelaWebDoes = (v) => String(v || '').split(',').map((s) => s.trim()).filter(Boolean)

test('control: what the phone used to hand favela-web parsed to nobody', (t) => {
  t.alike(parseAsFavelaWebDoes(undefined), [], 'unset FAVELA_ROSTER -> no DIDs -> joined 0 DID topic(s)')
  t.alike(parseAsFavelaWebDoes(''), [], 'empty string is the same silence')
})

test('the roster round-trips through favela-web own parser', (t) => {
  const env = rosterEnv({ hosted: [WJ62], tracked: [E2JEV] })
  t.alike(parseAsFavelaWebDoes(env), [WJ62, E2JEV], 'what we set is exactly what favela-web reads back')
})

test('hosted DIDs come first', (t) => {
  // addPeer() runs in order and the first roster entry is also what apProjected() asks about
  // (bin/favela-web.js:351-352), so a tracked stranger must not displace our own DID.
  t.alike(rosterFor({ hosted: [WJ62], tracked: [E2JEV, WQNW] }), [WJ62, E2JEV, WQNW])
})

test('every hosted DID is carried, not just the first', (t) => {
  // Two accounts on one handset. Reads are already multi-DID; taking nodes[0] here would silently
  // half-subscribe the second user.
  t.alike(rosterFor({ hosted: [WJ62, E2JEV] }), [WJ62, E2JEV])
})

test('deduped across sources', (t) => {
  // A hosted DID you also follow, or a tracked DID you later co-own. addPeer() dedups by `byDid`, but a
  // duplicate here would still be a lie in the logged topic count.
  t.alike(rosterFor({ hosted: [WJ62], tracked: [WJ62], following: [WJ62, E2JEV] }), [WJ62, E2JEV])
})

test('junk is dropped rather than passed through', (t) => {
  // tracked-dids.json is a file on disk that a crash can truncate; `following` comes off the index.
  // A malformed entry must not become a topic join or a verifyKey resolution attempt.
  t.alike(rosterFor({ hosted: [WJ62], tracked: ['', '   ', null, undefined, 42, 'not-a-did', 'did:'] }), [WJ62])
})

test('a comma inside a DID can never split one entry into two', (t) => {
  // The env format is comma-separated, so a DID containing a comma would silently become two bogus
  // entries in favela-web. Nothing should be able to smuggle one through.
  t.alike(rosterFor({ tracked: ['did:plc:aaa,did:plc:bbb'] }), [], 'rejected, not split')
})

test('an empty roster yields an empty string, not the word undefined', (t) => {
  // `String(undefined)` is 'undefined', which would parse to one garbage DID. A node with nothing to
  // subscribe to must produce the same silence as before, not a fake peer.
  t.is(rosterEnv({}), '')
  t.alike(parseAsFavelaWebDoes(rosterEnv({})), [])
})