fix(leaderboard): persistMatch lê os erros de insert e não deixa matches órfã (#112) - #127
Open
caioross wants to merge 1 commit into
Open
fix(leaderboard): persistMatch lê os erros de insert e não deixa matches órfã (#112)#127caioross wants to merge 1 commit into
caioross wants to merge 1 commit into
Conversation
…hes órfã (#112) O supabase-js não lança em erro de query — devolve `{ data, error }` —, e a persistência da partida descartava esse `error` nos dois inserts. Uma falha em `matches` caía no `return` sem passar pelo `catch` (zero log), e uma falha em `scores` deixava a `matches` já gravada: "Partidas recentes" mostrava a corrida com vencedor enquanto nenhum jogador entrava no ranking. - Extrai `persistMatch` do route file para `src/lib/persistMatch.ts` — um module de rota do App Router não pode exportar função arbitrária, e sem import não há como cobrir os caminhos de erro com um `sb` dublê. - Lê o `error` dos dois inserts e loga com `console.error` + código do PostgREST, distinguindo "não escreveu" de "escreveu e não voltou linha" (`.single()`). - `scores` falhou → apaga a `matches` que esta chamada acabou de criar, pelo id. O erro desse delete também é logado: é a órfã que a AC 3 proíbe. - `finish` intocado: continua `{ ok: true }` em qualquer desfecho da persistência.
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Contexto
persistMatché quem decide se uma corrida terminada existe para o mundo — ela alimentamatches(Partidas recentes) escores(a viewleaderboard). Osupabase-jsnão lançaem erro de query: devolve
{ data, error }. Os dois inserts descartavam esseerror, então:matchesfaziamatchvirarnull, caía noreturne nem passavapelo
catch— zero linhas de log. A partida simplesmente não aconteceu.scoresdeixava amatchesjá gravada: "Partidas recentes" exibiaa corrida com vencedor e
player_countenquanto nenhum jogador entrava no ranking.Nos dois casos o
finishrespondiaok: truee o pódio aparecia normalmente.O que mudou
persistMatchsaiu do route file parasrc/lib/persistMatch.ts, assinatura inalterada(
sb, room, results). Motivo: um route module do App Router não pode exportar funçãoarbitrária (o Next trata esses exports como configuração de rota), e sem import não há como
cobrir os caminhos de erro com um
sbdublê.route.tssó importa e chama.matcheslido e logado comconsole.error+ código do PostgREST,identificando a sala; falhou → não tenta
scores. Ramo separado para "sem erro e semlinha" (
.single()devolve erro quando a contagem ≠ 1 —PGRST116), que a issue pede paradistinguir de "não escreveu".
scoreslido e logado da mesma forma.matchesrecém-criada. O schema decide:scores.match_idreferencia
matches(id)on delete cascade(0001_coderacer_init.sql:24), então apagarpelo
idé um statement só, sem deixar filho órfão na direção oposta, e o insert roda comservice_role(ignora RLS), sem precisar de policy de DELETE. A variante (b) — "tornar aausência de
scoresimpossível" — exige transação, que o PostgREST não oferece: virariafunção Postgres + migração nova + deploy-ordering para o que (a) resolve em 4 linhas.
deletecompensatório também pode falhar. Se falhar, a órfã que aAC 3 proíbe existe de fato — esse caso tem
console.errorpróprio ([persistMatch:rollback]),nunca um
catchmudo.console.warndocatchexterno virouconsole.error(só exceção real chega lá).finishcontinua{ ok: true }em qualquer desfecho dapersistência — a linha da sala já flipou e o broadcast já saiu quando
persistMatchroda;devolver 500 aqui faria o jogador crer que a corrida não terminou.
Há teste garantindo isso.
Fora de escopo de propósito, como o parecer orienta: nenhum retry (o broadcast já saiu; um
retry sobre resposta perdida geraria uma segunda
matchespara a mesma corrida) e nenhumasinalização do erro ao cliente (a AC 4 proíbe; fica como issue futura de UX).
Validação (resultado real)
Lint = N/A (não há config ESLint no repo; a CI não roda lint).
Os 8 testes novos cobrem, com um
sbdublê que devolve{ data: null, error }por tabela:matches falha →
scoresnunca é chamada · matches sem linha (PGRST116) → para e loga ·scores falha →
deletechamado com oidcerto · delete também falha → não lança e loga aórfã · caminho feliz → 2 inserts, 0 deletes ·
resultsvazio → 0 inserts (regressão dobuildMatchRow → null) · exceção real não escapa · log sem nick de jogador.Riscos
deleteextra só existe no caminho de falha, depois do broadcast, fora da área sagrada.
deleteapaga apenas oidque esta mesma chamada acabou de criar — nunca uma partidapreexistente. Não é migração nem operação destrutiva sobre dados existentes (§7.1 não se
aplica; ver nota de escopo na AC 3 da issue).
SUPABASE_SERVICE_ROLE_KEY,em que
getServerSupabasecai para a chave anon ematches/scoressó têm policy de SELECT),este PR faz esse modo de falha aparecer no log — é o objetivo, não um efeito colateral.
Segui o plano do Parecer do Conselho de 2026-07-31 na íntegra, incluindo a extração para
src/lib/e a escolha (a) justificada peloon delete cascade. Confirmei contraorigin/mainas três premissas que ele pediu para verificar: o
on delete cascadeem0001:24, obuildMatchRow → nullcomresultsvazio (src/lib/room.ts:382) e a inexistência de testeimportando o route file.
Solicito quórum (HANDBOOK §7)
Closes #112