背景
PR #257(issue #256 — ResponseBodyValidator の malformed-schema ガード)の multi-agent レビュー(/pr-review-toolkit:review-pr)で silent-failure-hunter が指摘。#256 以前から存在する既存ギャップで、#256 が追加した content / content[mediaType] / schema の非配列ガードの一階層上にあたるため、content/schema 階層に限定された #256 のスコープ外として切り出し。
現状
OpenApiResponseValidator::validate() は matched response key を解決したあと:
// src/OpenApiResponseValidator.php:190
$responseSpec = $responses[$matchedResponseKey];
を validateBody() / validateHeaders() に渡す。両メソッドは引数を array 型で受ける:
private function validateBody(..., array $responseSpec, ...): ResponseBodyValidationResult
private function validateHeaders(..., array $responseSpec, ...): array
問題
spec の responses map のエントリが非配列スカラーのとき(例: responses: {"200": "oops"}、未解決 $ref がスカラーに decode された場合等)、$responseSpec がスカラーになり、validateBody() / validateHeaders() の array 引数型で TypeError が発生する。
TypeError extends Error(RuntimeException ではない)ため、validateBody() 内の catch (RuntimeException $e) boundary を素通りし、読みやすい spec エラーではなくスタックトレースだけのクラッシュになる。
これは PR #257 (#256) が content 非配列に対して追加したガードと 構造的に同一の問題。RequestBodyValidator は requestBody 自体が非配列のケースを loud な spec エラー(Malformed 'requestBody' ...)として弾いており、response 側の responses[$status] にはその対称物が無い。
既存 fixture tests/fixtures/specs/malformed-response.json が "200": "this should be an object, not a scalar" を持つが、現状この fixture は OpenApiCoverageTrackerTest でのみ使われ、validator 経路では未テスト=このクラッシュ経路が露出している。
やること
OpenApiResponseValidator::validate() の $responseSpec = $responses[$matchedResponseKey](line 190 付近)直後に、!is_array($responseSpec) ガードを追加し、Malformed 'responses[$matchedResponseKey]' for {METHOD} {path} in '{spec}' spec: expected object, got scalar. 形式の loud な spec エラーを OpenApiValidationResult::failure() で返す
RequestBodyValidator の requestBody 非配列ガード(Malformed 'requestBody' ...)と対称
- TDD: 非配列
responses[$status] エントリを持つ spec(malformed-response.json 流用可)に対し、未捕捉 TypeError ではなく isValid() === false + spec エラーが返ることを検証する失敗テストを先に追加
ゴール
- 非配列
responses[$status] エントリで TypeError が出ず、RequestBodyValidator と同じ粒度の loud な spec エラーとして surface する
- request/response で malformed-spec 検出の挙動が揃う
関連
背景
PR #257(issue #256 —
ResponseBodyValidatorの malformed-schema ガード)の multi-agent レビュー(/pr-review-toolkit:review-pr)で silent-failure-hunter が指摘。#256 以前から存在する既存ギャップで、#256 が追加したcontent/content[mediaType]/schemaの非配列ガードの一階層上にあたるため、content/schema階層に限定された #256 のスコープ外として切り出し。現状
OpenApiResponseValidator::validate()は matched response key を解決したあと:を
validateBody()/validateHeaders()に渡す。両メソッドは引数をarray型で受ける:問題
spec の
responsesmap のエントリが非配列スカラーのとき(例:responses: {"200": "oops"}、未解決$refがスカラーに decode された場合等)、$responseSpecがスカラーになり、validateBody()/validateHeaders()のarray引数型でTypeErrorが発生する。TypeError extends Error(RuntimeExceptionではない)ため、validateBody()内のcatch (RuntimeException $e)boundary を素通りし、読みやすい spec エラーではなくスタックトレースだけのクラッシュになる。これは PR #257 (#256) が
content非配列に対して追加したガードと 構造的に同一の問題。RequestBodyValidatorはrequestBody自体が非配列のケースを loud な spec エラー(Malformed 'requestBody' ...)として弾いており、response 側のresponses[$status]にはその対称物が無い。既存 fixture
tests/fixtures/specs/malformed-response.jsonが"200": "this should be an object, not a scalar"を持つが、現状この fixture はOpenApiCoverageTrackerTestでのみ使われ、validator 経路では未テスト=このクラッシュ経路が露出している。やること
OpenApiResponseValidator::validate()の$responseSpec = $responses[$matchedResponseKey](line 190 付近)直後に、!is_array($responseSpec)ガードを追加し、Malformed 'responses[$matchedResponseKey]' for {METHOD} {path} in '{spec}' spec: expected object, got scalar.形式の loud な spec エラーをOpenApiValidationResult::failure()で返すRequestBodyValidatorのrequestBody非配列ガード(Malformed 'requestBody' ...)と対称responses[$status]エントリを持つ spec(malformed-response.json流用可)に対し、未捕捉TypeErrorではなくisValid() === false+ spec エラーが返ることを検証する失敗テストを先に追加ゴール
responses[$status]エントリでTypeErrorが出ず、RequestBodyValidatorと同じ粒度の loud な spec エラーとして surface する関連
ResponseBodyValidatorの非配列content/schemaガード(同種・直接の親、content/schema階層に限定)src/Validation/Request/RequestBodyValidator.phpのrequestBody非配列ガード(対称実装の参照元)