const { test, expect } = require('bun:test'); const { AS_PUBLIC, isAddressedToPublic, hasPublicIntent } = require('./public-intent'); const ALICE = 'http://localhost:3000/alice'; const FRIEND = 'http://localhost:3000/bob'; test('an object addressed to the Public collection matches, in each of its three spellings', () => { // The pod's own representation (observed): `"to": "as:Public"`. expect(hasPublicIntent({ type: 'Event', to: 'as:Public' })).toBe(true); expect(hasPublicIntent({ type: 'Event', to: AS_PUBLIC })).toBe(true); expect(hasPublicIntent({ type: 'Event', to: 'Public' })).toBe(true); }); test('public addressing matches in cc, in a list, and as a node', () => { expect(hasPublicIntent({ to: [FRIEND], cc: 'as:Public' })).toBe(true); expect(hasPublicIntent({ to: [FRIEND, AS_PUBLIC] })).toBe(true); expect(hasPublicIntent({ to: { id: AS_PUBLIC } })).toBe(true); expect(hasPublicIntent({ to: [{ '@id': 'as:Public' }] })).toBe(true); expect(hasPublicIntent({ 'as:to': 'as:Public' })).toBe(true); expect(hasPublicIntent({ 'https://www.w3.org/ns/activitystreams#cc': { '@id': AS_PUBLIC } })).toBe(true); }); test('an object with no addressing never matches — whoever created it', () => { // A private event posted directly on the pod, or created by another app: no `to`/`cc`. expect(hasPublicIntent({ id: `${ALICE}/data/1`, type: 'Event', name: 'Dîner privé' })).toBe(false); // A meeting point without addressing is not public either: the signal, not the shape, decides. expect(hasPublicIntent({ type: 'Event', 'as:context': `${ALICE}/data/0` })).toBe(false); }); test('addressing to chosen recipients only never matches', () => { expect(hasPublicIntent({ type: 'Event', to: [FRIEND] })).toBe(false); expect(hasPublicIntent({ type: 'Event', to: FRIEND, cc: `${ALICE}/followers` })).toBe(false); // A protected-scope record addressed to the creator's contacts collection. expect(hasPublicIntent({ type: 'Event', to: `${ALICE}/contacts` })).toBe(false); }); test('Public anywhere but to/cc does not match', () => { expect(hasPublicIntent({ bto: AS_PUBLIC })).toBe(false); expect(hasPublicIntent({ bcc: 'as:Public' })).toBe(false); expect(hasPublicIntent({ audience: AS_PUBLIC })).toBe(false); // The IRI as a plain value of another predicate is not addressing. expect(hasPublicIntent({ name: AS_PUBLIC, content: 'as:Public' })).toBe(false); }); test('look-alike addresses do not match', () => { expect(hasPublicIntent({ to: 'https://www.w3.org/ns/activitystreams#PublicX' })).toBe(false); expect(hasPublicIntent({ to: 'public' })).toBe(false); expect(hasPublicIntent({ to: `${ALICE}/Public` })).toBe(false); expect(hasPublicIntent({ to: { name: 'as:Public' } })).toBe(false); }); test('the creating activity counts when the caller holds it', () => { const privateObject = { type: 'Event', to: [FRIEND] }; expect(hasPublicIntent(privateObject, { type: 'Create', to: AS_PUBLIC })).toBe(true); expect(hasPublicIntent(privateObject, { type: 'Create', to: [FRIEND] })).toBe(false); // The Create the pod generates for a container POST carries no addressing (observed). expect(hasPublicIntent({ type: 'Event' }, { type: 'Create', actor: ALICE, object: `${ALICE}/data/1` })).toBe(false); }); test('missing or non-object input is not public', () => { expect(hasPublicIntent(undefined)).toBe(false); expect(hasPublicIntent(null, undefined)).toBe(false); expect(hasPublicIntent(`${ALICE}/data/1`)).toBe(false); expect(isAddressedToPublic({ to: null, cc: [] })).toBe(false); }); // ── Listed public: what the catalog may list ───────────────────────────────────────────────── const { isListedToPublic, hasListedPublicIntent } = require('./public-intent'); test('Public in `to` is listed, in each spelling and shape', () => { // The pod's own representation of a Festipod event (observed): `"to": "as:Public"`. expect(hasListedPublicIntent({ type: 'Event', to: 'as:Public' })).toBe(true); expect(hasListedPublicIntent({ type: 'Event', to: AS_PUBLIC })).toBe(true); expect(hasListedPublicIntent({ type: 'Event', to: 'Public' })).toBe(true); expect(hasListedPublicIntent({ to: [FRIEND, AS_PUBLIC], cc: `${ALICE}/followers` })).toBe(true); expect(hasListedPublicIntent({ to: { id: AS_PUBLIC } })).toBe(true); expect(hasListedPublicIntent({ 'as:to': [{ '@id': 'as:Public' }] })).toBe(true); expect(hasListedPublicIntent({ 'https://www.w3.org/ns/activitystreams#to': AS_PUBLIC })).toBe(true); }); test('Public in `cc` only is public but UNLISTED: readable, never listed', () => { const unlisted = { type: 'Event', to: [FRIEND], cc: 'as:Public' }; expect(hasPublicIntent(unlisted)).toBe(true); expect(hasListedPublicIntent(unlisted)).toBe(false); expect(hasListedPublicIntent({ cc: AS_PUBLIC })).toBe(false); expect(hasListedPublicIntent({ 'as:cc': { '@id': 'as:Public' } })).toBe(false); }); test('no addressing, chosen recipients, bto/bcc/audience and look-alikes are never listed', () => { for (const resource of [ { id: `${ALICE}/data/1`, type: 'Event', name: 'Dîner privé' }, { type: 'Event', to: [FRIEND] }, { type: 'Event', to: `${ALICE}/contacts` }, { bto: AS_PUBLIC }, { bcc: 'as:Public' }, { audience: AS_PUBLIC }, { name: AS_PUBLIC, content: 'as:Public' }, { to: 'https://www.w3.org/ns/activitystreams#PublicX' }, { to: 'public' }, { to: `${ALICE}/Public` }, { to: { name: 'as:Public' } }, undefined, null, `${ALICE}/data/1` ]) { expect(hasListedPublicIntent(resource)).toBe(false); } }); test('the creating activity counts the same way: `to` lists, `cc` does not', () => { const noAddressing = { type: 'Event' }; expect(hasListedPublicIntent(noAddressing, { type: 'Create', to: AS_PUBLIC })).toBe(true); expect(hasListedPublicIntent(noAddressing, { type: 'Create', cc: AS_PUBLIC })).toBe(false); expect(hasListedPublicIntent(noAddressing, { type: 'Create', to: [FRIEND] })).toBe(false); }); test('listed implies public: never true where hasPublicIntent is false', () => { const addresses = [undefined, AS_PUBLIC, 'as:Public', 'Public', FRIEND, [FRIEND, 'as:Public'], { id: AS_PUBLIC }, 'public']; const shapes = []; for (const to of addresses) for (const cc of addresses) shapes.push({ to, cc }); for (const object of shapes) { for (const activity of [undefined, ...shapes.slice(0, 8)]) { if (hasListedPublicIntent(object, activity)) expect(hasPublicIntent(object, activity)).toBe(true); } expect(isListedToPublic(object)).toBe(hasListedPublicIntent(object)); } });