OverviewFilesCommitsIssuesDocsReleasesPeers

favela-web / test / writer.test.mjs

114 lines · javascript ref:
// favela-web Writer — the POSSE write path. Proves the langs fix: bsky-fronted posts (public scope) MUST
// declare `langs` so bsky.app's custom-feed language tuner (FeedTuner.preferredLangOnly -> lande) never
// mis-detects short/technical text and silently drops the post from the feed; favela-local posts carry no
// langs (net.favela.local.post has no such field). Pure — stubs the hpp `_call`. Run: node test/writer.test.mjs
import { Writer, writeTransportConfigured } from '../lib/writer.js'

const assert = (c, m) => { if (!c) { console.error('FAIL:', m); process.exit(1) } console.log('  ok -', m) }

const mk = () => {
  const w = new Writer({})
  const calls = []
  w._call = async (method, params) => { calls.push({ method, params }); return { commit: 'stub' } }
  return { w, calls }
}

// public (bsky) posts declare langs so bsky takes the fast path and never runs lande on the raw text
{
  const { w, calls } = mk()
  await w.post('test 2') // default scope = public; "test 2" -> lande mis-detects as French with no langs
  const { collection, record } = calls[0].params
  assert(collection === 'app.bsky.feed.post', 'public scope -> app.bsky.feed.post')
  assert(Array.isArray(record.langs) && record.langs.length === 1 && record.langs[0] === 'en', 'public post declares langs:[en]')
  assert(record.$type === 'app.bsky.feed.post' && record.text === 'test 2', 'record shape intact')
}

// caller can override the declared language
{
  const { w, calls } = mk()
  await w.post('hola', { langs: ['es'] })
  assert(calls[0].params.record.langs[0] === 'es', 'langs override honored')
}

// favela-local posts carry NO langs (never fronted to bsky; local lexicon has no langs field)
{
  const { w, calls } = mk()
  await w.post('local note', { scope: 'local' })
  const { collection, record } = calls[0].params
  assert(collection === 'net.favela.local.post', 'local scope -> net.favela.local.post')
  assert(!('langs' in record), 'local post has no langs field')
}

// an explicitly empty langs array adds no field (avoid emitting langs:[])
{
  const { w, calls } = mk()
  await w.post('x', { langs: [] })
  assert(!('langs' in calls[0].params.record), 'empty langs => no langs field')
}

console.log('writer.test.mjs — all passed')

// ── IN-PROCESS TRANSPORT (favela-android) ───────────────────────────────────────────────────────────
// favela-android is favela-web's UI ported to React Native over a node running IN THE SAME PROCESS.
// There is no UDS and no swarm to reach: the SwarmDevice object is right there. Every method on this
// class is record-BUILDING (scope->collection, facets, embeds, langs, the 300-grapheme cap) plus one
// `_call` — so injecting the device makes the phone reuse this write path verbatim instead of
// reimplementing the record shapes, which is exactly how favela shipped uri-only strongRefs once.
console.log('\n# in-process device transport')
{
  const seen = []
  const device = {
    applyWrite: async (op) => { seen.push(['apply-write', op]); return { ok: true, recordCount: 1 } },
    state: async () => ({ did: 'did:plc:inproc' }),
    delegate: async (op) => { seen.push(['delegate', op]); return { delegation: 'x' } },
    listDevices: async () => [{ subKey: 'aa' }],
    revokeDevice: async (k) => { seen.push(['revoke', k]); return { ok: true } },
    isDeviceRevoked: async () => false
  }
  const blobs = { put: async (bytes, mimeType) => ({ cid: 'bafyfake', size: bytes.length, mimeType }) }
  const w = new Writer({ device, blobs })

  assert(await w.ready() === true, 'ready() is immediate with an injected device — no socket, no DHT')

  const r = await w.post('hello from the phone')
  assert(seen[0][0] === 'apply-write', 'post routed to device.applyWrite, not a socket')
  assert(seen[0][1].collection === 'app.bsky.feed.post', 'the SAME record shape the web path builds')
  assert(Array.isArray(seen[0][1].record.langs), 'and the same langs handling')
  assert(r.rkey && r.collection, 'post returns rkey/collection as before')

  const st = await w.state()
  assert(st.did === 'did:plc:inproc', 'get-state maps to device.state()')

  const b = await w.uploadBlob(Buffer.from('abc'), 'image/png')
  assert(b.cid === 'bafyfake' && b.size === 3, 'upload-blob maps to the blob store, bytes NOT hex-encoded in-process')

  const devs = await w.listDevices()
  assert(Array.isArray(devs) && devs[0].subKey === 'aa', 'list-devices maps to device.listDevices()')
}

// ── the WRITE GATE: which transports count as "configured" ────────────────────────────────────────────
// WHY THIS IS A FUNCTION AND NOT AN `if`. bin/favela-web.js disables writes wholesale unless a transport
// is named, because an instance that cannot name the node it writes to must not guess — the 2026-08-09
// accident was a test run posting into live wj62 through a default. But the gate tested ONLY
// FAVELA_CONTROL_KEY (the swarm transport), while Writer has always supported a LOCAL UDS. So a
// co-located instance with hpp's socket right there — favela-android's embedded favela-web — was refused
// its own local device and the phone could not post.
//
// ⛔ EXPLICIT CONFIG ONLY. Writer falls back to DEFAULT_SOCK when nothing is set, so gating on
// "does Writer have a uds" would re-open the exact hazard: every unconfigured instance would silently
// acquire writes against whatever hpp owns the default socket. The gate must read the ENV, not the Writer.
console.log('write gate')
{
  assert(writeTransportConfigured({}) === null, 'nothing configured -> writes refused')
  assert(writeTransportConfigured({ FAVELA_CONTROL_KEY: 'abc' }) === 'swarm', 'control key -> swarm transport')
  assert(writeTransportConfigured({ FAVELA_HPP_SOCK: '/run/hpp.sock' }) === 'uds', 'explicit local socket -> uds transport')
  assert(writeTransportConfigured({ FAVELA_CONTROL_KEY: 'abc', FAVELA_HPP_SOCK: '/run/hpp.sock' }) === 'uds',
    'local socket WINS over the swarm key — never take a DHT round-trip to reach a device on this box')
  assert(writeTransportConfigured({ FAVELA_CONTROL_KEY: '  ' }) === null, 'whitespace is not configuration')
  assert(writeTransportConfigured({ FAVELA_HPP_SOCK: '' }) === null, 'empty socket is not configuration')
  // the hazard, asserted directly: the DEFAULT socket must never open the gate by itself
  assert(writeTransportConfigured({ SOME_OTHER: 'x' }) === null, 'DEFAULT_SOCK does not count as configured')
}

console.log('\nall writer tests passed')