2022-12-05 20:14:56 +00:00
|
|
|
import * as activityHandler from 'wildebeest/backend/src/activitypub/activities/handle'
|
2023-01-11 14:32:06 +00:00
|
|
|
import type { JWK } from 'wildebeest/backend/src/webpush/jwk'
|
2022-12-05 20:14:56 +00:00
|
|
|
import * as ap_followers_page from 'wildebeest/functions/ap/users/[id]/followers/page'
|
|
|
|
import * as ap_following_page from 'wildebeest/functions/ap/users/[id]/following/page'
|
|
|
|
import * as ap_followers from 'wildebeest/functions/ap/users/[id]/followers'
|
|
|
|
import * as ap_following from 'wildebeest/functions/ap/users/[id]/following'
|
|
|
|
import { addFollowing, acceptFollowing } from 'wildebeest/backend/src/mastodon/follow'
|
|
|
|
import { strict as assert } from 'node:assert/strict'
|
2023-01-06 09:06:16 +00:00
|
|
|
import { makeDB } from '../utils'
|
2022-12-05 20:14:56 +00:00
|
|
|
import { createPerson } from 'wildebeest/backend/src/activitypub/actors'
|
|
|
|
|
|
|
|
const userKEK = 'test_kek10'
|
|
|
|
const domain = 'cloudflare.com'
|
2023-01-11 14:32:06 +00:00
|
|
|
const adminEmail = 'admin@example.com'
|
|
|
|
const vapidKeys = {} as JWK
|
2022-12-05 20:14:56 +00:00
|
|
|
|
|
|
|
describe('ActivityPub', () => {
|
|
|
|
describe('Follow', () => {
|
|
|
|
let receivedActivity: any = null
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
receivedActivity = null
|
|
|
|
|
2023-01-11 15:45:07 +00:00
|
|
|
globalThis.fetch = async (input: RequestInfo) => {
|
|
|
|
const request = new Request(input)
|
|
|
|
if (request.url === `https://${domain}/ap/users/sven2/inbox`) {
|
|
|
|
assert.equal(request.method, 'POST')
|
|
|
|
const data = await request.json()
|
2022-12-05 20:14:56 +00:00
|
|
|
receivedActivity = data
|
|
|
|
return new Response('')
|
|
|
|
}
|
|
|
|
|
2023-01-11 15:45:07 +00:00
|
|
|
throw new Error('unexpected request to ' + request.url)
|
2022-12-05 20:14:56 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
test('Receive follow with Accept reply', async () => {
|
|
|
|
const db = await makeDB()
|
2023-01-05 09:33:46 +00:00
|
|
|
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
|
|
|
const actor2 = await createPerson(domain, db, userKEK, 'sven2@cloudflare.com')
|
2022-12-05 20:14:56 +00:00
|
|
|
|
|
|
|
const activity = {
|
|
|
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
|
|
|
type: 'Follow',
|
|
|
|
actor: actor2.id.toString(),
|
|
|
|
object: actor.id.toString(),
|
|
|
|
}
|
|
|
|
|
2023-01-11 14:32:06 +00:00
|
|
|
await activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys)
|
2022-12-05 20:14:56 +00:00
|
|
|
|
|
|
|
const row = await db
|
|
|
|
.prepare(`SELECT target_actor_id, state FROM actor_following WHERE actor_id=?`)
|
|
|
|
.bind(actor2.id.toString())
|
2023-01-11 15:45:07 +00:00
|
|
|
.first<{
|
|
|
|
target_actor_id: object
|
|
|
|
state: string
|
|
|
|
}>()
|
2022-12-05 20:14:56 +00:00
|
|
|
assert(row)
|
|
|
|
assert.equal(row.target_actor_id.toString(), actor.id.toString())
|
|
|
|
assert.equal(row.state, 'accepted')
|
|
|
|
|
|
|
|
assert(receivedActivity)
|
|
|
|
assert.equal(receivedActivity.type, 'Accept')
|
2023-01-11 15:45:07 +00:00
|
|
|
assert.equal((receivedActivity.actor as object).toString(), actor.id.toString())
|
2022-12-05 20:14:56 +00:00
|
|
|
assert.equal(receivedActivity.object.actor, activity.actor)
|
|
|
|
assert.equal(receivedActivity.object.type, activity.type)
|
|
|
|
})
|
|
|
|
|
|
|
|
test('list actor following', async () => {
|
|
|
|
const db = await makeDB()
|
2023-01-05 09:33:46 +00:00
|
|
|
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
|
|
|
const actor2 = await createPerson(domain, db, userKEK, 'sven2@cloudflare.com')
|
|
|
|
const actor3 = await createPerson(domain, db, userKEK, 'sven3@cloudflare.com')
|
2022-12-05 20:14:56 +00:00
|
|
|
await addFollowing(db, actor, actor2, 'not needed')
|
|
|
|
await acceptFollowing(db, actor, actor2)
|
|
|
|
await addFollowing(db, actor, actor3, 'not needed')
|
|
|
|
await acceptFollowing(db, actor, actor3)
|
|
|
|
|
|
|
|
const res = await ap_following.handleRequest(domain, db, 'sven')
|
|
|
|
assert.equal(res.status, 200)
|
|
|
|
|
|
|
|
const data = await res.json<any>()
|
|
|
|
assert.equal(data.type, 'OrderedCollection')
|
|
|
|
assert.equal(data.totalItems, 2)
|
|
|
|
})
|
|
|
|
|
|
|
|
test('list actor following page', async () => {
|
|
|
|
const db = await makeDB()
|
2023-01-05 09:33:46 +00:00
|
|
|
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
|
|
|
const actor2 = await createPerson(domain, db, userKEK, 'sven2@cloudflare.com')
|
|
|
|
const actor3 = await createPerson(domain, db, userKEK, 'sven3@cloudflare.com')
|
2022-12-05 20:14:56 +00:00
|
|
|
await addFollowing(db, actor, actor2, 'not needed')
|
|
|
|
await acceptFollowing(db, actor, actor2)
|
|
|
|
await addFollowing(db, actor, actor3, 'not needed')
|
|
|
|
await acceptFollowing(db, actor, actor3)
|
|
|
|
|
|
|
|
const res = await ap_following_page.handleRequest(domain, db, 'sven')
|
|
|
|
assert.equal(res.status, 200)
|
|
|
|
|
|
|
|
const data = await res.json<any>()
|
|
|
|
assert.equal(data.type, 'OrderedCollectionPage')
|
|
|
|
assert.equal(data.orderedItems[0], `https://${domain}/ap/users/sven2`)
|
|
|
|
assert.equal(data.orderedItems[1], `https://${domain}/ap/users/sven3`)
|
|
|
|
})
|
|
|
|
|
|
|
|
test('list actor follower', async () => {
|
|
|
|
const db = await makeDB()
|
2023-01-05 09:33:46 +00:00
|
|
|
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
|
|
|
const actor2 = await createPerson(domain, db, userKEK, 'sven2@cloudflare.com')
|
2022-12-05 20:14:56 +00:00
|
|
|
await addFollowing(db, actor2, actor, 'not needed')
|
|
|
|
await acceptFollowing(db, actor2, actor)
|
|
|
|
|
|
|
|
const res = await ap_followers.handleRequest(domain, db, 'sven')
|
|
|
|
assert.equal(res.status, 200)
|
|
|
|
|
|
|
|
const data = await res.json<any>()
|
|
|
|
assert.equal(data.type, 'OrderedCollection')
|
|
|
|
assert.equal(data.totalItems, 1)
|
|
|
|
})
|
|
|
|
|
|
|
|
test('list actor follower page', async () => {
|
|
|
|
const db = await makeDB()
|
2023-01-05 09:33:46 +00:00
|
|
|
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
|
|
|
const actor2 = await createPerson(domain, db, userKEK, 'sven2@cloudflare.com')
|
2022-12-05 20:14:56 +00:00
|
|
|
await addFollowing(db, actor2, actor, 'not needed')
|
|
|
|
await acceptFollowing(db, actor2, actor)
|
|
|
|
|
|
|
|
const res = await ap_followers_page.handleRequest(domain, db, 'sven')
|
|
|
|
assert.equal(res.status, 200)
|
|
|
|
|
|
|
|
const data = await res.json<any>()
|
|
|
|
assert.equal(data.type, 'OrderedCollectionPage')
|
|
|
|
assert.equal(data.orderedItems[0], `https://${domain}/ap/users/sven2`)
|
|
|
|
})
|
|
|
|
|
|
|
|
test('creates a notification', async () => {
|
|
|
|
const db = await makeDB()
|
2023-01-05 09:33:46 +00:00
|
|
|
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
|
|
|
const actor2 = await createPerson(domain, db, userKEK, 'sven2@cloudflare.com')
|
2022-12-05 20:14:56 +00:00
|
|
|
|
|
|
|
const activity = {
|
|
|
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
|
|
|
type: 'Follow',
|
|
|
|
actor: actor2.id,
|
|
|
|
object: actor.id,
|
|
|
|
}
|
|
|
|
|
2023-01-11 14:32:06 +00:00
|
|
|
await activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys)
|
2022-12-05 20:14:56 +00:00
|
|
|
|
2023-01-11 15:45:07 +00:00
|
|
|
const entry = await db.prepare('SELECT * FROM actor_notifications').first<{
|
|
|
|
type: string
|
|
|
|
actor_id: object
|
|
|
|
from_actor_id: object
|
|
|
|
}>()
|
2022-12-05 20:14:56 +00:00
|
|
|
assert.equal(entry.type, 'follow')
|
|
|
|
assert.equal(entry.actor_id.toString(), actor.id.toString())
|
|
|
|
assert.equal(entry.from_actor_id.toString(), actor2.id.toString())
|
|
|
|
})
|
2023-01-16 13:26:10 +00:00
|
|
|
|
|
|
|
test('ignore when trying to follow multiple times', async () => {
|
|
|
|
const db = await makeDB()
|
|
|
|
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
|
|
|
const actor2 = await createPerson(domain, db, userKEK, 'sven2@cloudflare.com')
|
|
|
|
|
|
|
|
const activity = {
|
|
|
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
|
|
|
type: 'Follow',
|
|
|
|
actor: actor2.id,
|
|
|
|
object: actor.id,
|
|
|
|
}
|
|
|
|
|
|
|
|
await activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys)
|
|
|
|
await activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys)
|
|
|
|
await activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys)
|
|
|
|
|
|
|
|
// Even if we followed multiple times, only one row should be present.
|
2023-01-11 15:45:07 +00:00
|
|
|
const { count } = await db.prepare(`SELECT count(*) as count FROM actor_following`).first<{ count: number }>()
|
2023-01-16 13:26:10 +00:00
|
|
|
assert.equal(count, 1)
|
|
|
|
})
|
2022-12-05 20:14:56 +00:00
|
|
|
})
|
|
|
|
})
|