Pular para o conteúdo

Faça assert do formato e timing da response

Uma seção # expect: transforma um bloco de “me mostra o que voltou” em “falhe o runbook se qualquer um desses não for verdade”. Útil pra smoke tests commitados no git e rodados como step de CI, ou pro check local “será que staging derivou desde ontem?”.

Dentro de qualquer bloco (HTTP ou DB), adicione uma seção começando com # expect: em uma linha própria. Cada linha embaixo é uma assertion: <campo> <operador> <valor>.

```http
GET {{BASE_URL}}/health
# expect:
# status == 200
# time &lt; 500ms
# body.status == "ok"
# body.version matches /^v\\d+\\.\\d+\\.\\d+$/
```

Tudo depois de # expect: até o fence de fechamento (ou uma linha em branco seguida de conteúdo não-#) é parseado como assertions.

Os mesmos campos das referências entre blocos, mais dois só-HTTP:

CampoNotas
statusstatus HTTP numérico
timetempo total (em ms / s)
sizebytes do body da response
body.<path>JSON path
headers.<name>header de response (case-insensitive)
cookies.<name>valor de set-cookie
affectedsó DB — contagem de linhas de INSERT/UPDATE/DELETE
row[N].<col>só DB — coluna da n-ésima linha de resultado
rows.lengthsó DB — número de linhas retornadas
OperadorSignificadoFunciona em
==igualdade estritastrings, numbers, booleans, null
!=diferentemesmo
< <= > >=comparação numéricanumbers (incl. time, size)
containssubstring ou membership de arraystrings, arrays
not containsinverso de containsstrings, arrays
matchesmatch regexstrings (regex entre /.../)
existspath resolve (sem RHS)qualquer campo
not existsinversoqualquer campo
ischeck de tipo (number, string, array, object, null)qualquer campo

time e size aceitam sufixos:

time < 500ms
time < 1.5s
time < 1m
size < 10kb
size < 100mb
SufixoSignificado
msmilissegundos
ssegundos
mminutos (raro)
b / kb / mb / gbbytes (potências de 1024)

Sem sufixo significa integer puro (time < 500 é < 500ms por default, já que time está em ms; size < 100 é bytes).

Referências funcionam dentro de # expect: como em qualquer outro lugar:

```http alias=cutoff
GET {{BASE_URL}}/last-sync
```
```http
GET {{BASE_URL}}/changes
# expect:
# status == 200
# body.since == {{cutoff.body.timestamp}}
# body.changes.length &gt; 0
```

Útil pra checks “a API concordou com o que pegamos da última vez?”.

Quando qualquer assertion falha:

  1. A toolbar do bloco fica vermelha.
  2. O painel de resultado mostra expected vs actual lado a lado:
    ✗ status == 200
    expected: 200
    actual: 503
    ✓ time < 500ms (143ms)
    ✗ body.status == "ok"
    expected: "ok"
    actual: "degraded"
  3. Blocos downstream não rodam num Run-all — o httui curto-circuita na primeira falha.
  4. A contagem pass/fail da status bar atualiza: 2 passed · 1 failed.

Aperte Cmd+Shift+Enter (ou clique em Run all na status bar) pra rodar todo bloco de cima pra baixo. O sumário mostra total pass/fail entre todas as seções # expect:.

Pra CI, o binário httui-tui pega o mesmo vault e consegue rodar runbooks específicos:

Terminal window
httui-tui run runbooks/smoke-staging.md --env staging
# sai 0 se todos os expects passam, não-zero se algum falhou

Encadeie no seu step de CI como qualquer outro comando de teste.

Comente com um # extra:

# expect:
# status == 200
## time < 500ms ← pulado (instável em cache frio)
# body.status == "ok"

O ## duplo é a convenção do httui pra “ignore essa linha de expect”. Melhor que deletar porque a intenção fica preservada.

Você pode ter mais de uma — elas são agregadas:

GET {{BASE_URL}}/users
# expect:
# status == 200
GET {{BASE_URL}}/users/admin
# expect:
# status == 200
# body.role == "admin"

Cada # expect: aplica ao bloco mais recente. Útil quando o bloco tem sub-passos lógicos que você quer assertar separadamente.

SintomaCausaCorreção
Check body.X exists falha em nullexists retorna false pra campos de body nullUse body.X is null pra distinguir
Regex não matchaEsqueceu de escapar / dentro do regexmatches /^v\\d+\\/release$/ (escape / como \\/)
Assertion de time sempre falha na primeira runConexão fria / lookup DNSPrimeira run baseline é mais lenta; considere time < <p99-de-runs-típicas>
Mismatch de case em assertion de headerHeaders HTTP são case-insensitive — o httui normalizaheaders.content-type e headers.Content-Type são iguais