feat: botao de testes em dev, paginacao, controle otimista, fix datetime.utcnow #30

Merged
jhony75 merged 5 commits from feature/combined-perf-quality into develop 2026-09-06 02:32:11 -03:00
Owner

Quatro entregas independentes, cada uma seu proprio commit:

  • Botao "Rodar testes" em dev: tela /dev-tools dispara a suite pytest completa pela UI e mostra o resultado. So aparece com ENVIRONMENT=development no backend (fail-safe: 404 em qualquer outro caso). Imagem Docker so instala requirements-dev.txt quando o build recebe INSTALL_DEV_DEPS=true.
  • Paginacao opt-in em GET /api/reports, /issues e /runs (limit/offset opcionais, total no header X-Total-Count) � 100% retrocompativel.
  • Controle otimista de edicao via If-Unmodified-Since (RFC 7232) em PUT /api/reports, /runs, /technical-validations e PATCH /api/issues � opt-in, 412 se o registro mudou desde a leitura. Issue ganhou updated_at (migration nova).
  • Fix: datetime.utcnow() (deprecado) substituido por time_utils.utcnow_naive() em todo o backend � mudanca de implementacao, sem efeito em schema/comportamento.

Validado com suite completa do backend (85 testes, incluindo os novos) + typecheck/testes/build do frontend + smoke test manual no navegador (botao de testes) + testes via curl (paginacao, concorrencia otimista).

Quatro entregas independentes, cada uma seu proprio commit: - **Botao "Rodar testes" em dev**: tela /dev-tools dispara a suite pytest completa pela UI e mostra o resultado. So aparece com ENVIRONMENT=development no backend (fail-safe: 404 em qualquer outro caso). Imagem Docker so instala requirements-dev.txt quando o build recebe INSTALL_DEV_DEPS=true. - **Paginacao opt-in** em GET /api/reports, /issues e /runs (limit/offset opcionais, total no header X-Total-Count) � 100% retrocompativel. - **Controle otimista de edicao** via If-Unmodified-Since (RFC 7232) em PUT /api/reports, /runs, /technical-validations e PATCH /api/issues � opt-in, 412 se o registro mudou desde a leitura. Issue ganhou updated_at (migration nova). - **Fix**: datetime.utcnow() (deprecado) substituido por time_utils.utcnow_naive() em todo o backend � mudanca de implementacao, sem efeito em schema/comportamento. Validado com suite completa do backend (85 testes, incluindo os novos) + typecheck/testes/build do frontend + smoke test manual no navegador (botao de testes) + testes via curl (paginacao, concorrencia otimista).
Nova tela "Ferramentas de Dev" (/dev-tools) com um botao que dispara a suite
pytest completa e mostra passou/falhou, contagens e saida completa. So aparece
com ENVIRONMENT=development no backend (fail-safe: qualquer outro valor, ou
nao configurado, esconde a rota com 404, sem vazar que existe).

Cada teste da suite usa seu proprio banco SQLite temporario (ver tests/*.py) —
rodar isso pela UI nunca toca no banco de dev/prod de verdade.

Imagem Docker do backend so instala requirements-dev.txt (pytest) quando o
build recebe INSTALL_DEV_DEPS=true (BACKEND_INSTALL_DEV_DEPS=true no .env.dev),
mantendo a imagem de producao enxuta. Validado com build limpo da imagem +
container rodando em modo dev (endpoint disparando a suite real) e em modo
prod (endpoint escondido) + smoke test completo no navegador.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
limit/offset opcionais (services/pagination.py) — sem eles, comportamento de
sempre (lista completa, sem envelope); com eles, a lista e fatiada e o total
(antes do corte) vai no header X-Total-Count. 100% retrocompativel: nenhum
consumidor existente precisa mudar. limit vai de 1 a 200 (400 fora da faixa).

Validado com testes automatizados (4 novos, cobrindo os 3 endpoints + limites
invalidos) e manualmente via curl contra dados reais.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
PUT /api/reports/{id}, /runs/{id}, /technical-validations/{id} e
PATCH /api/issues/{id} suportam o header HTTP padrao If-Unmodified-Since (RFC
7232) — opt-in, sem quebrar quem nao manda o header (last-write-wins de
sempre). Quem manda e o registro mudou desde entao recebe 412 Precondition
Failed. Os GETs correspondentes mandam Last-Modified de volta.

Issue ganhou updated_at (migration 4ca07fcf36fd, backfill com created_at pras
linhas existentes) — nao tinha essa coluna ainda.

Achado durante a implementacao: Last-Modified/If-Unmodified-Since so tem
resolucao de segundos (padrao HTTP) — comparar direto contra updated_at (que
tem microssegundos) rejeitava toda edicao com 412 mesmo sem conflito real.
Corrigido truncando os dois lados pra segundos antes de comparar.

Frontend ainda nao consome isso (nenhum formulario captura Last-Modified nem
reenvia If-Unmodified-Since) — documentado em BACKLOG.md como proximo passo,
a capacidade ja existe pronta no backend.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
fix: substituir datetime.utcnow() deprecado por time_utils.utcnow_naive()
Some checks failed
EEG QA CI / validate-and-build (pull_request) Failing after 1m7s
EEG QA CI / Deploy production (pull_request) Has been skipped
EEG QA CI / Deploy development (pull_request) Has been skipped
8038d8ca68
datetime.utcnow() esta deprecado desde Python 3.12 (removido numa versao
futura) e gerava 121 DeprecationWarning na suite de testes. Substituido em
todo o backend (models.py, modules/issues, modules/reporting,
modules/eeg_reports/docx_export, scripts/generate-synthetic-data.py) por
time_utils.utcnow_naive() — datetime.now(timezone.utc).replace(tzinfo=None),
que devolve exatamente o mesmo tipo (naive, UTC) que as colunas DateTime do
SQLAlchemy/SQLite sempre esperaram. Mudanca de implementacao, nao de
comportamento nem de schema.

Suite completa (85 testes) roda sem esses warnings agora.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
fix: restaurar import de datetime usado como type hint em docx_export.py
All checks were successful
EEG QA CI / validate-and-build (pull_request) Successful in 4m25s
EEG QA CI / Deploy production (pull_request) Has been skipped
EEG QA CI / Deploy development (pull_request) Has been skipped
df4157435e
O commit anterior (8038d8c) removeu "from datetime import datetime" ao trocar
datetime.utcnow() por time_utils.utcnow_naive(), mas o nome datetime ainda era
usado como anotacao de tipo em _data_extenso_pt(dt: datetime) — nao capturado
pelo grep usado pra levantar os usos (so buscava "datetime\." com ponto, nao
pegando anotacoes de tipo puras). Python avalia anotacoes em tempo de definicao
por padrao, entao isso quebrava a importacao do modulo inteiro (NameError),
derrubando a app inteira (qualquer rota que dependa de modules.eeg_reports).

Passou despercebido nos testes locais por causa de bytecode cache
(__pycache__) nao invalidado — confirmado ao reproduzir com cache limpo antes
deste fix (51 failed) e depois (85 passed). Pego pela pipeline de CI (ambiente
limpo, sem cache local) — obrigado por reportar.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
jhony75 merged commit 9923c53a7f into develop 2026-09-06 02:32:11 -03:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
jhony75/eeg-qa!30
No description provided.