Unverified Commit 4038e5e0 authored by Hong Minhee's avatar Hong Minhee
Browse files

Test handleActor()

parent 2d2f9460
Loading
Loading
Loading
Loading
+142 −0
Original line number Diff line number Diff line
import { assert, assertEquals, assertFalse } from "jsr:@std/assert@^0.218.2";
import { createRequestContext } from "../testing/context.ts";
import { Person } from "../vocab/vocab.ts";
import { ActorDispatcher } from "./callback.ts";
import { acceptsJsonLd, handleActor } from "./handler.ts";

Deno.test("acceptsJsonLd()", () => {
  assert(acceptsJsonLd(
    new Request("https://example.com/", {
      headers: { Accept: "application/activity+json" },
    }),
  ));
  assert(acceptsJsonLd(
    new Request("https://example.com/", {
      headers: { Accept: "application/ld+json" },
    }),
  ));
  assert(acceptsJsonLd(
    new Request("https://example.com/", {
      headers: { Accept: "application/json" },
    }),
  ));
  assertFalse(acceptsJsonLd(
    new Request("https://example.com/", {
      headers: { Accept: "application/ld+json; q=0.5, text/html; q=0.8" },
    }),
  ));
  assertFalse(acceptsJsonLd(
    new Request("https://example.com/", {
      headers: {
        Accept: "application/ld+json; q=0.4, application/xhtml+xml; q=0.9",
      },
    }),
  ));
});

Deno.test("handleActor()", async () => {
  let context = createRequestContext<void>({
    data: undefined,
    url: new URL("https://example.com/"),
    getActorUri(handle) {
      return new URL(`https://example.com/users/${handle}`);
    },
  });
  const actorDispatcher: ActorDispatcher<void> = (ctx, handle, _key) => {
    if (handle !== "someone") return null;
    return new Person({
      id: ctx.getActorUri(handle),
      name: "Someone",
    });
  };
  let onNotFoundCalled: Request | null = null;
  const onNotFound = (request: Request) => {
    onNotFoundCalled = request;
    return new Response("Not found", { status: 404 });
  };
  let onNotAcceptableCalled: Request | null = null;
  const onNotAcceptable = (request: Request) => {
    onNotAcceptableCalled = request;
    return new Response("Not acceptable", { status: 406 });
  };
  let response = await handleActor(
    context.request,
    {
      context,
      handle: "someone",
      onNotFound,
      onNotAcceptable,
    },
  );
  assertEquals(response.status, 404);
  assertEquals(onNotFoundCalled, context.request);
  assertEquals(onNotAcceptableCalled, null);

  onNotFoundCalled = null;
  response = await handleActor(
    context.request,
    {
      context,
      handle: "someone",
      actorDispatcher,
      onNotFound,
      onNotAcceptable,
    },
  );
  assertEquals(response.status, 406);
  assertEquals(onNotFoundCalled, null);
  assertEquals(onNotAcceptableCalled, context.request);

  onNotAcceptableCalled = null;
  context = createRequestContext<void>({
    ...context,
    request: new Request(context.url, {
      headers: {
        Accept: "application/activity+json",
      },
    }),
  });
  response = await handleActor(
    context.request,
    {
      context,
      handle: "someone",
      actorDispatcher,
      onNotFound,
      onNotAcceptable,
    },
  );
  assertEquals(response.status, 200);
  assertEquals(await response.json(), {
    "@context": [
      "https://www.w3.org/ns/activitystreams",
      "https://w3id.org/security/v1",
      {
        discoverable: "toot:discoverable",
        indexable: "toot:indexable",
        memorial: "toot:memorial",
        suspended: "toot:suspended",
        toot: "http://joinmastodon.org/ns#",
      },
    ],
    id: "https://example.com/users/someone",
    type: "Person",
    name: "Someone",
  });
  assertEquals(onNotFoundCalled, null);
  assertEquals(onNotAcceptableCalled, null);

  response = await handleActor(
    context.request,
    {
      context,
      handle: "no-one",
      actorDispatcher,
      onNotFound,
      onNotAcceptable,
    },
  );
  assertEquals(response.status, 404);
  assertEquals(onNotFoundCalled, context.request);
  assertEquals(onNotAcceptableCalled, null);
});
+2 −4
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@ import {
} from "./callback.ts";
import { RequestContext } from "./context.ts";

function acceptsJsonLd(request: Request): boolean {
export function acceptsJsonLd(request: Request): boolean {
  const types = accepts(request);
  if (types == null) return true;
  if (types[0] === "text/html" || types[0] === "application/xhtml+xml") {
@@ -31,7 +31,6 @@ function acceptsJsonLd(request: Request): boolean {
export interface ActorHandlerParameters<TContextData> {
  handle: string;
  context: RequestContext<TContextData>;
  documentLoader: DocumentLoader;
  actorDispatcher?: ActorDispatcher<TContextData>;
  onNotFound(request: Request): Response | Promise<Response>;
  onNotAcceptable(request: Request): Response | Promise<Response>;
@@ -42,7 +41,6 @@ export async function handleActor<TContextData>(
  {
    handle,
    context,
    documentLoader,
    actorDispatcher,
    onNotFound,
    onNotAcceptable,
@@ -62,7 +60,7 @@ export async function handleActor<TContextData>(
    const response = onNotFound(request);
    return response instanceof Promise ? await response : response;
  }
  const jsonLd = await actor.toJsonLd({ documentLoader });
  const jsonLd = await actor.toJsonLd(context);
  return new Response(JSON.stringify(jsonLd), {
    headers: {
      "Content-Type": "application/activity+json",
+0 −1
Original line number Diff line number Diff line
@@ -654,7 +654,6 @@ export class Federation<TContextData> {
        return await handleActor(request, {
          handle: route.values.handle,
          context,
          documentLoader: this.#documentLoader,
          actorDispatcher: this.#actorCallbacks?.dispatcher,
          onNotFound,
          onNotAcceptable,

testing/context.ts

0 → 100644
+49 −0
Original line number Diff line number Diff line
import { Context, RequestContext } from "../federation/context.ts";
import { RouterError } from "../federation/router.ts";
import { mockDocumentLoader } from "./docloader.ts";

export function createContext<TContextData>(
  {
    data,
    documentLoader,
    getActorUri,
    getOutboxUri,
    getInboxUri,
    getFollowingUri,
    getFollowersUri,
    getActorKey,
    sendActivity,
  }: Partial<Context<TContextData>> & { data: TContextData },
): Context<TContextData> {
  function throwRouteError(_handle?: string): URL {
    throw new RouterError("Not implemented");
  }
  return {
    data,
    documentLoader: documentLoader ?? mockDocumentLoader,
    getActorUri: getActorUri ?? throwRouteError,
    getOutboxUri: getOutboxUri ?? throwRouteError,
    getInboxUri: getInboxUri ?? throwRouteError,
    getFollowingUri: getFollowingUri ?? throwRouteError,
    getFollowersUri: getFollowersUri ?? throwRouteError,
    getActorKey: getActorKey ?? ((_handle) => {
      return Promise.resolve(null);
    }),
    sendActivity: sendActivity ?? ((_params) => {
      throw new Error("Not implemented");
    }),
  };
}

export function createRequestContext<TContextData>(
  args: Partial<RequestContext<TContextData>> & {
    url: URL;
    data: TContextData;
  },
): RequestContext<TContextData> {
  return {
    ...createContext(args),
    request: args.request ?? new Request(args.url),
    url: args.url,
  };
}
+5 −23
Original line number Diff line number Diff line
import { assertEquals } from "jsr:@std/assert@^0.218.2";
import { ActorDispatcher } from "../federation/callback.ts";
import { RequestContext } from "../federation/context.ts";
import { Router, RouterError } from "../federation/router.ts";
import { mockDocumentLoader } from "../testing/docloader.ts";
import { Router } from "../federation/router.ts";
import { createRequestContext } from "../testing/context.ts";
import { CryptographicKey, Link, Person } from "../vocab/vocab.ts";
import { handleWebFinger } from "./handler.ts";

Deno.test("handleWebFinger()", async () => {
  const url = new URL("https://example.com/.well-known/webfinger");
  let request = new Request(url);
  const context: RequestContext<void> = {
  const context = createRequestContext<void>({
    url,
    request,
    data: undefined,
    documentLoader: mockDocumentLoader,
    getActorUri(handle) {
      return new URL(`https://example.com/users/${handle}`);
    },
    getOutboxUri(_handle) {
      throw new RouterError("Not implemented");
    },
    getInboxUri(_handle?) {
      throw new RouterError("Not implemented");
    },
    getFollowingUri(_handle) {
      throw new RouterError("Not implemented");
    },
    getFollowersUri(_handle) {
      throw new RouterError("Not implemented");
    },
    getActorKey(_handle) {
      return Promise.resolve(
        new CryptographicKey({
@@ -36,10 +20,7 @@ Deno.test("handleWebFinger()", async () => {
        }),
      );
    },
    sendActivity(_params) {
      throw new Error("Not implemented");
    },
  };
  });
  const router = new Router();
  router.add("/users/{handle}", "actor");
  const actorDispatcher: ActorDispatcher<void> = (ctx, handle, _key) => {
@@ -63,6 +44,7 @@ Deno.test("handleWebFinger()", async () => {
    return new Response("Not found", { status: 404 });
  };

  let request = context.request;
  let response = await handleWebFinger(request, {
    context,
    router,