# 通知設計書 6-リトライエラー通知

## 概要

本ドキュメントは、Jestテストフレームワークにおけるコンソール出力機能のうち、テストリトライ時のエラー情報をコンソールへ出力する「リトライエラー通知」の設計を記載する。

### 本通知の処理概要

リトライエラー通知は、テストがリトライされた際に各リトライのエラー情報をコンソールへ出力する機能である。DefaultReporter.printTestFileHeaderメソッド内で実行され、リトライ回数とそれぞれのエラーメッセージ・スタックトレースを表示する。

**業務上の目的・背景**：テストが不安定（フレーキー）な場合、Jestはテストを自動的にリトライする機能を持つ。リトライエラー通知により、開発者はどのテストがリトライされ、各リトライでどのようなエラーが発生したかを把握できる。これにより、不安定なテストの根本原因を特定しやすくなる。

**通知の送信タイミング**：テストファイルの実行完了時（onTestResult → printTestFileHeader内）で、各テストケースのリトライ情報がチェックされ、リトライエラーがある場合に出力される。

**通知の受信者**：テストを実行した開発者のコンソール（標準エラー出力）に表示される。

**通知内容の概要**：「LOGGING RETRY ERRORS」という黄色いヘッダーラベルと失敗したテスト名、続いて各リトライ（RETRY 1, RETRY 2, ...）のエラーメッセージとスタックトレースが表示される。

**期待されるアクション**：開発者はリトライが発生したテストを特定し、各リトライのエラー内容を分析して、テストの不安定性の原因を調査・修正する。

## 通知種別

コンソール出力（標準エラー出力）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（printTestFileHeader内で直接呼び出し） |
| 優先度 | 中 |
| リトライ | なし |

### 送信先決定ロジック

logメソッドを通じて標準エラー出力（process.stderr）に対して出力する。

## 通知テンプレート

### コンソール出力の場合

| 項目 | 内容 |
|-----|------|
| 出力先 | process.stderr |
| ヘッダーラベル | `LOGGING RETRY ERRORS`（黄色背景・太字・反転） |
| リトライラベル | `RETRY {N}`（青色背景・太字・反転） |

### 本文テンプレート

```
 LOGGING RETRY ERRORS  {testFullName}
 RETRY 1

{errorMessage}
{stackTrace}

 RETRY 2

{errorMessage}
{stackTrace}
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| testFullName | テストケースのフルネーム | TestCaseResult.fullName | Yes |
| index | リトライのインデックス（0始まり） | retryReasons配列のインデックス | Yes |
| errorMessage | エラーメッセージ | retryReasons[index]から分離 | Yes |
| stackTrace | スタックトレース | retryReasons[index]から分離・フォーマット | No（noStackTrace時は空） |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| イベント | onTestResult → printTestFileHeader | testResult.retryReasons.length > 0 | テスト結果にリトライ情報がある場合 |

### 送信条件詳細

printTestFileHeader内で各テストケースのtestResult.retryReasonsをチェックし、配列が存在し長さが1以上の場合に出力される。

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| retryReasons === undefined または length === 0 | リトライが発生していない場合 |
| globalConfig.noStackTrace === true | スタックトレースは出力されない（メッセージのみ） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[printTestFileHeader呼び出し] --> B[testResultsをループ]
    B --> C{retryReasons存在?}
    C -->|No| G[次のtestResult]
    C -->|Yes| D{length > 0?}
    D -->|No| G
    D -->|Yes| E[LOGGING RETRY ERRORSヘッダー出力]
    E --> F[retryReasonsをループ]
    F --> H[separateMessageFromStack呼び出し]
    H --> I{noStackTrace?}
    I -->|Yes| J[スタックトレース空文字]
    I -->|No| K[formatStackTrace呼び出し]
    J --> L[indentAllLines呼び出し]
    K --> L
    L --> M[RETRY {N}ラベル出力]
    M --> N[メッセージ・スタック出力]
    N --> O{次のretryReasonあり?}
    O -->|Yes| F
    O -->|No| G
    G --> P{次のtestResultあり?}
    P -->|Yes| B
    P -->|No| Q[終了]
```

## データベース参照・更新仕様

### 参照テーブル一覧

本通知はデータベースを使用しない。メモリ上のデータ構造からデータを取得する。

| データソース | 用途 | 備考 |
|-----------|------|------|
| TestResult | テスト結果 | printTestFileHeader引数 |
| TestCaseResult | 個別テストケース結果 | TestResult.testResultsの各要素 |
| GlobalConfig | グローバル設定 | noStackTrace設定の参照 |
| ProjectConfig | プロジェクト設定 | スタックトレースフォーマット用 |

### 参照項目詳細

#### TestCaseResult

| 参照項目 | 用途 | 取得条件 |
|--------|------|---------|
| retryReasons | リトライ時のエラー情報配列 | なし |
| fullName | テストケースのフルネーム | なし |

#### GlobalConfig

| 参照項目 | 用途 | 取得条件 |
|--------|------|---------|
| noStackTrace | スタックトレース表示制御 | なし |

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| なし | separateMessageFromStackとformatStackTraceは安全に実行される | - |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | N/A |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（テスト結果出力時に即座に出力）

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

- コンソール出力のみのため、外部への情報漏洩リスクはない
- スタックトレースにはソースコードの内容が含まれる可能性がある
- ローカル環境でのみ表示される

## 備考

- `separateMessageFromStack`関数は`jest-message-util`パッケージから提供される
- `formatStackTrace`関数も`jest-message-util`パッケージから提供される
- `indentAllLines`関数でエラーメッセージがインデントされる
- スタックトレースは`chalk.dim`でグレー表示される
- リトライインデックスは0始まりだが、表示時は1始まり（`index + 1`）

---

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

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

### 推奨読解順序

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

まず、通知処理で使用されるデータ構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | TestResult型 | `packages/jest-test-result` | TestCaseResult.retryReasonsの構造を確認 |

**読解のコツ**: retryReasonsは文字列の配列で、各要素がリトライ時のエラーメッセージとスタックトレースを含む。

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

処理の起点となるファイル・関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DefaultReporter.ts | `packages/jest-reporters/src/DefaultReporter.ts` | printTestFileHeaderメソッド内のリトライエラー処理 |

**主要処理フロー**:
1. **199-235行目**: `printTestFileHeader`メソッド
2. **205行目**: `result.testResults`のループ開始
3. **206行目**: `testResult.retryReasons`の取得
4. **207行目**: `retryReasons && retryReasons.length > 0`の条件チェック
5. **208-210行目**: ヘッダーラベルの出力
6. **211行目**: `retryReasons.entries()`でループ
7. **212行目**: `separateMessageFromStack`呼び出し
8. **213-217行目**: スタックトレースのフォーマット
9. **219行目**: `indentAllLines`でメッセージをインデント
10. **221-222行目**: RETRYラベルの出力
11. **224行目**: メッセージとスタックの出力

#### Step 3: メッセージ分離処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | jest-message-util | `packages/jest-message-util/src/index.ts` | separateMessageFromStack関数 |

**主要処理フロー**:
- エラー文字列をメッセージ部分とスタックトレース部分に分離

#### Step 4: スタックトレースフォーマットを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | jest-message-util | `packages/jest-message-util/src/index.ts` | formatStackTrace関数 |

**主要処理フロー**:
- スタックトレースを読みやすい形式にフォーマット
- ファイルパスの相対化、行番号のハイライトなど

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

```
DefaultReporter.onTestResult()
    │
    └─ printTestFileHeader()
           │
           └─ for (testResult of result.testResults)
                  │
                  └─ [retryReasons存在]
                         │
                         ├─ log(LOGGING RETRY ERRORS + fullName)
                         │
                         └─ for ([index, retryReasons] of retryReasons.entries())
                                │
                                ├─ separateMessageFromStack()
                                │      └─ {message, stack}を返却
                                │
                                ├─ [!noStackTrace] formatStackTrace()
                                │      └─ フォーマット済みスタック
                                │
                                ├─ indentAllLines()
                                │      └─ インデント済みメッセージ
                                │
                                └─ log(RETRY {N} + message + stack)
```

### データフロー図

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

TestResult ─────────────▶ printTestFileHeader()
  testResults[]                 │
    retryReasons[]              ├─▶ ループ処理
    fullName                    │       │
                                │       ├─▶ separateMessageFromStack()
GlobalConfig ───────────────────┤       │       │
  noStackTrace                  │       │       └─▶ {message, stack}
                                │       │
ProjectConfig ──────────────────┤       ├─▶ formatStackTrace()
                                │       │       └─▶ フォーマット済みstack
testPath ───────────────────────┤       │
                                │       ├─▶ indentAllLines()
                                │       │       └─▶ インデント済みmessage
                                │       │
                                │       └─▶ log() ───▶ stderr
                                │              │
                                │              ├─ "LOGGING RETRY ERRORS testName"
                                │              ├─ "RETRY 1"
                                │              └─ "message\nstack"
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| DefaultReporter.ts | `packages/jest-reporters/src/DefaultReporter.ts` | ソース | printTestFileHeader実装（205-227行目） |
| BaseReporter.ts | `packages/jest-reporters/src/BaseReporter.ts` | ソース | log()メソッド |
| jest-message-util | `packages/jest-message-util/src/index.ts` | ソース | separateMessageFromStack, formatStackTrace, indentAllLines |
| TestResult型 | `packages/jest-test-result` | 型定義 | TestCaseResult.retryReasons |
