Як зробити пошкоджений файл для тестів
Валідатор, якому показували лише здорові файли, насправді не перевірений. Ось як отримати файл, навмисно зіпсований, що виходить точно такого розміру, який ви просите, і несе маніфест із вказівкою, що ваша система має з ним зробити.
Коротка відповідь
tfg generate --format png --size 2mb --damage zero-head --out ./out записує PNG рівно в
2097152 байти, перші байти якого нулі, а маніфест поруч фіксує, що ваша система має його
відхилити.
Звичайний шлях
Чому файл, зіпсований вручну, - поганий тест
Зазвичай беруть шістнадцятковий редактор, скрипт, що перевертає кілька випадкових байтів, або
вкорочують файл через head чи truncate. Один раз це працює, а потім
коштує дорого:
- Щоразу по-різному. Випадковий байт при кожному запуску потрапляє в нове місце, тому збій у вівторок у середу може не повторитися.
- Змінюється розмір. Обрізаний файл менший за ліміт, під яким він мав залишатися, тому перевірка розміру відповідає раніше за перевірку вмісту, і тест проходить із неправильної причини.
- Це часто лишається непоміченим. Простий текст читається і зі зміненим байтом посередині, а поблажлива програма читання зображень просто малює його, тож файл, який мав бути зіпсований, приймається.
- Не сказано, що має статися. Файл - це лише байти, і тому, хто читатиме тест пізніше, доведеться гадати, чи йшлося про прийняття, чи про відхилення.
Що ви отримуєте
Пошкоджений файл лишається потрібного розміру
Файл створюється як зазвичай і псується потім, дорогою на диск. Він зберігає заданий розмір, а та сама команда знову записує ті самі байти.
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> каже, що приймає
одне з них.
| Пошкодження | Що воно робить із байтами | Найменший файл | Налаштування |
|---|---|---|---|
zero-head |
Перезаписує перші байти файлу нулями, не змінюючи його довжину. Більшість програм читання дивляться спочатку туди, тому це пошкодження помічає майже все. | 8 | bytes |
zero-head записує нулі поверх початку файлу. Більшість програм читання дивляться
спочатку туди, на сигнатуру й заголовок, які кажуть, що це за файл, тому помічає майже будь-яка.
У простого тексту та журналів сигнатури немає, і їх теж відхиляють, бо послідовність нульових
байтів не є текстом. Менше ніж чотири байти - і в деяких форматів виходить пошкодження, на яке
не скаржиться жодна програма читання, тому налаштування починається з чотирьох.
Що каже маніфест
Маніфест, який каже, що має статися
Кожен пошкоджений файл отримує запис про те, що ваша система має його відхилити, а поруч записано пошкодження:
"expected": {
"outcome": "reject",
"reason": "content_malformed",
"confidence": "certain"
},
"damage": [
{
"type": "zero-head",
"settings": {
"bytes": "8"
}
}
]
Два запити відхиляються, перш ніж щось буде записано, бо кожен залишив би на диску файл, який маніфест описує хибно:
- файл менший, ніж потрібно пошкодженню, який вийшов би недоторканим
-
expected: acceptпоруч із пошкодженням, бо ніщо не могло б цього виконати. Напишітьsanitize, якщо ваша система має полагодити файл, абоunspecified, якщо саме це ви й перевіряєте
У рецепті
Здорові й зламані файли за один запуск
Покладіть обидва види в один рецепт, і маніфест несе очікування для кожного файлу, тож тесту не потрібен список, який файл який:
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 ставить формі два інші запитання, про розмір і про тип.
- Тестові файли в CI запускають такий рецепт у конвеєрі.
-
Документація містить кожен параметр
tfg generate.