テスト用の破損ファイルを作る方法
健全なファイルしか見せたことのない検証は、本当にはテストされていません。ここでは、意図的に壊してあり、求めたとおりのサイズで出てきて、システムがそれをどう扱うべきかを示すマニフェストを伴うファイルの作り方を説明します。
短い答え
tfg generate --format png --size 2mb --damage zero-head --out
./outは、ちょうど2097152バイトで先頭のバイトがゼロのPNGを書き出し、隣のマニフェストにはシステムがそれを拒否すべきだと記録されます。
よくあるやり方
手作業で壊したファイルがよくないテストになる理由
よくあるのは、16進エディタ、いくつかのランダムなバイトを反転させるスクリプト、headやtruncateでファイルを短く切る方法です。一度は使えますが、あとで手間がかかります。
- 毎回違います。ランダムなバイトは実行のたびに別の場所に当たるので、火曜日の失敗が水曜日には再現しないことがあります。
- サイズが変わります。切り詰めたファイルは、本来下回るはずだった上限よりも小さくなるため、サイズの検査が内容の検査より先に答えてしまい、テストは誤った理由で通ります。
- 気づかれないことが多くあります。プレーンテキストは途中の1バイトが変わっても読めますし、寛容な画像リーダーはそのまま描画するので、壊れているはずのファイルが受け入れられてしまいます。
- 何が起こるべきかを伝えません。ファイルはただのバイト列で、後からテストを読む人は、受け入れと拒否のどちらが意図だったのかを推測するしかありません。
得られるもの
破損したファイルも、求めたサイズのままです
ファイルは通常どおり生成され、ディスクへ向かう途中で壊されます。求めたサイズは保たれ、同じコマンドは同じバイトをもう一度書き出します。
tfg generate --format png --size 2mb --damage zero-head --out ./out
tfg generate --format pdf --size 1mb --count 5 --damage zero-head:bytes=16 --out ./broken
設定はコロンの後ろに書きます。オプションは繰り返せて、破損は書いた順に適用されます。26種類すべての形式で使えます。
できること
どんな破損がありますか。
これはプログラムが出力する一覧で、このページを作るときにプログラムから読み取っています。tfg damageは同じ一覧を出力し、tfg damage
<id>はそのうちの1つが受け取る設定を示します。
| 破損 | バイトへの作用 | 最小ファイル | 設定 |
|---|---|---|---|
zero-head |
ファイルの先頭のバイトをゼロで上書きし、長さは変えません。ほとんどのリーダーはまずそこを見るので、この破損にはほぼどれも気づきます。 | 8 | bytes |
zero-headはファイルの先頭をゼロで上書きします。ほとんどのリーダーはまずそこを見ます。ファイルが何であるかを示すシグネチャとヘッダーです。そのため、ほぼどのリーダーでも気づきます。プレーンテキストやログにはシグネチャがありませんが、これらも拒否されます。ゼロのバイトの連なりはテキストではないからです。4バイト未満では、どのリーダーも文句を言わない破損になる形式があります。設定が4から始まるのはそのためです。
マニフェストの内容
何が起こるべきかを示すマニフェスト
破損したファイルにはそれぞれ、システムがそれを拒否すべきことを示す項目が付き、破損の内容がその横に記録されます。
"expected": {
"outcome": "reject",
"reason": "content_malformed",
"confidence": "certain"
},
"damage": [
{
"type": "zero-head",
"settings": {
"bytes": "8"
}
}
]
2種類のリクエストは、何かが書き込まれる前に拒否されます。どちらもマニフェストの記述が誤ったファイルをディスクに残してしまうからです。
- 破損に必要なサイズより小さいファイル。そのままの状態で出てきてしまいます
-
破損と並べた
expected: accept。何もそれを満たせないためです。システムがファイルを修復するはずならsanitizeを、まさにそれを確かめたいならunspecifiedを書いてください
レシピで
1回の実行に健全なファイルと壊れたファイルを
両方を1つのレシピに入れると、マニフェストが各ファイルの期待を持つので、テストにどれがどれかの一覧は要りません。
version: 1
targets:
- id: healthy
format: pdf
size: 1mb
expected: accept
- id: broken
format: pdf
size: 1mb
damage:
- zero-head
テストで
テストにする
テストはマニフェストを読み、起きたことが宣言どおりかどうかを確かめます。ファイル名の一覧は要りません。
import json, os
directory = "healthy-and-broken"
manifest = json.load(open(os.path.join(directory, "manifest.json")))
for entry in manifest["files"]:
response = upload(os.path.join(directory, entry["path"]))
if entry["expected"]["outcome"] == "reject":
assert not response.ok
else:
assert response.ok
よい拒否は、きれいな拒否です。何が悪かったかを伝えるメッセージが、求めている答えです。サーバーエラー、ハング、書きかけで保存されたファイルは、このテストが見つけるために存在する欠陥です。
次へ
ここからどこへ
- upload-validationプリセットは、フォームにほかの2つの問い、つまりサイズと種類を尋ねます。
- CIでのテストファイルは、このようなレシピをパイプラインで実行します。
-
ドキュメントには
tfg generateのすべてのオプションが載っています。