@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.
- 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
package/tests/botcontext.test.js
DELETED
|
@@ -1,720 +0,0 @@
|
|
|
1
|
-
import { describe, it, before, after, beforeEach } from 'node:test'
|
|
2
|
-
import assert from 'node:assert'
|
|
3
|
-
import { BotContext } from '../lib/botcontext.js'
|
|
4
|
-
import { Sequelize } from 'sequelize'
|
|
5
|
-
import { BotDataStorage } from '../lib/botdatastorage.js'
|
|
6
|
-
import { ObjectStorage } from '../lib/objectstorage.js'
|
|
7
|
-
import { KeyStorage } from '../lib/keystorage.js'
|
|
8
|
-
import { UrlFormatter } from '../lib/urlformatter.js'
|
|
9
|
-
import { ActivityPubClient } from '../lib/activitypubclient.js'
|
|
10
|
-
import { ActivityDistributor } from '../lib/activitydistributor.js'
|
|
11
|
-
import { ActorStorage } from '../lib/actorstorage.js'
|
|
12
|
-
import { Transformer } from '../lib/microsyntax.js'
|
|
13
|
-
import {
|
|
14
|
-
nockSetup,
|
|
15
|
-
postInbox,
|
|
16
|
-
resetInbox,
|
|
17
|
-
makeActor,
|
|
18
|
-
makeObject,
|
|
19
|
-
nockFormat
|
|
20
|
-
} from './utils/nock.js'
|
|
21
|
-
import Logger from 'pino'
|
|
22
|
-
import as2 from '../lib/activitystreams.js'
|
|
23
|
-
import { HTTPSignature } from '../lib/httpsignature.js'
|
|
24
|
-
import { Digester } from '../lib/digester.js'
|
|
25
|
-
import { runMigrations } from '../lib/migrations/index.js'
|
|
26
|
-
|
|
27
|
-
const AS2_NS = 'https://www.w3.org/ns/activitystreams#'
|
|
28
|
-
|
|
29
|
-
describe('BotContext', () => {
|
|
30
|
-
let connection = null
|
|
31
|
-
let botDataStorage = null
|
|
32
|
-
let objectStorage = null
|
|
33
|
-
let keyStorage = null
|
|
34
|
-
let actorStorage = null
|
|
35
|
-
let formatter = null
|
|
36
|
-
let client = null
|
|
37
|
-
let distributor = null
|
|
38
|
-
let context = null
|
|
39
|
-
let actor3 = null
|
|
40
|
-
let actor5 = null
|
|
41
|
-
let actor6 = null
|
|
42
|
-
let note = null
|
|
43
|
-
let transformer = null
|
|
44
|
-
let logger = null
|
|
45
|
-
const botName = 'test1'
|
|
46
|
-
before(async () => {
|
|
47
|
-
logger = Logger({
|
|
48
|
-
level: 'silent'
|
|
49
|
-
})
|
|
50
|
-
formatter = new UrlFormatter('https://activitypubbot.example')
|
|
51
|
-
connection = new Sequelize({ dialect: 'sqlite', storage: ':memory:', logging: false })
|
|
52
|
-
await connection.authenticate()
|
|
53
|
-
await runMigrations(connection)
|
|
54
|
-
botDataStorage = new BotDataStorage(connection)
|
|
55
|
-
objectStorage = new ObjectStorage(connection)
|
|
56
|
-
keyStorage = new KeyStorage(connection, logger)
|
|
57
|
-
actorStorage = new ActorStorage(connection, formatter)
|
|
58
|
-
const signer = new HTTPSignature(logger)
|
|
59
|
-
const digester = new Digester(logger)
|
|
60
|
-
client = new ActivityPubClient(keyStorage, formatter, signer, digester, logger)
|
|
61
|
-
distributor = new ActivityDistributor(client, formatter, actorStorage, logger)
|
|
62
|
-
transformer = new Transformer('https://activitypubbot.example/tag/', client)
|
|
63
|
-
await objectStorage.create(
|
|
64
|
-
await as2.import({
|
|
65
|
-
id: formatter.format({
|
|
66
|
-
username: botName,
|
|
67
|
-
type: 'object',
|
|
68
|
-
nanoid: '_pEWsKke-7lACTdM3J_qd'
|
|
69
|
-
}),
|
|
70
|
-
type: 'Object',
|
|
71
|
-
attributedTo: formatter.format({ username: botName }),
|
|
72
|
-
to: 'https://www.w3.org/ns/activitystreams#Public'
|
|
73
|
-
})
|
|
74
|
-
)
|
|
75
|
-
nockSetup('social.example')
|
|
76
|
-
})
|
|
77
|
-
after(async () => {
|
|
78
|
-
await connection.close()
|
|
79
|
-
context = null
|
|
80
|
-
distributor = null
|
|
81
|
-
client = null
|
|
82
|
-
formatter = null
|
|
83
|
-
actorStorage = null
|
|
84
|
-
keyStorage = null
|
|
85
|
-
botDataStorage = null
|
|
86
|
-
objectStorage = null
|
|
87
|
-
connection = null
|
|
88
|
-
})
|
|
89
|
-
beforeEach(async () => {
|
|
90
|
-
resetInbox()
|
|
91
|
-
})
|
|
92
|
-
it('can initialize', async () => {
|
|
93
|
-
context = new BotContext(
|
|
94
|
-
botName,
|
|
95
|
-
botDataStorage,
|
|
96
|
-
objectStorage,
|
|
97
|
-
actorStorage,
|
|
98
|
-
client,
|
|
99
|
-
distributor,
|
|
100
|
-
formatter,
|
|
101
|
-
transformer,
|
|
102
|
-
logger
|
|
103
|
-
)
|
|
104
|
-
})
|
|
105
|
-
it('can get the bot ID', () => {
|
|
106
|
-
assert.strictEqual(context.botId, botName)
|
|
107
|
-
})
|
|
108
|
-
it('can set a value', async () => {
|
|
109
|
-
await context.setData('key1', 'value1')
|
|
110
|
-
})
|
|
111
|
-
it('can get a value', async () => {
|
|
112
|
-
const value = await context.getData('key1')
|
|
113
|
-
assert.equal(value, 'value1')
|
|
114
|
-
})
|
|
115
|
-
it('can delete a value', async () => {
|
|
116
|
-
await context.deleteData('key1')
|
|
117
|
-
})
|
|
118
|
-
it('can return the correct flag for an unset key', async () => {
|
|
119
|
-
const result = await context.hasData('doesnotexist')
|
|
120
|
-
assert.strictEqual(result, false)
|
|
121
|
-
})
|
|
122
|
-
it('can return the correct flag for a set key', async () => {
|
|
123
|
-
await context.setData('setkey', 'value')
|
|
124
|
-
const result = await context.hasData('setkey')
|
|
125
|
-
assert.strictEqual(result, true)
|
|
126
|
-
})
|
|
127
|
-
it('can get a local object', async () => {
|
|
128
|
-
const id = formatter.format({
|
|
129
|
-
username: botName,
|
|
130
|
-
type: 'object',
|
|
131
|
-
nanoid: '_pEWsKke-7lACTdM3J_qd'
|
|
132
|
-
})
|
|
133
|
-
const object = await context.getObject(id)
|
|
134
|
-
assert.ok(object)
|
|
135
|
-
assert.strictEqual(object.id, id)
|
|
136
|
-
assert.strictEqual(
|
|
137
|
-
object.type,
|
|
138
|
-
'https://www.w3.org/ns/activitystreams#Object'
|
|
139
|
-
)
|
|
140
|
-
})
|
|
141
|
-
it('can get a remote object', async () => {
|
|
142
|
-
const id = 'https://social.example/user/test2/object/1'
|
|
143
|
-
const object = await context.getObject(id)
|
|
144
|
-
assert.ok(object)
|
|
145
|
-
assert.strictEqual(object.id, id)
|
|
146
|
-
assert.strictEqual(
|
|
147
|
-
object.type,
|
|
148
|
-
'https://www.w3.org/ns/activitystreams#Object'
|
|
149
|
-
)
|
|
150
|
-
})
|
|
151
|
-
it('can send a note', async () => {
|
|
152
|
-
const actor2 = await makeActor('test2')
|
|
153
|
-
await actorStorage.addToCollection(botName, 'followers', actor2)
|
|
154
|
-
let followers = await actorStorage.getCollection(botName, 'followers')
|
|
155
|
-
assert.strictEqual(followers.totalItems, 1)
|
|
156
|
-
const content = 'Hello World'
|
|
157
|
-
const to = 'https://www.w3.org/ns/activitystreams#Public'
|
|
158
|
-
note = await context.sendNote(content, { to })
|
|
159
|
-
assert.ok(note)
|
|
160
|
-
assert.strictEqual(note.type, 'https://www.w3.org/ns/activitystreams#Note')
|
|
161
|
-
assert.strictEqual(await note.content.get(), `<p>${content}</p>`)
|
|
162
|
-
const iter = note.attributedTo[Symbol.iterator]()
|
|
163
|
-
const actor = iter.next().value
|
|
164
|
-
assert.strictEqual(actor.id, 'https://activitypubbot.example/user/test1')
|
|
165
|
-
const iter2 = note.to[Symbol.iterator]()
|
|
166
|
-
const addressee = iter2.next().value
|
|
167
|
-
assert.strictEqual(addressee.id, to)
|
|
168
|
-
assert.strictEqual(typeof note.published, 'object')
|
|
169
|
-
assert.strictEqual(typeof note.id, 'string')
|
|
170
|
-
await context.onIdle()
|
|
171
|
-
assert.strictEqual(postInbox.test2, 1)
|
|
172
|
-
const outbox = await actorStorage.getCollection(botName, 'outbox')
|
|
173
|
-
assert.strictEqual(outbox.totalItems, 1)
|
|
174
|
-
const inbox = await actorStorage.getCollection(botName, 'inbox')
|
|
175
|
-
assert.strictEqual(inbox.totalItems, 1)
|
|
176
|
-
followers = await actorStorage.getCollection(botName, 'followers')
|
|
177
|
-
assert.strictEqual(followers.totalItems, 1)
|
|
178
|
-
})
|
|
179
|
-
it('can like an object', async () => {
|
|
180
|
-
const id = 'https://social.example/user/test2/object/1'
|
|
181
|
-
const obj = await context.getObject(id)
|
|
182
|
-
await context.likeObject(obj)
|
|
183
|
-
await context.onIdle()
|
|
184
|
-
assert.strictEqual(postInbox.test2, 1)
|
|
185
|
-
const outbox = await actorStorage.getCollection(botName, 'outbox')
|
|
186
|
-
assert.strictEqual(outbox.totalItems, 2)
|
|
187
|
-
const inbox = await actorStorage.getCollection(botName, 'inbox')
|
|
188
|
-
assert.strictEqual(inbox.totalItems, 2)
|
|
189
|
-
const liked = await actorStorage.getCollection(botName, 'liked')
|
|
190
|
-
assert.strictEqual(liked.totalItems, 1)
|
|
191
|
-
})
|
|
192
|
-
it('can unlike an object', async () => {
|
|
193
|
-
const id = 'https://social.example/user/test2/object/1'
|
|
194
|
-
const obj = await context.getObject(id)
|
|
195
|
-
await context.unlikeObject(obj)
|
|
196
|
-
await context.onIdle()
|
|
197
|
-
assert.strictEqual(postInbox.test2, 1)
|
|
198
|
-
const outbox = await actorStorage.getCollection(botName, 'outbox')
|
|
199
|
-
assert.strictEqual(outbox.totalItems, 3)
|
|
200
|
-
const inbox = await actorStorage.getCollection(botName, 'inbox')
|
|
201
|
-
assert.strictEqual(inbox.totalItems, 3)
|
|
202
|
-
const liked = await actorStorage.getCollection(botName, 'liked')
|
|
203
|
-
assert.strictEqual(liked.totalItems, 0)
|
|
204
|
-
})
|
|
205
|
-
it('can follow an actor', async () => {
|
|
206
|
-
actor3 = await makeActor('test3')
|
|
207
|
-
await context.followActor(actor3)
|
|
208
|
-
await context.onIdle()
|
|
209
|
-
assert.strictEqual(postInbox.test3, 1)
|
|
210
|
-
const outbox = await actorStorage.getCollection(botName, 'outbox')
|
|
211
|
-
assert.strictEqual(outbox.totalItems, 4)
|
|
212
|
-
const inbox = await actorStorage.getCollection(botName, 'inbox')
|
|
213
|
-
assert.strictEqual(inbox.totalItems, 4)
|
|
214
|
-
const pendingFollowing = await actorStorage.getCollection(
|
|
215
|
-
botName,
|
|
216
|
-
'pendingFollowing'
|
|
217
|
-
)
|
|
218
|
-
assert.strictEqual(pendingFollowing.totalItems, 1)
|
|
219
|
-
})
|
|
220
|
-
it('can unfollow a pending actor', async () => {
|
|
221
|
-
await context.unfollowActor(actor3)
|
|
222
|
-
await context.onIdle()
|
|
223
|
-
assert.strictEqual(postInbox.test3, 1)
|
|
224
|
-
const outbox = await actorStorage.getCollection(botName, 'outbox')
|
|
225
|
-
assert.strictEqual(outbox.totalItems, 5)
|
|
226
|
-
const inbox = await actorStorage.getCollection(botName, 'inbox')
|
|
227
|
-
assert.strictEqual(inbox.totalItems, 5)
|
|
228
|
-
const pendingFollowing = await actorStorage.getCollection(
|
|
229
|
-
botName,
|
|
230
|
-
'pendingFollowing'
|
|
231
|
-
)
|
|
232
|
-
assert.strictEqual(pendingFollowing.totalItems, 0)
|
|
233
|
-
})
|
|
234
|
-
it('can unfollow a followed actor', async () => {
|
|
235
|
-
const actor4 = await makeActor('test4')
|
|
236
|
-
await actorStorage.addToCollection(botName, 'following', actor4)
|
|
237
|
-
let following = await actorStorage.getCollection(botName, 'following')
|
|
238
|
-
assert.strictEqual(following.totalItems, 1)
|
|
239
|
-
await context.unfollowActor(actor4)
|
|
240
|
-
await context.onIdle()
|
|
241
|
-
assert.strictEqual(postInbox.test4, 1)
|
|
242
|
-
const outbox = await actorStorage.getCollection(botName, 'outbox')
|
|
243
|
-
assert.strictEqual(outbox.totalItems, 6)
|
|
244
|
-
const inbox = await actorStorage.getCollection(botName, 'inbox')
|
|
245
|
-
assert.strictEqual(inbox.totalItems, 6)
|
|
246
|
-
following = await actorStorage.getCollection(botName, 'following')
|
|
247
|
-
assert.strictEqual(following.totalItems, 0)
|
|
248
|
-
})
|
|
249
|
-
it('can block an actor without a relationship', async () => {
|
|
250
|
-
let followers = await actorStorage.getCollection(botName, 'followers')
|
|
251
|
-
assert.strictEqual(followers.totalItems, 1)
|
|
252
|
-
actor5 = await makeActor('test5')
|
|
253
|
-
await context.blockActor(actor5)
|
|
254
|
-
await context.onIdle()
|
|
255
|
-
assert.ok(!postInbox.test5)
|
|
256
|
-
const outbox = await actorStorage.getCollection(botName, 'outbox')
|
|
257
|
-
assert.strictEqual(outbox.totalItems, 7)
|
|
258
|
-
const inbox = await actorStorage.getCollection(botName, 'inbox')
|
|
259
|
-
assert.strictEqual(inbox.totalItems, 7)
|
|
260
|
-
const blocked = await actorStorage.getCollection(botName, 'blocked')
|
|
261
|
-
assert.strictEqual(blocked.totalItems, 1)
|
|
262
|
-
followers = await actorStorage.getCollection(botName, 'followers')
|
|
263
|
-
assert.strictEqual(followers.totalItems, 1)
|
|
264
|
-
})
|
|
265
|
-
it('can unblock an actor without a relationship', async () => {
|
|
266
|
-
let followers = await actorStorage.getCollection(botName, 'followers')
|
|
267
|
-
assert.strictEqual(followers.totalItems, 1)
|
|
268
|
-
await context.unblockActor(actor5)
|
|
269
|
-
await context.onIdle()
|
|
270
|
-
assert.ok(!postInbox.test5)
|
|
271
|
-
const outbox = await actorStorage.getCollection(botName, 'outbox')
|
|
272
|
-
assert.strictEqual(outbox.totalItems, 8)
|
|
273
|
-
const inbox = await actorStorage.getCollection(botName, 'inbox')
|
|
274
|
-
assert.strictEqual(inbox.totalItems, 8)
|
|
275
|
-
const blocked = await actorStorage.getCollection(botName, 'blocked')
|
|
276
|
-
assert.strictEqual(blocked.totalItems, 0)
|
|
277
|
-
followers = await actorStorage.getCollection(botName, 'followers')
|
|
278
|
-
assert.strictEqual(followers.totalItems, 1)
|
|
279
|
-
})
|
|
280
|
-
it('can block an actor with a relationship', async () => {
|
|
281
|
-
actor6 = await makeActor('test6')
|
|
282
|
-
let followers = await actorStorage.getCollection(botName, 'followers')
|
|
283
|
-
assert.strictEqual(followers.totalItems, 1)
|
|
284
|
-
await actorStorage.addToCollection(botName, 'following', actor6)
|
|
285
|
-
await actorStorage.addToCollection(botName, 'followers', actor6)
|
|
286
|
-
followers = await actorStorage.getCollection(botName, 'followers')
|
|
287
|
-
assert.strictEqual(followers.totalItems, 2)
|
|
288
|
-
await context.blockActor(actor6)
|
|
289
|
-
await context.onIdle()
|
|
290
|
-
assert.ok(!postInbox.test6)
|
|
291
|
-
const outbox = await actorStorage.getCollection(botName, 'outbox')
|
|
292
|
-
assert.strictEqual(outbox.totalItems, 9)
|
|
293
|
-
const inbox = await actorStorage.getCollection(botName, 'inbox')
|
|
294
|
-
assert.strictEqual(inbox.totalItems, 9)
|
|
295
|
-
const blocked = await actorStorage.getCollection(botName, 'blocked')
|
|
296
|
-
assert.strictEqual(blocked.totalItems, 1)
|
|
297
|
-
const following = await actorStorage.getCollection(botName, 'following')
|
|
298
|
-
assert.strictEqual(following.totalItems, 0)
|
|
299
|
-
followers = await actorStorage.getCollection(botName, 'followers')
|
|
300
|
-
assert.strictEqual(followers.totalItems, 1)
|
|
301
|
-
})
|
|
302
|
-
it('can unblock an actor with a former relationship', async () => {
|
|
303
|
-
await context.unblockActor(actor6)
|
|
304
|
-
assert.ok(!postInbox.test6)
|
|
305
|
-
const outbox = await actorStorage.getCollection(botName, 'outbox')
|
|
306
|
-
assert.strictEqual(outbox.totalItems, 10)
|
|
307
|
-
const inbox = await actorStorage.getCollection(botName, 'inbox')
|
|
308
|
-
assert.strictEqual(inbox.totalItems, 10)
|
|
309
|
-
const blocked = await actorStorage.getCollection(botName, 'blocked')
|
|
310
|
-
assert.strictEqual(blocked.totalItems, 0)
|
|
311
|
-
const following = await actorStorage.getCollection(botName, 'following')
|
|
312
|
-
assert.strictEqual(following.totalItems, 0)
|
|
313
|
-
const followers = await actorStorage.getCollection(botName, 'followers')
|
|
314
|
-
assert.strictEqual(followers.totalItems, 1)
|
|
315
|
-
})
|
|
316
|
-
it('can update a note', async () => {
|
|
317
|
-
const content = 'Hello World 2'
|
|
318
|
-
await context.updateNote(note, content)
|
|
319
|
-
await context.onIdle()
|
|
320
|
-
assert.strictEqual(postInbox.test2, 1)
|
|
321
|
-
const outbox = await actorStorage.getCollection(botName, 'outbox')
|
|
322
|
-
assert.strictEqual(outbox.totalItems, 11)
|
|
323
|
-
const inbox = await actorStorage.getCollection(botName, 'inbox')
|
|
324
|
-
assert.strictEqual(inbox.totalItems, 11)
|
|
325
|
-
const copy = await context.getObject(note.id)
|
|
326
|
-
assert.strictEqual(copy.content.get(), content)
|
|
327
|
-
})
|
|
328
|
-
it('can delete a note', async () => {
|
|
329
|
-
await context.deleteNote(note)
|
|
330
|
-
await context.onIdle()
|
|
331
|
-
assert.strictEqual(postInbox.test2, 1)
|
|
332
|
-
const outbox = await actorStorage.getCollection(botName, 'outbox')
|
|
333
|
-
assert.strictEqual(outbox.totalItems, 12)
|
|
334
|
-
const inbox = await actorStorage.getCollection(botName, 'inbox')
|
|
335
|
-
assert.strictEqual(inbox.totalItems, 12)
|
|
336
|
-
const copy = await context.getObject(note.id)
|
|
337
|
-
assert.ok(copy)
|
|
338
|
-
assert.strictEqual(
|
|
339
|
-
copy.type,
|
|
340
|
-
'https://www.w3.org/ns/activitystreams#Tombstone'
|
|
341
|
-
)
|
|
342
|
-
assert.ok(copy.deleted)
|
|
343
|
-
// FIXME: check for formerType when activitystrea.ms supports it
|
|
344
|
-
})
|
|
345
|
-
it('fails when liking an object twice', async () => {
|
|
346
|
-
const id = 'https://social.example/user/test2/object/2'
|
|
347
|
-
const obj = await context.getObject(id)
|
|
348
|
-
await context.likeObject(obj)
|
|
349
|
-
await context.onIdle()
|
|
350
|
-
try {
|
|
351
|
-
await context.likeObject(obj)
|
|
352
|
-
assert.fail('Expected an error')
|
|
353
|
-
} catch (error) {
|
|
354
|
-
assert.ok(true)
|
|
355
|
-
}
|
|
356
|
-
})
|
|
357
|
-
it('fails when unliking an object never seen before', async () => {
|
|
358
|
-
const id = 'https://social.example/user/test2/object/3'
|
|
359
|
-
const obj = await context.getObject(id)
|
|
360
|
-
try {
|
|
361
|
-
await context.unlikeObject(obj)
|
|
362
|
-
assert.fail('Expected an error')
|
|
363
|
-
} catch (error) {
|
|
364
|
-
assert.ok(true)
|
|
365
|
-
}
|
|
366
|
-
})
|
|
367
|
-
it('can send a reply', async () => {
|
|
368
|
-
const actor3 = await makeActor('test3')
|
|
369
|
-
const object = await makeObject('test7', 'Note', 1)
|
|
370
|
-
const content = '@test2@social.example hello back'
|
|
371
|
-
const to = [actor3.id, 'as:Public']
|
|
372
|
-
const inReplyTo = object.id
|
|
373
|
-
note = await context.sendNote(content, { to, inReplyTo })
|
|
374
|
-
assert.ok(note)
|
|
375
|
-
assert.strictEqual(note.type, 'https://www.w3.org/ns/activitystreams#Note')
|
|
376
|
-
assert.strictEqual(
|
|
377
|
-
await note.content.get(),
|
|
378
|
-
'<p>' +
|
|
379
|
-
'<a href="https://social.example/profile/test2">' +
|
|
380
|
-
'@test2@social.example' +
|
|
381
|
-
'</a> hello back</p>'
|
|
382
|
-
)
|
|
383
|
-
const iter = note.attributedTo[Symbol.iterator]()
|
|
384
|
-
const actor = iter.next().value
|
|
385
|
-
assert.strictEqual(actor.id, 'https://activitypubbot.example/user/test1')
|
|
386
|
-
const iter2 = note.to[Symbol.iterator]()
|
|
387
|
-
const addressee = iter2.next().value
|
|
388
|
-
assert.strictEqual(addressee.id, actor3.id)
|
|
389
|
-
assert.strictEqual(typeof note.published, 'object')
|
|
390
|
-
assert.strictEqual(typeof note.id, 'string')
|
|
391
|
-
const tag = note.tag.first
|
|
392
|
-
assert.strictEqual(
|
|
393
|
-
tag.type,
|
|
394
|
-
'https://www.w3.org/ns/activitystreams#Mention'
|
|
395
|
-
)
|
|
396
|
-
assert.strictEqual(tag.href, 'https://social.example/profile/test2')
|
|
397
|
-
await context.onIdle()
|
|
398
|
-
})
|
|
399
|
-
it('can send a tag', async () => {
|
|
400
|
-
const content = 'Thank you Sally! #gratitude'
|
|
401
|
-
const to = 'as:Public'
|
|
402
|
-
note = await context.sendNote(content, { to })
|
|
403
|
-
assert.ok(note)
|
|
404
|
-
assert.strictEqual(
|
|
405
|
-
note.content.get(),
|
|
406
|
-
'<p>Thank you Sally! ' +
|
|
407
|
-
'<a href="https://activitypubbot.example/tag/gratitude">#gratitude</a></p>'
|
|
408
|
-
)
|
|
409
|
-
const tag = note.tag.first
|
|
410
|
-
assert.strictEqual(
|
|
411
|
-
tag.type,
|
|
412
|
-
'https://www.w3.org/ns/activitystreams#Hashtag'
|
|
413
|
-
)
|
|
414
|
-
assert.strictEqual(tag.name.get(), '#gratitude')
|
|
415
|
-
})
|
|
416
|
-
it('can send an url', async () => {
|
|
417
|
-
const content = 'Check out this link: https://example.com'
|
|
418
|
-
const to = 'as:Public'
|
|
419
|
-
note = await context.sendNote(content, { to })
|
|
420
|
-
assert.ok(note)
|
|
421
|
-
assert.strictEqual(
|
|
422
|
-
note.content.get(),
|
|
423
|
-
'<p>Check out this link: ' +
|
|
424
|
-
'<a href="https://example.com">https://example.com</a></p>'
|
|
425
|
-
)
|
|
426
|
-
})
|
|
427
|
-
it('can get an actor ID from a Webfinger ID', async () => {
|
|
428
|
-
const webfinger = 'test3@social.example'
|
|
429
|
-
const actorId = await context.toActorId(webfinger)
|
|
430
|
-
assert.ok(actorId)
|
|
431
|
-
assert.strictEqual(typeof actorId, 'string')
|
|
432
|
-
assert.strictEqual(actorId, 'https://social.example/user/test3')
|
|
433
|
-
})
|
|
434
|
-
|
|
435
|
-
it('can get a Webfinger ID from an actor ID', async () => {
|
|
436
|
-
const actorId = 'https://social.example/user/test4'
|
|
437
|
-
const webfinger = await context.toWebfinger(actorId)
|
|
438
|
-
assert.ok(webfinger)
|
|
439
|
-
assert.strictEqual(typeof webfinger, 'string')
|
|
440
|
-
assert.strictEqual(webfinger, 'test4@social.example')
|
|
441
|
-
})
|
|
442
|
-
|
|
443
|
-
it('can reply to a note', async () => {
|
|
444
|
-
const noteIn = await makeObject('test5', 'Note', 1)
|
|
445
|
-
const note = await context.sendReply('@test5@social.example OK', noteIn)
|
|
446
|
-
assert.ok(note)
|
|
447
|
-
assert.strictEqual(note.type, AS2_NS + 'Note')
|
|
448
|
-
const actor = note.attributedTo?.first
|
|
449
|
-
assert.strictEqual(actor.id, 'https://activitypubbot.example/user/test1')
|
|
450
|
-
const recipients = [
|
|
451
|
-
'https://social.example/user/test5',
|
|
452
|
-
'https://www.w3.org/ns/activitystreams#Public'
|
|
453
|
-
]
|
|
454
|
-
for (const addressee in note.to) {
|
|
455
|
-
assert.ok(recipients.includes(addressee.id))
|
|
456
|
-
}
|
|
457
|
-
await context.onIdle()
|
|
458
|
-
assert.strictEqual(postInbox.test5, 1)
|
|
459
|
-
})
|
|
460
|
-
|
|
461
|
-
it('can reply to self', async () => {
|
|
462
|
-
const original = await context.sendNote("s'alright?", { to: 'as:Public' })
|
|
463
|
-
const reply = await context.sendReply("@test1@activitypubbot.example s'alright.", original)
|
|
464
|
-
assert.ok(reply)
|
|
465
|
-
assert.strictEqual(reply.type, AS2_NS + 'Note')
|
|
466
|
-
const actor = reply.attributedTo?.first
|
|
467
|
-
assert.strictEqual(actor.id, 'https://activitypubbot.example/user/test1')
|
|
468
|
-
const recipients = [
|
|
469
|
-
'https://activitypubbot.example/user/test1',
|
|
470
|
-
'https://www.w3.org/ns/activitystreams#Public'
|
|
471
|
-
]
|
|
472
|
-
for (const addressee in reply.to) {
|
|
473
|
-
assert.ok(recipients.includes(addressee.id))
|
|
474
|
-
}
|
|
475
|
-
await context.onIdle()
|
|
476
|
-
assert.strictEqual(postInbox.test2, 2)
|
|
477
|
-
let found = false
|
|
478
|
-
for await (const item of actorStorage.items(botName, 'inbox')) {
|
|
479
|
-
const full = await objectStorage.read(item.id)
|
|
480
|
-
if (full.object?.first?.id === reply.id) {
|
|
481
|
-
found = true
|
|
482
|
-
break
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
assert.ok(found)
|
|
486
|
-
})
|
|
487
|
-
|
|
488
|
-
it('does local delivery', async () => {
|
|
489
|
-
const note = await context.sendNote('say OK please',
|
|
490
|
-
{ to: 'https://activitypubbot.example/user/ok' }
|
|
491
|
-
)
|
|
492
|
-
await context.onIdle()
|
|
493
|
-
assert.ok(note)
|
|
494
|
-
let found = null
|
|
495
|
-
for await (const item of actorStorage.items('ok', 'inbox')) {
|
|
496
|
-
const full = await objectStorage.read(item.id)
|
|
497
|
-
if (full.object?.first?.id === note.id) {
|
|
498
|
-
found = full
|
|
499
|
-
break
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
assert.ok(found)
|
|
503
|
-
for await (const item of actorStorage.items(botName, 'inbox')) {
|
|
504
|
-
const full = await objectStorage.read(item.id)
|
|
505
|
-
if (full.object?.first?.inReplyTo?.first?.id === note.id) {
|
|
506
|
-
found = full
|
|
507
|
-
break
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
assert.ok(found)
|
|
511
|
-
})
|
|
512
|
-
|
|
513
|
-
it('has a working logger', async () => {
|
|
514
|
-
assert.ok(context.logger)
|
|
515
|
-
assert.doesNotThrow(() => context.logger.debug('debug'))
|
|
516
|
-
assert.doesNotThrow(() => context.logger.info('info'))
|
|
517
|
-
assert.doesNotThrow(() => context.logger.warn('warn'))
|
|
518
|
-
assert.doesNotThrow(() => context.logger.error('error'))
|
|
519
|
-
})
|
|
520
|
-
|
|
521
|
-
describe('reactions in new content', async () => {
|
|
522
|
-
let note = null
|
|
523
|
-
before(async () => {
|
|
524
|
-
const content = 'Hello World'
|
|
525
|
-
const to = 'https://www.w3.org/ns/activitystreams#Public'
|
|
526
|
-
note = await context.sendNote(content, { to })
|
|
527
|
-
})
|
|
528
|
-
|
|
529
|
-
it('has a replies property', async () => {
|
|
530
|
-
const repliesProp = note.get('replies')
|
|
531
|
-
assert.ok(repliesProp)
|
|
532
|
-
const replieses = Array.from(repliesProp)
|
|
533
|
-
assert.strictEqual(replieses.length, 1)
|
|
534
|
-
const replies = replieses[0]
|
|
535
|
-
assert.ok(replies.id)
|
|
536
|
-
})
|
|
537
|
-
|
|
538
|
-
it('has a shares property', async () => {
|
|
539
|
-
const sharesProp = note.get('shares')
|
|
540
|
-
assert.ok(sharesProp)
|
|
541
|
-
const shareses = Array.from(sharesProp)
|
|
542
|
-
assert.strictEqual(shareses.length, 1)
|
|
543
|
-
const shares = shareses[0]
|
|
544
|
-
assert.ok(shares.id)
|
|
545
|
-
})
|
|
546
|
-
|
|
547
|
-
it('has a likes property', async () => {
|
|
548
|
-
const likesProp = note.get('likes')
|
|
549
|
-
assert.ok(likesProp)
|
|
550
|
-
const likeses = Array.from(likesProp)
|
|
551
|
-
assert.strictEqual(likeses.length, 1)
|
|
552
|
-
const likes = likeses[0]
|
|
553
|
-
assert.ok(likes.id)
|
|
554
|
-
})
|
|
555
|
-
})
|
|
556
|
-
|
|
557
|
-
describe('threads in new content', async () => {
|
|
558
|
-
let note = null
|
|
559
|
-
before(async () => {
|
|
560
|
-
const content = 'Hello World'
|
|
561
|
-
const to = 'https://www.w3.org/ns/activitystreams#Public'
|
|
562
|
-
note = await context.sendNote(content, { to })
|
|
563
|
-
})
|
|
564
|
-
|
|
565
|
-
it('has a thread property', async () => {
|
|
566
|
-
const threadProp = note.get('https://purl.archive.org/socialweb/thread#thread')
|
|
567
|
-
assert.ok(threadProp)
|
|
568
|
-
const threads = Array.from(threadProp)
|
|
569
|
-
assert.strictEqual(threads.length, 1)
|
|
570
|
-
const thread = threads[0]
|
|
571
|
-
assert.ok(thread.id)
|
|
572
|
-
})
|
|
573
|
-
|
|
574
|
-
it('has a context property', async () => {
|
|
575
|
-
const contextProp = note.get('context')
|
|
576
|
-
assert.ok(contextProp)
|
|
577
|
-
const contexts = Array.from(contextProp)
|
|
578
|
-
assert.strictEqual(contexts.length, 1)
|
|
579
|
-
const context = contexts[0]
|
|
580
|
-
assert.ok(context.id)
|
|
581
|
-
})
|
|
582
|
-
|
|
583
|
-
it('has an ostatus:conversation property', async () => {
|
|
584
|
-
const conversationProp = note.get('http://ostatus.org/schema/1.0/conversation')
|
|
585
|
-
assert.ok(conversationProp)
|
|
586
|
-
const conversations = Array.from(conversationProp)
|
|
587
|
-
assert.strictEqual(conversations.length, 1)
|
|
588
|
-
const conversation = conversations[0]
|
|
589
|
-
assert.ok(conversation)
|
|
590
|
-
})
|
|
591
|
-
})
|
|
592
|
-
|
|
593
|
-
describe('reactions in a reply', async () => {
|
|
594
|
-
let note = null
|
|
595
|
-
before(async () => {
|
|
596
|
-
const noteIn = await makeObject('test8', 'Note', 1)
|
|
597
|
-
const content = '@test8@social.example OK'
|
|
598
|
-
note = await context.sendReply(content, noteIn)
|
|
599
|
-
})
|
|
600
|
-
|
|
601
|
-
it('has a replies property', async () => {
|
|
602
|
-
const repliesProp = note.get('replies')
|
|
603
|
-
assert.ok(repliesProp)
|
|
604
|
-
const replieses = Array.from(repliesProp)
|
|
605
|
-
assert.strictEqual(replieses.length, 1)
|
|
606
|
-
const replies = replieses[0]
|
|
607
|
-
assert.ok(replies.id)
|
|
608
|
-
})
|
|
609
|
-
|
|
610
|
-
it('has a shares property', async () => {
|
|
611
|
-
const sharesProp = note.get('shares')
|
|
612
|
-
assert.ok(sharesProp)
|
|
613
|
-
const shareses = Array.from(sharesProp)
|
|
614
|
-
assert.strictEqual(shareses.length, 1)
|
|
615
|
-
const shares = shareses[0]
|
|
616
|
-
assert.ok(shares.id)
|
|
617
|
-
})
|
|
618
|
-
|
|
619
|
-
it('has a likes property', async () => {
|
|
620
|
-
const likesProp = note.get('likes')
|
|
621
|
-
assert.ok(likesProp)
|
|
622
|
-
const likeses = Array.from(likesProp)
|
|
623
|
-
assert.strictEqual(likeses.length, 1)
|
|
624
|
-
const likes = likeses[0]
|
|
625
|
-
assert.ok(likes.id)
|
|
626
|
-
})
|
|
627
|
-
})
|
|
628
|
-
|
|
629
|
-
describe('threads in a reply', async () => {
|
|
630
|
-
let note = null
|
|
631
|
-
let noteIn = null
|
|
632
|
-
before(async () => {
|
|
633
|
-
noteIn = await makeObject('test8', 'Note', 2)
|
|
634
|
-
const content = '@test8@social.example OK'
|
|
635
|
-
note = await context.sendReply(content, noteIn)
|
|
636
|
-
})
|
|
637
|
-
|
|
638
|
-
it('has a matching thread property', async () => {
|
|
639
|
-
const propName = 'https://purl.archive.org/socialweb/thread#thread'
|
|
640
|
-
const threadProp = note.get(propName)
|
|
641
|
-
assert.ok(threadProp)
|
|
642
|
-
const threads = Array.from(threadProp)
|
|
643
|
-
assert.strictEqual(threads.length, 1)
|
|
644
|
-
const thread = threads[0]
|
|
645
|
-
assert.ok(thread.id)
|
|
646
|
-
const threadIn = Array.from(noteIn.get(propName))[0]
|
|
647
|
-
assert.strictEqual(thread.id, threadIn.id)
|
|
648
|
-
})
|
|
649
|
-
|
|
650
|
-
it('has a matching context property', async () => {
|
|
651
|
-
const propName = 'context'
|
|
652
|
-
const contextProp = note.get(propName)
|
|
653
|
-
assert.ok(contextProp)
|
|
654
|
-
const contexts = Array.from(contextProp)
|
|
655
|
-
assert.strictEqual(contexts.length, 1)
|
|
656
|
-
const context = contexts[0]
|
|
657
|
-
assert.ok(context.id)
|
|
658
|
-
const contextIn = Array.from(noteIn.get(propName))[0]
|
|
659
|
-
assert.strictEqual(context.id, contextIn.id)
|
|
660
|
-
})
|
|
661
|
-
|
|
662
|
-
it('has a matching ostatus:conversation property', async () => {
|
|
663
|
-
const propName = 'http://ostatus.org/schema/1.0/conversation'
|
|
664
|
-
const conversationProp = note.get(propName)
|
|
665
|
-
assert.ok(conversationProp)
|
|
666
|
-
const conversations = Array.from(conversationProp)
|
|
667
|
-
assert.strictEqual(conversations.length, 1)
|
|
668
|
-
const conversation = conversations[0]
|
|
669
|
-
assert.ok(conversation)
|
|
670
|
-
const conversationIn = Array.from(noteIn.get(propName))[0]
|
|
671
|
-
assert.strictEqual(context.id, conversationIn.id)
|
|
672
|
-
})
|
|
673
|
-
})
|
|
674
|
-
|
|
675
|
-
it('can duplicate', async () => {
|
|
676
|
-
const username = 'dupe1'
|
|
677
|
-
let dupe = null
|
|
678
|
-
dupe = await context.duplicate(username)
|
|
679
|
-
assert.ok(dupe)
|
|
680
|
-
assert.strictEqual(dupe.botId, username)
|
|
681
|
-
})
|
|
682
|
-
|
|
683
|
-
it('can announce an object', async () => {
|
|
684
|
-
const username = 'test9'
|
|
685
|
-
const type = 'Note'
|
|
686
|
-
const num = 3035
|
|
687
|
-
|
|
688
|
-
const id = nockFormat({ username, type, num })
|
|
689
|
-
|
|
690
|
-
const obj = await context.getObject(id)
|
|
691
|
-
const activity = await context.announceObject(obj)
|
|
692
|
-
|
|
693
|
-
assert.ok(activity)
|
|
694
|
-
|
|
695
|
-
assert.strictEqual(activity.type, `${AS2_NS}Announce`)
|
|
696
|
-
assert.strictEqual(activity.object?.first?.id, obj.id)
|
|
697
|
-
|
|
698
|
-
await context.onIdle()
|
|
699
|
-
|
|
700
|
-
assert.strictEqual(postInbox[username], 1)
|
|
701
|
-
|
|
702
|
-
let foundInOutbox = false
|
|
703
|
-
for await (const item of actorStorage.items(botName, 'outbox')) {
|
|
704
|
-
if (item.id === activity.id) {
|
|
705
|
-
foundInOutbox = true
|
|
706
|
-
break
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
assert.ok(foundInOutbox)
|
|
710
|
-
|
|
711
|
-
let foundInInbox = false
|
|
712
|
-
for await (const item of actorStorage.items(botName, 'inbox')) {
|
|
713
|
-
if (item.id === activity.id) {
|
|
714
|
-
foundInInbox = true
|
|
715
|
-
break
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
assert.ok(foundInInbox)
|
|
719
|
-
})
|
|
720
|
-
})
|