Skip to content

Commit 8c1980e

Browse files
committed
Fix getActorKeyPairs() returning empty array
TestContext.getActorKeyPairs() method was always returning an empty array instead of calling the registered key pairs dispatcher. This made it impossible to test actor key pairs retrieval with the @fedify/testing package. The bug was caused by the setKeyPairsDispatcher() method not storing the dispatcher, and MockContext.getActorKeyPairs() being hardcoded to return an empty array without checking for registered dispatchers in the MockFederation instance. Updated the implementation to: - Store the key pairs dispatcher when setKeyPairsDispatcher() is called - Call the stored dispatcher in getActorKeyPairs() when available - Wrap returned key pairs with CryptographicKey and Multikey objects to match the real implementation behavior Added regression tests to ensure the method correctly calls the dispatcher and returns an empty array only when no dispatcher is registered. Fixes #530
1 parent a7ec220 commit 8c1980e

File tree

3 files changed

+111
-3
lines changed

3 files changed

+111
-3
lines changed

CHANGES.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ Version 1.9.4
88

99
To be released.
1010

11+
### @fedify/testing
12+
13+
- Fixed `TestContext.getActorKeyPairs()` returning empty array instead of
14+
calling registered key pairs dispatcher. The method now properly invokes
15+
the key pairs dispatcher when it is registered via `setKeyPairsDispatcher()`.
16+
[[#530]]
17+
18+
[#530]: https://github.com/fedify-dev/fedify/issues/530
19+
1120

1221
Version 1.9.3
1322
-------------

packages/testing/src/mock.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,3 +440,73 @@ test("MockContext.getObject() returns null when no dispatcher registered", async
440440
});
441441
assertEquals(note, null);
442442
});
443+
444+
test("MockContext.getActorKeyPairs() calls registered key pairs dispatcher", async () => {
445+
const mockFederation = createFederation<void>();
446+
447+
// Generate a test RSA key pair
448+
const keyPair = await crypto.subtle.generateKey(
449+
{
450+
name: "RSASSA-PKCS1-v1_5",
451+
modulusLength: 2048,
452+
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
453+
hash: "SHA-256",
454+
},
455+
true,
456+
["sign", "verify"],
457+
);
458+
459+
// Register actor dispatcher with key pairs dispatcher
460+
mockFederation
461+
.setActorDispatcher("/users/{identifier}", (ctx, identifier) => {
462+
return new Person({
463+
id: ctx.getActorUri(identifier),
464+
preferredUsername: identifier,
465+
});
466+
})
467+
.setKeyPairsDispatcher((ctx, identifier) => {
468+
return [
469+
{
470+
keyId: new URL(`${ctx.getActorUri(identifier).href}#main-key`),
471+
privateKey: keyPair.privateKey,
472+
publicKey: keyPair.publicKey,
473+
},
474+
];
475+
});
476+
477+
const context = mockFederation.createContext(
478+
new URL("https://example.com"),
479+
undefined,
480+
);
481+
482+
const keyPairs = await context.getActorKeyPairs("alice");
483+
484+
assertEquals(keyPairs.length, 1);
485+
assertEquals(
486+
keyPairs[0].keyId.href,
487+
"https://example.com/users/alice#main-key",
488+
);
489+
assertEquals(keyPairs[0].privateKey, keyPair.privateKey);
490+
assertEquals(keyPairs[0].publicKey, keyPair.publicKey);
491+
assertEquals(keyPairs[0].cryptographicKey.id?.href, keyPairs[0].keyId.href);
492+
assertEquals(
493+
keyPairs[0].cryptographicKey.ownerId?.href,
494+
"https://example.com/users/alice",
495+
);
496+
assertEquals(keyPairs[0].multikey.id?.href, keyPairs[0].keyId.href);
497+
assertEquals(
498+
keyPairs[0].multikey.controllerId?.href,
499+
"https://example.com/users/alice",
500+
);
501+
});
502+
503+
test("MockContext.getActorKeyPairs() returns empty array when no dispatcher registered", async () => {
504+
const mockFederation = createFederation<void>();
505+
const context = mockFederation.createContext(
506+
new URL("https://example.com"),
507+
undefined,
508+
);
509+
510+
const keyPairs = await context.getActorKeyPairs("alice");
511+
assertEquals(keyPairs, []);
512+
});

packages/testing/src/mock.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {
1111
RouteActivityOptions,
1212
} from "@fedify/fedify/federation";
1313
import type { DocumentLoader } from "@fedify/fedify/runtime";
14+
import { CryptographicKey, Multikey } from "@fedify/fedify/vocab";
1415
import type {
1516
Activity,
1617
Collection,
@@ -179,6 +180,7 @@ class MockFederation<TContextData> implements Federation<TContextData> {
179180
// types present in Context.tracerProvider (issue #468).
180181
private webFingerDispatcher?: any;
181182
public actorDispatchers: Map<string, any> = new Map();
183+
public actorKeyPairsDispatcher?: any;
182184
public actorPath?: string;
183185
public inboxPath?: string;
184186
public outboxPath?: string;
@@ -229,7 +231,10 @@ class MockFederation<TContextData> implements Federation<TContextData> {
229231
this.actorDispatchers.set(path, dispatcher);
230232
this.actorPath = path;
231233
return {
232-
setKeyPairsDispatcher: () => this as any,
234+
setKeyPairsDispatcher: (keyPairsDispatcher: any) => {
235+
this.actorKeyPairsDispatcher = keyPairsDispatcher;
236+
return this as any;
237+
},
233238
mapHandle: () => this as any,
234239
mapAlias: () => this as any,
235240
authorize: () => this as any,
@@ -836,8 +841,32 @@ class MockContext<TContextData> implements Context<TContextData> {
836841
return null;
837842
}
838843

839-
getActorKeyPairs(_identifier: string): Promise<ActorKeyPair[]> {
840-
return Promise.resolve([]);
844+
async getActorKeyPairs(identifier: string): Promise<ActorKeyPair[]> {
845+
if (
846+
this.federation instanceof MockFederation &&
847+
this.federation.actorKeyPairsDispatcher
848+
) {
849+
const keyPairs = await this.federation.actorKeyPairsDispatcher(
850+
this,
851+
identifier,
852+
);
853+
// Wrap with CryptographicKey and Multikey objects like real implementation
854+
const owner = this.getActorUri(identifier);
855+
return keyPairs.map((kp: any) => ({
856+
...kp,
857+
cryptographicKey: new CryptographicKey({
858+
id: kp.keyId,
859+
owner,
860+
publicKey: kp.publicKey,
861+
}),
862+
multikey: new Multikey({
863+
id: kp.keyId,
864+
controller: owner,
865+
publicKey: kp.publicKey,
866+
}),
867+
}));
868+
}
869+
return [];
841870
}
842871

843872
getDocumentLoader(params: any): any {

0 commit comments

Comments
 (0)