---
generated_at: 2026-02-04 10:30:00
metrics:
  claims_total: 74
  claims_with_evidence: 71
  claims_without_evidence: 3
confidence_derived: 0.96
---

# 根拠レポート：connector-kafka-0-10-token-provider 単体テストケース一覧

## 本レポートについて

### 目的
本レポートは、生成された単体テストケース一覧の信頼性を検証し、人間レビュアーが効率的にレビューできるようにすることを目的としています。

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

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

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

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

---

## 1) サマリー（まず見るところ）
- 総合信頼度（derived）：**0.96**
  - 根拠あり：71 / 74、根拠なし：3
- 優先レビュー（高）
  1. **C-20（obtainDelegationTokens正常系・トークン取得成功）**：外部依存（Kafkaブローカー）のためモック戦略の確認が必要
  2. **C-72（obtainToken正常系）**：AdminClient.createの外部依存がありテスト実装にモックが必要
  3. **C-62（findMatchingTokenClusterConfig正常系）**：UGIのCredentialsへのトークン登録が前提条件となる

## 2) 参照した情報（Evidence一覧）
> ここに「実在するもの」だけ列挙。

- E-01: `connector/kafka-0-10-token-provider/src/main/scala/org/apache/spark/kafka010/KafkaConfigUpdater.scala`（KafkaConfigUpdaterクラス、set/setIfUnset/setAuthenticationConfigIfNeeded/buildメソッド）
- E-02: `connector/kafka-0-10-token-provider/src/main/scala/org/apache/spark/kafka010/KafkaDelegationTokenProvider.scala`（KafkaDelegationTokenProviderクラス、serviceName/obtainDelegationTokens/delegationTokensRequiredメソッド）
- E-03: `connector/kafka-0-10-token-provider/src/main/scala/org/apache/spark/kafka010/KafkaRedactionUtil.scala`（KafkaRedactionUtilオブジェクト、redactParams/redactJaasParamメソッド）
- E-04: `connector/kafka-0-10-token-provider/src/main/scala/org/apache/spark/kafka010/KafkaTokenProviderException.scala`（ExceptionsHelper/KafkaTokenProviderExceptionsオブジェクト）
- E-05: `connector/kafka-0-10-token-provider/src/main/scala/org/apache/spark/kafka010/KafkaTokenSparkConf.scala`（KafkaTokenClusterConfケースクラス、KafkaTokenSparkConfオブジェクト）
- E-06: `connector/kafka-0-10-token-provider/src/main/scala/org/apache/spark/kafka010/KafkaTokenUtil.scala`（KafkaTokenUtilオブジェクト、各publicメソッド）
- E-07: `connector/kafka-0-10-token-provider/src/main/resources/error/kafka-token-provider-error-conditions.json`（エラー条件定義）

## 3) Claims と根拠の対応（レビューの主戦場）
| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-01 | KafkaConfigUpdater.setで指定キーと値がmapに設定される | E-01: `map.put(key, value)` (L38) | ○ |
| C-02 | KafkaConfigUpdater.setで既存キーを上書きできる | E-01: `map.put(key, value)` はHashMap.putで上書き動作 | ○ |
| C-03 | KafkaConfigUpdater.setでthisが返却されメソッドチェーン可能 | E-01: 戻り値型 `this.type` (L37) | ○ |
| C-04 | KafkaConfigUpdater.setIfUnsetでキー未設定時に値が設定される | E-01: `if (!map.containsKey(key))` (L49) | ○ |
| C-05 | KafkaConfigUpdater.setIfUnsetでキー設定済みの場合は上書きしない | E-01: `if (!map.containsKey(key))` の条件分岐 (L49) | ○ |
| C-06 | JVM global JAAS設定存在時に追加設定をしない | E-01: `if (KafkaTokenUtil.isGlobalJaasConfigurationProvided)` (L78) | ○ |
| C-07 | 委任トークン検出時にSASL設定が行われる | E-01: `clusterConfig.foreach` ブロック (L81-89) | ○ |
| C-08 | clusterConfigがNoneの場合に追加設定をしない | E-01: `clusterConfig.foreach` はNoneで何もしない (L81) | ○ |
| C-09 | tokenMechanismがSCRAM以外でrequire例外 | E-01: `require(clusterConf.tokenMechanism.startsWith("SCRAM")` (L86) | ○ |
| C-10 | bootstrap.servers未設定時にSparkException | E-01: `throw KafkaTokenProviderExceptions.missingKafkaOption` (L63-64) | ○ |
| C-11 | buildでju.Map[String, Object]が返却される | E-01: `def build(): ju.Map[String, Object] = map` (L94) | ○ |
| C-12 | 空のkafkaParamsでbuild時に空のMapが返却される | E-01: コンストラクタで `new ju.HashMap[String, Object](kafkaParams.asJava)` (L35) | ○ |
| C-13 | serviceNameが"kafka"を返却する | E-02: `override def serviceName: String = "kafka"` (L34) | ○ |
| C-14 | SASL_SSLプロトコルでdelegationTokensRequired=true | E-02: `clusterConf.securityProtocol == SASL_SSL.name` (L84) | ○ |
| C-15 | SSLプロトコルでdelegationTokensRequired=true | E-02: `clusterConf.securityProtocol == SSL.name` (L85) | ○ |
| C-16 | SASL_PLAINTEXTプロトコルでdelegationTokensRequired=true | E-02: `clusterConf.securityProtocol == SASL_PLAINTEXT.name` (L86) | ○ |
| C-17 | PLAINTEXTプロトコルでdelegationTokensRequired=false | E-02: PLAINTEXT条件が存在しないためfalse (L83-86) | ○ |
| C-18 | クラスタ設定なしでdelegationTokensRequired=false | E-02: `exists(delegationTokensRequired(_))` で空コレクションはfalse (L75) | ○ |
| C-19 | NonFatal例外発生時にfalseが返却される | E-02: `catch { case NonFatal(e) => ... false }` (L77-79) | ○ |
| C-20 | トークン取得成功時に最小の更新日時が返却される | E-02: `if (lowestNextRenewalDate.isEmpty || nextRenewalDate < lowestNextRenewalDate.get)` (L49) | ○ |
| C-21 | 複数クラスタで最小の更新日時が返却される | E-02: ループ内で最小値を更新 (L49-51) | ○ |
| C-22 | 委任トークン不要なクラスタがスキップされる | E-02: `if (delegationTokensRequired(clusterConf))` (L44) | ○ |
| C-23 | 個別クラスタのトークン取得失敗時にログ警告して継続 | E-02: 内側の`catch { case NonFatal(e) => logWarning }` (L56-61) | ○ |
| C-24 | getAllClusterConfigs失敗時にNoneが返却される | E-02: 外側の`catch { case NonFatal(e) => logWarning }` (L64-67) | ○ |
| C-25 | 通常パラメータがそのまま返却される | E-03: `redact(redactionPattern, Seq((key, value.toString))).head` (L36) | ○ |
| C-26 | SASL_JAAS_CONFIGのpasswordがリダクションされる | E-03: `if (key.equalsIgnoreCase(SaslConfigs.SASL_JAAS_CONFIG))` -> `redactJaasParam` (L33-34) | ○ |
| C-27 | 値がnullの場合にnullが返却される | E-03: `if (value != null) ... else (key, value.asInstanceOf[String])` (L32, 40) | ○ |
| C-28 | SECRET_REDACTION_PATTERNに一致するキーがリダクションされる | E-03: `redact(redactionPattern, ...)` (L36) | ○ |
| C-29 | 空のSeqで空のSeqが返却される | E-03: `params.map` で空入力は空出力 (L31) | ○ |
| C-30 | redactJaasParamでpassword部分がリダクションされる | E-03: `param.replaceAll("password=\".*\"", ...)` (L47) | ○ |
| C-31 | passwordが含まれない場合はそのまま返却される | E-03: replaceAllで一致なしの場合は入力がそのまま返る (L47) | ○ |
| C-32 | redactJaasParamでnull入力時にnullが返却される | E-03: `if (param != null && !param.isEmpty)` の条件分岐 (L46) | ○ |
| C-33 | redactJaasParamで空文字列が返却される | E-03: `if (param != null && !param.isEmpty)` でfalse、elseブランチ (L46, 49) | ○ |
| C-34 | ErrorClassesJsonReaderが正しく初期化される | E-04, E-07: JSONファイルパスを指定してnew (L23-28) | ○ |
| C-35 | missingKafkaOptionで指定オプション名を含むSparkExceptionが生成される | E-04: `new SparkException(... errorClass = Some("MISSING_KAFKA_OPTION"), messageParameters = Map("option" -> option))` (L36-43) | ○ |
| C-36 | 異なるオプション名でも正しくSparkExceptionが生成される | E-04: `val param = Map("option" -> option)` (L34) | ○ |
| C-37 | KafkaTokenClusterConf.toStringでパスワードがリダクションされる | E-05: `trustStorePassword.map(_ => REDACTION_REPLACEMENT_TEXT)` 等 (L51, 54, 55) | ○ |
| C-38 | パスワードがNoneの場合にNoneと表示される | E-05: `Option.map` でNoneの場合はNoneのまま (L51, 54, 55) | ○ |
| C-39 | getClusterConfigで有効な設定からKafkaTokenClusterConfが生成される | E-05: `KafkaTokenClusterConf(...)` コンストラクタ呼び出し (L72-94) | ○ |
| C-40 | オプション項目未設定時にデフォルト値が使用される | E-05: `getOrElse(...)` でDEFAULT_SECURITY_PROTOCOL_CONFIG等 (L80-92) | ○ |
| C-41 | auth.bootstrap.servers未設定時にNoSuchElementException | E-05: `throw new NoSuchElementException(...)` (L76-77) | ○ |
| C-42 | SSL関連オプション設定が正しく読み込まれる | E-05: `sparkClusterConf.get(SslConfigs.SSL_TRUSTSTORE_*_CONFIG)` (L84-90) | ○ |
| C-43 | specifiedKafkaParamsが正しく読み込まれる | E-05: `sparkClusterKafkaConf` (L71, 93) | ○ |
| C-44 | getAllClusterConfigsで複数クラスタ設定が取得される | E-05: `flatMap ... .map(getClusterConfig(...))` (L100-108) | ○ |
| C-45 | クラスタ設定なしで空のSetが返却される | E-05: `getAllWithPrefix` で空の場合はkeySetが空 (L100) | ○ |
| C-46 | 1つのクラスタ設定が正しく取得される | E-05: `flatMap ... .map(getClusterConfig(...))` (L100-108) | ○ |
| C-47 | getTokenServiceで正しいフォーマットのTextが返却される | E-06: `new Text(s"$TOKEN_SERVICE_PREFIX.$identifier")` (L56) | ○ |
| C-48 | 空文字列identifierでもTextが返却される | E-06: 文字列連結のため空文字列でも動作 (L56) | ○ |
| C-49 | プロキシユーザーでない場合に例外がスローされない | E-06: `require(!SparkHadoopUtil.get.isProxyUser(currentUser)...)` (L88) | ○ |
| C-50 | プロキシユーザーの場合にIllegalArgumentException | E-06: `require(!SparkHadoopUtil.get.isProxyUser(currentUser)...)` (L88) | ○ |
| C-51 | SASL_SSLでTrustStore設定が含まれる | E-06: `case SASL_SSL.name => setTrustStoreProperties` (L103-104) | ○ |
| C-52 | SSLでTrustStoreとKeyStore設定が含まれる | E-06: `case SSL.name => setTrustStoreProperties ... setKeyStoreProperties` (L106-108) | ○ |
| C-53 | SASL_PLAINTEXTでSSL設定が含まれない | E-06: `case SASL_PLAINTEXT.name =>` にSSL設定呼び出しなし (L112-114) | ○ |
| C-54 | Keytab設定時にKeytab JAASパラメータが使用される | E-06: `if (sparkConf.contains(KEYTAB))` -> `getKeytabJaasParams` (L131-136) | ○ |
| C-55 | Keytab未設定時にチケットキャッシュJAASパラメータが使用される | E-06: `else { ... getTicketCacheJaasParams }` (L137-141) | ○ |
| C-56 | specifiedKafkaParamsがPropertiesに適用される | E-06: `clusterConf.specifiedKafkaParams.foreach { param => adminClientProperties.setProperty }` (L147-149) | ○ |
| C-57 | specifiedKafkaParamsが既存設定を上書きする | E-06: `setProperty` はHashtable.putで上書き (L148) | ○ |
| C-58 | JVM global JAAS設定ありでtrue | E-06: `JaasContext.loadClientContext` 成功でtrue (L158-160) | ○ |
| C-59 | JVM global JAAS設定なしでfalse | E-06: `catch { case NonFatal(_) => false }` (L162) | ○ |
| C-60 | getKeytabJaasParamsで正しいJAAS文字列が生成される | E-06: テンプレート文字列 (L201-209) | ○ |
| C-61 | 特殊文字を含むパス・プリンシパルがダブルクォートで囲まれる | E-06: `keyTab="$keyTab"`, `principal="$principal"` (L207-208) | ○ |
| C-62 | findMatchingTokenClusterConfigで一致するクラスタ設定が返却される | E-06: `pattern.matcher(_).matches()` フィルタ (L253), **UGI Credentials事前設定が前提** | △ |
| C-63 | 一致なしでNoneが返却される | E-06: `clusterConfigs.headOption` で空の場合None (L257) | ○ |
| C-64 | 複数一致時にIllegalArgumentException | E-06: `require(clusterConfigs.size <= 1, ...)` (L255) | ○ |
| C-65 | トークンなしでNoneが返却される | E-06: `tokens.filter` で空の場合、最終的にNone (L247, 257) | ○ |
| C-66 | getTokenJaasParamsでSCRAM JAAS文字列が生成される | E-06: テンプレート文字列 with `ScramLoginModule` (L267-275) | ○ |
| C-67 | トークン不在時にIllegalArgumentException | E-06: `require(token != null, ...)` (L263) | ○ |
| C-68 | needTokenUpdateでトークン最新でない場合にtrue | E-06: `getTokenJaasParams(clusterConfig.get) != connectorJaasParams` (L287) | ○ |
| C-69 | needTokenUpdateでトークン最新の場合にfalse | E-06: 同上の比較で等しい場合false (L287) | ○ |
| C-70 | clusterConfigがNoneでfalse | E-06: `if (clusterConfig.isDefined && ...)` の条件 (L284) | ○ |
| C-71 | SASL_JAAS_CONFIGなしでfalse | E-06: `params.containsKey(SaslConfigs.SASL_JAAS_CONFIG)` の条件 (L284) | ○ |
| C-72 | obtainTokenで有効な設定でトークンとexpiryTimestampが返却される | E-06: `AdminClient.create` -> `createDelegationToken` (L70-81), **外部依存あり** | △ |
| C-73 | obtainTokenでプロキシユーザー時にIllegalArgumentException | E-06: `checkProxyUser()` (L68) | ○ |
| C-74 | KafkaDelegationTokenIdentifier.getKindがTOKEN_KINDを返却する | E-06: `override def getKind: Text = TOKEN_KIND` (L62) | ○ |

## 4) 不足情報（Unknown / Missing）
- **C-62**: `findMatchingTokenClusterConfig` のテストでは `UserGroupInformation.getCurrentUser().getCredentials` にKafka委任トークンが事前登録されている必要がある。テスト実装時にUGIのモックまたはテスト用Credentials設定が必要。
  - 候補：UGI.createRemoteUser + token追加 / モックフレームワーク / テスト用SecurityContext設定
- **C-72**: `obtainToken` はAdminClient.createを呼び出し、Kafkaブローカーへの実接続が発生する。単体テストではAdminClientのモックが必須。
  - 候補：Mockito / ScalaMock / テスト用Kafkaブローカー（embedded kafka）

## 5) リスクフラグ（レビュー観点）
- **リスク1（中）**: KafkaTokenUtilのobtainToken/findMatchingTokenClusterConfig/getTokenJaasParamsは外部依存（Kafkaブローカー、UGI Credentials）が強く、テスト実装時にモック戦略の適切な設計が求められる
- **リスク0（低）**: KafkaConfigUpdater, KafkaRedactionUtil, KafkaTokenSparkConf, KafkaTokenProviderExceptionはほぼ自己完結しており、テスト実装が容易
- **リスク1（中）**: KafkaDelegationTokenProvider.obtainDelegationTokensはKafkaTokenUtil.obtainTokenを内部で呼び出すため、統合的なモック設定が必要

## 6) レビュアーチェックリスト（最小）
- [ ] 各ファイルのpublicメソッドが網羅されているか
- [ ] 正常系・異常系・境界値の観点が各メソッドに対して適切にカバーされているか
- [ ] セキュリティ関連（リダクション、認証）のテストケースが十分か
- [ ] 外部依存（Kafkaブローカー、UGI）のモック戦略が適切に考慮されているか
- [ ] テストIDの命名規則（UT-KTP-XXX）が一貫しているか
- [ ] requireやthrowによるバリデーションの異常系が全てカバーされているか
- [ ] デフォルト値の適用テスト（KafkaTokenSparkConf.getClusterConfig）が十分か
