Skip to content

test: isolamento multi-tenant contra um MongoDB de verdade - #39

Merged
janeiaraujo merged 2 commits into
mainfrom
test/integracao-multi-tenant
Aug 3, 2026
Merged

test: isolamento multi-tenant contra um MongoDB de verdade#39
janeiaraujo merged 2 commits into
mainfrom
test/integracao-multi-tenant

Conversation

@janeiaraujo

Copy link
Copy Markdown
Owner

Por que

A varredura estática que já existe (tests/tenant-isolation.test.js) prova que
o filtro por tenant_id está escrito — não que ele funciona. Um filtro no
lugar 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?

  • leitura por id → deve dar 404
  • listagem → o KB da A não pode aparecer
  • PATCH cruzado → recusado, e o título da A conferido depois, intacto
  • DELETE cruzado → recusado, e o KB da A conferido depois, ainda lá
  • comentários, incidentes (id e listagem) e contadores
  • rota protegida sem token → 401
  • e um controle: o token da B é válido na própria organização — senão os
    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_URI aponta para a base
que o seed popula. Reutilizá-la apagaria os dados de demonstração no meio do
pipeline. Agora ele aproveita só o host da variável e força a base
incident_kb_itest.

O glob de npm test era tests/**/*.test.js, que passaria a incluir os
testes 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 (ECONNREFUSED em 27017), o que já limitou o trabalho no backend
antes. O que validei localmente foi apenas: sintaxe (node --check), o YAML do
workflow, os contratos reais das rotas usadas — POST /records devolve
{ success, recordId } e não { record }, POST /incidents devolve 201 — e
que 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 job
do backend ficar verde neste PR, o teste rodou de fato contra um banco real. Se
ficar vermelho, eu corrijo antes de você mergear.

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.
Copilot AI review requested due to automatic review settings August 3, 2026 20:42
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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-tenant que 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.

Comment on lines +179 to +186
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}`);
}
});
Comment on lines +117 to +123
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;
});
Comment on lines +4 to +8
* 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.
@janeiaraujo

Copy link
Copy Markdown
Owner Author

CI verde: 12/12 contra o mongo:7 do runner. Registro do que aconteceu, porque é o motivo de o teste existir:

A primeira execução reprovou — DELETE cruzado entre organizações devolvia 400, não 404. Investigando, não 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 requisição. O Fastify recusa com FST_ERR_CTP_EMPTY_JSON_BODY um DELETE que declara JSON e manda corpo vazio — e esse 400 se disfarçava de recusa da rota.

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.

@janeiaraujo
janeiaraujo merged commit 5f560f2 into main Aug 3, 2026
3 checks passed
@janeiaraujo janeiaraujo mentioned this pull request Aug 3, 2026
@janeiaraujo
janeiaraujo deleted the test/integracao-multi-tenant branch August 3, 2026 23:37
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.

2 participants