Commit f09ef6cf authored by ChanHaeng Lee's avatar ChanHaeng Lee
Browse files

Added tests

parent a5c2177e
Loading
Loading
Loading
Loading
+79 −0
Original line number Diff line number Diff line
import type {
  KVNamespace,
  Queue,
} from "@cloudflare/workers-types/experimental";
import { assertEquals } from "@std/assert";
import { delay } from "es-toolkit";
import { WorkersKvStore, WorkersMessageQueue } from "./mod.ts";

Deno.test({
  name: "WorkersKvStore",
  ignore: !("navigator" in globalThis &&
    navigator.userAgent === "Cloudflare-Workers"),
  async fn(t) {
    const { env } = t as unknown as {
      env: Record<string, KVNamespace<string>>;
    };
    const store = new WorkersKvStore(env.KV1);

    await t.step("set() & get()", async () => {
      await store.set(["foo", "bar"], { foo: 1, bar: 2 });
      assertEquals(await store.get(["foo", "bar"]), { foo: 1, bar: 2 });
      assertEquals(await store.get(["foo"]), undefined);

      await store.set(["foo", "baz"], "baz", {
        ttl: Temporal.Duration.from({ seconds: 0 }),
      });
      assertEquals(await store.get(["foo", "baz"]), undefined);
    });

    await t.step("delete()", async () => {
      await store.delete(["foo", "bar"]);
      assertEquals(await store.get(["foo", "bar"]), undefined);
    });
  },
});

Deno.test({
  name: "WorkersMessageQueue",
  ignore: !("navigator" in globalThis &&
    navigator.userAgent === "Cloudflare-Workers"),
  async fn(t) {
    const { env, messageBatches } = t as unknown as {
      env: Record<string, Queue>;
      messageBatches: MessageBatch[];
    };
    const queue = new WorkersMessageQueue(env.Q1);
    await queue.enqueue({ foo: 1, bar: 2 });
    await waitFor(() => messageBatches.length > 0, 5000);
    assertEquals(messageBatches.length, 1);
    assertEquals(messageBatches[0].queue, "Q1");
    assertEquals(messageBatches[0].messages.length, 1);
    assertEquals(messageBatches[0].messages[0].body, { foo: 1, bar: 2 });

    await queue.enqueue(
      { baz: 3, qux: 4 },
      { delay: Temporal.Duration.from({ seconds: 3 }) },
    );
    await delay(2000);
    assertEquals(messageBatches.length, 1);
    await waitFor(() => messageBatches.length > 1, 6000);
    assertEquals(messageBatches[1].queue, "Q1");
    assertEquals(messageBatches[1].messages.length, 1);
    assertEquals(messageBatches[1].messages[0].body, { baz: 3, qux: 4 });
  },
});

async function waitFor(
  predicate: () => boolean,
  timeoutMs: number,
): Promise<void> {
  let delayed = 0;
  while (!predicate()) {
    await delay(500);
    delayed += 500;
    if (delayed > timeoutMs) {
      throw new Error("Timeout");
    }
  }
}
+92 −0
Original line number Diff line number Diff line
import { assertEquals, assertGreater } from "@std/assert";
import { delay } from "es-toolkit";
import { DenoKvMessageQueue, DenoKvStore } from "./mod.ts";

Deno.test("DenoKvStore", async (t) => {
  const kv = await Deno.openKv(":memory:");
  const store = new DenoKvStore(kv);

  await t.step("get()", async () => {
    await kv.set(["foo", "bar"], "foobar");
    assertEquals(await store.get(["foo", "bar"]), "foobar");
  });

  await t.step("set()", async () => {
    await store.set(["foo", "baz"], "baz");
    assertEquals((await kv.get<string>(["foo", "baz"])).value, "baz");
  });

  await t.step("delete()", async () => {
    await store.delete(["foo", "baz"]);
    assertEquals((await kv.get<string>(["foo", "baz"])).value, null);
  });

  await t.step("cas()", async () => {
    await store.set(["foo", "bar"], "foobar");
    assertEquals(await store.cas(["foo", "bar"], "bar", "baz"), false);
    assertEquals(await store.get(["foo", "bar"]), "foobar");
    assertEquals(await store.cas(["foo", "bar"], "foobar", "baz"), true);
    assertEquals(await store.get(["foo", "bar"]), "baz");
    await store.delete(["foo", "bar"]);
    assertEquals(await store.cas(["foo", "bar"], "foobar", "baz"), false);
    assertEquals(await store.get(["foo", "bar"]), undefined);
    assertEquals(await store.cas(["foo", "bar"], undefined, "baz"), true);
    assertEquals(await store.get(["foo", "bar"]), "baz");
  });

  kv.close();
});

Deno.test("DenoKvMessageQueue", async (t) => {
  const kv = await Deno.openKv(":memory:");
  const mq = new DenoKvMessageQueue(kv);

  const messages: string[] = [];
  const controller = new AbortController();
  const listening = mq.listen((message: string) => {
    messages.push(message);
  }, { signal: controller.signal });

  await t.step("enqueue()", async () => {
    await mq.enqueue("Hello, world!");
  });

  await waitFor(() => messages.length > 0, 15_000);

  await t.step("listen()", () => {
    assertEquals(messages, ["Hello, world!"]);
  });

  let started = 0;
  await t.step("enqueue() with delay", async () => {
    started = Date.now();
    await mq.enqueue(
      "Delayed message",
      { delay: Temporal.Duration.from({ seconds: 3 }) },
    );
  });

  await waitFor(() => messages.length > 1, 15_000);

  await t.step("listen() with delay", () => {
    assertEquals(messages, ["Hello, world!", "Delayed message"]);
    assertGreater(Date.now() - started, 3_000);
  });

  controller.abort();
  await listening;
  mq[Symbol.dispose]();
});

async function waitFor(
  predicate: () => boolean,
  timeoutMs: number,
): Promise<void> {
  const started = Date.now();
  while (!predicate()) {
    await delay(500);
    if (Date.now() - started > timeoutMs) {
      throw new Error("Timeout");
    }
  }
}
+86 −0
Original line number Diff line number Diff line
import { assertEquals } from "@std/assert";
import { type ContextDataFactory, federation } from "./mod.ts";

interface MockHonoContext {
  req: {
    raw: Request;
  };
  res: Response;
}

interface MockFederation<T> {
  fetch(request: Request, options: any): Promise<Response>;
}

Deno.test("federation middleware", async (t) => {
  await t.step("creates middleware function", () => {
    const mockFederation: MockFederation<undefined> = {
      fetch: async () => new Response("OK"),
    };

    const contextDataFactory: ContextDataFactory<undefined, MockHonoContext> =
      () => undefined;

    const middleware = federation(mockFederation as any, contextDataFactory);
    assertEquals(typeof middleware, "function");
  });

  await t.step("calls federation.fetch with correct parameters", async () => {
    let capturedRequest: Request | undefined;
    let capturedOptions: any;

    const mockFederation: MockFederation<string> = {
      fetch: async (request, options) => {
        capturedRequest = request;
        capturedOptions = options;
        return new Response("Federation response");
      },
    };

    const contextDataFactory: ContextDataFactory<string, MockHonoContext> =
      () => "test-context";

    const middleware = federation(mockFederation as any, contextDataFactory);

    const mockRequest = new Request("https://example.com/test");
    const mockContext: MockHonoContext = {
      req: { raw: mockRequest },
      res: new Response("Hono response"),
    };

    const result = await middleware(mockContext, async () => {});

    assertEquals(capturedRequest, mockRequest);
    assertEquals(capturedOptions.contextData, "test-context");
    assertEquals(result?.status, 200);
  });

  await t.step("handles async context data factory", async () => {
    let capturedContextData: any;

    const mockFederation: MockFederation<string> = {
      fetch: async (request, options) => {
        capturedContextData = options.contextData;
        return new Response("OK");
      },
    };

    const contextDataFactory: ContextDataFactory<string, MockHonoContext> =
      async () => {
        await new Promise((resolve) => setTimeout(resolve, 10));
        return "async-context";
      };

    const middleware = federation(mockFederation as any, contextDataFactory);

    const mockRequest = new Request("https://example.com/test");
    const mockContext: MockHonoContext = {
      req: { raw: mockRequest },
      res: new Response("Hono response"),
    };

    await middleware(mockContext, async () => {});

    assertEquals(capturedContextData, "async-context");
  });
});
+84 −0
Original line number Diff line number Diff line
import { assertEquals } from "@std/assert";
import { fedifyHook } from "./mod.ts";

interface MockRequestEvent {
  request: Request;
}

interface MockHookParams {
  event: MockRequestEvent;
  resolve: (event: MockRequestEvent) => Promise<Response>;
}

interface MockFederation<T> {
  fetch(request: Request, options: any): Promise<Response>;
}

Deno.test("fedifyHook", async (t) => {
  await t.step("creates hook handler function", () => {
    const mockFederation: MockFederation<undefined> = {
      fetch: async () => new Response("OK"),
    };

    const createContextData = () => undefined;

    const hookHandler = fedifyHook(mockFederation as any, createContextData);
    assertEquals(typeof hookHandler, "function");
  });

  await t.step("calls federation.fetch with correct parameters", async () => {
    let capturedRequest: Request | undefined;
    let capturedOptions: any;

    const mockFederation: MockFederation<string> = {
      fetch: async (request, options) => {
        capturedRequest = request;
        capturedOptions = options;
        return new Response("Federation response");
      },
    };

    const createContextData = () => "test-context";

    const hookHandler = fedifyHook(mockFederation as any, createContextData);

    const mockRequest = new Request("https://example.com/test");
    const mockEvent: MockRequestEvent = { request: mockRequest };
    const mockResolve = async () => new Response("SvelteKit response");

    const result = await hookHandler({
      event: mockEvent,
      resolve: mockResolve,
    });

    assertEquals(capturedRequest, mockRequest);
    assertEquals(capturedOptions.contextData, "test-context");
    assertEquals(result.status, 200);
  });

  await t.step("handles async context data creation", async () => {
    let capturedContextData: any;

    const mockFederation: MockFederation<string> = {
      fetch: async (request, options) => {
        capturedContextData = options.contextData;
        return new Response("OK");
      },
    };

    const createContextData = async () => {
      await new Promise((resolve) => setTimeout(resolve, 10));
      return "async-context";
    };

    const hookHandler = fedifyHook(mockFederation as any, createContextData);

    const mockRequest = new Request("https://example.com/test");
    const mockEvent: MockRequestEvent = { request: mockRequest };
    const mockResolve = async () => new Response("SvelteKit response");

    await hookHandler({ event: mockEvent, resolve: mockResolve });

    assertEquals(capturedContextData, "async-context");
  });
});