Cloudflare Workers, vitestのsetupFilesの設定がわからなかった話

に公開

Cloudflare Workersのvitestの設定の仕方

背景

最近 Cloudflare Workersのプロジェクトを始めた。
Googleのjulesで段階的に進めていたが、特にテストの進め方の面でCloudflareの現在のバージョンのやり方をjulesにインプットできないとスムーズに進まないことが分かった。

やりたかったこと / できなかったこと

D1のテストで使用するテーブルやデータはテストのたびに用意する必要がある
vitestを動かすときに、D1のテーブル作成とか登録とかをしたい。
「vitest.config.tsのsetupFilesでテスト前に呼びたいコードのパスを置けばできる」
と各種AIが教えてくれたのでコードサンプルも書かせて実行してみた。
だがログを見る限りどうもsetup.tsは動いていない。(テストは実行されている)

分かった設定/Setting

分かれば単純。
poolOptionsの中でなく、test: 中でsetupFilesの設定を行えばよい。
*LLMに書かせるとpoolOptions: の中にsetupFilesを記述していた。

vitest.config.ts
// vitest.config.ts
import { defineWorkersConfig,readD1Migrations } from '@cloudflare/vitest-pool-workers/config';
export default defineWorkersConfig({
  test: {
    setupFiles: ['./setup.ts'], // <- here
    poolOptions: {
      workers: {
        wrangler: {
          configPath: './wrangler.jsonc',
        },
        miniflare: {
          d1Persist: './.wrangler/state',
        },
      },
    },
  },
})

やったことの経緯

  • パスのチェック
    • 問題なし
  • LLM(chatGPT,Cloude,Gemini)への問い合わせ
    • 正解を出したのはなかった(なので時間がかかった)
  • Cloudflare Docのチェック
  • WEBでの検索
    • 少し前の開発環境の話が多く、直接的なコードが見つからない。
      • 結果が分かってからみればあーと思うところはあるが。
  • setpu.ts に console.log("ログ文字列") をいれて実行をチェック
    • 表示されない
  • node_moduleの中身をたどってデバッグ。
    • node_moduleも中身全部書いてあるんだしたどればわかるだろうと調べてみた。
vitestconfigts(当初)
import { defineWorkersConfig,readD1Migrations } from '@cloudflare/vitest-pool-workers/config';
export default defineWorkersConfig({
  test: {
    poolOptions: {
      setupFiles: './setup.ts',
      workers: {
        wrangler: {
          configPath: './wrangler.jsonc',
        },
        miniflare: {
          d1Persist: './.wrangler/state',
        },
      },
    },
  },
})

node_moduleの検索

node_moduleのコードを'setupFiles'で検索

最初は@vitest-pool-worker内を調べていたが、ためにはなったが原因までは遠そうだった。
視点を変えて、ファイルに'setupFiles'と中に書いてあるファイルを探し、そこを起点に原因を探ろうと思った。
こういうコードはLLMに依頼するのがよいのでGeiminiにサクッと作って貰う。

 Get-ChildItem -File -Recurse |
 Select-String -Pattern "setupFiles" -SimpleMatch |
 Select-Object -ExpandProperty Path -Unique |
 ForEach-Object {
   # フルパスから $base を取り除く
   $_ -replace [regex]::Escape($base), ''
 }

結果は以下。cloudflare特融の@vitest\runner\distの中に手がかりがありそう。

@vitest\runner\dist\index.d.ts
@vitest\runner\dist\index.js
@vitest\runner\dist\tasks.d-hsdzc98-.d.ts
@vitest\runner\dist\types.d.ts
vitest\dist\index.d.ts
vitest\dist\node.d.ts
vitest\dist\chunks\cac.KrMo52r1.js
vitest\dist\chunks\cli-api.BzebkJv7.js
vitest\dist\chunks\config.d.UqE-KR0o.d.ts
vitest\dist\chunks\coverage.D6LCUsnS.js
vitest\dist\chunks\reporters.d.C-cu31ET.d.ts
vitest\dist\chunks\utils.CgTj3MsC.js

調べた結果、セットアップファイルがあったらcollectTestsがrunSetupFilesを呼ぶ構造になっている。そこでconsoleで関数が呼ばれたら


async function runSetupFiles(config, files, runner){
 console.debug(`runSetupFiles`, files, config);
 // ファイルパスのスクリプトを実行する
}

async function collectTests(specs, runner) {
  console.log("collectTests");
  // この中でrunSetupFilesが呼ばれている
}

上のようにデバッグ用のログを仕込んで vitest run したところ、
collectTestsは実行されるけど、runSetupFilesは実行されないことがわかった。

試しに以下のようにオブジェクトから取得するのではなく、パス直書きにしてみたらsetupが実行された。

@vitest\runner\dist\index.js - function collectTests()
//const setupFiles = toArray(config.setupFiles);
const setupFiles = toArray("./setup.ts")

多分パスが伝わってないなと思い、configを出力

@vitest\runner\dist\index.js - function collectTests()
async function collectTests(specs, runner) {
console.log("collectTests");
const files = [];
const config = runner.config;

// config全体を出力 
console.log(`[DEBUG @vitest/runner] , JSON.stringify(config, null, 2)); 

"setupFiles": [],

やはり空。
ただ、ログをGeminiに入れて解析してたら興味深い解析をした。

LLMのデバッグ出力の解析
[DEBUG @vitest/runner] runner.config object: {
  // ... (多くの設定) ...
  "setupFiles": [], // ← ここが空の配列!
  // ...
  "poolOptions": {
    "threads": {
      "isolate": false
    },
    "workers": { // ← ここに setupFiles がない!
      "main": "C:\\Users\\tomiy\\Documents\\GitHub\\EdgeAuth\\experiments\\12-D1Migration\\d1-test\\src\\index.ts",
      "durableObjectBindingDesignators": {},
      "isolatedStorage": true
    }
  },
  // ...
  "sequence": {
    "hooks": "stack",
    "setupFiles": undefined // ← runner.config.sequence.setupFiles も undefined
  },
  // ...
}

「ここが空の配列!」と言っている場所が、vitest.config.tsの内容と違う!!! 
*Gemiinのこの時点ではここが原因とは気づいていない。

ということで、コードの中身を追っていくことで解決できた。

学び

設定わからない場合は、設定名でnode_moduleを検索してLLMに解析させるのは意外と近道かも。

Discussion