No description
Find a file
Jonatas Oliveira 1c24fe9221 test(spider): a propriedade dos dois jogadores exigia nomes distintos
O CI derrubou `partida_aceita_dois_jogadores_e_recusa_o_terceiro` com
`nome1 = "r", nome2 = "r", nome3 = "a"`. Os dois primeiros jogadores
receberam o mesmo id, e o terceiro foi aceito em vez de recusado.

Não é defeito do servidor: entrar com um nome que já está na partida é
reconexão, por desenho. O `join_game` devolve o id que aquele jogador já
tinha e não consome a vaga -- é o que permite fechar o navegador e voltar
para a mesma partida, e é o que os testes de unidade
`test_join_game_reconnection` já afirmavam.

A propriedade é que estava escrita como se três nomes quaisquer fossem
três jogadores. Ganha `prop_assume!` de nomes distintos.

E a regra que o proptest encontrou por acidente passa a ser afirmada de
propósito, em `entrar_com_o_nome_de_quem_ja_esta_e_reconexao`: a
reconexão devolve o mesmo id, não consome a vaga do segundo jogador, vale
também para o segundo, e continua valendo com a partida cheia.

O caso só aparecia quando o proptest sorteava nomes iguais -- raro com
nomes de até 12 letras, e por isso passou despercebido localmente até o
runner tirar a carta.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PhdPjHUuVhXaitwT4mG53t
2026-09-08 12:26:30 +02:00
.cargo fix(hooks): reprovar quando o teste de mutação não gera mutante nenhum 2026-09-08 07:15:14 +02:00
.githooks fix(hooks): reprovar quando o teste de mutação não gera mutante nenhum 2026-09-08 07:15:14 +02:00
.woodpecker ci: tirar a mutação do pipeline e rodar o CI inteiro no pre-commit 2026-09-05 07:41:17 +02:00
battleship-client@60983b15d7 fix(hooks): reprovar quando o teste de mutação não gera mutante nenhum 2026-09-08 07:15:14 +02:00
battleship-common@1910b91495 fix(hooks): reprovar quando o teste de mutação não gera mutante nenhum 2026-09-08 07:15:14 +02:00
battleship-server@c8dd5899fe test(spider): a propriedade dos dois jogadores exigia nomes distintos 2026-09-08 12:26:30 +02:00
.gitignore build: turn the three crates into a workspace 2026-09-01 22:20:42 +02:00
.gitmodules refactor: split the three crates into their own repositories 2026-09-03 13:28:26 +02:00
Cargo.lock build: turn the three crates into a workspace 2026-09-01 22:20:42 +02:00
Cargo.toml refactor: split the three crates into their own repositories 2026-09-03 13:28:26 +02:00
LICENSE docs: add a README and the GPLv3 licence text 2026-09-01 22:20:42 +02:00
README.md ci: tirar a mutação do pipeline e rodar o CI inteiro no pre-commit 2026-09-05 07:41:17 +02:00

Batalha Naval

Batalha Naval multijogador com servidor em axum e cliente web em WebAssembly. Escrito como protótipo de curso de Rust.

Dois jogadores entram numa partida, posicionam a frota e alternam tiros até um deles afundar todos os navios do outro. A comunicação é por WebSocket.

Estrutura

Workspace com três crates, cada um em seu próprio submódulo git:

crate o que é repositório
battleship-common tipos, regras e protocolo — a biblioteca compartilhada battleship-common
battleship-server servidor axum com WebSocket, persistência em sled battleship-server
battleship-client cliente web em Yew, compilado para WebAssembly battleship-client
cargo run -p battleship-server        # sobe o servidor
cd battleship-client && trunk serve   # sobe o cliente

Como os crates são submódulos, depois de clonar:

git submodule update --init --recursive

Cada um tem o seu próprio pipeline, que responde a uma pergunta que esta build integrada não responde: o crate se sustenta sozinho? Por isso o servidor e o cliente declaram battleship-common pelo repositório dela, e não por caminho — sem isso um clone isolado nem compila. Dentro deste workspace o [patch] do Cargo.toml da raiz redireciona essa dependência de volta para o diretório ao lado, então a build integrada usa o código do submódulo, no SHA fixado aqui, e não a ponta de main.

Sobre o battleship-common

Vale um parágrafo, porque é o que este projeto ensina de mais caro.

Havia três cópias dos tipos do jogo: o servidor compilava contra uma cópia própria de 513 linhas, o cliente tinha outra em src/common.rs, e o battleship-common de verdade — 782 linhas, com testes — não era usado por ninguém. As cópias divergiram, e o jogo parou de funcionar ponta a ponta:

  • o envelope da mensagem (tag = "type" de um lado, com content = "data" do outro);
  • o nome da célula de água (Empty no servidor, Water no cliente);
  • o eixo em que um navio horizontal se estende.

Cada lado testava a sua própria cópia, e as duas suítes passavam. O defeito só aparecia com os dois processos no ar.

Hoje os três crates compartilham a biblioteca, e há testes cujo único propósito é impedir que a cópia volte: battleship-client/tests/spider compara byte a byte a serialização feita pelo caminho do cliente com a feita pela biblioteca.

Testes

cargo test --workspace                     # 265 testes
cargo test -p battleship-common --test spider
cargo mutants -p battleship-common         # índice de mutação: 0 sobreviventes

O cliente é wasm: os componentes Yew e o WebSocketService só rodam dentro de um navegador, e por isso ficam fora da conta de mutação (veja battleship-client/.cargo/mutants.toml). Para cobri-los é preciso teste de navegador:

cd battleship-client && wasm-pack test --headless --firefox

Verificações antes do commit

O .githooks/pre-commit roda aqui o que o Woodpecker roda no servidor. Ele é gerado a partir do .woodpecker/pipelines.yml deste repositório, então os comandos não podem divergir do CI. Ative uma vez por clone -- o hook é versionado, a configuração que aponta para ele não é:

git config core.hooksPath .githooks

Roda tudo a cada git commit, do mais barato ao mais caro, e para no primeiro que falhar: lint do pipeline, cargo fmt --check, clippy com -D warnings, build, testes, cobertura (piso 90%), auditoria de dependências e a perna MSRV 1.90.

O teste de mutação só existe aqui. Ele saiu do pipeline: mutação recompila o crate uma vez por mutante, e no runner isso eram dezenas de minutos por push, num passo que ninguém acompanha enquanto espera. O teto continua em .mutants-teto, versionado -- a regra não afrouxou, mudou de lugar. É de longe a etapa mais lenta; para pular só ela:

PRE_COMMIT_SEM_MUTACAO=1 git commit ...

Para pular tudo, git commit --no-verify. Um hook que não se pode pular é um hook que todo mundo desativa.

Licença

GPL-3.0-or-later. Veja LICENSE.