Skip to content

Commit 068ad47

Browse files
committed
Merge tag '1.9.3' into 1.10-maintenance
Fedify 1.9.3
2 parents 33b0f6f + 22314ac commit 068ad47

File tree

3 files changed

+133
-6
lines changed

3 files changed

+133
-6
lines changed

CHANGES.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ Version 1.10.1
88

99
To be released.
1010

11+
### @fedify/testing
12+
13+
- Fixed `TestContext.getActor()` and `TestContext.getObject()` returning
14+
`null` instead of calling registered dispatchers. The methods now properly
15+
invoke actor and object dispatchers when they are registered via
16+
`setActorDispatcher()` and `setObjectDispatcher()`. [[#530]]
17+
1118

1219
Version 1.10.0
1320
--------------
@@ -106,6 +113,21 @@ Released on December 24, 2025.
106113
- Implemented `list()` method in `WorkersKvStore`. [[#498], [#500]]
107114

108115

116+
Version 1.9.3
117+
-------------
118+
119+
Released on January 22, 2026.
120+
121+
### @fedify/testing
122+
123+
- Fixed `TestContext.getActor()` and `TestContext.getObject()` returning
124+
`null` instead of calling registered dispatchers. The methods now properly
125+
invoke actor and object dispatchers when they are registered via
126+
`setActorDispatcher()` and `setObjectDispatcher()`. [[#530]]
127+
128+
[#530]: https://github.com/fedify-dev/fedify/issues/530
129+
130+
109131
Version 1.9.2
110132
-------------
111133

packages/testing/src/mock.test.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,3 +357,86 @@ test("MockFederation without queue sends all activities immediately", async () =
357357
assertEquals(mockFederation.sentActivities[0].queue, undefined);
358358
assertEquals(mockFederation.sentActivities[0].sentOrder, 1);
359359
});
360+
361+
test("MockContext.getActor() calls registered actor dispatcher", async () => {
362+
const mockFederation = createFederation<void>();
363+
364+
// Register actor dispatcher
365+
mockFederation.setActorDispatcher(
366+
"/users/{identifier}",
367+
(ctx, identifier) => {
368+
return new Person({
369+
id: ctx.getActorUri(identifier),
370+
preferredUsername: identifier,
371+
name: `Test User ${identifier}`,
372+
});
373+
},
374+
);
375+
376+
const context = mockFederation.createContext(
377+
new URL("https://example.com"),
378+
undefined,
379+
);
380+
381+
const actor = await context.getActor("alice");
382+
383+
assertEquals(actor instanceof Person, true);
384+
assertEquals(actor?.preferredUsername, "alice");
385+
assertEquals(actor?.name, "Test User alice");
386+
assertEquals(actor?.id?.href, "https://example.com/users/alice");
387+
});
388+
389+
test("MockContext.getObject() calls registered object dispatcher", async () => {
390+
const mockFederation = createFederation<void>();
391+
392+
// Register object dispatcher
393+
mockFederation.setObjectDispatcher(
394+
Note,
395+
"/users/{identifier}/posts/{postId}",
396+
(ctx, values) => {
397+
return new Note({
398+
id: ctx.getObjectUri(Note, values),
399+
content: `Post ${values.postId} by ${values.identifier}`,
400+
});
401+
},
402+
);
403+
404+
const context = mockFederation.createContext(
405+
new URL("https://example.com"),
406+
undefined,
407+
);
408+
409+
const note = await context.getObject(Note, {
410+
identifier: "alice",
411+
postId: "123",
412+
});
413+
414+
assertEquals(note instanceof Note, true);
415+
assertEquals(note?.content, "Post 123 by alice");
416+
assertEquals(note?.id?.href, "https://example.com/users/alice/posts/123");
417+
});
418+
419+
test("MockContext.getActor() returns null when no dispatcher registered", async () => {
420+
const mockFederation = createFederation<void>();
421+
const context = mockFederation.createContext(
422+
new URL("https://example.com"),
423+
undefined,
424+
);
425+
426+
const actor = await context.getActor("alice");
427+
assertEquals(actor, null);
428+
});
429+
430+
test("MockContext.getObject() returns null when no dispatcher registered", async () => {
431+
const mockFederation = createFederation<void>();
432+
const context = mockFederation.createContext(
433+
new URL("https://example.com"),
434+
undefined,
435+
);
436+
437+
const note = await context.getObject(Note, {
438+
identifier: "alice",
439+
postId: "123",
440+
});
441+
assertEquals(note, null);
442+
});

packages/testing/src/mock.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ class MockFederation<TContextData> implements Federation<TContextData> {
178178
// which causes JSR type analyzer to hang when combined with @opentelemetry/api
179179
// types present in Context.tracerProvider (issue #468).
180180
private webFingerDispatcher?: any;
181-
private actorDispatchers: Map<string, any> = new Map();
181+
public actorDispatchers: Map<string, any> = new Map();
182182
public actorPath?: string;
183183
public inboxPath?: string;
184184
public outboxPath?: string;
@@ -190,7 +190,7 @@ class MockFederation<TContextData> implements Federation<TContextData> {
190190
public nodeInfoPath?: string;
191191
public sharedInboxPath?: string;
192192
public objectPaths: Map<string, string> = new Map();
193-
private objectDispatchers: Map<string, any> = new Map();
193+
public objectDispatchers: Map<string, any> = new Map();
194194
private inboxDispatcher?: any;
195195
private outboxDispatcher?: any;
196196
private followingDispatcher?: any;
@@ -622,12 +622,34 @@ class MockContext<TContextData> implements Context<TContextData> {
622622
this.tracerProvider = options.tracerProvider ?? noopTracerProvider;
623623
}
624624

625-
getActor(_handle: string): Promise<any> {
626-
return Promise.resolve(null);
625+
async getActor(handle: string): Promise<any> {
626+
if (
627+
this.federation instanceof MockFederation && this.federation.actorPath
628+
) {
629+
const dispatcher = this.federation.actorDispatchers.get(
630+
this.federation.actorPath,
631+
);
632+
if (dispatcher) {
633+
return await dispatcher(this, handle);
634+
}
635+
}
636+
return null;
627637
}
628638

629-
getObject(_cls: any, _values: any): Promise<any> {
630-
return Promise.resolve(null);
639+
async getObject<TObject extends Object>(
640+
cls: (new (...args: any[]) => TObject) & { typeId: URL },
641+
values: Record<string, string>,
642+
): Promise<TObject | null> {
643+
if (this.federation instanceof MockFederation) {
644+
const path = this.federation.objectPaths.get(cls.typeId.href);
645+
if (path) {
646+
const dispatcher = this.federation.objectDispatchers.get(path);
647+
if (dispatcher) {
648+
return await dispatcher(this, values);
649+
}
650+
}
651+
}
652+
return null;
631653
}
632654

633655
getSignedKey(): Promise<any> {

0 commit comments

Comments
 (0)