@fedify/testing 1.8.1-dev.1237 → 1.8.1-dev.1238

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/dist/mod.d.ts CHANGED
@@ -5,7 +5,7 @@ import { ResourceDescriptor } from "@fedify/fedify/webfinger";
5
5
  import { JsonValue, NodeInfo } from "@fedify/fedify/nodeinfo";
6
6
  import { DocumentLoader } from "@fedify/fedify/runtime";
7
7
 
8
- //#region mock.d.ts
8
+ //#region src/mock.d.ts
9
9
 
10
10
  /**
11
11
  * Represents a sent activity with metadata about how it was sent.
package/dist/mod.js CHANGED
@@ -3,7 +3,7 @@ import { RouterError } from "@fedify/fedify/federation";
3
3
  import { lookupObject, traverseCollection } from "@fedify/fedify/vocab";
4
4
  import { lookupWebFinger } from "@fedify/fedify/webfinger";
5
5
 
6
- //#region docloader.ts
6
+ //#region src/docloader.ts
7
7
  const mockDocumentLoader = async (url) => ({
8
8
  contextUrl: null,
9
9
  document: {},
@@ -11,7 +11,7 @@ const mockDocumentLoader = async (url) => ({
11
11
  });
12
12
 
13
13
  //#endregion
14
- //#region context.ts
14
+ //#region src/context.ts
15
15
  function createContext(values) {
16
16
  const { federation, url = new URL("http://example.com/"), canonicalOrigin, data, documentLoader, contextLoader, tracerProvider, clone, getNodeInfoUri, getActorUri, getObjectUri, getCollectionUri, getOutboxUri, getInboxUri, getFollowingUri, getFollowersUri, getLikedUri, getFeaturedUri, getFeaturedTagsUri, parseUri, getActorKeyPairs, getDocumentLoader, lookupObject: lookupObject$1, traverseCollection: traverseCollection$1, lookupNodeInfo, lookupWebFinger: lookupWebFinger$1, sendActivity, routeActivity } = values;
17
17
  function throwRouteError() {
@@ -108,7 +108,7 @@ function createInboxContext(args) {
108
108
  }
109
109
 
110
110
  //#endregion
111
- //#region mock.ts
111
+ //#region src/mock.ts
112
112
  /**
113
113
  * Helper function to expand URI templates with values.
114
114
  * Supports simple placeholders like {identifier}, {handle}, etc.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/testing",
3
- "version": "1.8.1-dev.1237+e0b7ea0e",
3
+ "version": "1.8.1-dev.1238+4e631e65",
4
4
  "description": "Testing utilities for Fedify applications",
5
5
  "keywords": [
6
6
  "fedify",
@@ -19,7 +19,7 @@
19
19
  "repository": {
20
20
  "type": "git",
21
21
  "url": "git+https://github.com/fedify-dev/fedify.git",
22
- "directory": "testing"
22
+ "directory": "packages/testing"
23
23
  },
24
24
  "bugs": {
25
25
  "url": "https://github.com/fedify-dev/fedify/issues"
@@ -40,8 +40,15 @@
40
40
  },
41
41
  "./package.json": "./package.json"
42
42
  },
43
+ "files": [
44
+ "dist",
45
+ "package.json"
46
+ ],
43
47
  "peerDependencies": {
44
- "@fedify/fedify": "1.8.1-dev.1237+e0b7ea0e"
48
+ "@fedify/fedify": "1.8.1-dev.1238+4e631e65"
49
+ },
50
+ "dependencies": {
51
+ "@opentelemetry/api": "^1.9.0"
45
52
  },
46
53
  "devDependencies": {
47
54
  "@js-temporal/polyfill": "^0.5.1",
@@ -50,9 +57,6 @@
50
57
  "tsdown": "^0.12.9",
51
58
  "typescript": "^5.8.3"
52
59
  },
53
- "dependencies": {
54
- "@opentelemetry/api": "^1.9.0"
55
- },
56
60
  "scripts": {
57
61
  "build": "tsdown",
58
62
  "prepublish": "tsdown",
package/context.ts DELETED
@@ -1,156 +0,0 @@
1
- import type {
2
- Context,
3
- Federation,
4
- InboxContext,
5
- RequestContext,
6
- } from "@fedify/fedify/federation";
7
- import { RouterError } from "@fedify/fedify/federation";
8
- import {
9
- lookupObject as globalLookupObject,
10
- traverseCollection as globalTraverseCollection,
11
- } from "@fedify/fedify/vocab";
12
- import { lookupWebFinger as globalLookupWebFinger } from "@fedify/fedify/webfinger";
13
- import { trace } from "@opentelemetry/api";
14
- import { mockDocumentLoader } from "./docloader.ts";
15
-
16
- // NOTE: Copied from @fedify/fedify/testing/context.ts
17
-
18
- export function createContext<TContextData>(
19
- values: Partial<Context<TContextData>> & {
20
- url?: URL;
21
- data: TContextData;
22
- federation: Federation<TContextData>;
23
- },
24
- ): Context<TContextData> {
25
- const {
26
- federation,
27
- url = new URL("http://example.com/"),
28
- canonicalOrigin,
29
- data,
30
- documentLoader,
31
- contextLoader,
32
- tracerProvider,
33
- clone,
34
- getNodeInfoUri,
35
- getActorUri,
36
- getObjectUri,
37
- getCollectionUri,
38
- getOutboxUri,
39
- getInboxUri,
40
- getFollowingUri,
41
- getFollowersUri,
42
- getLikedUri,
43
- getFeaturedUri,
44
- getFeaturedTagsUri,
45
- parseUri,
46
- getActorKeyPairs,
47
- getDocumentLoader,
48
- lookupObject,
49
- traverseCollection,
50
- lookupNodeInfo,
51
- lookupWebFinger,
52
- sendActivity,
53
- routeActivity,
54
- } = values;
55
- function throwRouteError(): URL {
56
- throw new RouterError("Not implemented");
57
- }
58
- return {
59
- federation,
60
- data,
61
- origin: url.origin,
62
- canonicalOrigin: canonicalOrigin ?? url.origin,
63
- host: url.host,
64
- hostname: url.hostname,
65
- documentLoader: documentLoader ?? mockDocumentLoader,
66
- contextLoader: contextLoader ?? mockDocumentLoader,
67
- tracerProvider: tracerProvider ?? trace.getTracerProvider(),
68
- clone: clone ?? ((data) => createContext({ ...values, data })),
69
- getNodeInfoUri: getNodeInfoUri ?? throwRouteError,
70
- getActorUri: getActorUri ?? throwRouteError,
71
- getObjectUri: getObjectUri ?? throwRouteError,
72
- getCollectionUri: getCollectionUri ?? throwRouteError,
73
- getOutboxUri: getOutboxUri ?? throwRouteError,
74
- getInboxUri: getInboxUri ?? throwRouteError,
75
- getFollowingUri: getFollowingUri ?? throwRouteError,
76
- getFollowersUri: getFollowersUri ?? throwRouteError,
77
- getLikedUri: getLikedUri ?? throwRouteError,
78
- getFeaturedUri: getFeaturedUri ?? throwRouteError,
79
- getFeaturedTagsUri: getFeaturedTagsUri ?? throwRouteError,
80
- parseUri: parseUri ?? ((_uri) => {
81
- throw new Error("Not implemented");
82
- }),
83
- getDocumentLoader: getDocumentLoader ?? ((_params) => {
84
- throw new Error("Not implemented");
85
- }),
86
- getActorKeyPairs: getActorKeyPairs ?? ((_handle) => Promise.resolve([])),
87
- lookupObject: lookupObject ?? ((uri, options = {}) => {
88
- return globalLookupObject(uri, {
89
- documentLoader: options.documentLoader ?? documentLoader ??
90
- mockDocumentLoader,
91
- contextLoader: options.contextLoader ?? contextLoader ??
92
- mockDocumentLoader,
93
- });
94
- }),
95
- traverseCollection: traverseCollection ?? ((collection, options = {}) => {
96
- return globalTraverseCollection(collection, {
97
- documentLoader: options.documentLoader ?? documentLoader ??
98
- mockDocumentLoader,
99
- contextLoader: options.contextLoader ?? contextLoader ??
100
- mockDocumentLoader,
101
- });
102
- }),
103
- lookupNodeInfo: lookupNodeInfo ?? ((_params) => {
104
- throw new Error("Not implemented");
105
- }),
106
- lookupWebFinger: lookupWebFinger ?? ((resource, options = {}) => {
107
- return globalLookupWebFinger(resource, options);
108
- }),
109
- sendActivity: sendActivity ?? ((_params) => {
110
- throw new Error("Not implemented");
111
- }),
112
- routeActivity: routeActivity ?? ((_params) => {
113
- throw new Error("Not implemented");
114
- }),
115
- };
116
- }
117
-
118
- export function createRequestContext<TContextData>(
119
- args: Partial<RequestContext<TContextData>> & {
120
- url: URL;
121
- data: TContextData;
122
- federation: Federation<TContextData>;
123
- },
124
- ): RequestContext<TContextData> {
125
- return {
126
- ...createContext(args),
127
- clone: args.clone ?? ((data) => createRequestContext({ ...args, data })),
128
- request: args.request ?? new Request(args.url),
129
- url: args.url,
130
- getActor: args.getActor ?? (() => Promise.resolve(null)),
131
- getObject: args.getObject ?? (() => Promise.resolve(null)),
132
- getSignedKey: args.getSignedKey ?? (() => Promise.resolve(null)),
133
- getSignedKeyOwner: args.getSignedKeyOwner ?? (() => Promise.resolve(null)),
134
- sendActivity: args.sendActivity ?? ((_params) => {
135
- throw new Error("Not implemented");
136
- }),
137
- };
138
- }
139
-
140
- export function createInboxContext<TContextData>(
141
- args: Partial<InboxContext<TContextData>> & {
142
- url?: URL;
143
- data: TContextData;
144
- recipient?: string | null;
145
- federation: Federation<TContextData>;
146
- },
147
- ): InboxContext<TContextData> {
148
- return {
149
- ...createContext(args),
150
- clone: args.clone ?? ((data) => createInboxContext({ ...args, data })),
151
- recipient: args.recipient ?? null,
152
- forwardActivity: args.forwardActivity ?? ((_params) => {
153
- throw new Error("Not implemented");
154
- }),
155
- };
156
- }
package/deno.json DELETED
@@ -1,41 +0,0 @@
1
- {
2
- "name": "@fedify/testing",
3
- "version": "1.8.1-dev.1237+e0b7ea0e",
4
- "license": "MIT",
5
- "exports": {
6
- ".": "./mod.ts"
7
- },
8
- "imports": {
9
- "@opentelemetry/api": "npm:@opentelemetry/api@^1.9.0"
10
- },
11
- "exclude": [
12
- ".github",
13
- "node_modules",
14
- "npm",
15
- "pnpm-lock.yaml"
16
- ],
17
- "tasks": {
18
- "build": "pnpm build",
19
- "check": "deno fmt --check && deno lint && deno check mod.ts",
20
- "test": "deno test --allow-read --allow-net",
21
- "test:node": {
22
- "dependencies": [
23
- "build"
24
- ],
25
- "command": "node --experimental-transform-types --test"
26
- },
27
- "test:bun": {
28
- "dependencies": [
29
- "build"
30
- ],
31
- "command": "bun test --timeout 15000"
32
- },
33
- "test-all": {
34
- "dependencies": [
35
- "test",
36
- "test:node",
37
- "test:bun"
38
- ]
39
- }
40
- }
41
- }
package/docloader.ts DELETED
@@ -1,8 +0,0 @@
1
- import type { DocumentLoader } from "@fedify/fedify/runtime";
2
-
3
- // deno-lint-ignore require-await
4
- export const mockDocumentLoader: DocumentLoader = async (url: string) => ({
5
- contextUrl: null,
6
- document: {},
7
- documentUrl: url,
8
- });
package/mock.test.ts DELETED
@@ -1,361 +0,0 @@
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
- });