Loading CHANGES.md +11 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,17 @@ Version 1.8.14 To be released. ### @fedify/testing - Fixed JSR publishing hanging indefinitely at the *processing* stage. The issue was caused by TypeScript function overload signatures in `MockContext` and `MockFederation` classes that triggered a bug in JSR's type analyzer. All method overloads have been removed and simplified to use `any` types where necessary. [[#468], [#470]] [#468]: https://github.com/fedify-dev/fedify/issues/468 [#470]: https://github.com/fedify-dev/fedify/pull/470 ### @fedify/cli - Fixed `fedify` command failing on Windows with `PermissionDenied` error Loading docs/manual/test.md +35 −37 Original line number Diff line number Diff line Loading @@ -230,23 +230,22 @@ await federation.receiveActivity(activity); console.log("Sent activities:", federation.sentActivities); ~~~~ ### `MockContext` ### Creating mock contexts The `MockContext` class provides a mock implementation of the `Context` interface that tracks sent activities and provides mock implementations of URI generation methods: You can create mock contexts using `MockFederation.createContext()` method, which provides a mock implementation of the `Context` interface that tracks sent activities and provides mock implementations of URI generation methods: ~~~~ typescript twoslash import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; import { Create, Note, Person } from "@fedify/fedify/vocab"; // Create a mock federation and context const federation = new MockFederation<{ userId: string }>(); const context = new MockContext({ url: new URL("https://example.com"), data: { userId: "test-user" }, federation: federation }); const context = federation.createContext( new URL("https://example.com"), { userId: "test-user" } ); // Send an activity const activity = new Create({ Loading Loading @@ -274,11 +273,12 @@ console.log("Federation sent activities:", federation.sentActivities); ### Testing URI generation `MockContext` provides mock implementations of all URI generation methods that work with the paths configured in your `MockFederation`: Mock contexts created with `MockFederation.createContext()` provide mock implementations of all URI generation methods that work with the paths configured in your `MockFederation`: ~~~~ typescript twoslash import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; import { Note } from "@fedify/fedify/vocab"; const federation = new MockFederation(); Loading @@ -303,37 +303,36 @@ console.log(context.getObjectUri(Note, { id: "123" })); // https://example.com/n ### Tracking sent activities Both `MockFederation` and `MockContext` track sent activities with detailed Both `MockFederation` and mock contexts track sent activities with detailed metadata: ~~~~ typescript twoslash // @noErrors: 2554 import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; const federation = new MockFederation(); const context = new MockContext({ federation, url: new URL("https://example.com/"), data: undefined, }); const context = federation.createContext( new URL("https://example.com/"), undefined ); // Send some activities... await context.sendActivity(/* ... */); // Check federation-level tracking federation.sentActivities.forEach(sent => { for (const sent of federation.sentActivities) { console.log("Activity:", sent.activity.id); console.log("Queued:", sent.queued); console.log("Queue type:", sent.queue); console.log("Send order:", sent.sentOrder); }); } // Check context-level tracking context.getSentActivities().forEach(sent => { for (const sent of context.getSentActivities()) { console.log("Sender:", sent.sender); console.log("Recipients:", sent.recipients); console.log("Activity:", sent.activity); }); } ~~~~ ### Simulating queue processing Loading @@ -342,7 +341,7 @@ You can test queue-based activity processing by starting the mock queue: ~~~~ typescript twoslash // @noErrors: 2554 import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; const federation = new MockFederation(); Loading @@ -350,11 +349,10 @@ const federation = new MockFederation(); await federation.startQueue({ contextData: { userId: "test" } }); // Now sent activities will be marked as queued const context = new MockContext({ federation, url: new URL("https://example.com/"), data: { userId: "test" }, }) const context = federation.createContext( new URL("https://example.com/"), { userId: "test" } ); await context.sendActivity(/* ... */); // Check if activities were queued Loading @@ -364,16 +362,16 @@ console.log("Queued activities:", queued.length); ### Resetting mock state Both mock classes provide `reset()` methods to clear tracked activities: Both `MockFederation` and mock contexts provide `reset()` methods to clear tracked activities: ~~~~ typescript twoslash import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; const federation = new MockFederation(); const context = new MockContext({ federation, url: new URL("https://example.com/"), data: undefined, }); const context = federation.createContext( new URL("https://example.com/"), undefined ); // ---cut-before--- // Clear all sent activities federation.reset(); Loading packages/testing/deno.json +0 −3 Original line number Diff line number Diff line Loading @@ -5,9 +5,6 @@ "exports": { ".": "./src/mod.ts" }, "imports": { "@opentelemetry/api": "npm:@opentelemetry/api@^1.9.0" }, "exclude": [ "dist", "node_modules", Loading packages/testing/package.json +0 −3 Original line number Diff line number Diff line Loading @@ -47,9 +47,6 @@ "peerDependencies": { "@fedify/fedify": "workspace:^" }, "dependencies": { "@opentelemetry/api": "^1.9.0" }, "devDependencies": { "@js-temporal/polyfill": "catalog:", "@std/assert": "catalog:", Loading packages/testing/src/context.ts +27 −2 Original line number Diff line number Diff line // deno-lint-ignore-file no-explicit-any import type { Context, Federation, Loading @@ -10,9 +11,21 @@ import { traverseCollection as globalTraverseCollection, } from "@fedify/fedify/vocab"; import { lookupWebFinger as globalLookupWebFinger } from "@fedify/fedify/webfinger"; import { trace } from "@opentelemetry/api"; import { mockDocumentLoader } from "./docloader.ts"; // Create a no-op tracer provider. // We use `any` type instead of importing TracerProvider from @opentelemetry/api // to avoid type graph analysis issues in JSR. When @opentelemetry/api types are // imported alongside ResourceDescriptor from @fedify/fedify/webfinger, JSR's type // analyzer hangs indefinitely during the "processing" stage. // See: https://github.com/fedify-dev/fedify/issues/468 const noopTracerProvider: any = { getTracer: () => ({ startActiveSpan: () => undefined as any, startSpan: () => undefined as any, }), }; // NOTE: Copied from @fedify/fedify/testing/context.ts export function createContext<TContextData>( Loading Loading @@ -64,7 +77,7 @@ export function createContext<TContextData>( hostname: url.hostname, documentLoader: documentLoader ?? mockDocumentLoader, contextLoader: contextLoader ?? mockDocumentLoader, tracerProvider: tracerProvider ?? trace.getTracerProvider(), tracerProvider: tracerProvider ?? noopTracerProvider, clone: clone ?? ((data) => createContext({ ...values, data })), getNodeInfoUri: getNodeInfoUri ?? throwRouteError, getActorUri: getActorUri ?? throwRouteError, Loading Loading @@ -115,6 +128,12 @@ export function createContext<TContextData>( }; } /** * Creates a RequestContext for testing purposes. * @param args Partial RequestContext properties * @returns A RequestContext instance * @since 1.8.0 */ export function createRequestContext<TContextData>( args: Partial<RequestContext<TContextData>> & { url: URL; Loading @@ -137,6 +156,12 @@ export function createRequestContext<TContextData>( }; } /** * Creates an InboxContext for testing purposes. * @param args Partial InboxContext properties * @returns An InboxContext instance * @since 1.8.0 */ export function createInboxContext<TContextData>( args: Partial<InboxContext<TContextData>> & { url?: URL; Loading Loading
CHANGES.md +11 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,17 @@ Version 1.8.14 To be released. ### @fedify/testing - Fixed JSR publishing hanging indefinitely at the *processing* stage. The issue was caused by TypeScript function overload signatures in `MockContext` and `MockFederation` classes that triggered a bug in JSR's type analyzer. All method overloads have been removed and simplified to use `any` types where necessary. [[#468], [#470]] [#468]: https://github.com/fedify-dev/fedify/issues/468 [#470]: https://github.com/fedify-dev/fedify/pull/470 ### @fedify/cli - Fixed `fedify` command failing on Windows with `PermissionDenied` error Loading
docs/manual/test.md +35 −37 Original line number Diff line number Diff line Loading @@ -230,23 +230,22 @@ await federation.receiveActivity(activity); console.log("Sent activities:", federation.sentActivities); ~~~~ ### `MockContext` ### Creating mock contexts The `MockContext` class provides a mock implementation of the `Context` interface that tracks sent activities and provides mock implementations of URI generation methods: You can create mock contexts using `MockFederation.createContext()` method, which provides a mock implementation of the `Context` interface that tracks sent activities and provides mock implementations of URI generation methods: ~~~~ typescript twoslash import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; import { Create, Note, Person } from "@fedify/fedify/vocab"; // Create a mock federation and context const federation = new MockFederation<{ userId: string }>(); const context = new MockContext({ url: new URL("https://example.com"), data: { userId: "test-user" }, federation: federation }); const context = federation.createContext( new URL("https://example.com"), { userId: "test-user" } ); // Send an activity const activity = new Create({ Loading Loading @@ -274,11 +273,12 @@ console.log("Federation sent activities:", federation.sentActivities); ### Testing URI generation `MockContext` provides mock implementations of all URI generation methods that work with the paths configured in your `MockFederation`: Mock contexts created with `MockFederation.createContext()` provide mock implementations of all URI generation methods that work with the paths configured in your `MockFederation`: ~~~~ typescript twoslash import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; import { Note } from "@fedify/fedify/vocab"; const federation = new MockFederation(); Loading @@ -303,37 +303,36 @@ console.log(context.getObjectUri(Note, { id: "123" })); // https://example.com/n ### Tracking sent activities Both `MockFederation` and `MockContext` track sent activities with detailed Both `MockFederation` and mock contexts track sent activities with detailed metadata: ~~~~ typescript twoslash // @noErrors: 2554 import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; const federation = new MockFederation(); const context = new MockContext({ federation, url: new URL("https://example.com/"), data: undefined, }); const context = federation.createContext( new URL("https://example.com/"), undefined ); // Send some activities... await context.sendActivity(/* ... */); // Check federation-level tracking federation.sentActivities.forEach(sent => { for (const sent of federation.sentActivities) { console.log("Activity:", sent.activity.id); console.log("Queued:", sent.queued); console.log("Queue type:", sent.queue); console.log("Send order:", sent.sentOrder); }); } // Check context-level tracking context.getSentActivities().forEach(sent => { for (const sent of context.getSentActivities()) { console.log("Sender:", sent.sender); console.log("Recipients:", sent.recipients); console.log("Activity:", sent.activity); }); } ~~~~ ### Simulating queue processing Loading @@ -342,7 +341,7 @@ You can test queue-based activity processing by starting the mock queue: ~~~~ typescript twoslash // @noErrors: 2554 import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; const federation = new MockFederation(); Loading @@ -350,11 +349,10 @@ const federation = new MockFederation(); await federation.startQueue({ contextData: { userId: "test" } }); // Now sent activities will be marked as queued const context = new MockContext({ federation, url: new URL("https://example.com/"), data: { userId: "test" }, }) const context = federation.createContext( new URL("https://example.com/"), { userId: "test" } ); await context.sendActivity(/* ... */); // Check if activities were queued Loading @@ -364,16 +362,16 @@ console.log("Queued activities:", queued.length); ### Resetting mock state Both mock classes provide `reset()` methods to clear tracked activities: Both `MockFederation` and mock contexts provide `reset()` methods to clear tracked activities: ~~~~ typescript twoslash import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; const federation = new MockFederation(); const context = new MockContext({ federation, url: new URL("https://example.com/"), data: undefined, }); const context = federation.createContext( new URL("https://example.com/"), undefined ); // ---cut-before--- // Clear all sent activities federation.reset(); Loading
packages/testing/deno.json +0 −3 Original line number Diff line number Diff line Loading @@ -5,9 +5,6 @@ "exports": { ".": "./src/mod.ts" }, "imports": { "@opentelemetry/api": "npm:@opentelemetry/api@^1.9.0" }, "exclude": [ "dist", "node_modules", Loading
packages/testing/package.json +0 −3 Original line number Diff line number Diff line Loading @@ -47,9 +47,6 @@ "peerDependencies": { "@fedify/fedify": "workspace:^" }, "dependencies": { "@opentelemetry/api": "^1.9.0" }, "devDependencies": { "@js-temporal/polyfill": "catalog:", "@std/assert": "catalog:", Loading
packages/testing/src/context.ts +27 −2 Original line number Diff line number Diff line // deno-lint-ignore-file no-explicit-any import type { Context, Federation, Loading @@ -10,9 +11,21 @@ import { traverseCollection as globalTraverseCollection, } from "@fedify/fedify/vocab"; import { lookupWebFinger as globalLookupWebFinger } from "@fedify/fedify/webfinger"; import { trace } from "@opentelemetry/api"; import { mockDocumentLoader } from "./docloader.ts"; // Create a no-op tracer provider. // We use `any` type instead of importing TracerProvider from @opentelemetry/api // to avoid type graph analysis issues in JSR. When @opentelemetry/api types are // imported alongside ResourceDescriptor from @fedify/fedify/webfinger, JSR's type // analyzer hangs indefinitely during the "processing" stage. // See: https://github.com/fedify-dev/fedify/issues/468 const noopTracerProvider: any = { getTracer: () => ({ startActiveSpan: () => undefined as any, startSpan: () => undefined as any, }), }; // NOTE: Copied from @fedify/fedify/testing/context.ts export function createContext<TContextData>( Loading Loading @@ -64,7 +77,7 @@ export function createContext<TContextData>( hostname: url.hostname, documentLoader: documentLoader ?? mockDocumentLoader, contextLoader: contextLoader ?? mockDocumentLoader, tracerProvider: tracerProvider ?? trace.getTracerProvider(), tracerProvider: tracerProvider ?? noopTracerProvider, clone: clone ?? ((data) => createContext({ ...values, data })), getNodeInfoUri: getNodeInfoUri ?? throwRouteError, getActorUri: getActorUri ?? throwRouteError, Loading Loading @@ -115,6 +128,12 @@ export function createContext<TContextData>( }; } /** * Creates a RequestContext for testing purposes. * @param args Partial RequestContext properties * @returns A RequestContext instance * @since 1.8.0 */ export function createRequestContext<TContextData>( args: Partial<RequestContext<TContextData>> & { url: URL; Loading @@ -137,6 +156,12 @@ export function createRequestContext<TContextData>( }; } /** * Creates an InboxContext for testing purposes. * @param args Partial InboxContext properties * @returns An InboxContext instance * @since 1.8.0 */ export function createInboxContext<TContextData>( args: Partial<InboxContext<TContextData>> & { url?: URL; Loading