本ドキュメントは、『beautyspot』のテスト計画および検証内容を定義する。
各機能が仕様通りに動作することを確認するための検証観点およびテストシナリオを定義する。
@spot.mark() はユーザーが最も頻繁に使う公開APIであり、キャッシュの正確性・透過性・拡張性の基盤となる。デコレーションによって元の関数の挙動やメタデータが損なわれると、ユーザーコードのデバッグや型チェックに支障をきたすため、ラッパーの透過性を厳密に検証する。
save_blob=True 指定時にDB直接保存ではなくBlobストレージ経由で保存されること__name__, __doc__, inspect.signature が元の関数と一致することserializer= パラメータでデフォルトの MsgpackSerializer をカスタム実装に置換できることversion= パラメータを変更するとキャッシュキーが変わり、既存キャッシュがヒットしなくなることcached_run はデコレータを直接付与できない外部ライブラリ関数にキャッシュを適用する唯一の手段であり、このAPIの不具合はサードパーティ統合シナリオ全体に影響する。戻り値の型が関数の数で変わるスマートリターン仕様は、誤実装すると型安全性を損なうため重点的に検証する。
ValidationError が送出されることbeautyspot は同期・非同期の両方の関数を透過的にキャッシュするが、asyncio 固有のイベントループ制約(スレッドをまたぐコルーチン実行、例外伝播のタイミング差異)により、同期版とは異なる不具合が発生しやすい。非同期パスの堅牢性を独立して検証する。
inspect.iscoroutinefunction() により同期/非同期が正しく識別され、適切なラッパーが適用されることawait が1回の実行結果を共有することsave_sync=False 時に _BackgroundLoop 経由でキャッシュ保存が非同期実行されることキャッシュキーはキャッシュの同一性判定の基盤であり、ハッシュが不安定だとキャッシュミスの頻発(性能劣化)や誤ヒット(データ不整合)を引き起こす。Python バージョンやプラットフォームに依存しないキー生成の安定性を保証する。
func_identifier:input_id[:version] 入力に対して、複数回の呼び出しで常に同じ SHA-256 ハッシュが生成されることmodule.qualname 形式の func_identifier を含む正しい構造であることcanonicalize() は引数の型ごとに正規化ルールを適用し、論理的に同値な入力が同じキャッシュキーを生成することを保証する。正規化の不備は「同じ入力なのにキャッシュミス」または「異なる入力なのに誤ヒット」という致命的なバグに直結する。
{"a": 1, "b": 2} と {"b": 2, "a": 1} が同一キーを生成すること[1, 2] (list) と (1, 2) (tuple) が異なるキーを生成すること(型タグ付き正規化)型名.メンバー名 で正規化されることshape + dtype + tobytes による正規化で、同値配列が同一キーを生成することTrue と 1 が異なるキーを生成すること(Python では True == 1 だが区別が必要)KeyGenPolicy は引数ごとのキー生成戦略をカスタマイズする機構であり、ログレベルやデバッグフラグのような非決定的引数をキーから除外したり、ファイルパスの代わりにファイル内容でキーを生成するユースケースを支える。戦略の誤適用はキャッシュの正確性を根本から損なう。
KeyGenPolicy.bind(func) が関数シグネチャ(位置引数・キーワード引数・デフォルト値)を正しく解釈すること@spot.mark(keygen=KeyGen.ignore("verbose")) のようにデコレータ経由で戦略が正しく適用されることSQLiteTaskDB はキャッシュメタデータの永続化を担う中核コンポーネントであり、DB の不整合はキャッシュの消失や重複実行を引き起こす。WALモード・専用ライタースレッドという独自アーキテクチャの信頼性を保証する。
TaskDBCore / TaskDBBase のプロトコル階層は、サードパーティによるカスタムDB実装(Redis、PostgreSQL 等)の差し替えを可能にする拡張ポイントである。インターフェース契約が不明確だと、カスタム実装が実行時に予期しないエラーを起こす。
TaskDBCore の必須メソッド(save, load, delete 等)を実装したクラスが isinstance チェックをパスすることTaskDBBase がメンテナンス系メソッド(prune, stats 等)のデフォルト実装を提供し、サブクラスが必須メソッドのみの実装で動作することbs.Spot(db=CustomDB()) でプロトコル準拠のカスタムDBが正常に動作することLocalStorage はBlobデータのファイルシステム永続化を担い、並行書き込み時のデータ破損やパストラバーサルによるセキュリティ脆弱性が発生しうる。アトミック書き込みとセキュリティバリデーションの正確性を保証する。
base_dir が存在しない場合に自動作成され、.gitignore が配置されることtempfile.mkstemp → fsync → os.replace のフローで、書き込み中のクラッシュや並行アクセスでファイルが破損しないこと.. や / を含む場合に ValidationError が送出され、base_dir 外へのアクセスが不可能であること。load() でも is_relative_to 検証が機能することclean_temp_files() が猶予期間超過の .spot_tmp ファイルを削除し、猶予期間内のファイルは保持することdelete() が存在しないキーに対してもエラーを送出しないことS3Storage はクラウド環境での大規模Blob保存を担い、ネットワーク障害・認証エラー・バケット不在などオンプレミスにはない障害モードが存在する。boto3 オプショナル依存のガード処理も含め、クラウドストレージ統合の信頼性を検証する。
save / load / delete / list_keys が正しく動作することimport 時にわかりやすいエラーメッセージが表示されることストレージポリシーは「データをDB直接保存するか、Blobストレージに分離するか」を決定する戦略レイヤーである。閾値判定の誤りはDBの肥大化(性能劣化)や不要なBlob分離(オーバーヘッド増加)を招く。3種のポリシー実装がそれぞれの契約を正しく満たすことを検証する。
False、閾値以上のデータで True を返すこと。境界値(ちょうど閾値サイズ)での挙動が明確であることFalse を返しDB直接保存を選択すること。ただし閾値超過時に WARNING レベルのログが出力されることTrue を返すこと@spot.mark(save_blob=True/False) の明示指定がポリシー判定より優先されること。save_blob=None(デフォルト)時にポリシーの should_save_as_blob() が呼ばれることMsgpackSerializer はキャッシュデータの永続化形式を決定する。シリアライズの不具合はデータ消失(デシリアライズ不能)やサイレントなデータ劣化(精度低下等)に直結する。スレッドセーフ性の欠如はマルチスレッド環境での競合状態を引き起こす。
dumps → loads で元の値と一致することカスタム型登録はユーザー定義クラスのキャッシュを可能にする拡張ポイントである。登録の不備は SerializationError によるキャッシュ保存失敗を引き起こし、ユーザーの既存コードとの統合を阻害する。デコレータ方式と命令方式の両方の登録パスを検証する。
@spot.register(code=N, encoder=..., decoder=...) でクラスが登録され、そのインスタンスのシリアライズ/デシリアライズが正常に動作することspot.register_type(MyClass, code=N, ...) でも同等の登録が行われることmodel_validate をデコーダとして使用する場合に、バリデーション付きの復元が正しく動作すること_BackgroundLoop はデーモンスレッドで asyncio イベントループを駆動し、非同期キャッシュ保存を処理する。スレッド間のコルーチン受け渡しは競合状態やデッドロックが発生しやすく、シャットダウン時のタスク消失はデータロスに直結する。
submit(coro) で投入したコルーチンが正しく実行され、結果がDBとストレージに反映されることdrain_timeout 秒以内に完了すること。タイムアウト後は強制終了されることsubmit() 呼び出しが受け付けられないことsave_sync パラメータと flush / drain メカニズムは、キャッシュ保存のレイテンシとデータ安全性のトレードオフをユーザーが制御する手段である。save_sync=False 使用時にデータが失われないことと、コンテキストマネージャによる確実なフラッシュを保証する。
spot.flush(timeout) 呼び出しで全ペンディング保存が完了するまで待機すること。タイムアウト時の挙動が明確であることwith spot: ブロック終了時に未完了の保存が自動的に drain されることwith ブロックで再利用でき、各ブロック終了時に適切に drain されることon_background_error コールバックが呼ばれ、メインスレッドには影響しないことLifecyclePolicy はキャッシュデータの保持期間を関数名パターンで制御する。パターンマッチングの不備は、重要なキャッシュの意図しない削除(データロス)や不要なキャッシュの蓄積(ディスク圧迫)を引き起こす。
"train_*", "*.preprocess" 等)がルールの func_pattern として正しく機能し、マッチした関数に対して指定の Retention が適用されることmodule.qualname)でルールが見つからない場合に、短縮名(qualname のみ)でフォールバック検索が行われることdefault_retention で指定された保持期間(デフォルト: 30日)が適用されることdefault_retention=None を指定した場合に無期限保持(None)が返されることRetention はキャッシュの有効期間をユーザーフレンドリーな形式で指定する値オブジェクトである。パースの不備は意図しない保持期間(例: "7d" を 7秒と誤解釈)を招き、重要なキャッシュの早期削除やディスクの際限ない肥大化に繋がる。
"7d" → 7日、"12h" → 12時間、"30m" → 30分、"10s" → 10秒として正しく解釈されることtimedelta(days=7) がそのまま保持期間として受け入れられることRetention.FOREVER が無期限保持を表し、シングルトンであること(is 比較が成立)"-1d")やゼロ("0s")が ValidationError を送出すること。不正な形式("abc", "" 等)も拒否されることTokenBucket はAPI呼び出しやリソースアクセスのレート制限を実現する。レートリミッターの不具合はAPIの過負荷(制限が甘い場合)やスループットの不必要な低下(制限が厳しすぎる場合)を招く。GCRA アルゴリズムのスムーズなレート制御を検証する。
Hook システムはキャッシュライフサイクルへのユーザー拡張ポイント(ロギング、メトリクス収集、監査等)であり、フック内の不具合がメインの関数実行に影響を与えないことが最重要の契約である。また ThreadSafeHookBase の自動ロック機構の正確性を保証する。
pre_execute → 関数実行 → on_cache_miss の順でフックが呼ばれること。キャッシュヒット時に pre_execute → on_cache_hit の順で呼ばれることPreExecuteContext, CacheHitContext, CacheMissContext)が正しい情報(func_name, args, result 等)を含むこと__init_subclass__ によりユーザー定義メソッドが自動的に RLock でラップされ、複数スレッドからの同時呼び出しで競合状態が発生しないことMaintenanceService は CLI やダッシュボードからのシステム管理操作を仲介するサービス層である。タスク詳細の表示ミスは運用者の誤判断を招き、メンテナンス操作の不備はデータの意図しない削除に繋がる。
None が返却され、例外が送出されないことCLI はユーザーがキャッシュの状況確認・管理を行う主要インターフェースであり、コマンドの不具合はユーザー体験と運用効率に直接影響する。CliRunner によるコマンド実行と出力の正確性を E2E で検証する。
--force フラグでプロンプトがスキップされること@mark でキャッシュ保存 → beautyspot list で確認 → beautyspot clear で削除、という一連のワークフローが正常に動作することbs.Spot() ファクトリ関数は全コンポーネントの DI 配線を担う唯一のパブリックエントリポイントであり、デフォルト構成の正確性とカスタム実装の差し替え可能性がシステム全体の柔軟性と正確性を決定する。
SQLiteTaskDB, LocalStorage, MsgpackSerializer, TokenBucket, WarningOnlyPolicy が自動注入されること。ワークスペースディレクトリ(.beautyspot/)が自動作成されることTaskDBBase / TaskDBCore プロトコル準拠のカスタムDBを db= で注入し、正常にキャッシュ操作が行えることBlobStorageBase 準拠のモック Storage を storage_backend= で注入し、Blob保存が委譲されることSerializerProtocol 準拠のカスタム Serializer を注入し、シリアライズ処理が差し替わることbs.Spot() の戻り値型が pyright / mypy で正しく推論されることThundering Herd Protection は、同一キーへの並行リクエストが一斉に関数を実行する「Thundering Herd」問題を防ぐ。この保護が不完全だと、重い計算やAPI呼び出しが不要に多重実行され、リソース浪費やレートリミット超過を招く。並行性バグはテスト困難であるため、複数の観点から網羅的に検証する。
asyncio.Task が同一キーで同時にキャッシュミスした場合も、関数が1回のみ実行されること_inflight エントリがクリーンアップされることHERD_TIMEOUT(300秒)を超過した場合にタイムアウト処理が発動し、無限待機にならないことHERD_MAX_RETRIES(3回)の範囲でリトライが行われ、上限超過で最終エラーが返ること_inflight 辞書の状態遷移(エントリ追加→結果セット→エントリ削除)が正しい順序で行われることBlobStorageBase は全ストレージバックエンド(LocalStorage, S3Storage, サードパーティ)が
準拠すべき抽象契約を定義する。この契約が正しく機能しないと、キャッシュデータの
保存・復元・削除・メンテナンス(GC)の全てが破綻する。
LocalStorage を具象実装として使い、インターフェース契約を網羅的に検証する。
bytes, bytearray, memoryview の全てを受け入れること(ゼロコピー書き込み)b"") を正常に保存・復元できることCacheCorruptedError を送出すること