Skip to content

refactor(persistencia): extrai buildMatchRow/buildScoreRows de persistMatch e cobre com Vitest (#50) - #55

Merged
caioross merged 2 commits into
mainfrom
auto/issue-50-persist-builders
Jul 26, 2026
Merged

refactor(persistencia): extrai buildMatchRow/buildScoreRows de persistMatch e cobre com Vitest (#50)#55
caioross merged 2 commits into
mainfrom
auto/issue-50-persist-builders

Conversation

@caioross

Copy link
Copy Markdown
Owner

Contexto

persistMatch (src/app/api/rooms/[code]/route.ts) mapeava os results já sanitizados para as
linhas de matches/scores inline, dentro de uma função async privada só alcançável atrás de
uma corrida multiplayer completa — lógica real (eleição de vencedor, arredondamentos, coerções) com
zero teste, alimentando o leaderboard global público.

O que mudou e por quê

  • src/lib/room.ts: novos builders puros e determinísticos, ao lado de sanitizeResults:
    • buildMatchRow(room, results, finishedAt): MatchInsert | nullnull quando results está
      vazio (nenhuma matches órfã). finished_at entra por parâmetro justamente para manter a
      função determinística/testável.
    • buildScoreRows(matchId, room, results): ScoreInsert[].
    • Tipos MatchInsert/ScoreInsert exportados.
  • route.ts: persistMatch virou wrapper fino — chama os builders e faz só os dois insert,
    mantendo o try/catch best-effort atual. Nenhuma aritmética de mapeamento sobrou inline.
  • src/lib/room.test.ts: +16 casos cobrindo o contrato — vencedor = menor place (com empate,
    place ausente caindo no fallback 99 e "ninguém colocado"), winner_wpm/wpm/accuracy/errors
    arredondados e nunca NaN, place 0/ausente → null, finished coagido a booleano,
    snippet ausente → snippet_title: null, e results vazio → null + zero linhas de score.

Refactor puro: nenhum comportamento observável muda em produção (a semântica sutil do
Math.round(x) || 0 foi preservada de propósito — agora com contrato versionado em teste).

Nota de contrato descoberta ao testar

r.place || null só normaliza falsy: um place negativo passaria direto. Não "consertei" isso
aqui (seria mudança de comportamento fora do escopo) — na prática ele nunca chega aos builders porque
sanitizeResults já transforma place < 1 em null. Um teste explícito versiona essa divisão de
responsabilidade em vez de deixá-la implícita.

Gate (resultado real, no worktree)

  • pnpm install --frozen-lockfile
  • pnpm typecheck
  • pnpm build
  • pnpm test ✅ — 36 testes, 0 falhas (era 20)
  • node scripts/validate-persistence.mjs ✅ 33/33
  • node .claude/skills/cr-typing-engine/scripts/validate-metrics.mjs ✅ 37/37
  • pnpm lint = N/A (sem config ESLint no repo; a CI não roda lint)

Riscos

Baixo. Diff restrito a src/lib/room.ts (adição), ao corpo de persistMatch e ao arquivo de teste.
Nada toca a área sagrada (input da corrida), banco, migrations, RLS ou anti-cheat — sanitizeResults
segue intacta como fronteira. O único risco é divergência acidental de mapeamento, e é exatamente
isso que os testes novos travam.

Sem Parecer do Conselho nesta issue — plano seguido é o da própria issue (espelha o padrão #31/#32).

Solicito quórum (HANDBOOK §7)

Closes #50

…tMatch e cobre com Vitest (#50)

O mapeamento results→matches/scores vivia inline numa função async privada,
só alcançável atrás de uma corrida completa — zero teste. Agora é puro e
determinístico em src/lib/room.ts (finished_at entra por parâmetro), e
persistMatch só orquestra os dois inserts.

Refactor puro: nenhum comportamento observável muda.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

@caioross is attempting to deploy a commit to the caioross' projects team on Vercel, but is not a member of this team. To resolve this issue, you can:

  • Make your repository public. Collaboration is free for open source and public repositories.
  • Upgrade to pro and add @caioross as a member. A Pro subscription is required to access Vercel's collaborative features.
    • If you're the owner of the team, click here to upgrade and add @caioross as a member.
    • If you're the user who initiated this build request, click here to request access.
    • If you're already a member of the caioross' projects team, make sure that your Vercel account is connected to your GitHub account.

To read more about collaboration on Vercel, click here.

@caioross

Copy link
Copy Markdown
Owner Author

🔧 PR Doctor — conflito após #60; reparo por união

Este PR ficou CONFLICTING (DIRTY): a #60 (liveness) que acabou de mergear também adiciona funções puras a src/lib/room.ts (shouldFinishRace/raceTimeoutMs), e este PR adiciona buildMatchRow/buildScoreRows no mesmo arquivo → sobreposição.

  • Ação (reparo por união, HANDBOOK §5.D): na branch, git merge origin/main (nunca --force/rebase), resolva mantendo ambas as adições em room.ts, rode o gate (pnpm typecheck && pnpm build && pnpm test && node scripts/validate-persistence.mjs) e faça push.
  • Depois entra na fila de quórum §7.2 (persistência do leaderboard). O refactor continua válido e valioso (cobre persistMatch com Vitest); só precisa reencostar na main.

Não mergeio nesta rodada — teto de 2 merges atingido (#57, #60) e o conflito precisa ser resolvido antes.

caioross added a commit that referenced this pull request Jul 26, 2026
… a escrita (#56) (#96)

As actions `settings`, `start`, `reset` e `claim-leader` faziam `.update()` sem
desestruturar o `error` — e o supabase-js DEVOLVE `{ error }` em vez de lançar.
Constraint violada (a 0004 pendente torna isso provável), RLS ou timeout viravam
"deu certo" na tela do líder, com a sala parada no lobby e nenhum log.

- `roomUpdateOutcome` em `src/lib/room.ts`: decisão pura (erro ⇒ 500, zero linhas
  confirmadas ⇒ 409 neutro, ≥1 linha ⇒ ok), coberta por 5 casos no Vitest.
- `applyRoomUpdate` em `route.ts`: adaptador fino que encadeia `.select("code")`,
  loga o detalhe no servidor e devolve `NextResponse` de erro ou `null`.
- `finish` passa a reportar o `error`, PRESERVANDO `flipped.length === 0` como
  "outro cliente já finalizou" ⇒ segue `ok: true` sem repersistir.
- GET deixa de devolver `error.message` do PostgREST ao cliente (virava toast).

Fora do escopo, declarado na PR: `persistMatch` (colide com a #55) e
`src/app/api/rooms/route.ts`.
@caioross

Copy link
Copy Markdown
Owner Author

🔧 PR Doctor — ainda CONFLITANTE; reparo por união + re-quórum

O refactor (extrair buildMatchRow/buildScoreRows de persistMatch + cobertura Vitest) segue válido e não superado — a #96 mexeu no tratamento de erro do finish/em applyRoomUpdate, mas não nas entranhas do persistMatch, que continuam inline. Só que a main andou muito (#60, #79, #96, #89): room.ts e room.test.ts divergiram bastante.

Ação (§5.D): na branch auto/issue-50-persist-builders, resetar ao head da PR, git merge origin/main (união), resolver room.ts/room.test.ts mantendo os builders novos e tudo que entrou (shouldFinishRace/kick/vote/roomUpdateOutcome), rodar o gate (typecheck+build+test+validate-persistence) e push. Depois entra na fila de quórum §7.2 (persistência do leaderboard). Não mergeada nesta rodada — precisa reencostar na main atual primeiro.

Uniao com origin/main (que trouxe #60/#79/#96/#89). Resolucao:
- room.ts: mantidos os builders (buildMatchRow/buildScoreRows/MatchInsert/
  ScoreInsert) E as funcoes de kick/vote/outcome que entraram na main.
- route.ts: imports unificados; persistMatch segue como wrapper fino dos builders.
- room.test.ts: imports unificados; adicionado kicked_ids:[] ao FULL_ROOM
  (campo virou obrigatorio em RoomRow via #79).
Gate: typecheck OK, build OK, test 103 OK, validate-persistence 72/0.
@vercel

vercel Bot commented Jul 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
code-racer Ready Ready Preview, Comment Jul 26, 2026 8:01pm

@caioross

Copy link
Copy Markdown
Owner Author

🏛️ Quórum §7.2 — APROVADA (refactor puro) + reparo por união

Lente Domínio/persistência: APROVA, nenhum vetor. Equivalência comportamental confirmada campo a campo entre o mapeamento inline antigo e os novos buildMatchRow/buildScoreRows: matches (winner = menor place via sort estável, Math.round||0, ||null) e scores (uma linha por resultado na ordem, !!finished, place||null) saem idênticos. finished_at segue new Date().toISOString() na rota. Guarda de results vazio migrou p/ buildMatchRow → null (sem matches órfã), try/catch best-effort preservado, ordem de insert (matches→scores) intacta (FK ok).

Reparo por união: reencostado na main (#60/#79/#96/#89); resolvidos room.ts (builders + kick coexistem), imports de route.ts/room.test.ts, e kicked_ids:[] no FULL_ROOM (campo virou obrigatório via #79). Gate: typecheck ✓, test 103 ✓, build ✓, validate-persistence 72/0 ✓. CI + Vercel verdes. Closes #50. Squash-merge.

@caioross
caioross merged commit 4aabeac into main Jul 26, 2026
3 checks passed
@caioross
caioross deleted the auto/issue-50-persist-builders branch July 26, 2026 20:02
caioross added a commit that referenced this pull request Jul 26, 2026
Uniao com origin/main (#55/#60/#79/#89/#96). Resolucao (tudo aditivo):
- useRoom.ts: mantidos isSpectatorJoin + isSpectator E as importacoes/logica de
  votacao (#89, tallyVotes/pickVoteWinner/activeVotes/voteTally/myVote).
- RoomView.tsx: mantido o branch isSpectator?SpectatorView:Race E os props
  isLeader/onKick do Race (#79), alem de voteTally/myVote no destructure.
- room.test.ts: mantidos os blocos de isSpectatorJoin (#64) e votacao/outcome.
Gate: typecheck OK, test 109 OK, build OK, validate-persistence 72/0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Testes/Persistência: extrair mapeamento puro de persistMatch (results→matches/scores) e cobrir com Vitest

1 participant