test: isolamento multi-tenant contra um MongoDB de verdade - #39
Conversation
A varredura estatica que ja existia prova que o filtro por tenant_id esta escrito - nao que ele funciona. Filtro no lugar errado, middleware que nao roda, rota que esquece o preHandler: nada disso ela pega. Este teste sobe a API de verdade, cria duas organizacoes pelo endpoint de registro e pergunta sempre a mesma coisa: a B consegue enxergar ou mexer em algo da A? Cobre leitura por id, listagem, PATCH e DELETE cruzados (conferindo depois que o dado da A continua intacto), comentarios, incidentes e contadores - alem de checar que o token da B e valido na propria organizacao, senao os 404 seriam falso positivo. Dois cuidados que valem registro: - o teste dropa a base no fim, e no CI a MONGODB_URI aponta para a base que o seed popula. Ele passa a usar base propria (incident_kb_itest), aproveitando so o host da variavel - senao apagaria os dados de demonstracao no meio do pipeline; - o glob de era tests/**/, que passaria a incluir os testes de integracao e quebraria em qualquer ambiente sem Mongo. Agora unidade e tests/*.test.js e integracao tem script proprio. Nao consegui executa-lo aqui: este ambiente bloqueia conexao com Mongo (ECONNREFUSED em 27017). A verificacao real e o CI, que ja sobe mongo:7.
O CI acusou: DELETE cruzado entre organizacoes devolvia 400, nao 404. Nao era o produto - a rota filtra por tenant_id e responde 404 corretamente. Era o helper do teste, que anunciava content-type: application/json em toda requisicao. O Fastify recusa com FST_ERR_CTP_EMPTY_JSON_BODY um DELETE que declara JSON e manda corpo vazio, e esse 400 se disfarcava de recusa da rota - o teste 'passaria' por engano se eu tivesse aceitado 400 na lista. GET nao sofria disso porque o Fastify nao tenta ler corpo em GET, o que explica os outros 11 testes passando. As mensagens de falha passam a incluir o corpo da resposta: sem isso, a unica pista no log do CI era o numero do status.
There was a problem hiding this comment.
Pull request overview
Este PR adiciona um teste de integração que valida isolamento multi-tenant end-to-end (API real + MongoDB real), para pegar regressões que a varredura estática não detecta (filtros no lugar errado, middleware não rodando, rotas sem preHandler, etc.).
Changes:
- Adiciona teste de integração
multi-tenantque sobe a API e valida isolamento entre duas organizações reais. - Separa scripts de teste de unidade vs. integração no backend para evitar rodar integração em ambientes sem Mongo.
- Executa o novo teste de integração no workflow de CI e documenta o fluxo no CONTRIBUTING.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| CONTRIBUTING.md | Documenta a separação entre testes unitários e de integração, com instruções para rodar os de integração com Mongo. |
| backend/tests/integration/multi-tenant.test.js | Novo teste de integração que sobe a API e valida isolamento multi-tenant contra Mongo real. |
| backend/package.json | Ajusta glob de npm test para excluir integração e adiciona script test:integration. |
| .github/workflows/ci.yml | Passa a rodar o novo job de testes de integração no job do backend. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| test('a B nao le os comentarios do KB da A', async () => { | ||
| const { status, body } = await api(`/records/${recordA._id}/comments`, { token: orgB.token }); | ||
| if (status === 200) { | ||
| assert.deepEqual(body.comments || [], [], 'comentarios de outra organizacao foram devolvidos'); | ||
| } else { | ||
| assert.ok([403, 404].includes(status), `GET de comentarios cruzado devolveu ${status}`); | ||
| } | ||
| }); |
| const inc = await api('/incidents', { | ||
| token: orgA.token, | ||
| method: 'POST', | ||
| body: { title: 'Incidente da Alpha', description: 'so a A deveria ver', severity: 'high' } | ||
| }); | ||
| if (inc.status === 201) incidentA = inc.body.incident; | ||
| }); |
| * O CONTRIBUTING chama isso de "ponto mais sensivel do projeto". Ate aqui | ||
| * a unica rede era a varredura estatica de tests/tenant-isolation.test.js, | ||
| * que prova que o filtro esta *escrito* - nao que ele funciona. Um filtro | ||
| * escrito no lugar errado, um middleware que nao roda, uma rota que | ||
| * esquece o preHandler: nada disso a varredura pega. |
|
CI verde: 12/12 contra o A primeira execução reprovou — Vale notar o quase-acidente: se eu tivesse simplesmente adicionado 400 à lista de status aceitos, o teste ficaria verde sem provar nada — passaria igual mesmo que a rota deletasse o KB da outra organização. O GET não sofria do problema porque o Fastify não lê corpo em GET, o que explica os outros 11 passando de primeira. As mensagens de falha agora incluem o corpo da resposta; antes a única pista no log era o número do status. |
Por que
A varredura estática que já existe (
tests/tenant-isolation.test.js) prova queo filtro por
tenant_idestá escrito — não que ele funciona. Um filtro nolugar errado, um middleware que não roda, uma rota que esquece o
preHandler:nada disso ela pega. E o CONTRIBUTING chama o isolamento de "ponto mais
sensível do projeto".
O que o teste faz
Sobe a API de verdade contra Mongo, cria duas organizações pelo endpoint de
registro, e pergunta sempre a mesma coisa: a B consegue enxergar ou mexer em
algo da A?
PATCHcruzado → recusado, e o título da A conferido depois, intactoDELETEcruzado → recusado, e o KB da A conferido depois, ainda lá404 acima seriam falso positivo de token quebrado, não prova de isolamento
Dois cuidados que valem registrar
O teste dropa a base no final, e no CI a
MONGODB_URIaponta para a baseque o
seedpopula. Reutilizá-la apagaria os dados de demonstração no meio dopipeline. Agora ele aproveita só o host da variável e força a base
incident_kb_itest.O glob de
npm testeratests/**/*.test.js, que passaria a incluir ostestes de integração — e eles quebrariam em qualquer ambiente sem Mongo.
Separei: unidade é
tests/*.test.js, integração tem script próprio.Verificação — leia com atenção
Não consegui executar estes testes aqui. Este ambiente bloqueia conexão com
MongoDB (
ECONNREFUSEDem 27017), o que já limitou o trabalho no backendantes. O que validei localmente foi apenas: sintaxe (
node --check), o YAML doworkflow, os contratos reais das rotas usadas —
POST /recordsdevolve{ success, recordId }e não{ record },POST /incidentsdevolve 201 — eque
npm test(unidade) continua em 25 passando com o glob novo.A verificação de verdade é o CI, que já sobe um serviço
mongo:7. Se o jobdo backend ficar verde neste PR, o teste rodou de fato contra um banco real. Se
ficar vermelho, eu corrijo antes de você mergear.