Unverified Commit 21177540 authored by Hong Minhee (洪 民憙)'s avatar Hong Minhee (洪 民憙) Committed by GitHub
Browse files

Merge pull request #470 from dahlia/testing-circular-references

Fix JSR publishing hang by removing function overloads
parents 1e16969f f29ef3bd
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -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
+35 −37
Original line number Diff line number Diff line
@@ -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({
@@ -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();
@@ -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
@@ -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();

@@ -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
@@ -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();
+0 −3
Original line number Diff line number Diff line
@@ -5,9 +5,6 @@
  "exports": {
    ".": "./src/mod.ts"
  },
  "imports": {
    "@opentelemetry/api": "npm:@opentelemetry/api@^1.9.0"
  },
  "exclude": [
    "dist",
    "node_modules",
+0 −3
Original line number Diff line number Diff line
@@ -47,9 +47,6 @@
  "peerDependencies": {
    "@fedify/fedify": "workspace:^"
  },
  "dependencies": {
    "@opentelemetry/api": "^1.9.0"
  },
  "devDependencies": {
    "@js-temporal/polyfill": "catalog:",
    "@std/assert": "catalog:",
+27 −2
Original line number Diff line number Diff line
// deno-lint-ignore-file no-explicit-any
import type {
  Context,
  Federation,
@@ -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>(
@@ -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,
@@ -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;
@@ -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