OverviewFilesCommitsIssuesDocsReleasesPeers

favela-web / test / postgate.test.mjs

50 lines · javascript ref:
// postgate — who may QUOTE. Run: node test/postgate.test.mjs
//
// THE TRAP: the polarity is INVERTED relative to a threadgate.
//   threadgate  allow: []          -> nobody may reply
//   postgate    embeddingRules: [] -> ANYONE may quote
// They look alike and mean opposite things, and writing one with the other's polarity silently produces
// the reverse of what the author asked for.
import { QUOTE_CHOICES, postgateRecord, quoteChoiceOf, canQuote, describeQuoteGate, detachedUris } from '../lib/postgate.js'

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

// ── the inverted polarity ────────────────────────────────────────────────────────────────────────
ok(quoteChoiceOf({ embeddingRules: [] }) === 'anyone', 'an EMPTY embeddingRules means ANYONE may quote (opposite of threadgate allow: [])')
ok(quoteChoiceOf({}) === 'anyone', 'no rules at all is also anyone')
ok(quoteChoiceOf(null) === 'anyone', 'and no record at all')
ok(quoteChoiceOf({ embeddingRules: [{ $type: 'app.bsky.feed.postgate#disableRule' }] }) === 'nobody', 'a disableRule turns quoting off')
ok(postgateRecord({ postUri: 'at://x', choice: 'anyone' }) === null, "'anyone' writes NO record — unambiguous, and leaves no artefact to misread")
const rec = postgateRecord({ postUri: 'at://x', choice: 'nobody' })
ok(rec.embeddingRules.length === 1 && rec.embeddingRules[0].$type.endsWith('#disableRule'), "'nobody' writes a disableRule")
ok(rec.post === 'at://x', 'and carries the post uri')
ok(Array.isArray(rec.detachedEmbeddingUris), 'plus detachedEmbeddingUris, which the lexicon expects')

// Round-trip for every choice.
for (const c of QUOTE_CHOICES) {
  const r = postgateRecord({ postUri: 'at://x', choice: c })
  ok(quoteChoiceOf(r) === c, `${c} round-trips`)
}

// An unrecognised rule is 'limited' and fails CLOSED — assuming an unknown restriction is harmless is how
// a protected post ends up quotable.
ok(quoteChoiceOf({ embeddingRules: [{ $type: 'app.bsky.feed.postgate#futureRule' }] }) === 'limited', 'an unknown rule reads as limited')

// ── who may quote ────────────────────────────────────────────────────────────────────────────────
const A = 'did:plc:author'; const V = 'did:plc:viewer'
ok(canQuote({ viewerDid: V, authorDid: A, record: null }) === true, 'no gate → anyone signed in may quote')
ok(canQuote({ viewerDid: null, authorDid: A, record: null }) === false, 'signed out → no')
ok(canQuote({ viewerDid: V, authorDid: A, record: rec }) === false, 'quotes off → no')
ok(canQuote({ viewerDid: A, authorDid: A, record: rec }) === true, 'but the author may always quote their own post')
ok(canQuote({ viewerDid: V, authorDid: A, record: { embeddingRules: [{ $type: 'x#futureRule' }] } }) === false, 'an unknown rule fails CLOSED')

// Detached quotes are preserved on read, so a later gate change cannot silently re-attach one the author
// pulled down.
ok(detachedUris({ detachedEmbeddingUris: ['at://a', 'at://b'] }).length === 2, 'detached uris are readable')
ok(detachedUris({}).length === 0 && detachedUris(null).length === 0, 'and absent ones do not throw')
ok(describeQuoteGate('nobody').includes('turned off'), 'the gate describes itself')
ok(describeQuoteGate('anyone') === '', 'and an ungated post says nothing')

console.log(`\nPOSTGATE OK: ${pass} assertions`)