検索/グラフ型の認可サービスを、テストを先に書いて直す

グラフ型の認可サービスを、テストを先に書いて直す

グラフ型の認可サービスを直す時に使う。リクエストの通り道を追い、リポジトリのビルドターゲットとlintの決まりに従い、失敗するテストを先に書き、生成ファイルには手を入れない。

このAIルールを使うには
## 正しさを最優先にし、リポジトリのビルドターゲットを使う プロジェクトの優先順位を次の順序で適用する。 1. 正確性:すべての認可判定が正しくなければならない。 2. 信頼性:サービスが利用可能な状態を保ち、予測可能な方法で復旧できなければならない。 3. 性能:正確性と信頼性を守ったうえでのみ、レイテンシーとメモリ割り当てを最小化する。 リポジトリが管理するビルドターゲットを正本として扱う。必要な生成処理、フラグ、競合検出、カバレッジ、lint、環境設定が含まれるため、その場限りのコマンドより優先する。 標準的なターゲット群には次を含める。 - 本番用ビルド。 - 完全なテストスイート。 - 指定したテストケースだけを対象とするテスト。 - メモリ指標を含むベンチマーク。 - コンテナを必要とする結合テスト。 - リポジトリが設定したツールバージョンによるlint。 - 生成されたテストダブル。 - インメモリまたはコンテナ上のデータストアを使うローカル開発。 ターゲットを実行する前に、その定義と必要な環境変数を読む。正規ターゲットが利用できない、または不明確な場合は、弱いコマンドへ黙って置き換えず、その不足を解消する。 ## 直す前に、リクエストがリゾルバーチェーンを通る道を追う 振る舞いを変更する前に、リクエストが通る完全なランタイム経路を対応付ける。順序は次のとおり。 1. HTTPまたはRPCリクエストを受け取る。 2. ミドルウェアを適用する。 3. トランスポートハンドラーへ入る。 4. コマンドまたはアプリケーションサービスを呼び出す。 5. 認可グラフを解決する。 6. ストレージへアクセスする。 7. レスポンスを返す。 起動と終了、ハンドラー、ビジネスコマンド、グラフ評価、計画、ストレージのインターフェースと実装、モデル解析、タプル検証、認証、API認可をそれぞれ担当するモジュールを特定する。 グラフ型チェックでは、リゾルバービルダーとリゾルバーインターフェースを一緒に確認する。リゾルバーパイプラインがキャッシュ、ディスパッチのスロットリング、シャドー比較を追加するのは、設定で有効になっている場合だけとなる。 チェーンが直線的だと決めつけない。リゾルバーシステムによっては、最後のリゾルバーが最初のリゾルバーへ戻る循環委譲チェーンを形成する。ディスパッチの振る舞いを変える前に、構築順序、終了条件、任意ラッパーと中核ローカルチェッカーの違いを確認する。 ディレクトリ名や設計上の推測より、実行時の配線を強い証拠として扱う。 ## lintの命名の決まりに従い、ラップしたエラーを見分けられるようにする importや名前を編集する前に、有効なlint設定を読む。次を維持する。 - 生成APIとパーサーに必要なalias。 - 設定済みのimportグループと順序。 - 余分なprefixを付けないインターフェース名。 - `NewTypeName`パターンのコンストラクター名。 - `WithPropertyName`パターンの関数型option名。 - `TypeNameOption func(*TypeName)`形式のoption型。 - プロジェクトのexport命名様式に従うexport済み定数。 - プロジェクトのローカル命名様式に従う未export定数とcontext key。 安定したドメインエラーをsentinel値として定義する。エラーの同一性を壊さずにcontextを追加する。 - `var ErrNotFound = errors.New("not found")`のようにsentinelを宣言する。 - `fmt.Errorf("load relationship: %w", ErrNotFound)`のようにcontextを付けてラップする。 照合には`errors.Is`または`errors.As`を使う。ラップされたエラーを直接比較せず、チェーンが失われるverbで整形しない。 ドメインからトランスポートへの変換を明示的な境界に置く。コマンドロジックはドメインエラーを返し、専用コンバーターがHTTPまたはRPCレスポンスへ対応付ける。 ## 失敗するテストを先に書き、認可のmatrix testを保つ 振る舞いを変更する時はテスト駆動開発を使う。 1. 欠けている振る舞いまたは誤った振る舞いを表す、対象を絞ったテストを追加する。 2. そのテストを実行し、意図した理由で失敗することを確認する。 3. 合格させられる最小の変更を実装する。 4. 対象を絞ったテストを実行し、その後に直接影響するテストスイートを実行する。 5. テストが合格してからだけリファクタリングする。 認可アルゴリズムでは、モデル、関係タプル、check API、オブジェクト一覧API、ユーザー一覧APIの期待結果を定義するmatrix testを維持する。matrixの各stageでは、モデルとassertionを一緒に読めるようにする。 最小のmatrix caseでは、次の順序と意味を維持する。 1. 例として`viewer_can_read_document`のようにテスト名を付ける。 2. stage modelで`user`型と`document`型を定義する。 3. userを受け入れる`viewer`関係を定義する。 4. `document:1`、`viewer`、`user:alice`のtupleを追加する。 5. 同じobject、relation、userをcheckした結果が`true`になることをassertする。 埋め込みテストアセットを対応するrunnerへ登録した状態に保つ。APIに複数の一覧取得面またはチェック面がある場合は、一つの面で他も網羅できると決めつけず、関係する各runnerを検証する。 高負荷のグラフ経路、リゾルバー選択、ストレージアクセス、メモリ割り当てに敏感なコードを変更する時はベンチマークを使う。データストア固有の振る舞いには、コンテナを使うテストを実施する。 ## 生成ファイルとほかのリポジトリのschemaを手で直さない 生成されたmock、生成されたprotocol binding、その他の生成物を直接編集しない。ソースまたはgeneratorを変更し、正規ターゲットを通じて再生成する。 下流サービスのリポジトリで、外部管理のschema定義を変更しない。schema変更はその定義を管理するリポジトリで行ってレビューし、その後に下流dependencyを更新する。 次の運用上の落とし穴を考慮する。 - 完全なテストターゲットはmockを自動再生成することがあるが、言語レベルのテストコマンドを直接実行しても再生成されない場合がある。 - SQLデータストアの結合テストにはコンテナランタイムが必要な場合がある。 - チェーンが循環しているため、リゾルバーの再帰が意図した動作である場合がある。 - lint設定の構文はバージョンに依存する。 - ストレージインターフェースを変更する時は、すべてのbackendと共有contract testを同時に変更する必要がある。 必要なgenerator、コンテナランタイム、上流schemaを利用できない場合は、不足している前提条件を報告する。派生成果物を手作業で変更して、完了したように見せかけない。 ## 新しいAPI endpointやストレージの変更で、必要な手順を全部たどる 新しいAPI endpointを追加する場合: 1. 管理対象のAPI schemaを更新する。 2. トランスポートハンドラーを追加する。 3. コマンドまたはアプリケーションサービスのロジックを追加する。 4. トランスポート境界でドメインエラーを対応付ける。 5. 対象を絞ったテストとmatrix testを追加する。 ストレージインターフェースを変更する場合: 1. インターフェースを更新する。 2. 共有ストレージcontract testを更新する。 3. サポート対象のすべてのbackendへ変更を実装する。 4. 影響を受けるインメモリおよびコンテナ使用のテストスイートを実行する。 グラフリゾルバーを変更する場合: 1. リゾルバーインターフェースを実装する。 2. builder optionを追加または更新する。 3. 条件付き構成と循環委譲を検証する。 4. 正確性、スロットリングまたはキャッシュとの相互作用、終了条件をテストする。 5. 高負荷経路が変わる可能性がある場合はベンチマークを実行する。 型システムを変更する場合は、解析、検証、重み付きまたは最適化済みグラフロジック、関連するvalidation testを一つのまとまった変更として更新する。 完了を報告する前に、意図しない生成ファイルの編集が差分にないか確認し、対象テストの失敗が合格へ変わったことを確かめ、直接影響する正規ターゲットを実行する。実行できなかったテスト種別がある場合は、必要だった前提条件を正確に記録する。