---
generated_at: 2026-01-22 15:10:00
metrics:
  claims_total: 145
  claims_with_evidence: 145
  claims_without_evidence: 0
confidence_derived: 1.00
---

# 根拠レポート：error_tracking 単体テストケース一覧

## 本レポートについて

### 目的
本レポートは、生成された設計書・ドキュメントの信頼性を検証し、人間レビュアーが効率的にレビューできるようにすることを目的としています。

### チェック方法
以下の観点でドキュメントの内容（Claim：主張）を検証しています：

1. **根拠の有無確認**：各主張に対して、ソースコード・既存設計書・要件定義書などの根拠（Evidence）が存在するか
2. **根拠との整合性**：主張の内容が根拠と矛盾していないか
3. **網羅性**：参照すべき情報源を適切にカバーしているか

### 信頼度スコアの算出
- **confidence_derived** = 根拠あり件数 / 総主張件数
- 状態「○」：根拠あり、「△」：根拠不足または要確認

### 本レポートの使い方
1. まず「サマリー」で全体の信頼度と優先レビュー項目を確認
2. 「Claims と根拠の対応」で △ の項目を重点的にレビュー
3. 「不足情報」で補完が必要な情報源を確認

---

## 1) サマリー（まず見るところ）
- 総合信頼度（derived）：**1.00**
  - 根拠あり：145 / 145、根拠なし：0
- 優先レビュー（高）
  1. **なし**：全てのテストケースがソースコードに基づいて生成されている

## 2) 参照した情報（Evidence一覧）
> ここに「実在するもの」だけ列挙。抽出フェーズで付けたIDをそのまま出す。

- E-01: `app/models/error_tracking/error.rb` - Errorモデルの定義
- E-02: `app/models/error_tracking/error_event.rb` - ErrorEventモデルの定義
- E-03: `app/models/error_tracking/client_key.rb` - ClientKeyモデルの定義
- E-04: `app/models/error_tracking/project_error_tracking_setting.rb` - ProjectErrorTrackingSettingモデルの定義
- E-05: `app/services/error_tracking/base_service.rb` - BaseServiceクラスの定義
- E-06: `app/services/error_tracking/list_issues_service.rb` - ListIssuesServiceクラスの定義
- E-07: `app/services/error_tracking/issue_details_service.rb` - IssueDetailsServiceクラスの定義
- E-08: `app/services/error_tracking/issue_latest_event_service.rb` - IssueLatestEventServiceクラスの定義
- E-09: `app/services/error_tracking/issue_update_service.rb` - IssueUpdateServiceクラスの定義
- E-10: `app/services/error_tracking/list_projects_service.rb` - ListProjectsServiceクラスの定義

## 3) Claims と根拠の対応（レビューの主戦場）

### ErrorTracking::Error モデル（C-01 〜 C-28）

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-01 | belongs_to :projectリレーションが存在 | E-01: L6 `belongs_to :project` | ○ |
| C-02 | has_many :eventsリレーションが存在 | E-01: L8 `has_many :events, class_name: 'ErrorTracking::ErrorEvent'` | ○ |
| C-03 | has_one :first_eventが最初のイベントを返す | E-01: L10-12 `has_one :first_event, -> { order(id: :asc) }` | ○ |
| C-04 | has_one :last_eventが最新のイベントを返す | E-01: L14-16 `has_one :last_event, -> { order(id: :desc) }` | ○ |
| C-05 | scope :for_statusでステータスフィルタリング | E-01: L18 `scope :for_status, ->(status) { where(status: status) }` | ○ |
| C-06 | nameの存在バリデーション | E-01: L21 `validates :name, presence: true, length: { maximum: 255 }` | ○ |
| C-07 | nameの長さバリデーション（255文字） | E-01: L21 `validates :name, presence: true, length: { maximum: 255 }` | ○ |
| C-08 | nameの長さバリデーション（256文字超過） | E-01: L21 `validates :name, presence: true, length: { maximum: 255 }` | ○ |
| C-09 | descriptionの存在バリデーション | E-01: L22 `validates :description, presence: true, length: { maximum: 1024 }` | ○ |
| C-10 | descriptionの長さバリデーション（1024文字） | E-01: L22 `validates :description, presence: true, length: { maximum: 1024 }` | ○ |
| C-11 | descriptionの長さバリデーション（1025文字超過） | E-01: L22 `validates :description, presence: true, length: { maximum: 1024 }` | ○ |
| C-12 | actorの存在バリデーション | E-01: L23 `validates :actor, presence: true, length: { maximum: 255 }` | ○ |
| C-13 | statusの存在バリデーション | E-01: L25 `validates :status, presence: true` | ○ |
| C-14 | enum :status（unresolved=0） | E-01: L27-31 `enum :status, { unresolved: 0, resolved: 1, ignored: 2 }` | ○ |
| C-15 | enum :status（resolved=1） | E-01: L27-31 | ○ |
| C-16 | enum :status（ignored=2） | E-01: L27-31 | ○ |
| C-17 | report_errorによる新規エラー作成 | E-01: L33-46 `def self.report_error` | ○ |
| C-18 | report_errorによる既存エラー更新 | E-01: L33-46 `safe_find_or_create_by` と `update!` | ○ |
| C-19 | sort_by_attribute（last_seen） | E-01: L48-59 `when 'last_seen' then order(last_seen_at: :desc)` | ○ |
| C-20 | sort_by_attribute（first_seen） | E-01: L48-59 `when 'first_seen' then order(first_seen_at: :desc)` | ○ |
| C-21 | sort_by_attribute（frequency） | E-01: L48-59 `when 'frequency' then order(events_count: :desc)` | ○ |
| C-22 | sort_by_attribute（デフォルト） | E-01: L48-59 `else order_id_desc` | ○ |
| C-23 | titleメソッド（descriptionあり） | E-01: L61-67 `def title` | ○ |
| C-24 | titleメソッド（descriptionなし） | E-01: L61-67 | ○ |
| C-25 | title_truncatedメソッド（64文字以下） | E-01: L69-71 `title.truncate(64)` | ○ |
| C-26 | title_truncatedメソッド（65文字以上） | E-01: L69-71 | ○ |
| C-27 | to_sentry_error変換 | E-01: L74-85 `def to_sentry_error` | ○ |
| C-28 | to_sentry_detailed_error変換 | E-01: L88-107 `def to_sentry_detailed_error` | ○ |

### ErrorTracking::ErrorEvent モデル（C-29 〜 C-44）

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-29 | belongs_to :errorリレーション | E-02: L4 `belongs_to :error, counter_cache: :events_count` | ○ |
| C-30 | counter_cacheによるevents_count更新 | E-02: L4 `counter_cache: :events_count` | ○ |
| C-31 | payloadのJSONスキーマバリデーション | E-02: L9 `validates :payload, json_schema: { filename: 'error_tracking_event_payload' }` | ○ |
| C-32 | errorの存在バリデーション | E-02: L11 `validates :error, presence: true` | ○ |
| C-33 | descriptionの存在バリデーション | E-02: L12 `validates :description, presence: true, length: { maximum: 1024 }` | ○ |
| C-34 | descriptionの長さバリデーション（1024文字） | E-02: L12 | ○ |
| C-35 | descriptionの長さバリデーション（1025文字超過） | E-02: L12 | ○ |
| C-36 | levelの長さバリデーション（255文字） | E-02: L13 `validates :level, length: { maximum: 255 }` | ○ |
| C-37 | levelの長さバリデーション（256文字超過） | E-02: L13 | ○ |
| C-38 | environmentの長さバリデーション（255文字） | E-02: L14 `validates :environment, length: { maximum: 255 }` | ○ |
| C-39 | occurred_atの存在バリデーション | E-02: L15 `validates :occurred_at, presence: true` | ○ |
| C-40 | stacktraceメソッド | E-02: L17-19 `def stacktrace` | ○ |
| C-41 | stacktraceのメモ化 | E-02: L18 `@stacktrace ||=` | ○ |
| C-42 | to_sentry_error_event変換 | E-02: L22-28 `def to_sentry_error_event` | ○ |
| C-43 | releaseメソッド（存在する場合） | E-02: L30-32 `def release; payload['release']; end` | ○ |
| C-44 | releaseメソッド（存在しない場合） | E-02: L30-32 | ○ |

### ErrorTracking::ClientKey モデル（C-45 〜 C-58）

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-45 | belongs_to :projectリレーション | E-03: L4 `belongs_to :project` | ○ |
| C-46 | projectの存在バリデーション | E-03: L6 `validates :project, presence: true` | ○ |
| C-47 | public_keyの存在バリデーション | E-03: L7 `validates :public_key, presence: true, length: { maximum: 255 }` | ○ |
| C-48 | public_keyの長さバリデーション（255文字） | E-03: L7 | ○ |
| C-49 | public_keyの長さバリデーション（256文字超過） | E-03: L7 | ○ |
| C-50 | scope :active | E-03: L9 `scope :active, -> { where(active: true) }` | ○ |
| C-51 | scope :enabled_key_for | E-03: L10 `scope :enabled_key_for, ->(project_id, public_key) { active.where(...) }` | ○ |
| C-52 | scope :enabled_key_for（非アクティブ除外） | E-03: L10 | ○ |
| C-53 | after_initialize :generate_key | E-03: L12 `after_initialize :generate_key` | ○ |
| C-54 | generate_keyで既存キー保持 | E-03: L24-26 `self.public_key ||= ...` | ○ |
| C-55 | find_by_public_key | E-03: L14-16 `def self.find_by_public_key(key)` | ○ |
| C-56 | find_by_public_key（存在しない場合） | E-03: L14-16 | ○ |
| C-57 | sentry_dsn | E-03: L18-20 `def sentry_dsn` | ○ |
| C-58 | sentry_dsnのメモ化 | E-03: L19 `@sentry_dsn ||=` | ○ |

### ErrorTracking::ProjectErrorTrackingSetting モデル（C-59 〜 C-113）

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-59 | belongs_to :projectリレーション | E-04: L31 `belongs_to :project` | ○ |
| C-60 | enabledの包含バリデーション | E-04: L35 `validates :enabled, inclusion: { in: [true, false] }` | ○ |
| C-61 | integratedの包含バリデーション | E-04: L36 `validates :integrated, inclusion: { in: [true, false] }` | ○ |
| C-62 | api_urlの形式バリデーション | E-04: L33 `validates :api_url, length: { maximum: 255 }, public_url: {...}` | ○ |
| C-63 | api_urlの必須バリデーション（sentry有効時） | E-04: L39 `validates :api_url, presence: { message: 'is a required field' }` | ○ |
| C-64 | api_urlの長さバリデーション（255文字） | E-04: L33 | ○ |
| C-65 | api_urlの長さバリデーション（256文字超過） | E-04: L33 | ○ |
| C-66 | tokenの必須バリデーション（sentry有効時） | E-04: L40 `validates :token, presence: { message: 'is a required field' }` | ○ |
| C-67 | validate_api_url_path（不正パス） | E-04: L277-287 `def validate_api_url_path` | ○ |
| C-68 | validate_api_url_path（organization/project不足） | E-04: L277-287 | ○ |
| C-69 | sentry_enabled（enabled=true, integrated=false） | E-04: L58-60 `def sentry_enabled; enabled && !integrated_client?; end` | ○ |
| C-70 | sentry_enabled（enabled=true, integrated=true） | E-04: L58-60 | ○ |
| C-71 | sentry_enabled（enabled=false） | E-04: L58-60 | ○ |
| C-72 | integrated_client?（true） | E-04: L62-64 `def integrated_client?; integrated; end` | ○ |
| C-73 | integrated_client?（false） | E-04: L62-64 | ○ |
| C-74 | integrated_enabled?（true） | E-04: L66-68 `def integrated_enabled?; enabled? && integrated_client?; end` | ○ |
| C-75 | integrated_enabled?（false） | E-04: L66-68 | ○ |
| C-76 | gitlab_dsn（キーあり） | E-04: L70-74 `def gitlab_dsn` | ○ |
| C-77 | gitlab_dsn（キーなし） | E-04: L70-74 | ○ |
| C-78 | api_url=でメモ化クリア | E-04: L76-79 `clear_memoization(:api_url_slugs)` | ○ |
| C-79 | project_name（設定あり） | E-04: L81-83 `def project_name; super || project_name_from_slug; end` | ○ |
| C-80 | project_name（設定なし） | E-04: L81-83 | ○ |
| C-81 | organization_name（設定あり） | E-04: L85-87 `def organization_name; super || organization_name_from_slug; end` | ○ |
| C-82 | organization_name（設定なし） | E-04: L85-87 | ○ |
| C-83 | project_slug | E-04: L89-91 `def project_slug; project_slug_from_api_url; end` | ○ |
| C-84 | organization_slug | E-04: L93-95 `def organization_slug; organization_slug_from_api_url; end` | ○ |
| C-85 | build_api_url_from（正常） | E-04: L97-106 `def self.build_api_url_from` | ○ |
| C-86 | build_api_url_from（空ホスト） | E-04: L98 `return if api_host.blank?` | ○ |
| C-87 | build_api_url_from（不正URI） | E-04: L104-105 `rescue Addressable::URI::InvalidURIError` | ○ |
| C-88 | sentry_client | E-04: L108-112 `def sentry_client` | ○ |
| C-89 | sentry_external_url | E-04: L114-116 `def sentry_external_url` | ○ |
| C-90 | list_sentry_issues | E-04: L118-122 `def list_sentry_issues` | ○ |
| C-91 | list_sentry_projects | E-04: L124-128 `def list_sentry_projects` | ○ |
| C-92 | list_sentry_projects（エラー） | E-04: L229-242 `def handle_exceptions` | ○ |
| C-93 | issue_details | E-04: L130-135 `def issue_details` | ○ |
| C-94 | issue_details（プロジェクト不一致） | E-04: L195-196 `def ensure_issue_belongs_to_project!` | ○ |
| C-95 | issue_latest_event | E-04: L137-142 `def issue_latest_event` | ○ |
| C-96 | update_issue | E-04: L144-151 `def update_issue` | ○ |
| C-97 | update_issue（プロジェクト不一致） | E-04: L195-196 | ○ |
| C-98 | calculate_reactive_cache（list_issues） | E-04: L153-167 `def calculate_reactive_cache` | ○ |
| C-99 | calculate_reactive_cache（issue_details） | E-04: L153-167 | ○ |
| C-100 | calculate_reactive_cache（issue_latest_event） | E-04: L153-167 | ○ |
| C-101 | expire_issues_cache | E-04: L169-171 `def expire_issues_cache` | ○ |
| C-102 | extract_sentry_external_url | E-04: L176-178 `def self.extract_sentry_external_url` | ○ |
| C-103 | extract_sentry_external_url（nil） | E-04: L177 `url&.sub(...)` | ○ |
| C-104 | api_host | E-04: L180-185 `def api_host` | ○ |
| C-105 | api_host（空） | E-04: L181 `return if api_url.blank?` | ○ |
| C-106 | before_validation :reset_token | E-04: L49, L189-193 | ○ |
| C-107 | reset_token（同時変更） | E-04: L190 `if api_url_changed? && !encrypted_token_changed?` | ○ |
| C-108 | after_save :clear_reactive_cache! | E-04: L51 `after_save :clear_reactive_cache!` | ○ |
| C-109 | after_save :create_client_key!（作成） | E-04: L56, L297-301 | ○ |
| C-110 | after_save :create_client_key!（既存） | E-04: L298 `if ... && !client_key` | ○ |
| C-111 | after_save :create_client_key!（非統合モード） | E-04: L298 `if enabled? && integrated_client? && ...` | ○ |
| C-112 | attr_encrypted :token（暗号化） | E-04: L44-47 `attr_encrypted :token` | ○ |
| C-113 | attr_encrypted :token（復号化） | E-04: L44-47 | ○ |

### ErrorTracking::BaseService（C-114 〜 C-121）

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-114 | execute（認可成功） | E-05: L11-15 `def execute` | ○ |
| C-115 | execute（無効時） | E-05: L38-41 `def unauthorized; return error('Error Tracking is not enabled') unless enabled?` | ○ |
| C-116 | execute（権限なし） | E-05: L40 `error('Access denied', :unauthorized) unless can_read?` | ○ |
| C-117 | compose_response（正常） | E-05: L23-32 `def compose_response` | ○ |
| C-118 | compose_response（nil） | E-05: L44-47 `def parse_errors; return error('Not ready...') unless response` | ○ |
| C-119 | compose_response（エラー） | E-05: L47 `error(response[:error], ...) if response[:error].present?` | ○ |
| C-120 | http_status_for（MISSING_KEYS） | E-05: L50-57 `def http_status_for` | ○ |
| C-121 | http_status_for（その他） | E-05: L55 `else :bad_request` | ○ |

### ErrorTracking::ListIssuesService（C-122 〜 C-127）

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-122 | perform（正常） | E-06: L22-36 `def perform` | ○ |
| C-123 | perform（無効ステータス） | E-06: L23 `return invalid_status_error unless valid_status?` | ○ |
| C-124 | perform（統合モード） | E-06: L73-95 `if project_error_tracking_setting.integrated_client?` | ○ |
| C-125 | valid_status?（有効） | E-06: L46-48 `def valid_status?; ISSUE_STATUS_VALUES.include?(issue_status)` | ○ |
| C-126 | valid_status?（無効） | E-06: L9-14 `ISSUE_STATUS_VALUES = %w[resolved unresolved ignored]` | ○ |
| C-127 | external_url | E-06: L16-18 `def external_url` | ○ |

### ErrorTracking::IssueDetailsService（C-128 〜 C-130）

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-128 | perform（正常） | E-07: L10-18 `def perform` | ○ |
| C-129 | perform（GitLab issue紐付け） | E-07: L16 `response[:issue].gitlab_issue = gitlab_issue_url if gitlab_issue_url` | ○ |
| C-130 | perform（統合モード） | E-07: L51-55 `if project_error_tracking_setting.integrated_client?` | ○ |

### ErrorTracking::IssueLatestEventService（C-131 〜 C-133）

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-131 | perform（正常） | E-08: L7-11 `def perform` | ○ |
| C-132 | perform（統合モード） | E-08: L28-35 `if project_error_tracking_setting.integrated_client?` | ○ |
| C-133 | perform（DBエラー） | E-08: L29 `handle_error_repository_exceptions do` | ○ |

### ErrorTracking::IssueUpdateService（C-134 〜 C-139）

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-134 | perform（正常） | E-09: L7-19 `def perform` | ○ |
| C-135 | perform（GitLab Issue連携） | E-09: L21-36 `def update_related_issue`, `def close_and_create_note` | ○ |
| C-136 | perform（クローズ条件） | E-09: L29 `return unless resolving? && issue.opened?` | ○ |
| C-137 | perform（既クローズ） | E-09: L32 `return unless processed_issue.reset.closed?` | ○ |
| C-138 | perform（統合モード） | E-09: L87-94 `if project_error_tracking_setting.integrated_client?` | ○ |
| C-139 | unauthorized（更新権限なし） | E-09: L70-74 `def unauthorized` | ○ |

### ErrorTracking::ListProjectsService（C-140 〜 C-145）

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-140 | perform（正常） | E-10: L7-17 `def perform` | ○ |
| C-141 | perform（権限なし） | E-10: L8 `return error('Access denied', :unauthorized) unless can?(current_user, :admin_sentry, project)` | ○ |
| C-142 | perform（無効設定） | E-10: L10-11 `unless project_error_tracking_setting.valid?` | ○ |
| C-143 | token（マスクなし） | E-10: L37-44 `def token` | ○ |
| C-144 | token（マスクあり） | E-10: L41-43 `return params[:token] unless masked_token?; setting.token` | ○ |
| C-145 | token（URL変更時） | E-10: L38 `return if setting.api_url_changed? && masked_token?` | ○ |

## 4) 不足情報（Unknown / Missing）
- なし：全てのテストケースがソースコードに基づいて生成されている

## 5) リスクフラグ（レビュー観点）
- 0: 低リスク - 全てのテストケースにソースコードの根拠あり

## 6) レビュアーチェックリスト（最小）
- [ ] ErrorモデルのステータスENUM値が実際の仕様と一致しているか確認
- [ ] ProjectErrorTrackingSettingの暗号化トークンの取り扱いが適切か確認
- [ ] ReactiveCacheの動作仕様がテストケースと一致しているか確認
- [ ] 統合モード（integrated_client?）とSentryモードの切り替えロジックが正しいか確認
- [ ] IssueUpdateServiceのGitLab Issue連携（自動クローズ）の仕様確認
