# 機能設計書 119-Testing API

## 概要

本ドキュメントは、Next.jsの実験的テスト支援API（`next/server/testing`）の設計を記載する。`unstable_getResponseFromNextConfig`と`unstable_doesMiddlewareMatch`の2つの主要関数により、next.config.jsのカスタムルート（headers/redirects/rewrites）とミドルウェアのマッチング条件をユニットテストで検証可能にする。

### 本機能の処理概要

**業務上の目的・背景**：Next.jsアプリケーションのルーティング設定（redirects、rewrites、headers）とミドルウェアのマッチング条件は、サーバーを起動しないと検証が困難であった。本機能は、これらの設定をユニットテストレベルで検証可能にすることで、開発者のフィードバックサイクルを短縮し、設定ミスの早期発見を実現する。

**機能の利用シーン**：next.config.jsのリダイレクトルールが正しく機能するかテストする場面、リライトルールの動作を検証する場面、ミドルウェアのマッチング条件（matcher）が意図したURLパターンに一致するかテストする場面で利用される。

**主要な処理内容**：
1. `unstable_getResponseFromNextConfig`: 指定URLとnext.config.jsを受け取り、headers/redirects/rewritesのルールを評価してNextResponseを返す
2. `unstable_doesMiddlewareMatch`: ミドルウェアのconfig.matcherがURLに一致するか判定する
3. ヘルパー関数: `getRedirectUrl`、`getRewrittenUrl`、`isRewrite`でレスポンス検査を簡便化

**関連システム・外部連携**：Jest、Vitest等のテストフレームワークと連携する。

**権限による制御**：特に権限制御はない。テストコードから直接呼び出すAPIである。

## 関連画面

本機能はテスト支援APIであり、直接関連する画面は存在しない。

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | テストコード内でのAPI呼び出し |

## 機能種別

テスト支援・バリデーション

## 入力仕様

### 入力パラメータ

#### unstable_getResponseFromNextConfig

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| url | string | Yes | テスト対象のURL | 有効なURL |
| nextConfig | NextConfig | Yes | Next.js設定オブジェクト | - |
| headers | IncomingHttpHeaders | No | リクエストヘッダー | - |
| cookies | Record<string, string> | No | リクエストCookie | - |

#### unstable_doesMiddlewareMatch

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| config | MiddlewareSourceConfig | Yes | ミドルウェアのconfig（matcher設定） | - |
| url | string | Yes | テスト対象のURL | 有効なURL |
| headers | IncomingHttpHeaders | No | リクエストヘッダー | - |
| cookies | Record<string, string> | No | リクエストCookie | - |
| nextConfig | NextConfig | No | Next.js設定オブジェクト | - |

### 入力データソース

- テストコードからの直接呼び出し

## 出力仕様

### 出力データ

#### unstable_getResponseFromNextConfig

| 項目名 | 型 | 説明 |
|--------|-----|------|
| response | NextResponse | カスタムルート評価結果のレスポンスオブジェクト |

#### unstable_doesMiddlewareMatch

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | boolean | ミドルウェアがURLに一致するかどうか |

#### ヘルパー関数

| 関数名 | 戻り値 | 説明 |
|--------|--------|------|
| getRedirectUrl(response) | string \| null | リダイレクトURLを取得（locationヘッダー） |
| getRewrittenUrl(response) | string \| null | リライトURLを取得（x-middleware-rewriteヘッダー） |
| isRewrite(response) | boolean | リライトレスポンスかどうか |

### 出力先

- テストフレームワークへの戻り値

## 処理フロー

### 処理シーケンス

#### unstable_getResponseFromNextConfig

```
1. URLのパース
   └─ parse(url, true)でURLをパース
2. リクエストオブジェクトの構築
   └─ constructRequest()でMockedRequestを生成
3. カスタムルートの読み込み
   └─ loadCustomRoutes(nextConfig)でheaders/redirects/rewritesを取得
4. ルートのビルド
   └─ buildCustomRoute()で各ルートをマニフェスト形式に変換
5. ヘッダールールの評価
   └─ headerRoutesを順次マッチングし、レスポンスヘッダーを設定
6. リダイレクトルールの評価
   └─ redirectRoutesを順次マッチングし、一致時はNextResponse.redirect()を返す
7. リライトルールの評価
   └─ rewriteRoutes（beforeFiles + afterFiles + fallback）を順次マッチングし、一致時はNextResponse.rewrite()を返す
8. デフォルトレスポンス
   └─ いずれにも一致しない場合、status 200のNextResponseを返す
```

### フローチャート

```mermaid
flowchart TD
    A[URL + nextConfig受信] --> B[URLパース]
    B --> C[MockedRequest構築]
    C --> D[loadCustomRoutes]
    D --> E[headerRouteマッチング]
    E --> F[レスポンスヘッダー設定]
    F --> G{redirectRouteマッチ?}
    G -->|Yes| H[NextResponse.redirect返却]
    G -->|No| I{rewriteRouteマッチ?}
    I -->|Yes| J[NextResponse.rewrite返却]
    I -->|No| K[200 NextResponse返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | ルール優先順序 | headers -> redirects -> rewrites（beforeFiles + afterFiles + fallback）の順で評価 | unstable_getResponseFromNextConfig |
| BR-02 | 最初の一致で返却 | リダイレクト・リライトは最初にマッチしたルールの結果を返す | ルール評価時 |
| BR-03 | ヘッダーは累積 | ヘッダールールは複数マッチ可能で、全てのヘッダーが累積される | ヘッダー評価時 |
| BR-04 | matcher未定義時は全一致 | ミドルウェアのmatcherが未定義の場合、全URLに一致する | unstable_doesMiddlewareMatch |
| BR-05 | has/missing条件 | ルートにhas/missing条件がある場合、リクエスト情報との追加マッチングを行う | ルール評価時 |

### 計算ロジック

- `matchHas()`関数による条件付きマッチング（ヘッダー、クエリパラメータ、Cookie等）
- `prepareDestination()`関数によるパラメータ展開付きURL構築

## データベース操作仕様

本機能はデータベースを使用しない。

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | データベース操作なし |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | パスマッチエラー | regexマッチ成功だがpath-to-regexpのparam抽出失敗 | Errorをスロー（不整合検出） |

### リトライ仕様

リトライ機能は不要。テスト実行のため即時結果を返す。

## トランザクション仕様

トランザクション管理は不要。

## パフォーマンス要件

- テスト実行のため高速な応答が求められる
- サーバー起動不要のため、ミリ秒単位での評価が可能

## セキュリティ考慮事項

- テスト専用APIのため、プロダクションコードには含まれない
- `unstable_`プレフィックスにより実験的APIであることを明示

## 備考

- `unstable_`プレフィックスは実験的APIであることを示し、将来的にAPI仕様が変更される可能性がある
- `next/server/testing`パスからインポートして使用する
- constructRequest内部でMockedRequestとNodeNextRequestを使用してリクエストオブジェクトを構築する

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | middleware-testing-utils.ts | `packages/next/src/experimental/testing/server/middleware-testing-utils.ts` | MiddlewareSourceConfig型の定義 |

**読解のコツ**: MiddlewareSourceConfigは`matcher`プロパティのみを持つシンプルな型。matcherはstring | string[] | MatcherConfig型。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/next/src/experimental/testing/server/index.ts` | パッケージのエクスポート一覧 |

**主要処理フロー**:
1. **1行目**: config-testing-utilsからunstable_getResponseFromNextConfigをre-export
2. **2行目**: middleware-testing-utilsからunstable_doesMiddlewareMatchをre-export
3. **3行目**: utilsからgetRedirectUrl, getRewrittenUrl, isRewriteをre-export

#### Step 3: カスタムルートテスト関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | config-testing-utils.ts | `packages/next/src/experimental/testing/server/config-testing-utils.ts` | unstable_getResponseFromNextConfigの実装 |

**主要処理フロー**:
- **28-53行目**: matchRoute関数（ルートマッチングとパラメータ抽出）
- **77-159行目**: unstable_getResponseFromNextConfig関数（メイン処理）
- **88-90行目**: URLパース、リクエスト構築、カスタムルート読み込み
- **92-102行目**: headerRoutes, redirectRoutes, rewriteRoutesのビルド
- **104-112行目**: ヘッダールールの評価（累積）
- **138-148行目**: リダイレクトルールの評価（最初の一致で返却）
- **149-157行目**: リライトルールの評価（最初の一致で返却）
- **158行目**: デフォルト200レスポンス

#### Step 4: ミドルウェアマッチングテスト関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | middleware-testing-utils.ts | `packages/next/src/experimental/testing/server/middleware-testing-utils.ts` | unstable_doesMiddlewareMatchの実装 |

**主要処理フロー**:
- **19-40行目**: unstable_doesMiddlewareMatch関数
- **32-34行目**: matcher未定義時はtrue返却
- **35行目**: getMiddlewareMatchersでmatcher設定を標準形式に変換
- **36行目**: getMiddlewareRouteMatcherでマッチング関数を生成
- **39行目**: routeMatchFnでマッチング実行

#### Step 5: ユーティリティを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | utils.ts | `packages/next/src/experimental/testing/server/utils.ts` | constructRequest, getRedirectUrl, isRewrite, getRewrittenUrl |

**主要処理フロー**:
- **8-32行目**: constructRequest関数（MockedRequest + NodeNextRequestの構築）
- **38-40行目**: getRedirectUrl（locationヘッダー取得）
- **46-48行目**: isRewrite（x-middleware-rewriteヘッダーの有無判定）
- **54-56行目**: getRewrittenUrl（x-middleware-rewriteヘッダー取得）

### プログラム呼び出し階層図

```
unstable_getResponseFromNextConfig
    |
    +-- parse(url, true)
    +-- constructRequest({ url, headers, cookies })
    |       +-- MockedRequest
    |       +-- NodeNextRequest
    +-- loadCustomRoutes(nextConfig)
    +-- buildCustomRoute() (header/redirect/rewrite)
    +-- matchRoute() (各ルート)
    |       +-- pathname.match(route.regex)
    |       +-- match(route.source)(pathname)
    |       +-- matchHas(request, query, has, missing)
    +-- prepareDestination()
    +-- NextResponse.redirect() / NextResponse.rewrite()

unstable_doesMiddlewareMatch
    |
    +-- getMiddlewareMatchers(config.matcher, nextConfig)
    +-- getMiddlewareRouteMatcher(matchers)
    +-- constructRequest({ url, headers, cookies })
    +-- routeMatchFn(pathname, request, searchParams)
```

### データフロー図

```
[入力]                           [処理]                        [出力]

url + nextConfig -----------> loadCustomRoutes -----> ルール一覧
headers + cookies ----------> constructRequest -----> MockedRequest
                                    |
                              ルール評価
                                    |
                              matchRoute -> prepareDestination
                                    |
                              NextResponse ---------> テスト検証値

url + config.matcher -------> getMiddlewareMatchers -> matcher一覧
                              getMiddlewareRouteMatcher
                                    |
                              routeMatchFn ---------> boolean
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/next/src/experimental/testing/server/index.ts` | ソース | パッケージエントリーポイント |
| config-testing-utils.ts | `packages/next/src/experimental/testing/server/config-testing-utils.ts` | ソース | unstable_getResponseFromNextConfig |
| middleware-testing-utils.ts | `packages/next/src/experimental/testing/server/middleware-testing-utils.ts` | ソース | unstable_doesMiddlewareMatch |
| utils.ts | `packages/next/src/experimental/testing/server/utils.ts` | ソース | ヘルパー関数群 |
| config-testing-utils.test.ts | `packages/next/src/experimental/testing/server/config-testing-utils.test.ts` | テスト | config-testing-utilsのユニットテスト |
| middleware-testing-utils.test.ts | `packages/next/src/experimental/testing/server/middleware-testing-utils.test.ts` | テスト | middleware-testing-utilsのユニットテスト |
