@evanp/activitypub-bot 0.13.0 → 0.13.1

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.
Files changed (41) hide show
  1. package/package.json +2 -2
  2. package/.github/dependabot.yml +0 -11
  3. package/.github/workflows/main.yml +0 -34
  4. package/.github/workflows/tag.yml +0 -106
  5. package/.nvmrc +0 -1
  6. package/Dockerfile +0 -17
  7. package/docs/activitypub.bot.drawio +0 -110
  8. package/tests/activitydistributor.test.js +0 -606
  9. package/tests/activityhandler.test.js +0 -2276
  10. package/tests/activitypubclient.test.js +0 -210
  11. package/tests/actorstorage.test.js +0 -283
  12. package/tests/app.test.js +0 -17
  13. package/tests/authorizer.test.js +0 -301
  14. package/tests/bot.donothing.test.js +0 -30
  15. package/tests/bot.ok.test.js +0 -101
  16. package/tests/botcontext.test.js +0 -720
  17. package/tests/botdatastorage.test.js +0 -88
  18. package/tests/botfactory.provincebotfactory.test.js +0 -430
  19. package/tests/digester.test.js +0 -56
  20. package/tests/fixtures/bots.js +0 -27
  21. package/tests/fixtures/eventloggingbot.js +0 -57
  22. package/tests/fixtures/provincebotfactory.js +0 -53
  23. package/tests/httpsignature.test.js +0 -199
  24. package/tests/httpsignatureauthenticator.test.js +0 -463
  25. package/tests/index.test.js +0 -12
  26. package/tests/keystorage.test.js +0 -124
  27. package/tests/microsyntax.test.js +0 -123
  28. package/tests/objectcache.test.js +0 -133
  29. package/tests/objectstorage.test.js +0 -149
  30. package/tests/remotekeystorage.test.js +0 -78
  31. package/tests/routes.actor.test.js +0 -214
  32. package/tests/routes.collection.test.js +0 -310
  33. package/tests/routes.health.test.js +0 -41
  34. package/tests/routes.inbox.test.js +0 -216
  35. package/tests/routes.object.test.js +0 -525
  36. package/tests/routes.server.test.js +0 -69
  37. package/tests/routes.sharedinbox.test.js +0 -473
  38. package/tests/routes.webfinger.test.js +0 -68
  39. package/tests/urlformatter.test.js +0 -164
  40. package/tests/utils/digest.js +0 -7
  41. 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
- })