Repository navigation
Description
Contexto
O projeto tem zero testes automatizados e nenhum test runner: package.json só expõe dev/build/start/typecheck/lint/db:migrate — não há test, nem Vitest/Jest nas devDependencies. A única "cobertura" hoje são scripts .mjs ad-hoc (scripts/validate-persistence.mjs, .claude/skills/*/scripts/validate-metrics.mjs) que a CI não roda (.github/workflows/ci.yml = typecheck + build). Isso é a dívida nº1 do backlog.
O melhor primeiro alvo já existe puro e pronto para teste: a fronteira anti-cheat do leaderboard em src/lib/room.ts:123 (sanitizeResults) + clampInt (room.ts:107). É código security-critical — a API roda com service_role e sanitizeResults é o único guardião das tabelas públicas matches/scores (ver comentário room.ts:93-104). Uma regressão aqui corrompe silenciosamente o ranking global (foi exatamente a classe de bug de #7 e #28), e nada na CI hoje pegaria.
O próprio docstring de sanitizeResults (room.ts:121) já afirma "coberto por scripts/validate-persistence.mjs" — mas isso é um script solto, não um teste versionado que roda no CI. Esta issue transforma essa promessa em rede de segurança real.
Escopo
- Adicionar Vitest como
devDependency(runner leve, dev-only — não entra no bundle de runtime, respeita HANDBOOK §7.1 sobre deps pesadas de runtime). Config mínima (vitest.config.ts) comenvironment: 'node'(as funções-alvo são puras, sem DOM). - Script
"test": "vitest run"(e opcionalmente"test:watch": "vitest") empackage.json. - Primeiro suite
src/lib/room.test.tscobrindosanitizeResultseclampInt. - A CI (
.github/workflows/ci.yml) passa a rodarpnpm testcomo etapa do gate.
Acceptance criteria (verificáveis)
-
pnpm testexiste e roda; sai verde com o novo suite. - Vitest está em
devDependencies(não emdependencies);pnpm buildcontinua verde e o bundle de produção não muda. -
src/lib/room.test.tscobre, no mínimo, parasanitizeResults:- input não-array →
[]; - linha com
wpm > MAX_PLAUSIBLE_WPM(350) → descartada (não clampada) — caso do recorde 3596 WPM de Anti-cheat/Leaderboard: recorde impossível (3596 WPM) no Ranking Global — sem teto de plausibilidade #28; - linha com
wpmnegativo /NaN/ não-finito → descartada; namevazio apóstrim→ descartada (score anônimo);name>MAX_NAME_LEN→ truncado em 20;accuracyfora de 0..100 → clampada para o intervalo;- array maior que
room.max_players→ cortado no cap;max_playersinválido → cap absoluto 12 (ABSOLUTE_MAX_PLAYERS); - campos cosméticos (
id/color/progress/finishedAt) preservados/normalizados como no código atual.
- input não-array →
-
clampInt:NaN/não-finito →min; arredonda; respeita[min,max]. - CI roda a suíte de testes (passo novo em
ci.yml) e falha o build se um teste quebrar.
Dica de abordagem
- Vitest integra com o tsconfig do Next sem transpiler extra;
import { sanitizeResults, clampInt, MAX_PLAUSIBLE_WPM } from './room'.clampInthoje não é exportado (room.ts:107) — exportá-lo é uma mudança segura de 1 linha, ou testá-lo indiretamente viasanitizeResults(accuracy/errors). - Reaproveite os casos que já existem em
scripts/validate-persistence.mjscomo fonte de casos-verdade — a ideia é migrar aquele conhecimento ad-hoc para um teste versionado que a CI executa (o script pode continuar existindo ou ser aposentado numa fatia futura; não é escopo desta issue removê-lo). - Gate (HANDBOOK §6):
pnpm typecheck && pnpm build && pnpm test. Como adiciona dep nova leve + mexe na CI, o PR cai na área de quórum (§7.2) — abra non-draft com a linhaSolicito quórum (HANDBOOK §7).
Activity
caioross commentedon Jul 14, 2026
Resolvida em PR #33 — instala Vitest (dev-only) +
src/lib/room.test.tscom 21 casos cobrindosanitizeResults(incl. o recorde impossível de 3596 WPM de #28) eclampInt, e adicionapnpm testao gate do CI.Gate: typecheck ✅ · test 21/21 ✅ · build ✅ (bundle inalterado) · validate-persistence 33/33 ✅. PR non-draft com pedido de quórum (§7.2, mexe em CI + dep nova leve).
fix(engine): Tab preenche a indentação esperada do target na corrida (#…
test: instala Vitest + suite anti-cheat de sanitizeResults no CI (#31) (