import { makeDB } from '../utils' import type { JWK } from 'wildebeest/backend/src/webpush/jwk' import * as objects from 'wildebeest/backend/src/activitypub/objects' import { createPublicNote } from 'wildebeest/backend/src/activitypub/objects/note' import * as ap_inbox from 'wildebeest/functions/ap/users/[id]/inbox' import { createPerson } from 'wildebeest/backend/src/activitypub/actors' import { strict as assert } from 'node:assert/strict' const userKEK = 'test_kek9' const domain = 'cloudflare.com' const adminEmail = 'admin@example.com' const vapidKeys = {} as JWK const kv_cache: any = { async put() {}, } const waitUntil = async (p: Promise) => await p describe('ActivityPub', () => { test('send Note to non existant user', async () => { const db = await makeDB() const activity: any = {} const res = await ap_inbox.handleRequest( domain, db, kv_cache, 'sven', activity, userKEK, waitUntil, adminEmail, vapidKeys ) assert.equal(res.status, 404) }) test('send Note to inbox stores in DB', async () => { const db = await makeDB() const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com') const activity: any = { type: 'Create', actor: actor.id.toString(), to: [actor.id.toString()], cc: [], object: { id: 'https://example.com/note1', type: 'Note', content: 'test note', }, } const res = await ap_inbox.handleRequest( domain, db, kv_cache, 'sven', activity, userKEK, waitUntil, adminEmail, vapidKeys ) assert.equal(res.status, 200) const entry = await db .prepare('SELECT objects.* FROM inbox_objects INNER JOIN objects ON objects.id=inbox_objects.object_id') .first() const properties = JSON.parse(entry.properties) assert.equal(properties.content, 'test note') }) test("send Note adds in remote actor's outbox", async () => { const remoteActorId = 'https://example.com/actor' globalThis.fetch = async (input: RequestInfo) => { if (input.toString() === remoteActorId) { return new Response( JSON.stringify({ id: remoteActorId, type: 'Person', }) ) } throw new Error('unexpected request to ' + input) } const db = await makeDB() await createPerson(domain, db, userKEK, 'sven@cloudflare.com') const activity: any = { type: 'Create', actor: remoteActorId, to: [], cc: [], object: { id: 'https://example.com/note1', type: 'Note', content: 'test note', }, } const res = await ap_inbox.handleRequest( domain, db, kv_cache, 'sven', activity, userKEK, waitUntil, adminEmail, vapidKeys ) assert.equal(res.status, 200) const entry = await db.prepare('SELECT * FROM outbox_objects WHERE actor_id=?').bind(remoteActorId).first() assert.equal(entry.actor_id, remoteActorId) }) test('local actor sends Note with mention create notification', async () => { const db = await makeDB() const actorA = await createPerson(domain, db, userKEK, 'a@cloudflare.com') const actorB = await createPerson(domain, db, userKEK, 'b@cloudflare.com') const activity: any = { type: 'Create', actor: actorB.id.toString(), to: [actorA.id.toString()], cc: [], object: { id: 'https://example.com/note2', type: 'Note', content: 'test note', }, } const res = await ap_inbox.handleRequest( domain, db, kv_cache, 'a', activity, userKEK, waitUntil, adminEmail, vapidKeys ) assert.equal(res.status, 200) const entry = await db.prepare('SELECT * FROM actor_notifications').first() assert.equal(entry.type, 'mention') assert.equal(entry.actor_id.toString(), actorA.id.toString()) assert.equal(entry.from_actor_id.toString(), actorB.id.toString()) }) test('remote actor sends Note with mention create notification and download actor', async () => { const actorB = 'https://remote.com/actorb' globalThis.fetch = async (input: RequestInfo) => { if (input.toString() === actorB) { return new Response( JSON.stringify({ id: actorB, type: 'Person', }) ) } throw new Error('unexpected request to ' + input) } const db = await makeDB() const actorA = await createPerson(domain, db, userKEK, 'a@cloudflare.com') const activity: any = { type: 'Create', actor: actorB, to: [actorA.id.toString()], cc: [], object: { id: 'https://example.com/note3', type: 'Note', content: 'test note', }, } const res = await ap_inbox.handleRequest( domain, db, kv_cache, 'a', activity, userKEK, waitUntil, adminEmail, vapidKeys ) assert.equal(res.status, 200) const entry = await db.prepare('SELECT * FROM actors WHERE id=?').bind(actorB).first() assert.equal(entry.id, actorB) }) test('send Note records reply', async () => { const db = await makeDB() const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com') { const activity: any = { type: 'Create', actor: actor.id.toString(), to: [actor.id.toString()], object: { id: 'https://example.com/note1', type: 'Note', content: 'post', }, } const res = await ap_inbox.handleRequest( domain, db, kv_cache, 'sven', activity, userKEK, waitUntil, adminEmail, vapidKeys ) assert.equal(res.status, 200) } { const activity: any = { type: 'Create', actor: actor.id.toString(), to: [actor.id.toString()], object: { inReplyTo: 'https://example.com/note1', id: 'https://example.com/note2', type: 'Note', content: 'reply', }, } const res = await ap_inbox.handleRequest( domain, db, kv_cache, 'sven', activity, userKEK, waitUntil, adminEmail, vapidKeys ) assert.equal(res.status, 200) } const entry = await db.prepare('SELECT * FROM actor_replies').first() assert.equal(entry.actor_id, actor.id.toString().toString()) const obj: any = await objects.getObjectById(db, entry.object_id) assert(obj) assert.equal(obj.originalObjectId, 'https://example.com/note2') const inReplyTo: any = await objects.getObjectById(db, entry.in_reply_to_object_id) assert(inReplyTo) assert.equal(inReplyTo.originalObjectId, 'https://example.com/note1') }) describe('Announce', () => { test('records reblog in db', async () => { const db = await makeDB() const actorA = await createPerson(domain, db, userKEK, 'a@cloudflare.com') const actorB = await createPerson(domain, db, userKEK, 'b@cloudflare.com') const note = await createPublicNote(domain, db, 'my first status', actorA) const activity: any = { type: 'Announce', actor: actorB.id, object: note.id, } const res = await ap_inbox.handleRequest( domain, db, kv_cache, 'a', activity, userKEK, waitUntil, adminEmail, vapidKeys ) assert.equal(res.status, 200) const entry = await db.prepare('SELECT * FROM actor_reblogs').first() assert.equal(entry.actor_id.toString(), actorB.id.toString()) assert.equal(entry.object_id.toString(), note.id.toString()) }) test('creates notification', async () => { const db = await makeDB() const actorA = await createPerson(domain, db, userKEK, 'a@cloudflare.com') const actorB = await createPerson(domain, db, userKEK, 'b@cloudflare.com') const note = await createPublicNote(domain, db, 'my first status', actorA) const activity: any = { type: 'Announce', actor: actorB.id, object: note.id, } const res = await ap_inbox.handleRequest( domain, db, kv_cache, 'a', activity, userKEK, waitUntil, adminEmail, vapidKeys ) assert.equal(res.status, 200) const entry = await db.prepare('SELECT * FROM actor_notifications').first() assert(entry) assert.equal(entry.type, 'reblog') assert.equal(entry.actor_id.toString(), actorA.id.toString()) assert.equal(entry.from_actor_id.toString(), actorB.id.toString()) }) }) describe('Like', () => { test('records like in db', async () => { const db = await makeDB() const actorA = await createPerson(domain, db, userKEK, 'a@cloudflare.com') const actorB = await createPerson(domain, db, userKEK, 'b@cloudflare.com') const note = await createPublicNote(domain, db, 'my first status', actorA) const activity: any = { type: 'Like', actor: actorB.id, object: note.id, } const res = await ap_inbox.handleRequest( domain, db, kv_cache, 'a', activity, userKEK, waitUntil, adminEmail, vapidKeys ) assert.equal(res.status, 200) const entry = await db.prepare('SELECT * FROM actor_favourites').first() assert.equal(entry.actor_id.toString(), actorB.id.toString()) assert.equal(entry.object_id.toString(), note.id.toString()) }) test('creates notification', async () => { const db = await makeDB() const actorA = await createPerson(domain, db, userKEK, 'a@cloudflare.com') const actorB = await createPerson(domain, db, userKEK, 'b@cloudflare.com') const note = await createPublicNote(domain, db, 'my first status', actorA) const activity: any = { type: 'Like', actor: actorB.id, object: note.id, } const res = await ap_inbox.handleRequest( domain, db, kv_cache, 'a', activity, userKEK, waitUntil, adminEmail, vapidKeys ) assert.equal(res.status, 200) const entry = await db.prepare('SELECT * FROM actor_notifications').first() assert.equal(entry.type, 'favourite') assert.equal(entry.actor_id.toString(), actorA.id.toString()) assert.equal(entry.from_actor_id.toString(), actorB.id.toString()) }) test('records like in db', async () => { const db = await makeDB() const actorA = await createPerson(domain, db, userKEK, 'a@cloudflare.com') const actorB = await createPerson(domain, db, userKEK, 'b@cloudflare.com') const note = await createPublicNote(domain, db, 'my first status', actorA) const activity: any = { type: 'Like', actor: actorB.id, object: note.id, } const res = await ap_inbox.handleRequest( domain, db, kv_cache, 'a', activity, userKEK, waitUntil, adminEmail, vapidKeys ) assert.equal(res.status, 200) const entry = await db.prepare('SELECT * FROM actor_favourites').first() assert.equal(entry.actor_id.toString(), actorB.id.toString()) assert.equal(entry.object_id.toString(), note.id.toString()) }) }) })