@fedify/testing 1.8.1-pr.283.1138
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/LICENSE +20 -0
- package/README.md +101 -0
- package/context.ts +154 -0
- package/deno.json +41 -0
- package/dist/mod.d.ts +274 -0
- package/dist/mod.js +612 -0
- package/docloader.ts +8 -0
- package/mock.test.ts +361 -0
- package/mock.ts +953 -0
- package/mod.ts +12 -0
- package/package.json +64 -0
- package/tsdown.config.ts +16 -0
package/dist/mod.js
ADDED
|
@@ -0,0 +1,612 @@
|
|
|
1
|
+
import { trace } from "@opentelemetry/api";
|
|
2
|
+
import { RouterError } from "@fedify/fedify/federation";
|
|
3
|
+
import { lookupObject, traverseCollection } from "@fedify/fedify/vocab";
|
|
4
|
+
import { lookupWebFinger } from "@fedify/fedify/webfinger";
|
|
5
|
+
|
|
6
|
+
//#region docloader.ts
|
|
7
|
+
const mockDocumentLoader = async (url) => ({
|
|
8
|
+
contextUrl: null,
|
|
9
|
+
document: {},
|
|
10
|
+
documentUrl: url
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region context.ts
|
|
15
|
+
function createContext(values) {
|
|
16
|
+
const { federation, url = new URL("http://example.com/"), canonicalOrigin, data, documentLoader, contextLoader, tracerProvider, clone, getNodeInfoUri, getActorUri, getObjectUri, getOutboxUri, getInboxUri, getFollowingUri, getFollowersUri, getLikedUri, getFeaturedUri, getFeaturedTagsUri, parseUri, getActorKeyPairs, getDocumentLoader, lookupObject: lookupObject$1, traverseCollection: traverseCollection$1, lookupNodeInfo, lookupWebFinger: lookupWebFinger$1, sendActivity, routeActivity } = values;
|
|
17
|
+
function throwRouteError() {
|
|
18
|
+
throw new RouterError("Not implemented");
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
federation,
|
|
22
|
+
data,
|
|
23
|
+
origin: url.origin,
|
|
24
|
+
canonicalOrigin: canonicalOrigin ?? url.origin,
|
|
25
|
+
host: url.host,
|
|
26
|
+
hostname: url.hostname,
|
|
27
|
+
documentLoader: documentLoader ?? mockDocumentLoader,
|
|
28
|
+
contextLoader: contextLoader ?? mockDocumentLoader,
|
|
29
|
+
tracerProvider: tracerProvider ?? trace.getTracerProvider(),
|
|
30
|
+
clone: clone ?? ((data$1) => createContext({
|
|
31
|
+
...values,
|
|
32
|
+
data: data$1
|
|
33
|
+
})),
|
|
34
|
+
getNodeInfoUri: getNodeInfoUri ?? throwRouteError,
|
|
35
|
+
getActorUri: getActorUri ?? throwRouteError,
|
|
36
|
+
getObjectUri: getObjectUri ?? throwRouteError,
|
|
37
|
+
getOutboxUri: getOutboxUri ?? throwRouteError,
|
|
38
|
+
getInboxUri: getInboxUri ?? throwRouteError,
|
|
39
|
+
getFollowingUri: getFollowingUri ?? throwRouteError,
|
|
40
|
+
getFollowersUri: getFollowersUri ?? throwRouteError,
|
|
41
|
+
getLikedUri: getLikedUri ?? throwRouteError,
|
|
42
|
+
getFeaturedUri: getFeaturedUri ?? throwRouteError,
|
|
43
|
+
getFeaturedTagsUri: getFeaturedTagsUri ?? throwRouteError,
|
|
44
|
+
parseUri: parseUri ?? ((_uri) => {
|
|
45
|
+
throw new Error("Not implemented");
|
|
46
|
+
}),
|
|
47
|
+
getDocumentLoader: getDocumentLoader ?? ((_params) => {
|
|
48
|
+
throw new Error("Not implemented");
|
|
49
|
+
}),
|
|
50
|
+
getActorKeyPairs: getActorKeyPairs ?? ((_handle) => Promise.resolve([])),
|
|
51
|
+
lookupObject: lookupObject$1 ?? ((uri, options = {}) => {
|
|
52
|
+
return lookupObject(uri, {
|
|
53
|
+
documentLoader: options.documentLoader ?? documentLoader ?? mockDocumentLoader,
|
|
54
|
+
contextLoader: options.contextLoader ?? contextLoader ?? mockDocumentLoader
|
|
55
|
+
});
|
|
56
|
+
}),
|
|
57
|
+
traverseCollection: traverseCollection$1 ?? ((collection, options = {}) => {
|
|
58
|
+
return traverseCollection(collection, {
|
|
59
|
+
documentLoader: options.documentLoader ?? documentLoader ?? mockDocumentLoader,
|
|
60
|
+
contextLoader: options.contextLoader ?? contextLoader ?? mockDocumentLoader
|
|
61
|
+
});
|
|
62
|
+
}),
|
|
63
|
+
lookupNodeInfo: lookupNodeInfo ?? ((_params) => {
|
|
64
|
+
throw new Error("Not implemented");
|
|
65
|
+
}),
|
|
66
|
+
lookupWebFinger: lookupWebFinger$1 ?? ((resource, options = {}) => {
|
|
67
|
+
return lookupWebFinger(resource, options);
|
|
68
|
+
}),
|
|
69
|
+
sendActivity: sendActivity ?? ((_params) => {
|
|
70
|
+
throw new Error("Not implemented");
|
|
71
|
+
}),
|
|
72
|
+
routeActivity: routeActivity ?? ((_params) => {
|
|
73
|
+
throw new Error("Not implemented");
|
|
74
|
+
})
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function createRequestContext(args) {
|
|
78
|
+
return {
|
|
79
|
+
...createContext(args),
|
|
80
|
+
clone: args.clone ?? ((data) => createRequestContext({
|
|
81
|
+
...args,
|
|
82
|
+
data
|
|
83
|
+
})),
|
|
84
|
+
request: args.request ?? new Request(args.url),
|
|
85
|
+
url: args.url,
|
|
86
|
+
getActor: args.getActor ?? (() => Promise.resolve(null)),
|
|
87
|
+
getObject: args.getObject ?? (() => Promise.resolve(null)),
|
|
88
|
+
getSignedKey: args.getSignedKey ?? (() => Promise.resolve(null)),
|
|
89
|
+
getSignedKeyOwner: args.getSignedKeyOwner ?? (() => Promise.resolve(null)),
|
|
90
|
+
sendActivity: args.sendActivity ?? ((_params) => {
|
|
91
|
+
throw new Error("Not implemented");
|
|
92
|
+
})
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function createInboxContext(args) {
|
|
96
|
+
return {
|
|
97
|
+
...createContext(args),
|
|
98
|
+
clone: args.clone ?? ((data) => createInboxContext({
|
|
99
|
+
...args,
|
|
100
|
+
data
|
|
101
|
+
})),
|
|
102
|
+
recipient: args.recipient ?? null,
|
|
103
|
+
forwardActivity: args.forwardActivity ?? ((_params) => {
|
|
104
|
+
throw new Error("Not implemented");
|
|
105
|
+
})
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region mock.ts
|
|
111
|
+
/**
|
|
112
|
+
* Helper function to expand URI templates with values.
|
|
113
|
+
* Supports simple placeholders like {identifier}, {handle}, etc.
|
|
114
|
+
* @param template The URI template pattern
|
|
115
|
+
* @param values The values to substitute
|
|
116
|
+
* @returns The expanded URI path
|
|
117
|
+
*/
|
|
118
|
+
function expandUriTemplate(template, values) {
|
|
119
|
+
return template.replace(/{([^}]+)}/g, (match, key) => {
|
|
120
|
+
return values[key] || match;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* A mock implementation of the {@link Federation} interface for unit testing.
|
|
125
|
+
* This class provides a way to test Fedify applications without needing
|
|
126
|
+
* a real federation setup.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```typescript
|
|
130
|
+
* import { Create } from "@fedify/fedify/vocab";
|
|
131
|
+
* import { MockFederation } from "@fedify/testing";
|
|
132
|
+
*
|
|
133
|
+
* // Create a mock federation with contextData
|
|
134
|
+
* const federation = new MockFederation<{ userId: string }>({
|
|
135
|
+
* contextData: { userId: "test-user" }
|
|
136
|
+
* });
|
|
137
|
+
*
|
|
138
|
+
* // Set up inbox listeners
|
|
139
|
+
* federation
|
|
140
|
+
* .setInboxListeners("/users/{identifier}/inbox")
|
|
141
|
+
* .on(Create, async (ctx, activity) => {
|
|
142
|
+
* console.log("Received:", activity);
|
|
143
|
+
* });
|
|
144
|
+
*
|
|
145
|
+
* // Simulate receiving an activity
|
|
146
|
+
* const createActivity = new Create({
|
|
147
|
+
* id: new URL("https://example.com/create/1"),
|
|
148
|
+
* actor: new URL("https://example.com/users/alice")
|
|
149
|
+
* });
|
|
150
|
+
* await federation.receiveActivity(createActivity);
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* @typeParam TContextData The context data to pass to the {@link Context}.
|
|
154
|
+
* @since 1.8.0
|
|
155
|
+
*/
|
|
156
|
+
var MockFederation = class {
|
|
157
|
+
sentActivities = [];
|
|
158
|
+
queueStarted = false;
|
|
159
|
+
activeQueues = /* @__PURE__ */ new Set();
|
|
160
|
+
sentCounter = 0;
|
|
161
|
+
nodeInfoDispatcher;
|
|
162
|
+
actorDispatchers = /* @__PURE__ */ new Map();
|
|
163
|
+
actorPath;
|
|
164
|
+
inboxPath;
|
|
165
|
+
outboxPath;
|
|
166
|
+
followingPath;
|
|
167
|
+
followersPath;
|
|
168
|
+
likedPath;
|
|
169
|
+
featuredPath;
|
|
170
|
+
featuredTagsPath;
|
|
171
|
+
nodeInfoPath;
|
|
172
|
+
sharedInboxPath;
|
|
173
|
+
objectPaths = /* @__PURE__ */ new Map();
|
|
174
|
+
objectDispatchers = /* @__PURE__ */ new Map();
|
|
175
|
+
inboxDispatcher;
|
|
176
|
+
outboxDispatcher;
|
|
177
|
+
followingDispatcher;
|
|
178
|
+
followersDispatcher;
|
|
179
|
+
likedDispatcher;
|
|
180
|
+
featuredDispatcher;
|
|
181
|
+
featuredTagsDispatcher;
|
|
182
|
+
inboxListeners = /* @__PURE__ */ new Map();
|
|
183
|
+
contextData;
|
|
184
|
+
receivedActivities = [];
|
|
185
|
+
constructor(options = {}) {
|
|
186
|
+
this.options = options;
|
|
187
|
+
this.contextData = options.contextData;
|
|
188
|
+
}
|
|
189
|
+
setNodeInfoDispatcher(path, dispatcher) {
|
|
190
|
+
this.nodeInfoDispatcher = dispatcher;
|
|
191
|
+
this.nodeInfoPath = path;
|
|
192
|
+
}
|
|
193
|
+
setActorDispatcher(path, dispatcher) {
|
|
194
|
+
this.actorDispatchers.set(path, dispatcher);
|
|
195
|
+
this.actorPath = path;
|
|
196
|
+
return {
|
|
197
|
+
setKeyPairsDispatcher: () => this,
|
|
198
|
+
mapHandle: () => this,
|
|
199
|
+
mapAlias: () => this,
|
|
200
|
+
authorize: () => this
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
setObjectDispatcher(cls, path, dispatcher) {
|
|
204
|
+
this.objectDispatchers.set(path, dispatcher);
|
|
205
|
+
this.objectPaths.set(cls.typeId.href, path);
|
|
206
|
+
return { authorize: () => this };
|
|
207
|
+
}
|
|
208
|
+
setInboxDispatcher(_path, dispatcher) {
|
|
209
|
+
this.inboxDispatcher = dispatcher;
|
|
210
|
+
return {
|
|
211
|
+
setCounter: () => this,
|
|
212
|
+
setFirstCursor: () => this,
|
|
213
|
+
setLastCursor: () => this,
|
|
214
|
+
authorize: () => this
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
setOutboxDispatcher(path, dispatcher) {
|
|
218
|
+
this.outboxDispatcher = dispatcher;
|
|
219
|
+
this.outboxPath = path;
|
|
220
|
+
return {
|
|
221
|
+
setCounter: () => this,
|
|
222
|
+
setFirstCursor: () => this,
|
|
223
|
+
setLastCursor: () => this,
|
|
224
|
+
authorize: () => this
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
setFollowingDispatcher(path, dispatcher) {
|
|
228
|
+
this.followingDispatcher = dispatcher;
|
|
229
|
+
this.followingPath = path;
|
|
230
|
+
return {
|
|
231
|
+
setCounter: () => this,
|
|
232
|
+
setFirstCursor: () => this,
|
|
233
|
+
setLastCursor: () => this,
|
|
234
|
+
authorize: () => this
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
setFollowersDispatcher(path, dispatcher) {
|
|
238
|
+
this.followersDispatcher = dispatcher;
|
|
239
|
+
this.followersPath = path;
|
|
240
|
+
return {
|
|
241
|
+
setCounter: () => this,
|
|
242
|
+
setFirstCursor: () => this,
|
|
243
|
+
setLastCursor: () => this,
|
|
244
|
+
authorize: () => this
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
setLikedDispatcher(path, dispatcher) {
|
|
248
|
+
this.likedDispatcher = dispatcher;
|
|
249
|
+
this.likedPath = path;
|
|
250
|
+
return {
|
|
251
|
+
setCounter: () => this,
|
|
252
|
+
setFirstCursor: () => this,
|
|
253
|
+
setLastCursor: () => this,
|
|
254
|
+
authorize: () => this
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
setFeaturedDispatcher(path, dispatcher) {
|
|
258
|
+
this.featuredDispatcher = dispatcher;
|
|
259
|
+
this.featuredPath = path;
|
|
260
|
+
return {
|
|
261
|
+
setCounter: () => this,
|
|
262
|
+
setFirstCursor: () => this,
|
|
263
|
+
setLastCursor: () => this,
|
|
264
|
+
authorize: () => this
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
setFeaturedTagsDispatcher(path, dispatcher) {
|
|
268
|
+
this.featuredTagsDispatcher = dispatcher;
|
|
269
|
+
this.featuredTagsPath = path;
|
|
270
|
+
return {
|
|
271
|
+
setCounter: () => this,
|
|
272
|
+
setFirstCursor: () => this,
|
|
273
|
+
setLastCursor: () => this,
|
|
274
|
+
authorize: () => this
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
setInboxListeners(inboxPath, sharedInboxPath) {
|
|
278
|
+
this.inboxPath = inboxPath;
|
|
279
|
+
this.sharedInboxPath = sharedInboxPath;
|
|
280
|
+
const self = this;
|
|
281
|
+
return {
|
|
282
|
+
on(type, listener) {
|
|
283
|
+
const typeName = type.name;
|
|
284
|
+
if (!self.inboxListeners.has(typeName)) self.inboxListeners.set(typeName, []);
|
|
285
|
+
self.inboxListeners.get(typeName).push(listener);
|
|
286
|
+
return this;
|
|
287
|
+
},
|
|
288
|
+
onError() {
|
|
289
|
+
return this;
|
|
290
|
+
},
|
|
291
|
+
setSharedKeyDispatcher() {
|
|
292
|
+
return this;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
async startQueue(contextData, options) {
|
|
297
|
+
this.contextData = contextData;
|
|
298
|
+
this.queueStarted = true;
|
|
299
|
+
if (options?.queue) this.activeQueues.add(options.queue);
|
|
300
|
+
else {
|
|
301
|
+
this.activeQueues.add("inbox");
|
|
302
|
+
this.activeQueues.add("outbox");
|
|
303
|
+
this.activeQueues.add("fanout");
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
async processQueuedTask(contextData, _message) {
|
|
307
|
+
this.contextData = contextData;
|
|
308
|
+
}
|
|
309
|
+
createContext(baseUrlOrRequest, contextData) {
|
|
310
|
+
const mockFederation = this;
|
|
311
|
+
if (baseUrlOrRequest instanceof Request) return createRequestContext({
|
|
312
|
+
url: new URL(baseUrlOrRequest.url),
|
|
313
|
+
request: baseUrlOrRequest,
|
|
314
|
+
data: contextData,
|
|
315
|
+
federation: mockFederation,
|
|
316
|
+
sendActivity: async (sender, recipients, activity, options) => {
|
|
317
|
+
const tempContext = new MockContext({
|
|
318
|
+
url: new URL(baseUrlOrRequest.url),
|
|
319
|
+
data: contextData,
|
|
320
|
+
federation: mockFederation
|
|
321
|
+
});
|
|
322
|
+
await tempContext.sendActivity(sender, recipients, activity, options);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
else return new MockContext({
|
|
326
|
+
url: baseUrlOrRequest,
|
|
327
|
+
data: contextData,
|
|
328
|
+
federation: mockFederation
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
async fetch(request, options) {
|
|
332
|
+
if (options.onNotFound) return options.onNotFound(request);
|
|
333
|
+
return new Response("Not Found", { status: 404 });
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Simulates receiving an activity. This method is specific to the mock
|
|
337
|
+
* implementation and is used for testing purposes.
|
|
338
|
+
*
|
|
339
|
+
* @param activity The activity to receive.
|
|
340
|
+
* @returns A promise that resolves when the activity has been processed.
|
|
341
|
+
* @since 1.8.0
|
|
342
|
+
*/
|
|
343
|
+
async receiveActivity(activity) {
|
|
344
|
+
this.receivedActivities.push(activity);
|
|
345
|
+
const typeName = activity.constructor.name;
|
|
346
|
+
const listeners = this.inboxListeners.get(typeName) || [];
|
|
347
|
+
if (listeners.length > 0 && this.contextData === void 0) throw new Error("MockFederation.receiveActivity(): contextData is not initialized. Please provide contextData through the constructor or call startQueue() before receiving activities.");
|
|
348
|
+
for (const listener of listeners) {
|
|
349
|
+
const context = createInboxContext({
|
|
350
|
+
data: this.contextData,
|
|
351
|
+
federation: this
|
|
352
|
+
});
|
|
353
|
+
await listener(context, activity);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Clears all sent activities from the mock federation.
|
|
358
|
+
* This method is specific to the mock implementation and is used for
|
|
359
|
+
* testing purposes.
|
|
360
|
+
*
|
|
361
|
+
* @since 1.8.0
|
|
362
|
+
*/
|
|
363
|
+
reset() {
|
|
364
|
+
this.sentActivities = [];
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
/**
|
|
368
|
+
* A mock implementation of the {@link Context} interface for unit testing.
|
|
369
|
+
* This class provides a way to test Fedify applications without needing
|
|
370
|
+
* a real federation context.
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```typescript
|
|
374
|
+
* import { Person, Create } from "@fedify/fedify/vocab";
|
|
375
|
+
* import { MockContext, MockFederation } from "@fedify/testing";
|
|
376
|
+
*
|
|
377
|
+
* // Create a mock context
|
|
378
|
+
* const mockFederation = new MockFederation<{ userId: string }>();
|
|
379
|
+
* const context = new MockContext({
|
|
380
|
+
* url: new URL("https://example.com"),
|
|
381
|
+
* data: { userId: "test-user" },
|
|
382
|
+
* federation: mockFederation
|
|
383
|
+
* });
|
|
384
|
+
*
|
|
385
|
+
* // Send an activity
|
|
386
|
+
* const recipient = new Person({ id: new URL("https://example.com/users/bob") });
|
|
387
|
+
* const activity = new Create({
|
|
388
|
+
* id: new URL("https://example.com/create/1"),
|
|
389
|
+
* actor: new URL("https://example.com/users/alice")
|
|
390
|
+
* });
|
|
391
|
+
* await context.sendActivity(
|
|
392
|
+
* { identifier: "alice" },
|
|
393
|
+
* recipient,
|
|
394
|
+
* activity
|
|
395
|
+
* );
|
|
396
|
+
*
|
|
397
|
+
* // Check sent activities
|
|
398
|
+
* const sent = context.getSentActivities();
|
|
399
|
+
* console.log(sent[0].activity);
|
|
400
|
+
* ```
|
|
401
|
+
*
|
|
402
|
+
* @typeParam TContextData The context data to pass to the {@link Context}.
|
|
403
|
+
* @since 1.8.0
|
|
404
|
+
*/
|
|
405
|
+
var MockContext = class MockContext {
|
|
406
|
+
origin;
|
|
407
|
+
canonicalOrigin;
|
|
408
|
+
host;
|
|
409
|
+
hostname;
|
|
410
|
+
data;
|
|
411
|
+
federation;
|
|
412
|
+
documentLoader;
|
|
413
|
+
contextLoader;
|
|
414
|
+
tracerProvider;
|
|
415
|
+
sentActivities = [];
|
|
416
|
+
constructor(options) {
|
|
417
|
+
const url = options.url ?? new URL("https://example.com");
|
|
418
|
+
this.origin = url.origin;
|
|
419
|
+
this.canonicalOrigin = url.origin;
|
|
420
|
+
this.host = url.host;
|
|
421
|
+
this.hostname = url.hostname;
|
|
422
|
+
this.data = options.data;
|
|
423
|
+
this.federation = options.federation;
|
|
424
|
+
this.documentLoader = options.documentLoader ?? (async (url$1) => ({
|
|
425
|
+
contextUrl: null,
|
|
426
|
+
document: {},
|
|
427
|
+
documentUrl: url$1
|
|
428
|
+
}));
|
|
429
|
+
this.contextLoader = options.contextLoader ?? this.documentLoader;
|
|
430
|
+
this.tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
|
|
431
|
+
}
|
|
432
|
+
clone(data) {
|
|
433
|
+
return new MockContext({
|
|
434
|
+
url: new URL(this.origin),
|
|
435
|
+
data,
|
|
436
|
+
federation: this.federation,
|
|
437
|
+
documentLoader: this.documentLoader,
|
|
438
|
+
contextLoader: this.contextLoader,
|
|
439
|
+
tracerProvider: this.tracerProvider
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
getNodeInfoUri() {
|
|
443
|
+
if (this.federation instanceof MockFederation && this.federation.nodeInfoPath) return new URL(this.federation.nodeInfoPath, this.origin);
|
|
444
|
+
return new URL("/nodeinfo/2.0", this.origin);
|
|
445
|
+
}
|
|
446
|
+
getActorUri(identifier) {
|
|
447
|
+
if (this.federation instanceof MockFederation && this.federation.actorPath) {
|
|
448
|
+
const path = expandUriTemplate(this.federation.actorPath, {
|
|
449
|
+
identifier,
|
|
450
|
+
handle: identifier
|
|
451
|
+
});
|
|
452
|
+
return new URL(path, this.origin);
|
|
453
|
+
}
|
|
454
|
+
return new URL(`/users/${identifier}`, this.origin);
|
|
455
|
+
}
|
|
456
|
+
getObjectUri(cls, values) {
|
|
457
|
+
if (this.federation instanceof MockFederation) {
|
|
458
|
+
const pathTemplate = this.federation.objectPaths.get(cls.typeId.href);
|
|
459
|
+
if (pathTemplate) {
|
|
460
|
+
const path$1 = expandUriTemplate(pathTemplate, values);
|
|
461
|
+
return new URL(path$1, this.origin);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
const path = globalThis.Object.entries(values).map(([key, value]) => `${key}/${value}`).join("/");
|
|
465
|
+
return new URL(`/objects/${cls.name.toLowerCase()}/${path}`, this.origin);
|
|
466
|
+
}
|
|
467
|
+
getOutboxUri(identifier) {
|
|
468
|
+
if (this.federation instanceof MockFederation && this.federation.outboxPath) {
|
|
469
|
+
const path = expandUriTemplate(this.federation.outboxPath, {
|
|
470
|
+
identifier,
|
|
471
|
+
handle: identifier
|
|
472
|
+
});
|
|
473
|
+
return new URL(path, this.origin);
|
|
474
|
+
}
|
|
475
|
+
return new URL(`/users/${identifier}/outbox`, this.origin);
|
|
476
|
+
}
|
|
477
|
+
getInboxUri(identifier) {
|
|
478
|
+
if (identifier) {
|
|
479
|
+
if (this.federation instanceof MockFederation && this.federation.inboxPath) {
|
|
480
|
+
const path = expandUriTemplate(this.federation.inboxPath, {
|
|
481
|
+
identifier,
|
|
482
|
+
handle: identifier
|
|
483
|
+
});
|
|
484
|
+
return new URL(path, this.origin);
|
|
485
|
+
}
|
|
486
|
+
return new URL(`/users/${identifier}/inbox`, this.origin);
|
|
487
|
+
}
|
|
488
|
+
if (this.federation instanceof MockFederation && this.federation.sharedInboxPath) return new URL(this.federation.sharedInboxPath, this.origin);
|
|
489
|
+
return new URL("/inbox", this.origin);
|
|
490
|
+
}
|
|
491
|
+
getFollowingUri(identifier) {
|
|
492
|
+
if (this.federation instanceof MockFederation && this.federation.followingPath) {
|
|
493
|
+
const path = expandUriTemplate(this.federation.followingPath, {
|
|
494
|
+
identifier,
|
|
495
|
+
handle: identifier
|
|
496
|
+
});
|
|
497
|
+
return new URL(path, this.origin);
|
|
498
|
+
}
|
|
499
|
+
return new URL(`/users/${identifier}/following`, this.origin);
|
|
500
|
+
}
|
|
501
|
+
getFollowersUri(identifier) {
|
|
502
|
+
if (this.federation instanceof MockFederation && this.federation.followersPath) {
|
|
503
|
+
const path = expandUriTemplate(this.federation.followersPath, {
|
|
504
|
+
identifier,
|
|
505
|
+
handle: identifier
|
|
506
|
+
});
|
|
507
|
+
return new URL(path, this.origin);
|
|
508
|
+
}
|
|
509
|
+
return new URL(`/users/${identifier}/followers`, this.origin);
|
|
510
|
+
}
|
|
511
|
+
getLikedUri(identifier) {
|
|
512
|
+
if (this.federation instanceof MockFederation && this.federation.likedPath) {
|
|
513
|
+
const path = expandUriTemplate(this.federation.likedPath, {
|
|
514
|
+
identifier,
|
|
515
|
+
handle: identifier
|
|
516
|
+
});
|
|
517
|
+
return new URL(path, this.origin);
|
|
518
|
+
}
|
|
519
|
+
return new URL(`/users/${identifier}/liked`, this.origin);
|
|
520
|
+
}
|
|
521
|
+
getFeaturedUri(identifier) {
|
|
522
|
+
if (this.federation instanceof MockFederation && this.federation.featuredPath) {
|
|
523
|
+
const path = expandUriTemplate(this.federation.featuredPath, {
|
|
524
|
+
identifier,
|
|
525
|
+
handle: identifier
|
|
526
|
+
});
|
|
527
|
+
return new URL(path, this.origin);
|
|
528
|
+
}
|
|
529
|
+
return new URL(`/users/${identifier}/featured`, this.origin);
|
|
530
|
+
}
|
|
531
|
+
getFeaturedTagsUri(identifier) {
|
|
532
|
+
if (this.federation instanceof MockFederation && this.federation.featuredTagsPath) {
|
|
533
|
+
const path = expandUriTemplate(this.federation.featuredTagsPath, {
|
|
534
|
+
identifier,
|
|
535
|
+
handle: identifier
|
|
536
|
+
});
|
|
537
|
+
return new URL(path, this.origin);
|
|
538
|
+
}
|
|
539
|
+
return new URL(`/users/${identifier}/tags`, this.origin);
|
|
540
|
+
}
|
|
541
|
+
parseUri(uri) {
|
|
542
|
+
if (uri.pathname.startsWith("/users/")) {
|
|
543
|
+
const parts = uri.pathname.split("/");
|
|
544
|
+
if (parts.length >= 3) return {
|
|
545
|
+
type: "actor",
|
|
546
|
+
identifier: parts[2],
|
|
547
|
+
handle: parts[2]
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
getActorKeyPairs(_identifier) {
|
|
553
|
+
return Promise.resolve([]);
|
|
554
|
+
}
|
|
555
|
+
getDocumentLoader(params) {
|
|
556
|
+
if ("keyId" in params) return this.documentLoader;
|
|
557
|
+
return Promise.resolve(this.documentLoader);
|
|
558
|
+
}
|
|
559
|
+
lookupObject(_uri, _options) {
|
|
560
|
+
return Promise.resolve(null);
|
|
561
|
+
}
|
|
562
|
+
traverseCollection(_collection, _options) {
|
|
563
|
+
return { async *[Symbol.asyncIterator]() {} };
|
|
564
|
+
}
|
|
565
|
+
lookupNodeInfo(_url, _options) {
|
|
566
|
+
return Promise.resolve(void 0);
|
|
567
|
+
}
|
|
568
|
+
lookupWebFinger(_resource, _options) {
|
|
569
|
+
return Promise.resolve(null);
|
|
570
|
+
}
|
|
571
|
+
sendActivity(sender, recipients, activity, _options) {
|
|
572
|
+
this.sentActivities.push({
|
|
573
|
+
sender,
|
|
574
|
+
recipients,
|
|
575
|
+
activity
|
|
576
|
+
});
|
|
577
|
+
if (this.federation instanceof MockFederation) {
|
|
578
|
+
const queued = this.federation.queueStarted;
|
|
579
|
+
this.federation.sentActivities.push({
|
|
580
|
+
queued,
|
|
581
|
+
queue: queued ? "outbox" : void 0,
|
|
582
|
+
activity,
|
|
583
|
+
sentOrder: ++this.federation.sentCounter
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
return Promise.resolve();
|
|
587
|
+
}
|
|
588
|
+
routeActivity(_recipient, _activity, _options) {
|
|
589
|
+
return Promise.resolve(true);
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Gets all activities that have been sent through this mock context.
|
|
593
|
+
* This method is specific to the mock implementation and is used for
|
|
594
|
+
* testing purposes.
|
|
595
|
+
*
|
|
596
|
+
* @returns An array of sent activity records.
|
|
597
|
+
*/
|
|
598
|
+
getSentActivities() {
|
|
599
|
+
return [...this.sentActivities];
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Clears all sent activities from the mock context.
|
|
603
|
+
* This method is specific to the mock implementation and is used for
|
|
604
|
+
* testing purposes.
|
|
605
|
+
*/
|
|
606
|
+
reset() {
|
|
607
|
+
this.sentActivities = [];
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
//#endregion
|
|
612
|
+
export { MockContext, MockFederation };
|
package/docloader.ts
ADDED