背景
PR #253(issue #251 — アダプタの JSON Content-Type 判定を ContentTypeMatcher::isJsonContentType() に統一)の multi-agent レビュー(/pr-review-toolkit:review-pr)で silent-failure-hunter が指摘。#251 以前から存在する既存ソフトスポットであり、アダプタ境界の修正とは独立のため #253 のスコープ外として切り出し。
#246 / #251 と同種(「非 JSON ボディの扱い」)の tech-debt。
現状
リクエスト / レスポンスの Content-Type が非 JSON だが、その media type が spec の content マップに宣言済みのとき、body validator はボディを一切検証せず成功扱いで返す。
src/Validation/Request/RequestBodyValidator.php:115-117 — isContentTypeInSpec() が true なら即 return []
src/Validation/Response/ResponseBodyValidator.php:76-79 — findContentTypeKey() がヒットすれば即 return new ResponseBodyValidationResult([], $matchedKey)
// RequestBodyValidator
if (!ContentTypeMatcher::isJsonContentType($normalizedType)) {
if (ContentTypeMatcher::isContentTypeInSpec($normalizedType, $content)) {
return []; // ← ボディを見ずに成功
}
// ...未宣言なら "Content-Type is not defined" で loud に落ちる
}
問題
spec が content: { "text/plain": { "schema": { ... } } } のように非 JSON media type に schema を明示的に付けている場合でも、その schema は一切評価されない。OpenAPI 仕様上 schema はどの media type にも付けられるが、本ツールの検証エンジンは JSON Schema 専用なので非 JSON ボディを検証できない。
これ自体は設計上の制約として妥当だが、「検証できないので skip した」という事実が一切表に出ないのが問題:
- レスポンス側は「spec が非 JSON content type しか宣言していない」ケースを
OpenApiValidationResult::skipped() で明示的に surface する経路を持つ(OpenApiResponseValidator.php 周辺)。しかしこの skip surfacing は matchedContentType === null のときだけ発火するため、spec キーにマッチした非 JSON type はこの経路を迂回し、clean な success として記録される。
- リクエスト側にはそもそも skip surfacing 相当の仕組みが無い。
RequestBodyValidator は単に return [](成功)で、skip reason も付かない。
結果、ユーザーは「非 JSON ボディの schema は検証されていない」ことに気付けず、coverage レポートも未検証のエンドポイントを clean pass として計上する。contract testing ツールの「検証していないものを検証済みに見せない」原則に反する。
やること
- 非 JSON Content-Type が spec の content マップにマッチし、かつその media type entry に
schema が付いているとき、検証をスキップした旨を診断として surface する
- レスポンス側:
matchedContentType が非 null の非 JSON ヒットでも Skipped + skip reason を立てられるよう経路を見直す
- リクエスト側:
Skipped surfacing 相当の仕組みを追加し、coverage tracker に skip reason を渡す(OpenApiCoverageTracker::recordRequest() は既に skip reason 引数を持つ)
schema が付いていない非 JSON entry(検証する schema がそもそも無い)は現状どおり静かに成功でよい — 区別する
- TDD: 非 JSON media type に
schema を付けた spec に対し、(a) 検証がスキップされること、(b) その skip が result / coverage に surface されることを検証する失敗テストを先に追加
ゴール
- spec が非 JSON media type に付けた
schema が「検証できないので skip した」と明示的に surface される(result の Skipped outcome / skip reason、coverage レポート)
- リクエスト側とレスポンス側で skip surface の挙動が揃う
schema 無しの非 JSON entry は従来どおり静かに成功
関連
背景
PR #253(issue #251 — アダプタの JSON Content-Type 判定を
ContentTypeMatcher::isJsonContentType()に統一)の multi-agent レビュー(/pr-review-toolkit:review-pr)で silent-failure-hunter が指摘。#251 以前から存在する既存ソフトスポットであり、アダプタ境界の修正とは独立のため #253 のスコープ外として切り出し。#246 / #251 と同種(「非 JSON ボディの扱い」)の tech-debt。
現状
リクエスト / レスポンスの Content-Type が非 JSON だが、その media type が spec の content マップに宣言済みのとき、body validator はボディを一切検証せず成功扱いで返す。
src/Validation/Request/RequestBodyValidator.php:115-117—isContentTypeInSpec()が true なら即return []src/Validation/Response/ResponseBodyValidator.php:76-79—findContentTypeKey()がヒットすれば即return new ResponseBodyValidationResult([], $matchedKey)問題
spec が
content: { "text/plain": { "schema": { ... } } }のように非 JSON media type にschemaを明示的に付けている場合でも、その schema は一切評価されない。OpenAPI 仕様上 schema はどの media type にも付けられるが、本ツールの検証エンジンは JSON Schema 専用なので非 JSON ボディを検証できない。これ自体は設計上の制約として妥当だが、「検証できないので skip した」という事実が一切表に出ないのが問題:
OpenApiValidationResult::skipped()で明示的に surface する経路を持つ(OpenApiResponseValidator.php周辺)。しかしこの skip surfacing はmatchedContentType === nullのときだけ発火するため、spec キーにマッチした非 JSON type はこの経路を迂回し、clean な success として記録される。RequestBodyValidatorは単にreturn [](成功)で、skip reason も付かない。結果、ユーザーは「非 JSON ボディの schema は検証されていない」ことに気付けず、coverage レポートも未検証のエンドポイントを clean pass として計上する。contract testing ツールの「検証していないものを検証済みに見せない」原則に反する。
やること
schemaが付いているとき、検証をスキップした旨を診断として surface するmatchedContentTypeが非 null の非 JSON ヒットでもSkipped+ skip reason を立てられるよう経路を見直すSkippedsurfacing 相当の仕組みを追加し、coverage tracker に skip reason を渡す(OpenApiCoverageTracker::recordRequest()は既に skip reason 引数を持つ)schemaが付いていない非 JSON entry(検証する schema がそもそも無い)は現状どおり静かに成功でよい — 区別するschemaを付けた spec に対し、(a) 検証がスキップされること、(b) その skip が result / coverage に surface されることを検証する失敗テストを先に追加ゴール
schemaが「検証できないので skip した」と明示的に surface される(result のSkippedoutcome / skip reason、coverage レポート)schema無しの非 JSON entry は従来どおり静かに成功関連
null/ scalar is silently treated as "no body" #246 — literal JSON null / スカラーボディの silent「ボディ無し」扱い(同種)