imnot: YAML 一枚からステートフルな mock server を立てる
imnot はステートフルな API mock server だ。外部インターフェースを YAML 一枚で記述し、imnot start を叩けば動く mock が立ち上がる。エンドポイントを足すのにコード変更は要らない。Python 製で、土台は FastAPI、セッション状態は SQLite に落とし、十分軽い。これは自分が書いたものではない。原作者は edu2105 で、fork して魔改造しながら研究している。
解決するのは、統合テストにおける外部 API の面倒さだ。サードパーティ決済、OAuth の token 交換、submit してからポーリングで結果を取る非同期フロー、webhook のコールバック、どれも罠だらけ。CI で相手のサンドボックスを本当に叩くわけにはいかない。遅いし、不安定だし、レート制限もかかる。従来は大量の mock コードを書くか、重い GUI を立てるかのどちらかだったが、imnot は YAML の宣言だけで済ませる。
一番面白いのは「ステートフル」を YAML に組み込んでいる点で、スクリプトに散らばらせない。いくつかの pattern がある:
oauthclient-credentials で token を交換し、静的な JWT を返すstaticYAML に書き込んだ JSON をずっと吐き続けるfetch同期 GET で、以前保存した payload を返すasyncN ステップの非同期フロー。submit からステータス確認、結果取得まで、ステップ数もメソッドも YAML で設定するpushまず即座に返し、その後コールバック URL へ能動的に payload を送る。相手が自分の webhook を叩いてくる状況を模倣する
payload はグローバルとセッションの二層に分かれる。テストを並行実行する際は X-Imnot-Session ヘッダー一つで分離し、各テストが自分の分を持って互いに衝突しない。一枚の fixture をみんなで共有するよりきれいだ。さらに常駐の admin API を備え、payload のアップロードや YAML のホットリロード(POST /imnot/admin/reload)ができる。YAML の断片を POST すれば新しい partner ルートがマウントされ、再起動は不要、Postman collection の書き出しもできる。一プロセスをコンテナに詰めて長期稼働させられるのは、自分のセルフホスト志向にちょうど合う。
fork の主な狙いは、imnot/engine/patterns/ にある handler が YAML をどう動的登録の FastAPI ルートに変えているかを見ることで、router.py のあのファクトリーの書き方が一番学びたいところだ。弱点も隠していない。push のコールバックにリトライはなく、ネイティブ HTTPS もないのでリバースプロキシで TLS を終端させる必要があり、Web UI もなく JSON しか出さない。SQLite はインスタンスをまたげず、単一ノードのおもちゃだ。家のマシンでテストを回すには十分。
一つの罠: 実行時に HTTP で書き込んだ partner はコンテナのローカルファイルシステムに落ちるため、Pod が再起動すると消える。/app/partners と /app/data に永続ボリュームを自分でマウントする必要がある。これは bug ではなく、永続化は自分の責任だと明言されている。