シフト管理SaaSを開発するフリーランスエンジニアの著者は、お知らせ既読化のPOST処理をBillingGuard(課金ゲート)の除外対象にする修正で思わぬ問題に遭遇しました。chi(Go向けの軽量HTTPルーティングライブラリ)のミドルウェア実行時点では、chiはまだリーフ(最終)のルートパターンを確定していません。著者が期待した値は/api/v1/announcements/{id}/readでしたが、実際にミドルウェア内のRoutePattern()が返したのはマウント側のワイルドカード/api/v1/*のみでした。この結果、除外されるべきリクエストが誤分類され、支払い保留テナントが403エラーを受けるという不具合につながりました。

さらに厄介だったのは、ユニットテストも課金マトリクステストも両方グリーン(パス)のまま、本番相当の不具合が見逃されていたことです。原因は、ルーターを経由しないテストではchiのルートコンテキストが存在せず、フォールバック処理が常に成立してしまうため、実運用の条件を再現できていなかったことにありました。著者は「ルーター無しで呼ぶテストは、ルーターがある時だけ壊れるバグを原理的に検出できない」と振り返っています。

対応として、著者は実行時の厳密化を諦め、テスト層での対策に切り替えました。chi.Walk()で全ルートを列挙し分類表と照合する「RouteCompletenessテスト」と、分類表と判定関数の整合性を確認する「ExemptSingleSourceOfTruthテスト」を導入し、乖離を実行時の静かな誤作動からCI段階での失敗に変換したとしています。

出典が伝えている要点

  • 著者はシフト管理SaaSを開発している
  • お知らせ既読化のPOST処理を課金ゲート(BillingGuard)の除外対象にする修正が必要だった
  • chiのミドルウェア実行時点では、chiがリーフ(最終)のルートパターンをまだ確定していない
  • 著者が期待した値は /api/v1/announcements/{id}/read だった
  • 実際にミドルウェア内のRoutePattern()が返した値はマウント側のワイルドカード /api/v1/* だった
  • この結果、除外されるべきリクエストが誤分類され、支払い保留テナントが403エラーを受けた
  • ユニットテストは緑(パス)だった
  • 課金マトリクステストも緑(パス)を維持していた
  • テストが緑であったにもかかわらず、実装は壊れていた
  • ルーターを経由しないテストでは、chiのルートコンテキストが存在しないためフォールバック処理が常に成立し、実運用での問題条件を再現できていなかった
  • 対応として、chi.Walk()で全ルートを列挙し分類表と照合する「RouteCompletenessテスト」を導入した
  • 対応として、分類表と判定関数の整合性を確認する「ExemptSingleSourceOfTruthテスト」を導入した

原文より

ユニットテストが緑、課金マトリクステストも緑を維持していたにもかかわらず、実装は壊れていました
ルーター無しで呼ぶテストは、ルーターがある時だけ壊れるバグを原理的に検出できない

出典: zenn.dev

← 2026.09.03 の号を通しで読む