OverviewFilesCommitsIssuesDocsReleasesPeers

ap-bridge / test / users.test.js

73 lines · javascript ref:
// ⛔ THE INCUMBENT MUST NOT MOVE.
//
// ap.7rnx.net's actor is https://ap.7rnx.net/ap/actor, 22 objects are federated under
// /ap/objects/<rkey>, and a follower at prsm.space is following that exact id. An actor that changes
// its id is a stranger to every server that already knows it, and a moved object id is a dead link
// in somebody's timeline. Multi-user is ADDITIVE or it is a migration.

import test from 'brittle'
import { Users, usersFromConfig } from '../lib/users.js'

const HOST = 'ap.7rnx.net'
const two = () => new Users([
  { username: 'p2p', primary: true },
  { username: 'ashatron' }
], HOST)

test('★★ the primary keeps the FLAT paths, byte for byte', async (t) => {
  const u = two()
  const ids = u.ids(u.get('p2p'))
  t.is(ids.actor, 'https://ap.7rnx.net/ap/actor')
  t.is(ids.inbox, 'https://ap.7rnx.net/ap/inbox')
  t.is(ids.followers, 'https://ap.7rnx.net/ap/followers')
  t.is(ids.objects, 'https://ap.7rnx.net/ap/objects', 'the 22 federated objects keep resolving')
})

test('★ a second user gets their own namespace', async (t) => {
  const ids = two().ids(two().get('ashatron'))
  t.is(ids.actor, 'https://ap.7rnx.net/ap/users/ashatron/actor')
  t.is(ids.objects, 'https://ap.7rnx.net/ap/users/ashatron/objects')
})

test('★★ routing: flat is the primary, /users/<name> is theirs', async (t) => {
  const u = two()
  t.is(u.route('/ap/actor').user.username, 'p2p')
  t.is(u.route('/ap/actor').rest, '/actor')
  t.is(u.route('/ap/objects/abc').rest, '/objects/abc', 'and the rest survives for the handler')
  t.is(u.route('/ap/users/ashatron/outbox').user.username, 'ashatron')
  t.is(u.route('/ap/users/ashatron/outbox').rest, '/outbox')
  t.is(u.route('/ap/users/ashatron').rest, '/', 'a bare user path is the root of theirs')
})

test('⛔ an unknown user is nothing, not the primary', async (t) => {
  // Falling back would answer for one person under another's name — the whole failure this exists
  // to prevent, arriving quietly.
  t.is(two().route('/ap/users/nobody/actor'), null)
  t.is(two().route('/notap/actor'), null)
})

test('⛔ webfinger matches the HOST exactly', async (t) => {
  const u = two()
  t.is(u.fromResource('acct:p2p@ap.7rnx.net').username, 'p2p')
  t.is(u.fromResource('acct:ashatron@ap.7rnx.net').username, 'ashatron')
  // Answering for another host's account would claim somebody we do not serve.
  t.is(u.fromResource('acct:p2p@evil.example'), null)
  t.is(u.fromResource('acct:nobody@ap.7rnx.net'), null)
  t.is(u.fromResource('https://ap.7rnx.net/ap/actor'), null, 'only acct: resources')
})

test('⛔ a duplicate username is REFUSED at construction', async (t) => {
  // Otherwise one of them silently never serves, and which one depends on config order.
  t.exception(() => new Users([{ username: 'a' }, { username: 'A' }], HOST), /duplicate/i)
  t.exception(() => new Users([{ username: 'has space' }], HOST), /invalid username/i)
})

test('★★ a single-actor config produces IDENTICAL ids to having no registry', async (t) => {
  // The compatibility story in one assertion: a node that never adds a second user cannot tell
  // this refactor happened.
  const u = usersFromConfig({ host: HOST, username: 'p2p', did: 'did:plc:x' })
  t.is(u.size, 1)
  t.is(u.ids(u.primary).actor, 'https://ap.7rnx.net/ap/actor')
  t.is(u.route('/ap/outbox').user.username, 'p2p')
})