Como criar um arquivo corrompido para testes
Um validador que só viu arquivos saudáveis não foi realmente testado. Veja como obter um arquivo quebrado de propósito, que sai com exatamente o tamanho que você pede e traz um manifesto dizendo o que o seu sistema deve fazer com ele.
A resposta curta
tfg generate --format png --size 2mb --damage zero-head --out ./out grava um PNG de
exatamente 2097152 bytes cujos primeiros bytes são zeros, e o manifesto ao lado registra que o
seu sistema deve rejeitá-lo.
O jeito de sempre
Por que um arquivo corrompido à mão é um teste ruim
O jeito de sempre é um editor hexadecimal, um script que troca alguns bytes aleatórios ou cortar um
arquivo com head ou truncate. Funciona uma vez, e depois sai caro:
- É diferente a cada vez. Um byte aleatório cai num lugar novo a cada execução, então uma falha de terça pode não voltar na quarta.
- Muda o tamanho. Um arquivo cortado fica menor que o limite abaixo do qual deveria ficar, então a checagem de tamanho responde antes da checagem de conteúdo e o teste passa pelo motivo errado.
- Muitas vezes passa despercebido. Texto simples continua legível com um byte alterado no meio, e um leitor de imagens tolerante simplesmente o desenha, então o arquivo que deveria estar quebrado é aceito.
- Não diz nada sobre o que deve acontecer. O arquivo é só bytes, e quem ler o teste depois precisa adivinhar se a intenção era aceitação ou rejeição.
O que você recebe
Um arquivo danificado continua com o tamanho que você pediu
O arquivo é gerado normalmente e quebrado depois, a caminho do disco. Ele mantém o tamanho pedido, e o mesmo comando grava os mesmos bytes outra vez.
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
As configurações vão depois de dois-pontos. A opção pode ser repetida, e os danos são aplicados na ordem em que você os escreve. Funciona com cada um dos 26 formatos.
O que ele sabe fazer
Quais danos existem?
Esta é a lista que o programa imprime, lida dele quando esta página é construída. tfg
damage imprime a mesma, e tfg damage <id> diz o que cada um aceita.
| Dano | O que faz com os bytes | Menor arquivo | Configurações |
|---|---|---|---|
zero-head |
Sobrescreve os primeiros bytes do arquivo com zeros, sem mexer no comprimento. A maioria dos leitores olha ali primeiro, então quase tudo percebe este dano. | 8 | bytes |
zero-head grava zeros sobre o início do arquivo. A maioria dos leitores olha ali
primeiro, para a assinatura e o cabeçalho que dizem o que o arquivo é, então quase todo leitor
percebe. Texto simples e logs não têm assinatura e também são recusados, porque uma sequência de
bytes zero não é texto. Abaixo de quatro bytes, alguns formatos saem com um dano de que nenhum
leitor reclama, e por isso a configuração começa em quatro.
O que o manifesto diz
Um manifesto que diz o que deve acontecer
Cada arquivo danificado recebe uma entrada dizendo que o seu sistema deve rejeitá-lo, com o dano registrado ao lado:
"expected": {
"outcome": "reject",
"reason": "content_malformed",
"confidence": "certain"
},
"damage": [
{
"type": "zero-head",
"settings": {
"bytes": "8"
}
}
]
Dois pedidos são recusados antes de qualquer coisa ser gravada, porque cada um deixaria no disco um arquivo que o manifesto descreve errado:
- um arquivo menor do que o dano precisa, que sairia intacto
-
expected: acceptao lado de um dano, porque nada poderia cumprir isso. Escrevasanitizese o seu sistema deve consertar o arquivo, ouunspecifiedse essa é justamente a pergunta que você faz
Em uma receita
Arquivos saudáveis e quebrados em uma execução
Ponha os dois em uma receita, e o manifesto leva a expectativa de cada arquivo, então o teste não precisa de uma lista de qual é qual:
version: 1
targets:
- id: healthy
format: pdf
size: 1mb
expected: accept
- id: broken
format: pdf
size: 1mb
damage:
- zero-head
Em um teste
Transformando em um teste
O teste lê o manifesto e confere se o que aconteceu é o que foi declarado. Não precisa de uma lista de nomes de arquivo:
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
Uma boa recusa é uma recusa limpa. Uma mensagem que diz o que estava errado é a resposta que você quer. Um erro de servidor, uma trava ou um arquivo salvo pela metade é o defeito que este teste existe para achar.
A seguir
Para onde ir daqui
- O preset upload-validation faz a um formulário as outras duas perguntas, a do tamanho e a do tipo.
- Arquivos de teste no CI executa uma receita como esta em um pipeline.
-
A documentação traz cada opção de
tfg generate.