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

Merge pull request #239 from dahlia/cloudflare-workers

parents f218880b 05827ff9
Loading
Loading
Loading
Loading
+23 −1
Original line number Diff line number Diff line
@@ -108,6 +108,28 @@ jobs:
    - run: deno task test:bun
      working-directory: ${{ github.workspace }}/fedify/

  test-cfworkers:
    runs-on: ubuntu-latest
    steps:
    - if: github.event_name == 'push'
      uses: actions/checkout@v4
    - if: github.event_name == 'pull_request_target'
      uses: actions/checkout@v4
      with:
        repository: ${{ github.event.pull_request.head.repo.full_name }}
        ref: ${{ github.event.pull_request.head.sha }}
    - uses: denoland/setup-deno@v2
      with:
        deno-version: v2.x
    - uses: actions/setup-node@v4
      with:
        node-version: lts/*
    - uses: pnpm/action-setup@v4
      with:
        version: 10
    - run: deno task test:cfworkers
      working-directory: ${{ github.workspace }}/fedify/

  lint:
    runs-on: ubuntu-latest
    steps:
@@ -159,7 +181,7 @@ jobs:
      working-directory: ${{ github.workspace }}/cli/

  publish:
    needs: [test, test-node, test-bun, lint, release-test]
    needs: [test, test-node, test-bun, test-cfworkers, lint, release-test]
    runs-on: ubuntu-latest
    permissions:
      id-token: write
+1 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
    "btos",
    "callouts",
    "cfworker",
    "cfworkers",
    "codegen",
    "compactable",
    "cryptosuite",
+6 −0
Original line number Diff line number Diff line
@@ -3,6 +3,12 @@
.dnt-import-map.json
.test-report.xml
apidoc/
cfworkers/dist/
cfworkers/fixtures/
cfworkers/imports.ts
cfworkers/README.md
cfworkers/server.js
cfworkers/server.js.map
coverage/
dist/
fedify-fedify-*.tgz
+72 −0
Original line number Diff line number Diff line
import { Miniflare } from "miniflare";
import { join } from "node:path";
import process from "node:process";
import { styleText } from "node:util";

const filters = process.argv.slice(2).map((f) => f.toLowerCase());

const mf = new Miniflare({
  // @ts-ignore: scriptPath is not recognized in the type definitions
  scriptPath: join(import.meta.dirname ?? ".", "server.js"),
  modules: [
    { type: "ESModule", path: join(import.meta.dirname ?? ".", "server.js") },
  ],
  async outboundService(request: Request) {
    const url = new URL(request.url);
    if (url.hostname.endsWith(".test")) {
      const host = url.hostname.slice(0, -5);
      try {
        const { default: document } = await import(
          "../testing/fixtures/" + host + url.pathname + ".json"
        );
        return new Response(JSON.stringify(document), {
          headers: {
            "Content-Type": "application/json",
          },
        });
      } catch (e) {
        return new Response(String(e), { status: 404 });
      }
    }
    return await fetch(request);
  },
  compatibilityDate: "2025-05-23",
  compatibilityFlags: ["nodejs_compat"],
});
const url = await mf.ready;
const response = await mf.dispatchFetch(url);
const tests = await response.json() as string[];
let passed = 0;
let failed = 0;
let skipped = 0;
for (const test of tests) {
  const testLower = test.toLowerCase();
  if (filters.length > 0 && !filters.some((f) => testLower.includes(f))) {
    continue;
  }
  const resp = await mf.dispatchFetch(url, {
    method: "POST",
    body: test,
    headers: { "Content-Type": "text/plain" },
  });
  if (resp.ok) {
    console.log(styleText("green", `PASS: ${test}`));
    passed++;
  } else if (resp.status === 404) {
    console.log(styleText("yellow", `SKIP: ${test}`));
    skipped++;
  } else {
    const text = await resp.text();
    console.log(styleText("red", `FAIL: ${test}`));
    console.log(text);
    failed++;
  }
}
await mf.dispose();
console.log(
  `Tests completed: ${styleText("green", `${passed} passed`)}, ${
    styleText("red", `${failed} failed`)
  }, ${styleText("yellow", `${skipped} skipped`)}.`,
);

// cSpell: ignore Miniflare
+133 −0
Original line number Diff line number Diff line
import {
  ansiColorFormatter,
  configure,
  type LogRecord,
} from "@logtape/logtape";
import { AsyncLocalStorage } from "node:async_hooks";
// @ts-ignore: The following code is generated
import { testDefinitions } from "./dist/testing/mod.js";
// @ts-ignore: The following code is generated
import "./imports.ts";

interface TestDefinition {
  name: string;
  ignore?: boolean;
  fn: (
    // deno-lint-ignore no-explicit-any
    ctx: { name: string; origin: string; step: any },
  ) => void | Promise<void>;
}

// @ts-ignore: testDefinitions is untyped
const tests: TestDefinition[] = testDefinitions;
const logs: LogRecord[] = [];

await configure({
  sinks: {
    buffer: logs.push.bind(logs),
  },
  loggers: [
    { category: [], sinks: ["buffer"], lowestLevel: "debug" },
  ],
  contextLocalStorage: new AsyncLocalStorage(),
});

export default {
  async fetch(request: Request): Promise<Response> {
    if (request.method === "GET") {
      return new Response(
        JSON.stringify(tests.map(({ name }) => name)),
        {
          headers: { "Content-Type": "application/json" },
        },
      );
    }
    const testName = await request.text();
    for (const def of tests) {
      const { name, fn, ignore } = def;
      if (testName !== name) continue;
      if (ignore) {
        return new Response(
          "",
          {
            status: 404,
            headers: { "Content-Type": "text/plain" },
          },
        );
      }
      let failed: unknown = undefined;
      let capturedLogs: LogRecord[] | undefined = undefined;
      // deno-lint-ignore no-inner-declarations
      async function step(
        arg: string | {
          name: string;
          ignore?: boolean;
          // deno-lint-ignore no-explicit-any
          fn: (def: any) => void | Promise<void>;
          // deno-lint-ignore no-explicit-any
        } | ((ctx: any) => void | Promise<void>),
        // deno-lint-ignore no-explicit-any
        fn?: (ctx: any) => void | Promise<void>,
      ) {
        let def: {
          name: string;
          ignore?: boolean;
          // deno-lint-ignore no-explicit-any
          fn: (def: any) => void | Promise<void>;
        };
        if (typeof arg === "string") {
          def = { name: arg, fn: fn! };
        } else if (typeof arg === "function") {
          def = { name: arg.name, fn: arg };
        } else {
          def = arg;
        }
        if (def.ignore) return;
        try {
          await def.fn({
            name: def.name,
            origin: "",
            step,
          });
        } catch (e) {
          failed ??= e;
          capturedLogs ??= [...logs];
          return false;
        }
        return true;
      }
      logs.splice(0, logs.length); // Clear logs
      try {
        await fn({ name, origin: "", step });
      } catch (e) {
        failed ??= e;
      }
      capturedLogs ??= [...logs];
      if (typeof failed === "undefined") {
        return new Response(
          "",
          { status: 200, headers: { "Content-Type": "text/plain" } },
        );
      } else {
        return new Response(
          `${
            failed instanceof Error
              ? `${failed.message}\n${failed.stack ?? ""}`
              : String(failed)
          }\n${capturedLogs.map(ansiColorFormatter).join("")}`,
          {
            status: 500,
            headers: { "Content-Type": "text/plain" },
          },
        );
      }
    }
    return new Response(
      "Test not found",
      {
        status: 404,
        headers: { "Content-Type": "text/plain" },
      },
    );
  },
};
Loading