Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,12 @@ updates:
groups:
actions:
update-types: ['minor', 'patch']

# Imagem da restful-booker local (docker-compose.yml). Ela só publica a tag
# "latest", então a atualização é uma troca de digest, sem versão semver:
# o auto-merge não se aplica e o PR passa por revisão manual, o que é o
# desejado, já que uma versão nova da API pode mudar o comportamento testado.
- package-ecosystem: 'docker-compose'
directory: '/'
schedule:
interval: 'weekly'
26 changes: 25 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,21 @@ jobs:
# Suíte completa contra a restful-booker. Roda também os unitários de novo
# (custam milissegundos) para que o relatório e a cobertura continuem
# cobrindo o projeto inteiro. Uma falha aqui com o job unit verde indica
# problema na API pública ou num cenário, não no código dos clients.
# problema na API ou num cenário, não no código dos clients.
#
# PRs e pushes usam uma restful-booker local (docker-compose.yml), com a
# versão fixada: o resultado não depende do Heroku estar no ar nem de dados
# alterados por terceiros. A execução diária usa a API pública, para
# perceber quando o comportamento dela mudar.
integration:
needs: unit
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.12', '3.13']
env:
BASE_URL: ${{ github.event_name == 'schedule' && 'https://restful-booker.herokuapp.com' || 'http://localhost:3001' }}

steps:
- name: Checkout código
Expand All @@ -84,6 +91,23 @@ jobs:
- name: Instalar dependências
run: uv sync --locked --extra dev

# O container leva alguns segundos para começar a responder; o /ping
# devolve 201 quando a API está pronta.
- name: Subir a restful-booker local
if: github.event_name != 'schedule'
run: |
docker compose up -d
for i in $(seq 1 30); do
if [ "$(curl -s -o /dev/null -w '%{http_code}' "$BASE_URL/ping")" = "201" ]; then
echo "API pronta em $BASE_URL"
exit 0
fi
sleep 2
done
echo "A API local não respondeu em 60 segundos"
docker compose logs
exit 1

- name: Rodar testes
run: uv run pytest

Expand Down
24 changes: 21 additions & 3 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ This repository contains automated tests for the public **[restful-booker](https

The goal is to practice real-world API testing: authentication, full CRUD, the difference between PUT and PATCH, and authorization rules.

What is tested, how and why (scope, approach by level, environments, risks and exit criteria) is described in the [test strategy](docs/test-strategy.en.md).

The Gherkin scenarios, the test case matrix and the commit history are written in Portuguese, the project's main language.

![Test run report generated by pytest-html](docs/assets/report-example.png)
Expand All @@ -34,7 +36,9 @@ qa-api-python/
│ └── dependabot.yml # Weekly dependency updates (uv and GitHub Actions)
├── docs/
│ ├── assets/ # Images used in the README
│ └── test-cases.md # Test case traceability matrix
│ ├── test-cases.md # Test case traceability matrix
│ ├── test-strategy.md # Portuguese version of the test strategy
│ └── test-strategy.en.md # Test strategy: scope, approach, environments, risks and criteria
├── features/ # Gherkin scenarios (.feature)
├── steps/ # pytest-bdd steps (integration tests, hit the real API)
│ ├── test_<feature>.py # One module per .feature file, with the steps only it uses
Expand All @@ -48,6 +52,7 @@ qa-api-python/
├── conftest.py # Fixtures (per-scenario context) and failure evidence capture
├── pyproject.toml # Dependencies and configuration (pytest, ruff, mypy)
├── uv.lock # Exact versions of every dependency (generated by uv)
├── docker-compose.yml # Local restful-booker with a pinned version, used in CI and to run the tests without the public API
├── .pre-commit-config.yaml # Pre-commit hooks (ruff, mypy)
├── codecov.yml # Coverage targets checked by Codecov on PRs
├── LICENSE
Expand Down Expand Up @@ -117,6 +122,19 @@ Codecov also blocks the PR merge (configured in `codecov.yml`) if the new or cha

A pre-commit hook (the `pre-commit` framework, installed with `pre-commit install` after `uv sync`) runs `ruff --fix` and `ruff format` on staged files, and `mypy .` on the whole project, before every commit. Most lint, formatting or type problems are caught locally before they reach CI.

### Run against the local API (Docker)

The integration tests can run against a local restful-booker, on the same version used in CI (pinned by the image digest in `docker-compose.yml`). The result then does not depend on the public API being up or on data changed by other people. With Docker running:

```
docker compose up -d # starts the API at http://localhost:3001
$env:BASE_URL="http://localhost:3001"; pytest # PowerShell
BASE_URL=http://localhost:3001 pytest # bash
docker compose down # removes the container
```

Data is kept in memory and goes back to its initial state on every `docker compose up`.

### Run against another environment

By default the tests target `https://restful-booker.herokuapp.com` and authenticate with the public credentials from the API documentation (`admin` / `password123`). To run against another environment (for example, a local or staging instance), set these environment variables:
Expand Down Expand Up @@ -168,8 +186,8 @@ Authentication: `POST /auth` with `{ "username": "admin", "password": "password1
- Automatic cleanup: the `context` fixture deletes the booking created in the scenario (with its own cleanup token) at the end of each test, so data does not pile up on the public API. If the deletion fails because of a network or authentication error, pytest shows a warning with the id of the booking left behind, without changing the test result.
- Unique test data: each created booking gets a random suffix in its last name (e.g. `Ciclano-3f9a1c`). Since the public API is shared, including by the parallel CI runs, searching by name finds only the test's own booking.
- Automatic retry for network failures only (`pytest-rerunfailures`, with `--reruns 1` and `only_rerun` in `pyproject.toml`): a test that fails because of a connection error or timeout runs a second time, absorbing the instability of the public demo API. Assertion or schema failures are never retried, so an intermittent bug cannot pass on the second attempt.
- Continuous integration with GitHub Actions, in two jobs: `unit` runs the static checks and the unit tests, with no network, and `integration` starts only if `unit` passes, running the full suite against the public API. A failure in `integration` with `unit` green therefore points to the API or a scenario, not to the clients' code. Both run on every push and pull request to `master`, on Python 3.12 and 3.13, and also daily at 06:00 UTC (see [.github/workflows/tests.yml](.github/workflows/tests.yml)) to catch breakage caused by the public API itself, with the HTML report published as a workflow artifact.
- Dependencies updated automatically by Dependabot (uv and GitHub Actions, weekly; see [.github/dependabot.yml](.github/dependabot.yml)). Patch and minor updates arrive grouped, one PR per week for each ecosystem, and are merged automatically when CI passes ([.github/workflows/dependabot-auto-merge.yml](.github/workflows/dependabot-auto-merge.yml)); major bumps require manual review.
- Continuous integration with GitHub Actions, in two jobs: `unit` runs the static checks and the unit tests, with no network, and `integration` starts only if `unit` passes, running the full suite against a local restful-booker, in a container with the version pinned in `docker-compose.yml`. A failure in `integration` with `unit` green therefore points to the API or a scenario, not to the clients' code. Both run on every push and pull request to `master`, on Python 3.12 and 3.13, and also daily at 06:00 UTC (see [.github/workflows/tests.yml](.github/workflows/tests.yml)). The daily run targets the public API, to catch changes in its behavior, with the HTML report published as a workflow artifact.
- Dependencies updated automatically by Dependabot (uv, GitHub Actions and the local API's Docker image, weekly; see [.github/dependabot.yml](.github/dependabot.yml)). Patch and minor updates arrive grouped, one PR per week for each ecosystem, and are merged automatically when CI passes ([.github/workflows/dependabot-auto-merge.yml](.github/workflows/dependabot-auto-merge.yml)); major bumps require manual review.
- Audit of known vulnerabilities in the installed dependencies on every CI run, with [`pip-audit`](https://github.com/pypa/pip-audit); finding a vulnerability breaks the build.
- QA traceability: test case matrix in [docs/test-cases.md](docs/test-cases.md), with `@TC-XXX` tags on every `Cenário` (scenario) and a `@smoke` subset (`pytest -m smoke`) covering the most critical end-to-end flows. The `TC-XXX` markers are registered automatically from the `.feature` tags, pytest runs with `--strict-markers` (a misspelled tag breaks collection), and the test [tests/unit/test_traceability.py](tests/unit/test_traceability.py) ensures the matrix lists exactly the TCs in the `.feature` files, with links to the right line of each scenario. Real bugs found are documented as GitHub Issues using the template in [.github/ISSUE_TEMPLATE/bug_report.md](.github/ISSUE_TEMPLATE/bug_report.md).

Expand Down
24 changes: 21 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Este repositório contém testes automatizados da API pública **[restful-booker

O objetivo é praticar testes de API "de verdade": autenticação, CRUD completo, diferença entre PUT e PATCH, e validação de regras de autorização.

O que é testado, como e por quê (escopo, abordagem por nível, ambientes, riscos e critérios de saída) está na [estratégia de testes](docs/test-strategy.md).

![Relatório de execução dos testes gerado pelo pytest-html](docs/assets/report-example.png)

*Exemplo do relatório de execução (captura de tela). O relatório ao vivo e sempre atualizado fica em [thomastds.github.io/qa-api-python](https://thomastds.github.io/qa-api-python/).*
Expand All @@ -32,7 +34,9 @@ qa-api-python/
│ └── dependabot.yml # Atualização semanal de dependências (uv e GitHub Actions)
├── docs/
│ ├── assets/ # Imagens usadas no README
│ └── test-cases.md # Matriz de rastreabilidade dos test cases
│ ├── test-cases.md # Matriz de rastreabilidade dos test cases
│ ├── test-strategy.md # Estratégia de testes: escopo, abordagem, ambientes, riscos e critérios
│ └── test-strategy.en.md # Versão em inglês da estratégia de testes
├── features/ # Cenários em Gherkin (.feature)
├── steps/ # Steps do pytest-bdd (testes de integração, batem na API real)
│ ├── test_<feature>.py # Um módulo por arquivo .feature, com os steps exclusivos dele
Expand All @@ -46,6 +50,7 @@ qa-api-python/
├── conftest.py # Fixtures (contexto por cenário) e captura de evidência de falha
├── pyproject.toml # Dependências e configuração (pytest, ruff, mypy)
├── uv.lock # Versões exatas de todas as dependências (gerado pelo uv)
├── docker-compose.yml # restful-booker local, com a versão fixada, usada no CI e para rodar os testes sem a API pública
├── .pre-commit-config.yaml # Hooks de pre-commit (ruff, mypy)
├── codecov.yml # Metas de cobertura verificadas pelo Codecov nos PRs
├── LICENSE
Expand Down Expand Up @@ -115,6 +120,19 @@ O Codecov também bloqueia o merge do PR (configuração em `codecov.yml`) se o

Um hook de pre-commit (framework `pre-commit`, instalado via `pre-commit install` após o `uv sync`) roda `ruff --fix` e `ruff format` nos arquivos staged, e também `mypy .` no projeto inteiro, antes de cada commit — então a maioria dos problemas de lint, formatação ou tipo já é pega localmente antes de chegar no CI.

### Rodar contra a API local (Docker)

Os testes de integração podem rodar contra uma restful-booker local, na mesma versão usada no CI (fixada pelo digest da imagem no `docker-compose.yml`). Assim o resultado não depende da API pública estar no ar nem de dados alterados por outras pessoas. Com o Docker em execução:

```
docker compose up -d # sobe a API em http://localhost:3001
$env:BASE_URL="http://localhost:3001"; pytest # PowerShell
BASE_URL=http://localhost:3001 pytest # bash
docker compose down # remove o container
```

Os dados ficam em memória e voltam ao estado inicial a cada `docker compose up`.

### Rodar contra outro ambiente

Por padrão os testes apontam para `https://restful-booker.herokuapp.com` e se autenticam com as credenciais públicas da documentação da API (`admin` / `password123`). Para rodar contra outro ambiente (ex: uma instância local ou de staging), defina as variáveis de ambiente:
Expand Down Expand Up @@ -166,8 +184,8 @@ Autenticação: `POST /auth` com `{ "username": "admin", "password": "password12
- Limpeza automática: a fixture `context` remove o booking criado no cenário (via token próprio de limpeza) ao final de cada teste, evitando acúmulo de dados na API pública. Se a remoção falhar por erro de rede ou de autenticação, o pytest mostra um aviso com o id do booking que ficou para trás, sem mudar o resultado do teste.
- Dados de teste únicos: cada booking criado leva um sufixo aleatório no sobrenome (ex: `Ciclano-3f9a1c`). Como a API pública é compartilhada, inclusive pelas execuções paralelas do CI, a busca por nome encontra só o booking do próprio teste.
- Retry automático só para falhas de rede (`pytest-rerunfailures`, com `--reruns 1` e `only_rerun` no `pyproject.toml`): um teste que falha por erro de conexão ou timeout roda uma segunda vez, amortecendo a instabilidade da API pública de demonstração. Falhas de asserção ou de schema nunca são repetidas, para que um bug intermitente não passe na segunda tentativa.
- Integração contínua via GitHub Actions, em dois jobs: `unit` roda as checagens estáticas e os testes unitários, sem rede, e `integration` só começa se o `unit` passar, rodando a suíte completa contra a API pública. Assim, uma falha no `integration` com o `unit` verde aponta para a API ou para um cenário, e não para o código dos clients. Os dois rodam a cada push e pull request para `master`, em Python 3.12 e 3.13, e também diariamente às 06:00 UTC (ver [.github/workflows/tests.yml](.github/workflows/tests.yml)) para detectar quebras causadas pela própria API pública, com o relatório HTML publicado como artifact do workflow.
- Dependências atualizadas automaticamente pelo Dependabot (uv e GitHub Actions, semanal — ver [.github/dependabot.yml](.github/dependabot.yml)). As atualizações de patch e minor chegam agrupadas, um PR por semana para cada ecossistema, e são mergeadas automaticamente quando o CI passa ([.github/workflows/dependabot-auto-merge.yml](.github/workflows/dependabot-auto-merge.yml)); bumps de major exigem revisão manual.
- Integração contínua via GitHub Actions, em dois jobs: `unit` roda as checagens estáticas e os testes unitários, sem rede, e `integration` só começa se o `unit` passar, rodando a suíte completa contra uma restful-booker local, num container com a versão fixada no `docker-compose.yml`. Assim, uma falha no `integration` com o `unit` verde aponta para a API ou para um cenário, e não para o código dos clients. Os dois rodam a cada push e pull request para `master`, em Python 3.12 e 3.13, e também diariamente às 06:00 UTC (ver [.github/workflows/tests.yml](.github/workflows/tests.yml)). A execução diária roda contra a API pública, para detectar quando o comportamento dela mudar, com o relatório HTML publicado como artifact do workflow.
- Dependências atualizadas automaticamente pelo Dependabot (uv, GitHub Actions e a imagem Docker da API local, semanal — ver [.github/dependabot.yml](.github/dependabot.yml)). As atualizações de patch e minor chegam agrupadas, um PR por semana para cada ecossistema, e são mergeadas automaticamente quando o CI passa ([.github/workflows/dependabot-auto-merge.yml](.github/workflows/dependabot-auto-merge.yml)); bumps de major exigem revisão manual.
- Auditoria de vulnerabilidades conhecidas nas dependências instaladas a cada execução do CI, via [`pip-audit`](https://github.com/pypa/pip-audit); encontrar uma vulnerabilidade quebra o build.
- Rastreabilidade de QA: matriz de test cases em [docs/test-cases.md](docs/test-cases.md), com tags `@TC-XXX` em cada `Cenário` e um subconjunto `@smoke` (`pytest -m smoke`) cobrindo os fluxos ponta-a-ponta mais críticos. Os marcadores `TC-XXX` são registrados automaticamente a partir das tags dos `.feature`, o pytest roda com `--strict-markers` (uma tag com erro de digitação quebra a coleta), e o teste [tests/unit/test_traceability.py](tests/unit/test_traceability.py) garante que a matriz lista exatamente os TCs dos `.feature`, com links para a linha certa de cada cenário. Bugs reais encontrados são documentados como GitHub Issues usando o template em [.github/ISSUE_TEMPLATE/bug_report.md](.github/ISSUE_TEMPLATE/bug_report.md).

Expand Down
10 changes: 10 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# restful-booker local, para rodar os testes de integração sem depender da API
# pública. A imagem é fixada pelo digest (a imagem só publica a tag "latest"),
# para que todo mundo teste contra a mesma versão; o Dependabot abre um PR
# quando sair uma nova. Os dados ficam em memória e voltam ao estado inicial
# a cada "docker compose up".
services:
restful-booker:
image: mwinteringham/restfulbooker:latest@sha256:a87ae093264caf64cad2557256921cbdb1fec466d6356b5e140e3f060b904f04
ports:
- '3001:3001'
Loading
Loading