Skip to content

tech-debt(validation) — spec が宣言した非 JSON Content-Type のボディが診断なしで素通りする #254

@wadakatu

Description

@wadakatu

背景

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-117isContentTypeInSpec() が true なら即 return []
  • src/Validation/Response/ResponseBodyValidator.php:76-79findContentTypeKey() がヒットすれば即 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 は従来どおり静かに成功

関連

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions