@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/mock.test.ts
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { Create, Note, Person } from "@fedify/fedify/vocab";
|
|
2
|
+
import { assertEquals, assertRejects } from "@std/assert";
|
|
3
|
+
import { test } from "../fedify/testing/mod.ts";
|
|
4
|
+
import { MockContext, MockFederation } from "./mock.ts";
|
|
5
|
+
|
|
6
|
+
test("getSentActivities returns sent activities", async () => {
|
|
7
|
+
const mockFederation = new MockFederation<void>();
|
|
8
|
+
const context = mockFederation.createContext(
|
|
9
|
+
new URL("https://example.com"),
|
|
10
|
+
undefined,
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
// Create a test activity
|
|
14
|
+
const activity = new Create({
|
|
15
|
+
id: new URL("https://example.com/activities/1"),
|
|
16
|
+
actor: new URL("https://example.com/users/alice"),
|
|
17
|
+
object: new Note({
|
|
18
|
+
id: new URL("https://example.com/notes/1"),
|
|
19
|
+
content: "Hello, world!",
|
|
20
|
+
}),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Send the activity
|
|
24
|
+
await context.sendActivity(
|
|
25
|
+
{ identifier: "alice" },
|
|
26
|
+
new Person({ id: new URL("https://example.com/users/bob") }),
|
|
27
|
+
activity,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
// Check that the activity was recorded
|
|
31
|
+
assertEquals(mockFederation.sentActivities.length, 1);
|
|
32
|
+
assertEquals(mockFederation.sentActivities[0].activity, activity);
|
|
33
|
+
assertEquals(mockFederation.sentActivities[0].queued, false);
|
|
34
|
+
assertEquals(mockFederation.sentActivities[0].sentOrder, 1);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("reset clears sent activities", async () => {
|
|
38
|
+
const mockFederation = new MockFederation<void>();
|
|
39
|
+
const context = mockFederation.createContext(
|
|
40
|
+
new URL("https://example.com"),
|
|
41
|
+
undefined,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Send an activity
|
|
45
|
+
const activity = new Create({
|
|
46
|
+
id: new URL("https://example.com/activities/1"),
|
|
47
|
+
actor: new URL("https://example.com/users/alice"),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
await context.sendActivity(
|
|
51
|
+
{ identifier: "alice" },
|
|
52
|
+
new Person({ id: new URL("https://example.com/users/bob") }),
|
|
53
|
+
activity,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Verify it was sent
|
|
57
|
+
assertEquals(mockFederation.sentActivities.length, 1);
|
|
58
|
+
assertEquals(mockFederation.sentActivities[0].activity, activity);
|
|
59
|
+
|
|
60
|
+
// Clear sent activities
|
|
61
|
+
mockFederation.reset();
|
|
62
|
+
|
|
63
|
+
// Verify they were cleared
|
|
64
|
+
assertEquals(mockFederation.sentActivities.length, 0);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("receiveActivity triggers inbox listeners", async () => {
|
|
68
|
+
// Provide contextData through constructor
|
|
69
|
+
const mockFederation = new MockFederation<{ test: string }>({
|
|
70
|
+
contextData: { test: "data" },
|
|
71
|
+
});
|
|
72
|
+
let receivedActivity: Create | null = null;
|
|
73
|
+
|
|
74
|
+
// Set up an inbox listener
|
|
75
|
+
mockFederation
|
|
76
|
+
.setInboxListeners("/users/{identifier}/inbox")
|
|
77
|
+
// deno-lint-ignore require-await
|
|
78
|
+
.on(Create, async (_ctx, activity) => {
|
|
79
|
+
receivedActivity = activity;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Create and receive an activity
|
|
83
|
+
const activity = new Create({
|
|
84
|
+
id: new URL("https://example.com/activities/1"),
|
|
85
|
+
actor: new URL("https://example.com/users/alice"),
|
|
86
|
+
object: new Note({
|
|
87
|
+
id: new URL("https://example.com/notes/1"),
|
|
88
|
+
content: "Test note",
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
await mockFederation.receiveActivity(activity);
|
|
93
|
+
|
|
94
|
+
// Verify the listener was triggered
|
|
95
|
+
assertEquals(receivedActivity, activity);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("MockContext tracks sent activities", async () => {
|
|
99
|
+
const mockFederation = new MockFederation<void>();
|
|
100
|
+
const mockContext = new MockContext({
|
|
101
|
+
url: new URL("https://example.com"),
|
|
102
|
+
data: undefined,
|
|
103
|
+
federation: mockFederation,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Create a test activity
|
|
107
|
+
const activity = new Create({
|
|
108
|
+
id: new URL("https://example.com/activities/1"),
|
|
109
|
+
actor: new URL("https://example.com/users/alice"),
|
|
110
|
+
object: new Note({
|
|
111
|
+
id: new URL("https://example.com/notes/1"),
|
|
112
|
+
content: "Hello from MockContext!",
|
|
113
|
+
}),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Send the activity
|
|
117
|
+
await mockContext.sendActivity(
|
|
118
|
+
{ identifier: "alice" },
|
|
119
|
+
new Person({ id: new URL("https://example.com/users/bob") }),
|
|
120
|
+
activity,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// Check that the activity was recorded in the context
|
|
124
|
+
const contextSentActivities = mockContext.getSentActivities();
|
|
125
|
+
assertEquals(contextSentActivities.length, 1);
|
|
126
|
+
assertEquals(contextSentActivities[0].activity, activity);
|
|
127
|
+
|
|
128
|
+
// Check that it was also recorded in the federation
|
|
129
|
+
assertEquals(mockFederation.sentActivities.length, 1);
|
|
130
|
+
assertEquals(mockFederation.sentActivities[0].activity, activity);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("MockContext URI methods should work correctly", () => {
|
|
134
|
+
const mockFederation = new MockFederation<void>();
|
|
135
|
+
const mockContext = new MockContext({
|
|
136
|
+
url: new URL("https://example.com"),
|
|
137
|
+
data: undefined,
|
|
138
|
+
federation: mockFederation,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Test URI generation methods
|
|
142
|
+
assertEquals(
|
|
143
|
+
mockContext.getActorUri("alice").href,
|
|
144
|
+
"https://example.com/users/alice",
|
|
145
|
+
);
|
|
146
|
+
assertEquals(
|
|
147
|
+
mockContext.getInboxUri("alice").href,
|
|
148
|
+
"https://example.com/users/alice/inbox",
|
|
149
|
+
);
|
|
150
|
+
assertEquals(mockContext.getInboxUri().href, "https://example.com/inbox");
|
|
151
|
+
assertEquals(
|
|
152
|
+
mockContext.getOutboxUri("alice").href,
|
|
153
|
+
"https://example.com/users/alice/outbox",
|
|
154
|
+
);
|
|
155
|
+
assertEquals(
|
|
156
|
+
mockContext.getFollowingUri("alice").href,
|
|
157
|
+
"https://example.com/users/alice/following",
|
|
158
|
+
);
|
|
159
|
+
assertEquals(
|
|
160
|
+
mockContext.getFollowersUri("alice").href,
|
|
161
|
+
"https://example.com/users/alice/followers",
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const actorUri = new URL("https://example.com/users/alice");
|
|
165
|
+
const parsed = mockContext.parseUri(actorUri);
|
|
166
|
+
assertEquals(parsed?.type, "actor");
|
|
167
|
+
if (parsed?.type === "actor") {
|
|
168
|
+
assertEquals(parsed.identifier, "alice");
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("MockContext URI methods respect registered paths", () => {
|
|
173
|
+
const mockFederation = new MockFederation<void>();
|
|
174
|
+
|
|
175
|
+
// Register custom paths with dummy dispatchers
|
|
176
|
+
mockFederation.setNodeInfoDispatcher("/.well-known/nodeinfo", () => ({
|
|
177
|
+
software: { name: "test", version: { major: 1, minor: 0, patch: 0 } },
|
|
178
|
+
protocols: [],
|
|
179
|
+
usage: {
|
|
180
|
+
users: {},
|
|
181
|
+
localPosts: 0,
|
|
182
|
+
localComments: 0,
|
|
183
|
+
},
|
|
184
|
+
}));
|
|
185
|
+
mockFederation.setActorDispatcher("/actors/{identifier}", () => null);
|
|
186
|
+
mockFederation.setObjectDispatcher(Note, "/notes/{id}", () => null);
|
|
187
|
+
mockFederation.setInboxListeners(
|
|
188
|
+
"/actors/{identifier}/inbox",
|
|
189
|
+
"/shared-inbox",
|
|
190
|
+
);
|
|
191
|
+
mockFederation.setOutboxDispatcher("/actors/{identifier}/outbox", () => null);
|
|
192
|
+
mockFederation.setFollowingDispatcher(
|
|
193
|
+
"/actors/{identifier}/following",
|
|
194
|
+
() => null,
|
|
195
|
+
);
|
|
196
|
+
mockFederation.setFollowersDispatcher(
|
|
197
|
+
"/actors/{identifier}/followers",
|
|
198
|
+
() => null,
|
|
199
|
+
);
|
|
200
|
+
mockFederation.setLikedDispatcher("/actors/{identifier}/liked", () => null);
|
|
201
|
+
mockFederation.setFeaturedDispatcher(
|
|
202
|
+
"/actors/{identifier}/featured",
|
|
203
|
+
() => null,
|
|
204
|
+
);
|
|
205
|
+
mockFederation.setFeaturedTagsDispatcher(
|
|
206
|
+
"/actors/{identifier}/tags",
|
|
207
|
+
() => null,
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const context = mockFederation.createContext(
|
|
211
|
+
new URL("https://example.com"),
|
|
212
|
+
undefined,
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// Test that URIs use the registered paths
|
|
216
|
+
assertEquals(
|
|
217
|
+
context.getNodeInfoUri().href,
|
|
218
|
+
"https://example.com/.well-known/nodeinfo",
|
|
219
|
+
);
|
|
220
|
+
assertEquals(
|
|
221
|
+
context.getActorUri("alice").href,
|
|
222
|
+
"https://example.com/actors/alice",
|
|
223
|
+
);
|
|
224
|
+
assertEquals(
|
|
225
|
+
context.getObjectUri(Note, { id: "123" }).href,
|
|
226
|
+
"https://example.com/notes/123",
|
|
227
|
+
);
|
|
228
|
+
assertEquals(
|
|
229
|
+
context.getInboxUri("alice").href,
|
|
230
|
+
"https://example.com/actors/alice/inbox",
|
|
231
|
+
);
|
|
232
|
+
assertEquals(
|
|
233
|
+
context.getInboxUri().href,
|
|
234
|
+
"https://example.com/shared-inbox",
|
|
235
|
+
);
|
|
236
|
+
assertEquals(
|
|
237
|
+
context.getOutboxUri("alice").href,
|
|
238
|
+
"https://example.com/actors/alice/outbox",
|
|
239
|
+
);
|
|
240
|
+
assertEquals(
|
|
241
|
+
context.getFollowingUri("alice").href,
|
|
242
|
+
"https://example.com/actors/alice/following",
|
|
243
|
+
);
|
|
244
|
+
assertEquals(
|
|
245
|
+
context.getFollowersUri("alice").href,
|
|
246
|
+
"https://example.com/actors/alice/followers",
|
|
247
|
+
);
|
|
248
|
+
assertEquals(
|
|
249
|
+
context.getLikedUri("alice").href,
|
|
250
|
+
"https://example.com/actors/alice/liked",
|
|
251
|
+
);
|
|
252
|
+
assertEquals(
|
|
253
|
+
context.getFeaturedUri("alice").href,
|
|
254
|
+
"https://example.com/actors/alice/featured",
|
|
255
|
+
);
|
|
256
|
+
assertEquals(
|
|
257
|
+
context.getFeaturedTagsUri("alice").href,
|
|
258
|
+
"https://example.com/actors/alice/tags",
|
|
259
|
+
);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test("receiveActivity throws error when contextData not initialized", async () => {
|
|
263
|
+
const mockFederation = new MockFederation<void>();
|
|
264
|
+
|
|
265
|
+
// Set up an inbox listener without initializing contextData
|
|
266
|
+
mockFederation
|
|
267
|
+
.setInboxListeners("/users/{identifier}/inbox")
|
|
268
|
+
.on(Create, async (_ctx, _activity) => {
|
|
269
|
+
/* should not happen */
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const activity = new Create({
|
|
273
|
+
id: new URL("https://example.com/activities/1"),
|
|
274
|
+
actor: new URL("https://example.com/users/alice"),
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Should throw error
|
|
278
|
+
await assertRejects(
|
|
279
|
+
() => mockFederation.receiveActivity(activity),
|
|
280
|
+
Error,
|
|
281
|
+
"MockFederation.receiveActivity(): contextData is not initialized. Please provide contextData through the constructor or call startQueue() before receiving activities.",
|
|
282
|
+
);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("MockFederation distinguishes between immediate and queued activities", async () => {
|
|
286
|
+
const mockFederation = new MockFederation<void>();
|
|
287
|
+
|
|
288
|
+
// Start the queue to enable queued sending
|
|
289
|
+
await mockFederation.startQueue(undefined);
|
|
290
|
+
|
|
291
|
+
const context = mockFederation.createContext(
|
|
292
|
+
new URL("https://example.com"),
|
|
293
|
+
undefined,
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
const activity1 = new Create({
|
|
297
|
+
id: new URL("https://example.com/activities/1"),
|
|
298
|
+
actor: new URL("https://example.com/users/alice"),
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const activity2 = new Create({
|
|
302
|
+
id: new URL("https://example.com/activities/2"),
|
|
303
|
+
actor: new URL("https://example.com/users/alice"),
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// Send activities after queue is started - should be marked as queued
|
|
307
|
+
await context.sendActivity(
|
|
308
|
+
{ identifier: "alice" },
|
|
309
|
+
new Person({ id: new URL("https://example.com/users/bob") }),
|
|
310
|
+
activity1,
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
await context.sendActivity(
|
|
314
|
+
{ identifier: "alice" },
|
|
315
|
+
new Person({ id: new URL("https://example.com/users/bob") }),
|
|
316
|
+
activity2,
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
// Check activity details
|
|
320
|
+
assertEquals(mockFederation.sentActivities.length, 2);
|
|
321
|
+
assertEquals(mockFederation.sentActivities[0].activity, activity1);
|
|
322
|
+
assertEquals(mockFederation.sentActivities[1].activity, activity2);
|
|
323
|
+
|
|
324
|
+
// Both should be marked as sent via queue
|
|
325
|
+
assertEquals(mockFederation.sentActivities[0].queued, true);
|
|
326
|
+
assertEquals(mockFederation.sentActivities[1].queued, true);
|
|
327
|
+
assertEquals(mockFederation.sentActivities[0].queue, "outbox");
|
|
328
|
+
assertEquals(mockFederation.sentActivities[1].queue, "outbox");
|
|
329
|
+
assertEquals(mockFederation.sentActivities[0].sentOrder, 1);
|
|
330
|
+
assertEquals(mockFederation.sentActivities[1].sentOrder, 2);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
test("MockFederation without queue sends all activities immediately", async () => {
|
|
334
|
+
const mockFederation = new MockFederation<void>();
|
|
335
|
+
|
|
336
|
+
const context = mockFederation.createContext(
|
|
337
|
+
new URL("https://example.com"),
|
|
338
|
+
undefined,
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
const activity = new Create({
|
|
342
|
+
id: new URL("https://example.com/activities/1"),
|
|
343
|
+
actor: new URL("https://example.com/users/alice"),
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// Send activity - should be marked as immediate since queue not started
|
|
347
|
+
await context.sendActivity(
|
|
348
|
+
{ identifier: "alice" },
|
|
349
|
+
new Person({ id: new URL("https://example.com/users/bob") }),
|
|
350
|
+
activity,
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
// Check activity details
|
|
354
|
+
assertEquals(mockFederation.sentActivities.length, 1);
|
|
355
|
+
assertEquals(mockFederation.sentActivities[0].activity, activity);
|
|
356
|
+
|
|
357
|
+
// Should be marked as sent immediately
|
|
358
|
+
assertEquals(mockFederation.sentActivities[0].queued, false);
|
|
359
|
+
assertEquals(mockFederation.sentActivities[0].queue, undefined);
|
|
360
|
+
assertEquals(mockFederation.sentActivities[0].sentOrder, 1);
|
|
361
|
+
});
|