# 帳票設計書 6-rerun

## 概要

本ドキュメントは、Node.jsテストランナーの「Rerunレポーター」の設計仕様を記載したものである。テスト再実行情報をJSON形式でファイルに保存するレポーター機能の実装詳細、出力形式、処理フローについて説明する。

### 本帳票の処理概要

Rerunレポーターは、Node.jsテストランナーが実行したテスト結果を、失敗テストの再実行管理のためにJSON形式でファイルに保存する帳票である。成功したテストの情報（テストID、名前、子テスト、成功した試行回数）を記録し、次回実行時に失敗テストのみを選択的に再実行できるようにする。

**業務上の目的・背景**：大規模なテストスイートでは、一部のテストが失敗した場合に全テストを再実行するのは非効率である。Rerunレポーターは失敗テストの追跡と選択的再実行を可能にし、テスト実行時間を短縮する。また、フラッキーテスト（断続的に失敗するテスト）の特定と管理にも活用される。`--test-rerun-failures`オプションで指定されたファイルに結果を保存する。

**帳票の利用シーン**：
- 大規模テストスイートでの失敗テストの再実行
- CI/CDパイプラインでの失敗テストの自動リトライ
- フラッキーテストの特定と管理
- テスト実行履歴の追跡
- 複数回実行での成功パターン分析

**主要な出力内容**：
1. テスト識別子（ファイルパス:行:列）
2. テスト名
3. 子テスト情報
4. 成功した試行回数（passed_on_attempt）
5. 複数実行の履歴（previousRuns配列）

**帳票の出力タイミング**：
- 全テスト完了後にJSONファイルとして出力
- `--test-rerun-failures`オプションで指定されたパスに保存
- 既存ファイルがある場合は履歴として追加

**帳票の利用者**：
- CI/CDシステム
- テスト自動化エンジニア
- ソフトウェア開発者

## 帳票種別

テスト再実行情報 / JSON形式

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | CLIターミナル | `node --test --test-rerun-failures=./rerun.json` | テスト実行コマンド |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | JSON |
| 用紙サイズ | N/A（ファイル出力） |
| 向き | N/A |
| ファイル名 | --test-rerun-failuresで指定されたパス |
| 出力方法 | ファイル書き込み（writeFileSync） |
| 文字コード | UTF-8 |

### JSON固有設定

| 項目 | 内容 |
|-----|------|
| インデント | 2スペース |
| 配列構造 | previousRuns履歴を配列で管理 |
| キー形式 | `file:line:column` 形式のテストID |

## 帳票レイアウト

### レイアウト概要

Rerunレポーターは、複数回の実行履歴を配列として保持し、各実行の成功テスト情報をオブジェクトとして格納する。

```json
[
  {
    "src/test.js:10:5": {
      "name": "test case 1",
      "children": [...],
      "passed_on_attempt": 1
    },
    "src/test.js:20:5:(1)": {
      "name": "test case 2",
      "children": [...],
      "passed_on_attempt": 2
    }
  },
  // 前回の実行結果...
]
```

### 実行履歴配列

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | 履歴配列 | 各実行の結果 | previousRuns | JSON配列 |

### テスト結果オブジェクト

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | テストID（キー） | テスト識別子 | getTestId() | `{file}:{line}:{column}` |
| 2 | name | テスト名 | data.name | 文字列 |
| 3 | children | 子テスト情報 | currentTest.children | オブジェクト配列 |
| 4 | passed_on_attempt | 成功した試行回数 | data.details.passed_on_attempt ?? data.details.attempt | 数値 |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| test:pass イベント | 成功テストのみ記録 | Yes |
| --test-rerun-failures | 出力ファイルパス指定 | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | イベント発生順 | 昇順（時系列） |

### 改ページ条件

なし（JSON構造）

## データベース参照仕様

### 参照テーブル一覧

本帳票はデータベースを参照しない。テストランナーからのイベントストリームと既存のrerun.jsonファイルを入力として使用する。

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| N/A | - | - |

### イベントデータ構造

#### test:start イベント

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| data.name | ツリー構築用 | - | currentSuiteに追加 |
| data.nesting | ネストレベル | - | 親子関係構築用 |

#### test:pass イベント

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| data.name | name | - | 表示用 |
| data.file | テストID | - | getTestId()で使用 |
| data.line | テストID | - | getTestId()で使用 |
| data.column | テストID | - | getTestId()で使用 |
| data.details.passed_on_attempt | passed_on_attempt | - | 成功試行回数 |
| data.details.attempt | passed_on_attempt | passed_on_attemptがない場合 | フォールバック |

#### test:fail イベント

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| - | - | - | ツリー構築のみ、結果には記録しない |

### グローバルオプション

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| globalOptions.cwd | パス計算基準 | - | 相対パス計算用 |
| globalOptions.rerunFailuresFilePath | 出力ファイルパス | - | JSON保存先 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| テストID | `${relative(cwd, file)}:${line}:${column}` | N/A | 相対パス使用 |
| 重複ID | `${identifier}:(${disambiguator[identifier]})` | N/A | 同一IDの場合に連番付与 |
| passed_on_attempt | passed_on_attempt ?? attempt | N/A | nullish coalescing |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[テストランナー開始] --> B[previousRuns読み込み]
    B --> C[イベント待機]
    C --> D{イベントタイプ判定}
    D -->|test:start| E[startTest: ツリーノード作成]
    D -->|test:pass| F[テスト結果処理]
    D -->|test:fail| G[ツリー更新のみ]
    E --> C
    F --> H[getTestId: ID生成]
    H --> I{ID重複?}
    I -->|Yes| J[連番付与]
    I -->|No| K[disambiguatorに登録]
    J --> K
    K --> L[objにテスト情報追加]
    L --> C
    G --> C
    C --> M[イベント終了]
    M --> N[objをpreviousRunsに追加]
    N --> O[writeFileSync: JSON保存]
```

### startTest関数処理

```mermaid
flowchart TD
    A[test:startイベント] --> B[現在のスイートを保存]
    B --> C[新しいノード作成]
    C --> D{親スイートあり?}
    D -->|Yes| E[親の子配列に追加]
    D -->|No| F[rootsに追加]
    E --> G[currentSuiteを更新]
    F --> G
```

### getTestId関数処理

```mermaid
flowchart TD
    A[テストデータ受信] --> B[相対パス計算]
    B --> C[ID生成: file:line:column]
    C --> D{disambiguatorに存在?}
    D -->|Yes| E[連番付与: ID:(count)]
    D -->|No| F[disambiguatorに登録]
    E --> G[カウントインクリメント]
    F --> G
    G --> H[IDを返却]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| ファイル読み込みエラー | rerun.jsonが存在しない | - | 空配列[]として扱う（parsePreviousRuns） |
| ファイル書き込みエラー | 書き込み権限がない | - | 例外がスロー |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | テスト数に依存（メモリに全結果を保持） |
| 目標出力時間 | 全テスト完了後に一括出力 |
| 同時出力数上限 | 1（シングルファイル） |

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

- ファイルパスが出力されるため、ディレクトリ構造が露出する
- テスト名に含まれる機密情報がそのまま出力される可能性がある
- 出力ファイルはファイルシステムに永続化され、適切なアクセス制御が必要

## 備考

- reportReruns関数はクロージャを返し、previousRunsとglobalOptionsを保持
- 標準出力ではなくファイルに直接書き込む（writeFileSync）
- JSONフォーマットはインデント2スペース（JSONStringify第3引数）
- test:failイベントはツリー構築には使用するが、結果オブジェクトには追加しない
- 重複テストID（同じファイル位置で複数回実行）は連番で区別

---

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

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

### 推奨読解順序

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

出力されるJSON構造とテストIDの形式を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | rerun.js | `lib/internal/test_runner/reporter/rerun.js` | objとdisambiguatorの構造 |
| 1-2 | utils.js | `lib/internal/test_runner/utils.js` | parsePreviousRuns関数（154-166行目） |

**読解のコツ**: disambiguatorは同一テストIDの重複を管理するオブジェクトで、カウンタとして使用される。

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

reportReruns関数のクロージャ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | rerun.js | `lib/internal/test_runner/reporter/rerun.js` | reportReruns関数全体（11-72行目） |

**主要処理フロー**:
1. **11行目**: reportReruns関数 - previousRunsとglobalOptionsを受け取るクロージャ
2. **12行目**: 内部のreporter関数を返却
3. **13-16行目**: 初期化（obj, disambiguator, currentSuite, roots）
4. **18-20行目**: getTestId関数 - テスト識別子生成
5. **22-31行目**: startTest関数 - ツリーノード作成
6. **33-67行目**: イベントループ処理
7. **69-70行目**: 結果の保存

#### Step 3: テストID生成ロジックを理解する

getTestId関数と重複ID処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | rerun.js | `lib/internal/test_runner/reporter/rerun.js` | getTestId関数（18-20行目） |
| 3-2 | rerun.js | `lib/internal/test_runner/reporter/rerun.js` | 重複ID処理（52-58行目） |

**主要処理フロー**:
- **19行目**: `relative(globalOptions.cwd, data.file):${data.line}:${data.column}`
- **52-58行目**: disambiguatorによる重複チェックと連番付与

#### Step 4: イベント処理ロジックを理解する

test:start, test:pass, test:failの処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | rerun.js | `lib/internal/test_runner/reporter/rerun.js` | イベント処理（33-67行目） |

**主要処理フロー**:
- **35-36行目**: test:start - startTest呼び出し
- **37-47行目**: test:pass/fail共通 - ツリー更新
- **51-66行目**: test:pass専用 - 結果オブジェクト作成

#### Step 5: ファイル出力ロジックを理解する

JSONファイルへの保存処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | rerun.js | `lib/internal/test_runner/reporter/rerun.js` | ファイル保存（69-70行目） |

**主要処理フロー**:
- **69行目**: previousRunsにobjを追加
- **70行目**: writeFileSyncでJSON保存（インデント2）

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

```
node --test --test-rerun-failures=./rerun.json
    │
    ├─ lib/internal/test_runner/runner.js
    │      └─ parseCommandLine() → rerunFailuresFilePath取得
    │
    ├─ lib/internal/test_runner/utils.js
    │      └─ parsePreviousRuns() → 既存ファイル読み込み
    │
    └─ lib/internal/test_runner/reporter/rerun.js
           │
           ├─ reportReruns(previousRuns, globalOptions)
           │      └─ reporter(source) [クロージャ]
           │             ├─ getTestId()
           │             ├─ startTest()
           │             └─ イベントループ
           │
           └─ writeFileSync()
                  └─ fs
```

### データフロー図

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

既存rerun.json ───▶ parsePreviousRuns() ───▶ previousRuns配列
                              │
テストイベント ───▶ reporter() ───▶ obj構築
  ストリーム              │
                         ├─ test:start → ツリー構築
                         ├─ test:pass → obj[id] = {name, children, passed_on_attempt}
                         └─ test:fail → ツリー更新のみ
                                │
                         全イベント完了
                                │
                         previousRuns.push(obj)
                                │
                         writeFileSync() ───▶ rerun.json
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| rerun.js | `lib/internal/test_runner/reporter/rerun.js` | ソース | Rerunレポーターのメイン実装 |
| utils.js | `lib/internal/test_runner/utils.js` | ソース | parsePreviousRuns関数 |
| fs.js | `fs` | Node.js組込み | writeFileSync関数 |
| path.js | `path` | Node.js組込み | relative関数 |
| runner.js | `lib/internal/test_runner/runner.js` | ソース | テストランナーのメイン処理 |
