遅延評価を観測するエクスポートテスト
目標
行の消費回数・改行のエスケープ・公開フィールド・レスポンス形式を検証します。
なぜ重要なのか
小さなリストでテストしたエクスポートのコードは正常に見えましたが、大きな入力をすべてlistに変換してメモリを使い果たしました。結果が同じだというだけでは、実行方式の契約まで同じだとはいえません。遅延消費を、目に見える状態に変える必要があります。
ステップ
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: validate_row(row)は、dictで、idがboolを除く正のint、nameが空でないstrのとき、rowを返します。それ以外はValueErrorです。追加の内部フィールドは許可します。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
最初に1回だけ準備してください。既存のファイルは上書きしません。
mkdir -p /root/work/test-lazy-export-lab
test -e /root/work/test-lazy-export-lab/service.py || cp /opt/fixtures/ten_labs/test-lazy-export-lab/service.py /root/work/test-lazy-export-lab/service.py
test -e /root/work/test-lazy-export-lab/test_service.py || cp /opt/fixtures/ten_labs/test-lazy-export-lab/test_service.py /root/work/test-lazy-export-lab/test_service.py
cd /root/work/test-lazy-export-lab
-
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: project(row)は、validate_rowの後、idとnameだけを持つ新しいdictを返します。元の内部フィールドはそのまま保持します。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。 -
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: encode_line(row)は、projectの結果を、ensure_ascii=False、separators=(',',':')、sort_keys=TrueでJSONエンコードし、最後に' 'を1つ付けたstrです。name内の改行はJSONのエスケープである必要があります。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。 -
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: validate_max(value)は、boolを除く1–1000のintだけをそのまま返し、それ以外はValueErrorです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。 -
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: take_rows(rows, maximum)は、isliceなどで最大maximum個だけを遅延して返すiteratorです。呼び出し時にmaximumを検証し、nextを1回呼ぶと入力を1回だけ消費します。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。 -
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: json_lines(rows, maximum=100)は、take_rowsから受け取った行ごとにencode_lineをyieldします。すべてを結合した文字列やリストは返しません。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。 -
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: decode_lines(text)は、splitlinesの空でない各行をjson.loadsしてvalidate_rowし、リストとして返します。空文字列は[]、空の中間行はValueErrorです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。 -
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: create_app(rows)は、GET /exportでjson_lines(rows, 100)を、application/x-ndjsonのStreamingResponseとして返します。rowsは、再び反復可能なリストです。内部フィールドがなく、各行の内容と順序を保持する必要があります。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
参考
- インターネットやパッケージのインストールなしで、既存のlab-dev環境で行います。
- 各ステップは、45秒の採点バジェットの範囲内で実行されます。実際のsleepやネットワーク呼び出しを追加しないでください。
- 提出したテストは、別の一時フォルダーで、正常な実装と欠陥のある実装に対して実行されます。正常な実装では、実際に実行したテストがすべて成功し、欠陥のある実装では、テスト本文が失敗する必要があります。収集エラー、実行0件、すべてスキップ、強制終了は成功ではありません。pytestの基本機能と提供されたライブラリだけを使ってください。
- FastAPI公式ドキュメント・pytest公式ドキュメント・Python sqlite3
- 制限: TestClientはレスポンスをバッファリングするので、ネットワークの最初のバイトの遅延や、メモリ上限の全体は証明しません。遅延評価かどうかは、別のカウント用ジェネレーターで検査します。ストリームの開始後に誤った行に遭遇すると、通常のエラーJSONにステータスを変えることは困難です。実際のサービスでは、事前検証・行ごとのエラー形式・中断ポリシーのどれを選ぶかを決める必要があります。提供された実装は読んでもかまいませんが、採点は別のコピーを使用します。ソースの文言の検査やファイルの修正で欠陥を回避せず、公開インターフェースの実行結果を検査してください。
行の契約を検証する(テスト)
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: validate_row(row)は、dictで、idがboolを除く正のint、nameが空でないstrのとき、rowを返します。それ以外はValueErrorです。追加の内部フィールドは許可します。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
最初に1回だけ準備してください。既存のファイルは上書きしません。
mkdir -p /root/work/test-lazy-export-lab
test -e /root/work/test-lazy-export-lab/service.py || cp /opt/fixtures/ten_labs/test-lazy-export-lab/service.py /root/work/test-lazy-export-lab/service.py
test -e /root/work/test-lazy-export-lab/test_service.py || cp /opt/fixtures/ten_labs/test-lazy-export-lab/test_service.py /root/work/test-lazy-export-lab/test_service.py
cd /root/work/test-lazy-export-lab
boolと数値を区別し、空の名前をエラーとして扱います。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-lazy-export-lab/01-contract.shで確認してください。
公開する行だけを作る(テスト)
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: project(row)は、validate_rowの後、idとnameだけを持つ新しいdictを返します。元の内部フィールドはそのまま保持します。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
エクスポートの経路にも、通常のAPIと同じ公開フィールドのポリシーを適用する必要があります。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-lazy-export-lab/02-contract.shで確認してください。
行の境界を保ったままエンコードする(テスト)
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: encode_line(row)は、projectの結果を、ensure_ascii=False、separators=(',',':')、sort_keys=TrueでJSONエンコードし、最後に'
'を1つ付けたstrです。name内の改行はJSONのエスケープである必要があります。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
文字列の連結でJSONを作ると、引用符や改行で形式が壊れます。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-lazy-export-lab/03-contract.shで確認してください。
出力件数の上限を検証する(テスト)
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: validate_max(value)は、boolを除く1–1000のintだけをそのまま返し、それ以外はValueErrorです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
無限の入力を誤って最後まで読み込まないように、呼び出し側に上限を要求します。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-lazy-export-lab/04-contract.shで確認してください。
必要な行だけを消費する(テスト)
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: take_rows(rows, maximum)は、isliceなどで最大maximum個だけを遅延して返すiteratorです。呼び出し時にmaximumを検証し、nextを1回呼ぶと入力を1回だけ消費します。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
list(rows)に変換した瞬間に、無限の入力や大容量の入力を処理できなくなります。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-lazy-export-lab/05-contract.shで確認してください。
行を遅延してシリアライズする(テスト)
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: json_lines(rows, maximum=100)は、take_rowsから受け取った行ごとにencode_lineをyieldします。すべてを結合した文字列やリストは返しません。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
オブジェクトの選択と表現の変換を、それぞれ遅延ステップのまま保ちます。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-lazy-export-lab/06-contract.shで確認してください。
ダウンロードした行を再検証する(テスト)
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: decode_lines(text)は、splitlinesの空でない各行をjson.loadsしてvalidate_rowし、リストとして返します。空文字列は[]、空の中間行はValueErrorです。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
空のファイルと、形式が壊れた空のレコードを区別します。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-lazy-export-lab/07-contract.shで確認してください。
HTTPダウンロードを完成させる(テスト)
/root/work/test-lazy-export-lab/test_service.pyで、提供されたservice.pyの次の公開契約をテストしてください: create_app(rows)は、GET /exportでjson_lines(rows, 100)を、application/x-ndjsonのStreamingResponseとして返します。rowsは、再び反復可能なリストです。内部フィールドがなく、各行の内容と順序を保持する必要があります。正常な実装では成功し、この契約に違反する実装では、実際のテスト本文の失敗として検出する必要があります。前のステップのテストを維持したまま、test_関数を追加してください。
Content-Typeだけをストリーミングと書いて、内部ではすべてを集めていないかを、ジェネレーターのテストと合わせて確認します。実装ファイルは修正しません。pytest.raisesで期待する例外を確認し、正常な結果には具体的な期待値をassertしてください。
保存後、bash /opt/lab/checks/test-lazy-export-lab/08-contract.shで確認してください。