6.8 KiB
6.8 KiB
テスト妥当性レビュー: session-metrics
- 評価: 概ね良い
確認範囲
- 対象 crate:
crates/session-metrics - 読んだ主なファイル:
crates/session-metrics/src/lib.rscrates/session-metrics/README.md- 関連する実利用側テスト:
crates/pod/tests/session_metrics_test.rs
- crate の責務:
LogEntry::Extension { domain: "metrics" }上に載せる軽量な metrics 型を定義するMetricの serde 表現と builder helper を提供するrecord_metricでsession-store::save_extensionに metrics domain として append するRestoredState.extensions相当の(domain, payload)列から metrics domain のMetricだけを抽出する
現在のテストがよくカバーしていること
session-metrics 自体の unit test は 4 件で、薄い crate の主要な純粋ロジックはおおむね押さえています。
metric_round_trip_via_jsonMetric::now(...)with_dimensionwith_valuewith_correlation_id- JSON round-trip 後の等価性 を確認している。
metric_serializes_minimal_form_compactly- 空の
dimensions、Noneのvalue/correlation_idが JSON に出ないことを確認している。 - session log に載せる payload を不要に膨らませない invariant の確認として妥当。
- 空の
fold_skips_other_domainsmetrics_from_extensionsがDOMAIN == "metrics"の entry だけを拾うことを確認している。- extension domain の分離という crate 境界に合っている。
fold_skips_undeserializable_payloads- metrics domain でも deserialize できない payload を落として継続することを確認している。
- コメントにある「schema 変更で読めない payload は無視する」方針に対応している。
加えて、pod crate 側の crates/pod/tests/session_metrics_test.rs が実利用パスをかなりよく補っています。
- prune metrics の
skip/fire/post_requestが session log に残り、metrics_from_extensionsで復元できる。 correlation_idによるprune.fireとprune.post_requestの join 前提を確認している。record_metric経由の metrics write failure が本体 run を abort しないことを確認している。- metrics 以前の old session が clean に replay できることを確認している。
このため、crate 単体テストは小さいものの、利用側の integration coverage まで含めると現在の責務に対する妥当性は高めです。
不足 / 疑問のあるテスト
record_metric自体の直接テストがsession-metricscrate 内にありません。- 現状は
pod側 integration test で間接的に成功・失敗パスを踏んでいます。 - ただし、この crate の public API としては
record_metricがDOMAINを正しく使い、StoreErrorをそのまま返し、restore 後にMetricとして読めることを直接確認するテストがある方がよいです。
- 現状は
- serde default の後方互換テストが片方向です。
- 現在は「空/None の field を serialization で省略する」ことは確認しています。
- しかし「省略された JSON を deserialize して
dimensions = {}、value = None、correlation_id = Noneになる」ことは直接確認していません。
metrics_from_extensionsの順序保持は実質的に確認されていますが、意図としてはもう少し明示してもよいです。fold_skips_other_domainsでa,bの順序は見ています。- これは十分とも言えますが、session-log の時系列を壊さないことが重要ならテスト名か assertion message で invariant として明記すると読みやすいです。
value: Option<f64>の非有限値の扱いは未定義に見えます。serde_jsonはNaN/ infinity の扱いが直感的でないため、metrics に非有限値が入る可能性を許すなら仕様化が必要です。- 現時点で producer が通常の token count / duration / savings だけなら優先度は低いです。
- 実装詳細に寄りすぎた脆いテストは少ないです。
- JSON の「field 名文字列を含まない」確認はやや表層的ですが、この crate では serde wire format 自体が API なので許容範囲です。
- ただし厳密な JSON 文字列全体比較ではなく
contains否定に留めているため、過度には脆くありません。
追加の提案
優先度順に追加するなら以下です。
-
record_metric_appends_metrics_extension_and_restoresFsStoreか軽い fakeStoreを使う。record_metric(...)を呼ぶ。session_store::restore(...)後にstate.extensionsからmetrics_from_extensions(...)して、元のMetricが 1 件取れることを確認する。- これで
DOMAIN、payload serde、session-store への append path を crate 側で直接守れる。
-
record_metric_propagates_store_error- append が必ず失敗する minimal fake
Storeを置く。 record_metricがErr(StoreError)を返すことを確認する。pod側の「失敗しても run 継続」は pod の責務なので、ここでは error propagation だけで十分。
- append が必ず失敗する minimal fake
-
metric_deserializes_minimal_legacy_payload{"name":"x","ts":1}からMetricに deserialize する。dimensions.is_empty()、value.is_none()、correlation_id.is_none()を確認する。- 現在の
#[serde(default)]/ optional field の後方互換意図を直接固定できる。
-
必要なら
metric_non_finite_value_behavior_is_documented- 非有限値を禁止するなら constructor/helper 側で弾く設計にしてからテスト。
- 許すなら JSON payload 上でどう表現されるかを明示する。
- 現時点では必須ではなく、producer が非有限値を作る可能性が出た時点でよいです。
実行したコマンド
cd /home/hare/Projects/yoi && cargo test -p session-metrics
結果:
running 4 tests
test tests::fold_skips_other_domains ... ok
test tests::fold_skips_undeserializable_payloads ... ok
test tests::metric_round_trip_via_json ... ok
test tests::metric_serializes_minimal_form_compactly ... ok
test result: ok. 4 passed; 0 failed
cd /home/hare/Projects/yoi && cargo test -p pod --test session_metrics_test
結果:
running 5 tests
test old_sessions_without_metrics_replay_cleanly ... ok
test metric_write_failure_emits_warn_alert_and_does_not_abort_run ... ok
test prune_metrics_record_below_min_savings_skip ... ok
test prune_metrics_emit_skip_then_fire_with_post_request_join ... ok
test prune_metrics_fire_during_single_long_task_without_multiple_user_turns ... ok
test result: ok. 5 passed; 0 failed