@evanp/activitypub-bot 0.13.0 → 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/.github/dependabot.yml +0 -11
- package/.github/workflows/main.yml +0 -34
- package/.github/workflows/tag.yml +0 -106
- package/.nvmrc +0 -1
- package/Dockerfile +0 -17
- package/docs/activitypub.bot.drawio +0 -110
- package/tests/activitydistributor.test.js +0 -606
- package/tests/activityhandler.test.js +0 -2276
- package/tests/activitypubclient.test.js +0 -210
- package/tests/actorstorage.test.js +0 -283
- package/tests/app.test.js +0 -17
- package/tests/authorizer.test.js +0 -301
- package/tests/bot.donothing.test.js +0 -30
- package/tests/bot.ok.test.js +0 -101
- package/tests/botcontext.test.js +0 -720
- package/tests/botdatastorage.test.js +0 -88
- package/tests/botfactory.provincebotfactory.test.js +0 -430
- package/tests/digester.test.js +0 -56
- package/tests/fixtures/bots.js +0 -27
- package/tests/fixtures/eventloggingbot.js +0 -57
- package/tests/fixtures/provincebotfactory.js +0 -53
- package/tests/httpsignature.test.js +0 -199
- package/tests/httpsignatureauthenticator.test.js +0 -463
- package/tests/index.test.js +0 -12
- package/tests/keystorage.test.js +0 -124
- package/tests/microsyntax.test.js +0 -123
- package/tests/objectcache.test.js +0 -133
- package/tests/objectstorage.test.js +0 -149
- package/tests/remotekeystorage.test.js +0 -78
- package/tests/routes.actor.test.js +0 -214
- package/tests/routes.collection.test.js +0 -310
- package/tests/routes.health.test.js +0 -41
- package/tests/routes.inbox.test.js +0 -216
- package/tests/routes.object.test.js +0 -525
- package/tests/routes.server.test.js +0 -69
- package/tests/routes.sharedinbox.test.js +0 -473
- package/tests/routes.webfinger.test.js +0 -68
- package/tests/urlformatter.test.js +0 -164
- package/tests/utils/digest.js +0 -7
- package/tests/utils/nock.js +0 -499
|
@@ -1,606 +0,0 @@
|
|
|
1
|
-
import { describe, it, before, after, beforeEach } from 'node:test'
|
|
2
|
-
import { ActorStorage } from '../lib/actorstorage.js'
|
|
3
|
-
import { Sequelize } from 'sequelize'
|
|
4
|
-
import { UrlFormatter } from '../lib/urlformatter.js'
|
|
5
|
-
import as2 from '../lib/activitystreams.js'
|
|
6
|
-
import nock from 'nock'
|
|
7
|
-
import { KeyStorage } from '../lib/keystorage.js'
|
|
8
|
-
import { ActivityPubClient } from '../lib/activitypubclient.js'
|
|
9
|
-
import assert from 'node:assert'
|
|
10
|
-
import { ActivityDistributor } from '../lib/activitydistributor.js'
|
|
11
|
-
import Logger from 'pino'
|
|
12
|
-
import { HTTPSignature } from '../lib/httpsignature.js'
|
|
13
|
-
import { Digester } from '../lib/digester.js'
|
|
14
|
-
import { runMigrations } from '../lib/migrations/index.js'
|
|
15
|
-
|
|
16
|
-
const makeActor = (domain, username, shared = true) =>
|
|
17
|
-
as2.import({
|
|
18
|
-
id: `https://${domain}/user/${username}`,
|
|
19
|
-
type: 'Person',
|
|
20
|
-
preferredUsername: username,
|
|
21
|
-
inbox: `https://${domain}/user/${username}/inbox`,
|
|
22
|
-
outbox: `https://${domain}/user/${username}/outbox`,
|
|
23
|
-
followers: `https://${domain}/user/${username}/followers`,
|
|
24
|
-
following: `https://${domain}/user/${username}/following`,
|
|
25
|
-
liked: `https://${domain}/user/${username}/liked`,
|
|
26
|
-
endpoints: {
|
|
27
|
-
sharedInbox: (shared) ? `https://${domain}/sharedInbox` : null
|
|
28
|
-
}
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
const makeObject = (domain, username, num) =>
|
|
32
|
-
as2.import({
|
|
33
|
-
id: `https://${domain}/user/${username}/object/${num}`,
|
|
34
|
-
type: 'Object',
|
|
35
|
-
attributedTo: `https://${domain}/user/${username}`,
|
|
36
|
-
to: 'https://www.w3.org/ns/activitystreams#Public'
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
describe('ActivityDistributor', () => {
|
|
40
|
-
let connection = null
|
|
41
|
-
let actorStorage = null
|
|
42
|
-
let keyStorage = null
|
|
43
|
-
let formatter = null
|
|
44
|
-
let client = null
|
|
45
|
-
let distributor = null
|
|
46
|
-
let actor1 = null
|
|
47
|
-
let actor2 = null
|
|
48
|
-
let actor3 = null
|
|
49
|
-
let signature = null
|
|
50
|
-
let digest = null
|
|
51
|
-
let date = null
|
|
52
|
-
let gotTest1 = 0
|
|
53
|
-
let gotTest2 = 0
|
|
54
|
-
let postedTest1Inbox = 0
|
|
55
|
-
let postedTest2Inbox = 0
|
|
56
|
-
let postedTest3Inbox = 0
|
|
57
|
-
let btoSeen = 0
|
|
58
|
-
let bccSeen = 0
|
|
59
|
-
let postInbox = {}
|
|
60
|
-
let postSignature = null
|
|
61
|
-
let postSharedInbox = {}
|
|
62
|
-
let getActor = {}
|
|
63
|
-
let logger = null
|
|
64
|
-
before(async () => {
|
|
65
|
-
logger = Logger({ level: 'silent' })
|
|
66
|
-
formatter = new UrlFormatter('https://activitypubbot.example')
|
|
67
|
-
connection = new Sequelize({ dialect: 'sqlite', storage: ':memory:', logging: false })
|
|
68
|
-
await connection.authenticate()
|
|
69
|
-
await runMigrations(connection)
|
|
70
|
-
actorStorage = new ActorStorage(connection, formatter)
|
|
71
|
-
keyStorage = new KeyStorage(connection, logger)
|
|
72
|
-
const signer = new HTTPSignature(logger)
|
|
73
|
-
const digester = new Digester(logger)
|
|
74
|
-
client = new ActivityPubClient(keyStorage, formatter, signer, digester, logger)
|
|
75
|
-
actor1 = await as2.import({
|
|
76
|
-
id: 'https://social.example/user/test1',
|
|
77
|
-
type: 'Person',
|
|
78
|
-
preferredUsername: 'test1',
|
|
79
|
-
inbox: 'https://social.example/user/test1/inbox',
|
|
80
|
-
outbox: 'https://social.example/user/test1/outbox',
|
|
81
|
-
followers: 'https://social.example/user/test1/followers',
|
|
82
|
-
following: 'https://social.example/user/test1/following',
|
|
83
|
-
liked: 'https://social.example/user/test1/liked'
|
|
84
|
-
})
|
|
85
|
-
const actor1Text = await actor1.export({ useOriginalContext: true })
|
|
86
|
-
actor2 = await as2.import({
|
|
87
|
-
id: 'https://other.example/user/test2',
|
|
88
|
-
type: 'Person',
|
|
89
|
-
preferredUsername: 'test2',
|
|
90
|
-
inbox: 'https://other.example/user/test2/inbox',
|
|
91
|
-
outbox: 'https://other.example/user/test2/outbox',
|
|
92
|
-
followers: 'https://other.example/user/test2/followers',
|
|
93
|
-
following: 'https://other.example/user/test2/following',
|
|
94
|
-
liked: 'https://other.example/user/test2/liked'
|
|
95
|
-
})
|
|
96
|
-
const actor2Text = await actor2.export({ useOriginalContext: true })
|
|
97
|
-
actor3 = await as2.import({
|
|
98
|
-
id: 'https://third.example/user/test3',
|
|
99
|
-
type: 'Person',
|
|
100
|
-
preferredUsername: 'test3',
|
|
101
|
-
inbox: 'https://third.example/user/test3/inbox',
|
|
102
|
-
outbox: 'https://third.example/user/test3/outbox',
|
|
103
|
-
followers: 'https://third.example/user/test3/followers',
|
|
104
|
-
following: 'https://third.example/user/test3/following',
|
|
105
|
-
liked: 'https://third.example/user/test3/liked'
|
|
106
|
-
})
|
|
107
|
-
const actor3Text = await actor3.export({ useOriginalContext: true })
|
|
108
|
-
await actorStorage.addToCollection('test0', 'followers', actor2)
|
|
109
|
-
await actorStorage.addToCollection('test0', 'followers', actor3)
|
|
110
|
-
nock('https://social.example')
|
|
111
|
-
.get('/user/test1')
|
|
112
|
-
.reply(function (uri, requestBody) {
|
|
113
|
-
gotTest1 += 1
|
|
114
|
-
signature = this.req.headers.signature
|
|
115
|
-
digest = this.req.headers.digest
|
|
116
|
-
date = this.req.headers.date
|
|
117
|
-
return [200, actor1Text,
|
|
118
|
-
{ 'Content-Type': 'application/activity+json' }]
|
|
119
|
-
})
|
|
120
|
-
.persist()
|
|
121
|
-
.post('/user/test1/inbox')
|
|
122
|
-
.reply(function (uri, requestBody) {
|
|
123
|
-
postedTest1Inbox += 1
|
|
124
|
-
postSignature = signature = this.req.headers.signature
|
|
125
|
-
digest = this.req.headers.digest
|
|
126
|
-
date = this.req.headers.date
|
|
127
|
-
return [202, 'accepted']
|
|
128
|
-
})
|
|
129
|
-
.persist()
|
|
130
|
-
nock('https://other.example')
|
|
131
|
-
.get('/user/test2')
|
|
132
|
-
.reply(function (uri, requestBody) {
|
|
133
|
-
gotTest2 += 1
|
|
134
|
-
signature = this.req.headers.signature
|
|
135
|
-
digest = this.req.headers.digest
|
|
136
|
-
date = this.req.headers.date
|
|
137
|
-
return [200, actor2Text,
|
|
138
|
-
{ 'Content-Type': 'application/activity+json' }]
|
|
139
|
-
})
|
|
140
|
-
.persist()
|
|
141
|
-
.post('/user/test2/inbox')
|
|
142
|
-
.reply(function (uri, requestBody) {
|
|
143
|
-
postedTest2Inbox += 1
|
|
144
|
-
const obj = JSON.parse(requestBody)
|
|
145
|
-
if (obj.bcc) {
|
|
146
|
-
bccSeen += 1
|
|
147
|
-
}
|
|
148
|
-
if (obj.bto) {
|
|
149
|
-
btoSeen += 1
|
|
150
|
-
}
|
|
151
|
-
postSignature = signature = this.req.headers.signature
|
|
152
|
-
digest = this.req.headers.digest
|
|
153
|
-
date = this.req.headers.date
|
|
154
|
-
return [202, 'accepted']
|
|
155
|
-
})
|
|
156
|
-
.persist()
|
|
157
|
-
nock('https://third.example')
|
|
158
|
-
.get('/user/test3')
|
|
159
|
-
.reply(function (uri, requestBody) {
|
|
160
|
-
return [200, actor3Text,
|
|
161
|
-
{ 'Content-Type': 'application/activity+json' }]
|
|
162
|
-
})
|
|
163
|
-
.persist()
|
|
164
|
-
.post('/user/test3/inbox')
|
|
165
|
-
.reply(function (uri, requestBody) {
|
|
166
|
-
postedTest3Inbox += 1
|
|
167
|
-
const obj = JSON.parse(requestBody)
|
|
168
|
-
if (obj.bcc) {
|
|
169
|
-
bccSeen += 1
|
|
170
|
-
}
|
|
171
|
-
if (obj.bto) {
|
|
172
|
-
btoSeen += 1
|
|
173
|
-
}
|
|
174
|
-
postSignature = signature = this.req.headers.signature
|
|
175
|
-
digest = this.req.headers.digest
|
|
176
|
-
date = this.req.headers.date
|
|
177
|
-
return [202, 'accepted']
|
|
178
|
-
})
|
|
179
|
-
.persist()
|
|
180
|
-
nock('https://shared.example')
|
|
181
|
-
.get(/\/user\/(\w+)$/)
|
|
182
|
-
.reply(async function (uri, requestBody) {
|
|
183
|
-
const username = uri.match(/\/user\/(\w+)$/)[1]
|
|
184
|
-
if (username in postInbox) {
|
|
185
|
-
getActor[username] += 1
|
|
186
|
-
} else {
|
|
187
|
-
getActor[username] = 1
|
|
188
|
-
}
|
|
189
|
-
const actor = await makeActor('shared.example', username)
|
|
190
|
-
const actorText = await actor.write()
|
|
191
|
-
return [200, actorText, { 'Content-Type': 'application/activity+json' }]
|
|
192
|
-
})
|
|
193
|
-
.persist()
|
|
194
|
-
.post(/\/user\/(\w+)\/inbox$/)
|
|
195
|
-
.reply(async function (uri, requestBody) {
|
|
196
|
-
const username = uri.match(/\/user\/(\w+)\/inbox$/)[1]
|
|
197
|
-
if (username in postInbox) {
|
|
198
|
-
postInbox[username] += 1
|
|
199
|
-
} else {
|
|
200
|
-
postInbox[username] = 1
|
|
201
|
-
}
|
|
202
|
-
postSignature = signature = this.req.headers.signature
|
|
203
|
-
digest = this.req.headers.digest
|
|
204
|
-
date = this.req.headers.date
|
|
205
|
-
return [202, 'accepted']
|
|
206
|
-
})
|
|
207
|
-
.persist()
|
|
208
|
-
.post(/\/sharedInbox$/)
|
|
209
|
-
.reply(async function (uri, requestBody) {
|
|
210
|
-
const domain = 'shared.example'
|
|
211
|
-
if (domain in postSharedInbox) {
|
|
212
|
-
postSharedInbox[domain] += 1
|
|
213
|
-
} else {
|
|
214
|
-
postSharedInbox[domain] = 1
|
|
215
|
-
}
|
|
216
|
-
postSignature = signature = this.req.headers.signature
|
|
217
|
-
digest = this.req.headers.digest
|
|
218
|
-
date = this.req.headers.date
|
|
219
|
-
return [202, 'accepted']
|
|
220
|
-
})
|
|
221
|
-
.persist()
|
|
222
|
-
.get(/\/user\/(\w+)\/object\/(\d+)$/)
|
|
223
|
-
.reply(async function (uri, requestBody) {
|
|
224
|
-
const match = uri.match(/\/user\/(\w+)\/object\/(\d+)$/)
|
|
225
|
-
const username = match[1]
|
|
226
|
-
const num = match[2]
|
|
227
|
-
const obj = await makeObject('shared.example', username, num)
|
|
228
|
-
const objText = await obj.write()
|
|
229
|
-
postSignature = signature = this.req.headers.signature
|
|
230
|
-
digest = this.req.headers.digest
|
|
231
|
-
date = this.req.headers.date
|
|
232
|
-
return [200, objText, { 'Content-Type': 'application/activity+json' }]
|
|
233
|
-
})
|
|
234
|
-
.persist()
|
|
235
|
-
|
|
236
|
-
nock('https://flaky.example')
|
|
237
|
-
.get(/\/user\/(\w+)$/)
|
|
238
|
-
.reply(async function (uri, requestBody) {
|
|
239
|
-
const username = uri.match(/\/user\/(\w+)$/)[1]
|
|
240
|
-
if (username in postInbox) {
|
|
241
|
-
getActor[username] += 1
|
|
242
|
-
} else {
|
|
243
|
-
getActor[username] = 1
|
|
244
|
-
}
|
|
245
|
-
const actor = await makeActor('flaky.example', username)
|
|
246
|
-
const actorText = await actor.write()
|
|
247
|
-
return [200, actorText, { 'Content-Type': 'application/activity+json' }]
|
|
248
|
-
})
|
|
249
|
-
.persist()
|
|
250
|
-
.post(/\/user\/(\w+)\/inbox$/)
|
|
251
|
-
.reply(async function (uri, requestBody) {
|
|
252
|
-
const username = uri.match(/\/user\/(\w+)\/inbox$/)[1]
|
|
253
|
-
if (username in postInbox) {
|
|
254
|
-
postInbox[username] += 1
|
|
255
|
-
return [202, 'accepted']
|
|
256
|
-
} else {
|
|
257
|
-
postInbox[username] = 0
|
|
258
|
-
return [503, 'service unavailable']
|
|
259
|
-
}
|
|
260
|
-
})
|
|
261
|
-
.persist()
|
|
262
|
-
.post(/\/sharedInbox$/)
|
|
263
|
-
.reply(async function (uri, requestBody) {
|
|
264
|
-
const domain = 'flaky.example'
|
|
265
|
-
if (domain in postSharedInbox) {
|
|
266
|
-
postSharedInbox[domain] += 1
|
|
267
|
-
return [202, 'accepted']
|
|
268
|
-
} else {
|
|
269
|
-
postSharedInbox[domain] = 0
|
|
270
|
-
return [503, 'service unavailable']
|
|
271
|
-
}
|
|
272
|
-
})
|
|
273
|
-
.persist()
|
|
274
|
-
.get(/\/user\/(\w+)\/object\/(\d+)$/)
|
|
275
|
-
.reply(async function (uri, requestBody) {
|
|
276
|
-
const match = uri.match(/\/user\/(\w+)\/object\/(\d+)$/)
|
|
277
|
-
const username = match[1]
|
|
278
|
-
const num = match[2]
|
|
279
|
-
const obj = await makeObject('flaky.example', username, num)
|
|
280
|
-
const objText = await obj.write()
|
|
281
|
-
return [200, objText, { 'Content-Type': 'application/activity+json' }]
|
|
282
|
-
})
|
|
283
|
-
})
|
|
284
|
-
after(async () => {
|
|
285
|
-
await connection.close()
|
|
286
|
-
distributor = null
|
|
287
|
-
client = null
|
|
288
|
-
connection = null
|
|
289
|
-
actorStorage = null
|
|
290
|
-
keyStorage = null
|
|
291
|
-
formatter = null
|
|
292
|
-
logger = null
|
|
293
|
-
})
|
|
294
|
-
beforeEach(async () => {
|
|
295
|
-
signature = null
|
|
296
|
-
digest = null
|
|
297
|
-
gotTest1 = 0
|
|
298
|
-
gotTest2 = 0
|
|
299
|
-
postedTest1Inbox = 0
|
|
300
|
-
postedTest2Inbox = 0
|
|
301
|
-
postedTest3Inbox = 0
|
|
302
|
-
btoSeen = 0
|
|
303
|
-
bccSeen = 0
|
|
304
|
-
postInbox = {}
|
|
305
|
-
postSharedInbox = {}
|
|
306
|
-
getActor = {}
|
|
307
|
-
postSignature = null
|
|
308
|
-
digest = null
|
|
309
|
-
date = null
|
|
310
|
-
})
|
|
311
|
-
it('can create an instance', () => {
|
|
312
|
-
distributor = new ActivityDistributor(client, formatter, actorStorage, logger)
|
|
313
|
-
assert.ok(distributor instanceof ActivityDistributor)
|
|
314
|
-
})
|
|
315
|
-
it('can distribute an activity to a single recipient', async () => {
|
|
316
|
-
const activity = await as2.import({
|
|
317
|
-
id: 'https://activitypubbot.example/user/test0/intransitiveactivity/1',
|
|
318
|
-
type: 'IntransitiveActivity',
|
|
319
|
-
actor: 'https://activitypubbot.example/user/test0',
|
|
320
|
-
to: ['https://social.example/user/test1']
|
|
321
|
-
})
|
|
322
|
-
await distributor.distribute(activity, 'test0')
|
|
323
|
-
await distributor.onIdle()
|
|
324
|
-
assert.equal(gotTest1, 1)
|
|
325
|
-
assert.equal(postedTest1Inbox, 1)
|
|
326
|
-
assert.equal(postedTest2Inbox, 0)
|
|
327
|
-
assert.ok(signature)
|
|
328
|
-
assert.ok(digest)
|
|
329
|
-
assert.ok(date)
|
|
330
|
-
assert.match(signature, /^keyId="https:\/\/activitypubbot\.example\/user\/test0\/publickey",headers="\(request-target\) host date user-agent content-type digest",signature=".*",algorithm="rsa-sha256"$/)
|
|
331
|
-
assert.match(digest, /^sha-256=[0-9a-zA-Z=+/]*$/)
|
|
332
|
-
assert.match(date, /^[A-Z][a-z]{2}, \d{2} [A-Z][a-z]{2} \d{4} \d{2}:\d{2}:\d{2} GMT$/)
|
|
333
|
-
})
|
|
334
|
-
it('can distribute an activity to all followers', async () => {
|
|
335
|
-
const activity = await as2.import({
|
|
336
|
-
id: 'https://activitypubbot.example/user/test0/intransitiveactivity/2',
|
|
337
|
-
type: 'IntransitiveActivity',
|
|
338
|
-
actor: 'https://activitypubbot.example/user/test0',
|
|
339
|
-
to: ['https://activitypubbot.example/user/test0/followers']
|
|
340
|
-
})
|
|
341
|
-
await distributor.distribute(activity, 'test0')
|
|
342
|
-
await distributor.onIdle()
|
|
343
|
-
assert.equal(postedTest1Inbox, 0)
|
|
344
|
-
assert.equal(gotTest2, 1)
|
|
345
|
-
assert.equal(postedTest2Inbox, 1)
|
|
346
|
-
assert.ok(signature)
|
|
347
|
-
assert.ok(digest)
|
|
348
|
-
assert.ok(date)
|
|
349
|
-
assert.match(signature, /^keyId="https:\/\/activitypubbot\.example\/user\/test0\/publickey",headers="\(request-target\) host date user-agent content-type digest",signature=".*",algorithm="rsa-sha256"$/)
|
|
350
|
-
assert.match(digest, /^sha-256=[0-9a-zA-Z=+/]*$/)
|
|
351
|
-
assert.match(date, /^[A-Z][a-z]{2}, \d{2} [A-Z][a-z]{2} \d{4} \d{2}:\d{2}:\d{2} GMT$/)
|
|
352
|
-
})
|
|
353
|
-
it('can distribute an activity to the public', async () => {
|
|
354
|
-
const activity = await as2.import({
|
|
355
|
-
id: 'https://activitypubbot.example/user/test0/intransitiveactivity/3',
|
|
356
|
-
type: 'IntransitiveActivity',
|
|
357
|
-
actor: 'https://activitypubbot.example/user/test0',
|
|
358
|
-
to: ['https://www.w3.org/ns/activitystreams#Public']
|
|
359
|
-
})
|
|
360
|
-
await distributor.distribute(activity, 'test0')
|
|
361
|
-
await distributor.onIdle()
|
|
362
|
-
assert.equal(postedTest1Inbox, 0)
|
|
363
|
-
assert.equal(postedTest2Inbox, 1)
|
|
364
|
-
assert.ok(signature)
|
|
365
|
-
assert.ok(digest)
|
|
366
|
-
assert.ok(date)
|
|
367
|
-
assert.match(signature, /^keyId="https:\/\/activitypubbot\.example\/user\/test0\/publickey",headers="\(request-target\) host date user-agent content-type digest",signature=".*",algorithm="rsa-sha256"$/)
|
|
368
|
-
assert.match(digest, /^sha-256=[0-9a-zA-Z=+/]*$/)
|
|
369
|
-
assert.match(date, /^[A-Z][a-z]{2}, \d{2} [A-Z][a-z]{2} \d{4} \d{2}:\d{2}:\d{2} GMT$/)
|
|
370
|
-
})
|
|
371
|
-
it('can distribute an activity to an addressed actor and followers', async () => {
|
|
372
|
-
const activity = await as2.import({
|
|
373
|
-
id: 'https://activitypubbot.example/user/test0/intransitiveactivity/4',
|
|
374
|
-
type: 'IntransitiveActivity',
|
|
375
|
-
actor: 'https://activitypubbot.example/user/test0',
|
|
376
|
-
to: ['https://social.example/user/test1'],
|
|
377
|
-
cc: ['https://activitypubbot.example/user/test0/followers']
|
|
378
|
-
})
|
|
379
|
-
await distributor.distribute(activity, 'test0')
|
|
380
|
-
await distributor.onIdle()
|
|
381
|
-
assert.equal(postedTest1Inbox, 1)
|
|
382
|
-
assert.equal(postedTest2Inbox, 1)
|
|
383
|
-
assert.ok(signature)
|
|
384
|
-
assert.ok(digest)
|
|
385
|
-
assert.ok(date)
|
|
386
|
-
assert.match(signature, /^keyId="https:\/\/activitypubbot\.example\/user\/test0\/publickey",headers="\(request-target\) host date user-agent content-type digest",signature=".*",algorithm="rsa-sha256"$/)
|
|
387
|
-
assert.match(digest, /^sha-256=[0-9a-zA-Z=+/]*$/)
|
|
388
|
-
assert.match(date, /^[A-Z][a-z]{2}, \d{2} [A-Z][a-z]{2} \d{4} \d{2}:\d{2}:\d{2} GMT$/)
|
|
389
|
-
})
|
|
390
|
-
it('can distribute an activity to an addressed actor and the public', async () => {
|
|
391
|
-
const activity = await as2.import({
|
|
392
|
-
id: 'https://activitypubbot.example/user/test0/intransitiveactivity/5',
|
|
393
|
-
type: 'IntransitiveActivity',
|
|
394
|
-
actor: 'https://activitypubbot.example/user/test0',
|
|
395
|
-
to: ['https://social.example/user/test1'],
|
|
396
|
-
cc: ['https://www.w3.org/ns/activitystreams#Public']
|
|
397
|
-
})
|
|
398
|
-
await distributor.distribute(activity, 'test0')
|
|
399
|
-
await distributor.onIdle()
|
|
400
|
-
assert.equal(postedTest1Inbox, 1)
|
|
401
|
-
assert.equal(postedTest2Inbox, 1)
|
|
402
|
-
assert.ok(signature)
|
|
403
|
-
assert.ok(digest)
|
|
404
|
-
assert.ok(date)
|
|
405
|
-
assert.match(signature, /^keyId="https:\/\/activitypubbot\.example\/user\/test0\/publickey",headers="\(request-target\) host date user-agent content-type digest",signature=".*",algorithm="rsa-sha256"$/)
|
|
406
|
-
assert.match(digest, /^sha-256=[0-9a-zA-Z=+/]*$/)
|
|
407
|
-
assert.match(date, /^[A-Z][a-z]{2}, \d{2} [A-Z][a-z]{2} \d{4} \d{2}:\d{2}:\d{2} GMT$/)
|
|
408
|
-
})
|
|
409
|
-
it('only sends once to an addressed follower', async () => {
|
|
410
|
-
const activity = await as2.import({
|
|
411
|
-
id: 'https://activitypubbot.example/user/test0/intransitiveactivity/6',
|
|
412
|
-
type: 'IntransitiveActivity',
|
|
413
|
-
actor: 'https://activitypubbot.example/user/test0',
|
|
414
|
-
to: ['https://other.example/user/test2'],
|
|
415
|
-
cc: ['https://activitypubbot.example/user/test0/followers']
|
|
416
|
-
})
|
|
417
|
-
await distributor.distribute(activity, 'test0')
|
|
418
|
-
await distributor.onIdle()
|
|
419
|
-
assert.equal(postedTest1Inbox, 0)
|
|
420
|
-
assert.equal(postedTest2Inbox, 1)
|
|
421
|
-
})
|
|
422
|
-
it('only sends once to an addressed follower for the public', async () => {
|
|
423
|
-
const activity = await as2.import({
|
|
424
|
-
id: 'https://activitypubbot.example/user/test0/intransitiveactivity/7',
|
|
425
|
-
type: 'IntransitiveActivity',
|
|
426
|
-
actor: 'https://activitypubbot.example/user/test0',
|
|
427
|
-
to: ['https://other.example/user/test2'],
|
|
428
|
-
cc: ['https://www.w3.org/ns/activitystreams#Public']
|
|
429
|
-
})
|
|
430
|
-
await distributor.distribute(activity, 'test0')
|
|
431
|
-
await distributor.onIdle()
|
|
432
|
-
assert.equal(postedTest1Inbox, 0)
|
|
433
|
-
assert.equal(postedTest2Inbox, 1)
|
|
434
|
-
assert.ok(postSignature)
|
|
435
|
-
})
|
|
436
|
-
it('does not send bcc or bto over the wire', async () => {
|
|
437
|
-
const activity = await as2.import({
|
|
438
|
-
id: 'https://activitypubbot.example/user/test0/intransitiveactivity/8',
|
|
439
|
-
type: 'IntransitiveActivity',
|
|
440
|
-
actor: 'https://activitypubbot.example/user/test0',
|
|
441
|
-
bto: ['https://other.example/user/test2'],
|
|
442
|
-
bcc: ['https://third.example/user/test3']
|
|
443
|
-
})
|
|
444
|
-
await distributor.distribute(activity, 'test0')
|
|
445
|
-
await distributor.onIdle()
|
|
446
|
-
assert.equal(postedTest2Inbox, 1)
|
|
447
|
-
assert.equal(postedTest3Inbox, 1)
|
|
448
|
-
assert.equal(bccSeen, 0, 'bcc should not be seen')
|
|
449
|
-
assert.equal(btoSeen, 0, 'bto should not be seen')
|
|
450
|
-
})
|
|
451
|
-
it('posts once to a shared inbox', async () => {
|
|
452
|
-
const nums = Array.from({ length: 10 }, (v, k) => k + 1)
|
|
453
|
-
const remotes = nums.map(n => `https://shared.example/user/test${n}`)
|
|
454
|
-
const activity = await as2.import({
|
|
455
|
-
id: 'https://activitypubbot.example/user/test0/intransitiveactivity/9',
|
|
456
|
-
type: 'IntransitiveActivity',
|
|
457
|
-
actor: 'https://activitypubbot.example/user/test0',
|
|
458
|
-
to: remotes
|
|
459
|
-
})
|
|
460
|
-
await distributor.distribute(activity, 'test0')
|
|
461
|
-
await distributor.onIdle()
|
|
462
|
-
assert.equal(postSharedInbox['shared.example'], 1)
|
|
463
|
-
for (const i of nums) {
|
|
464
|
-
assert.ok(!postInbox[`test${i}`])
|
|
465
|
-
assert.equal(getActor[`test${i}`], 1)
|
|
466
|
-
}
|
|
467
|
-
})
|
|
468
|
-
it('uses the cache for sending again to same actors', async () => {
|
|
469
|
-
const nums = Array.from({ length: 10 }, (v, k) => k + 1)
|
|
470
|
-
const remotes = nums.map(n => `https://shared.example/user/test${n}`)
|
|
471
|
-
const activity = await as2.import({
|
|
472
|
-
id: 'https://activitypubbot.example/user/test0/intransitiveactivity/10',
|
|
473
|
-
type: 'IntransitiveActivity',
|
|
474
|
-
actor: 'https://activitypubbot.example/user/test0',
|
|
475
|
-
to: remotes
|
|
476
|
-
})
|
|
477
|
-
await distributor.distribute(activity, 'test0')
|
|
478
|
-
await distributor.onIdle()
|
|
479
|
-
assert.equal(postSharedInbox['shared.example'], 1)
|
|
480
|
-
for (const i of nums) {
|
|
481
|
-
assert.ok(!getActor[`test${i}`])
|
|
482
|
-
}
|
|
483
|
-
})
|
|
484
|
-
it('delivers directly for addressees in bto', async () => {
|
|
485
|
-
const nums = Array.from({ length: 10 }, (v, k) => k + 1)
|
|
486
|
-
const remotes = nums.map(n => `https://shared.example/user/test${n}`)
|
|
487
|
-
const activity = await as2.import({
|
|
488
|
-
id: 'https://activitypubbot.example/user/test0/intransitiveactivity/11',
|
|
489
|
-
type: 'IntransitiveActivity',
|
|
490
|
-
actor: 'https://activitypubbot.example/user/test0',
|
|
491
|
-
bto: remotes
|
|
492
|
-
})
|
|
493
|
-
await distributor.distribute(activity, 'test0')
|
|
494
|
-
await distributor.onIdle()
|
|
495
|
-
assert.ok(!postSharedInbox['shared.example'])
|
|
496
|
-
for (const i of nums) {
|
|
497
|
-
assert.equal(postInbox[`test${i}`], 1, `did not delivery directly to test${i}`)
|
|
498
|
-
}
|
|
499
|
-
})
|
|
500
|
-
it('delivers directly for addressees in bto a second time', async () => {
|
|
501
|
-
const nums = Array.from({ length: 10 }, (v, k) => k + 1)
|
|
502
|
-
const remotes = nums.map(n => `https://shared.example/user/test${n}`)
|
|
503
|
-
const activity = await as2.import({
|
|
504
|
-
id: 'https://activitypubbot.example/user/test0/intransitiveactivity/12',
|
|
505
|
-
type: 'IntransitiveActivity',
|
|
506
|
-
actor: 'https://activitypubbot.example/user/test0',
|
|
507
|
-
bto: remotes
|
|
508
|
-
})
|
|
509
|
-
await distributor.distribute(activity, 'test0')
|
|
510
|
-
await distributor.onIdle()
|
|
511
|
-
assert.ok(!postSharedInbox['shared.example'])
|
|
512
|
-
for (const i of nums) {
|
|
513
|
-
assert.equal(postInbox[`test${i}`], 1, `did not delivery directly to test${i}`)
|
|
514
|
-
}
|
|
515
|
-
})
|
|
516
|
-
it('delivers directly for addressees in bcc', async () => {
|
|
517
|
-
const nums = Array.from({ length: 10 }, (v, k) => k + 1).map(n => n + 100)
|
|
518
|
-
const remotes = nums.map(n => `https://shared.example/user/test${n}`)
|
|
519
|
-
const activity = await as2.import({
|
|
520
|
-
id: 'https://activitypubbot.example/user/test0/intransitiveactivity/13',
|
|
521
|
-
type: 'IntransitiveActivity',
|
|
522
|
-
actor: 'https://activitypubbot.example/user/test0',
|
|
523
|
-
bcc: remotes
|
|
524
|
-
})
|
|
525
|
-
await distributor.distribute(activity, 'test0')
|
|
526
|
-
await distributor.onIdle()
|
|
527
|
-
assert.ok(!postSharedInbox['shared.example'])
|
|
528
|
-
for (const i of nums) {
|
|
529
|
-
assert.equal(postInbox[`test${i}`], 1, `did not delivery directly to test${i}`)
|
|
530
|
-
}
|
|
531
|
-
})
|
|
532
|
-
it('delivers directly for addressees in bcc a second time', async () => {
|
|
533
|
-
const nums = Array.from({ length: 10 }, (v, k) => k + 1).map(n => n + 100)
|
|
534
|
-
const remotes = nums.map(n => `https://shared.example/user/test${n}`)
|
|
535
|
-
const activity = await as2.import({
|
|
536
|
-
id: 'https://activitypubbot.example/user/test0/intransitiveactivity/14',
|
|
537
|
-
type: 'IntransitiveActivity',
|
|
538
|
-
actor: 'https://activitypubbot.example/user/test0',
|
|
539
|
-
bcc: remotes
|
|
540
|
-
})
|
|
541
|
-
await distributor.distribute(activity, 'test0')
|
|
542
|
-
await distributor.onIdle()
|
|
543
|
-
assert.ok(!postSharedInbox['shared.example'])
|
|
544
|
-
for (const i of nums) {
|
|
545
|
-
assert.equal(postInbox[`test${i}`], 1, `did not delivery directly to test${i}`)
|
|
546
|
-
}
|
|
547
|
-
})
|
|
548
|
-
it('retries delivery to a flaky recipient', async () => {
|
|
549
|
-
const activity = await as2.import({
|
|
550
|
-
id: 'https://activitypubbot.example/user/test0/intransitiveactivity/15',
|
|
551
|
-
type: 'IntransitiveActivity',
|
|
552
|
-
actor: 'https://activitypubbot.example/user/test0',
|
|
553
|
-
to: ['https://flaky.example/user/test1']
|
|
554
|
-
})
|
|
555
|
-
try {
|
|
556
|
-
await distributor.distribute(activity, 'test0')
|
|
557
|
-
} catch (error) {
|
|
558
|
-
assert.fail(`Error in distribution: ${error.message}`)
|
|
559
|
-
}
|
|
560
|
-
await new Promise((resolve) => setTimeout(resolve, 2000))
|
|
561
|
-
assert.equal(postSharedInbox['flaky.example'], 1)
|
|
562
|
-
})
|
|
563
|
-
it('can deliver a single activity to a local account', async () => {
|
|
564
|
-
const id = formatter.format({
|
|
565
|
-
username: 'test0',
|
|
566
|
-
type: 'intransitiveactivity',
|
|
567
|
-
nanoid: 'Ca45kO_L7haXDXWdqoWHE'
|
|
568
|
-
})
|
|
569
|
-
const activity = await as2.import({
|
|
570
|
-
id,
|
|
571
|
-
type: 'IntransitiveActivity',
|
|
572
|
-
actor: formatter.format({ username: 'test0' }),
|
|
573
|
-
to: formatter.format({ username: 'test1' })
|
|
574
|
-
})
|
|
575
|
-
await distributor.distribute(activity, 'test0')
|
|
576
|
-
await distributor.onIdle()
|
|
577
|
-
assert.ok(await actorStorage.isInCollection(
|
|
578
|
-
'test1',
|
|
579
|
-
'inbox',
|
|
580
|
-
activity
|
|
581
|
-
))
|
|
582
|
-
})
|
|
583
|
-
it('will not deliver an activity to the actor', async () => {
|
|
584
|
-
const id = formatter.format({
|
|
585
|
-
username: 'test0',
|
|
586
|
-
type: 'intransitiveactivity',
|
|
587
|
-
nanoid: 'ubiKNmJow3A_D52IZOsRL'
|
|
588
|
-
})
|
|
589
|
-
const activity = await as2.import({
|
|
590
|
-
id,
|
|
591
|
-
type: 'IntransitiveActivity',
|
|
592
|
-
actor: formatter.format({ username: 'test0' }),
|
|
593
|
-
to: formatter.format({ username: 'test0' })
|
|
594
|
-
})
|
|
595
|
-
// Add to inbox and outbox to simulate real activity generation
|
|
596
|
-
await actorStorage.addToCollection('test0', 'inbox', activity)
|
|
597
|
-
await actorStorage.addToCollection('test0', 'outbox', activity)
|
|
598
|
-
await distributor.distribute(activity, 'test0')
|
|
599
|
-
await distributor.onIdle()
|
|
600
|
-
assert.ok(await actorStorage.isInCollection(
|
|
601
|
-
'test0',
|
|
602
|
-
'inbox',
|
|
603
|
-
activity
|
|
604
|
-
))
|
|
605
|
-
})
|
|
606
|
-
})
|