Open6

`@cloudflare/vitest-pool-workers` を使用した service binding のテストを考える

naporitannaporitan

service binding RPC を利用する場合

https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/rpc

このサンプルでは wrangler.toml で自分自身のを service binding で参照している。このやり方はローカルでは通るが Workers の初回デプロイが失敗する。一度 deploy してから wrangler.toml を変える必要があるのは IaC の役割を持つファイルとして適切ではないので別のやり方を調べることにした。

こういうエラーが出ることから、個人的には自分自身を参照する service binding は利用するべきではないと考える。自己再帰してるインフラは危ないと思うし。

エラー内容

✘ [ERROR] Error on remote worker: APIError: A request to the Cloudflare API (/accounts/XXXXXXX/workers/scripts/worker-name/edge-preview) failed.

  at throwFetchError

(/home/user/project/node_modules/wrangler/wrangler-dist/cli.js:155177:18)
at fetchResult
(/home/user/project/node_modules/wrangler/wrangler-dist/cli.js:155090:5)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async createPreviewToken
(/home/user/project/node_modules/wrangler/wrangler-dist/cli.js:203375:29)
at async createWorkerPreview
(/home/user/project/node_modules/wrangler/wrangler-dist/cli.js:203396:17)
at async start
(/home/user/project/node_modules/wrangler/wrangler-dist/cli.js:204118:34)
{
text: 'A request to the Cloudflare API
(/accounts/XXXXXXX/workers/scripts/worker-name/edge-preview)
failed.',
notes: [
{
text: 'workers.api.error.service_binding_error: could not resolve binding "COMMENT_SERVICE":
script "worker-name" not found [code: 10143]'
}
],
location: undefined,
kind: 'error',
code: 10143
}

簡単な Cloudflare Workers RPC を利用するプロジェクトを用意した。
この TestService は Cloudflare Pages で使われることを想定するため、前述のことから自分自身では binding しない。ただ、定義元であるプロジェクトでテストケースを書きたい。そのやり方を模索する。

src/index.ts
import { Hono } from "hono";
import { WorkerEntrypoint } from "cloudfalre:workers";

export clsas TestService extends WorkerEntrypoint {
  add(a: number, b: number) {
    return a + b;
  }
}

const app = new Hono();
export default app;
wrangler.toml
name = "test-workers"
main = "src/index.ts"
compatibility_date = "2024-05-12"
compatibility_flags = ["nodejs_compat"]
workers_dev = false
naporitannaporitan

binding なしで vitest から env を見てみる。当然何も出てこない。これでは test できっこないので wrangler.toml に binding を追加してみる。

test/integration.test.ts
import { env } from "cloudflare:test";

it("test", () => {
  console.log(env)
})
stdout | test/integration.test.ts > test
{}
wrangler.toml
name = "test-workers"
main = "src/index.ts"
compatibility_date = "2024-05-12"
compatibility_flags = ["nodejs_compat"]
workers_dev = false
naporitannaporitan

でた。wrangler.toml を見てるのは間違いなく、ここに書く必要がありそう。
しらべたら defineWorkersProjectminiflare property にもかけるっぽいけどできれば 設定値は toml に書きたい。(これはどっちがいいかな?)

stdout | test/integration.test.ts > test
{ TEST_SERVICE: Fetcher {} }
wrangler.toml
name = "test-workers"
main = "src/index.ts"
compatibility_date = "2024-05-12"
compatibility_flags = ["nodejs_compat"]
workers_dev = false

[[services]]
binding = "TEST_SERVICE"
service = "test-workers"
entrypoint = "TestService"
environment = "production"
naporitannaporitan

それなら wrangler.toml を test 用と分ければいい。
toml ファイルが tsconfig みたいな感じで extend できれば一番いいんだけど、それはないので二重管理をするしかなさそう。miniflare property をいじる方法も模索していきたいが、そこそこ満足したのでいったんここでやめる。

vitest.config.ts
import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config";

export default defineWorkersProject({
  esbuild: {
    // Required for `using` support
    target: "ES2022",
  },
  test: {
    globals: true,
    poolOptions: {
      workers: {
        singleWorker: true,
        miniflare: {
          // Required to use `SELF.scheduled()`. This is an experimental
          // compatibility flag, and cannot be enabled in production.
          compatibilityFlags: ["service_binding_extra_handlers"],
        },
        wrangler: {
          configPath: "./wrangler.test.toml",
        },
      },
    },
  },
});
wrangler.test.toml
name = "test-workers"
main = "src/index.ts"
compatibility_date = "2024-05-12"
compatibility_flags = ["nodejs_compat"]
workers_dev = false

[[services]]
binding = "TEST_SERVICE"
service = "test-workers"
entrypoint = "TestService"
environment = "production"
naporitannaporitan

wrangler property に environment があった。
同一 wrangler に [[env.test.services]] 作ったらいけそう。後で試す。