Cobre rotas HTTP, proxy, worker assíncrono e barreiras do dev auth
Os 34 testes existentes batiam só na camada de serviço: as rotas, o proxy e o loop do worker não tinham cobertura alguma. A suíte vai de 34 para 102. - `tests/http/`: rotas reais com o caminho real de autenticação, sem subir servidor. Cobre 401 sem sessão, módulo desligado, papel sem permissão, 403 ao tentar gravar workspace de terceiro, e 429 com Retry-After. - `tests/http/proxy.test.ts`: o proxy não propaga query da requisição para o login, o que impediria um returnTo forjado. - `tests/integration/worker.test.ts`: jornada assíncrona pelo loop real. A web enfileira e devolve `queued`, o download falha antes do tick, o tick conclui e os três formatos saem. Também: job sobrevive à perda da fila do Redis, worker relê o contexto e não processa com a flag desligada, TTL expira temporários sem tocar o modelo, e desligamento gracioso. - `tests/unit/dev-auth-guard.test.ts`: matriz de ambientes das três barreiras do login local. Um token de dev válido é recusado quando o ambiente é staging. Espaço em volta de um valor de ADAPTER_ENV é tolerado de propósito, e isso está declarado no teste; valor desconhecido nunca vira development. - `tests/services/`: Postgres e Redis reais. Confere `app_user` sem BYPASSRLS, FORCE RLS em todas as tabelas, isolamento entre workspaces, auditoria não alterável pelo papel da aplicação, e lock entre conexões diferentes. Sem as variáveis de serviço os casos são IGNORADOS, nunca aprovados, e ficam fora de `npm test` para a suíte padrão passar em qualquer máquina. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dea510c3b4
commit
ce11a50091
10 files changed
+1182
No files matched your search
@@ -0,0 +1,55 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { NextRequest } from "next/server";
|
||||
import { proxy, config } from "@/proxy";
|
||||
|
||||
function request(path: string, cookies: Record<string, string> = {}): NextRequest {
|
||||
const next = new NextRequest(new URL(`http://localhost${path}`));
|
||||
for (const [name, value] of Object.entries(cookies)) next.cookies.set(name, value);
|
||||
return next;
|
||||
}
|
||||
|
||||
describe("proxy", () => {
|
||||
it("cobre as áreas protegidas e nada além", () => {
|
||||
expect(config.matcher).toContain("/adaptador/:path*");
|
||||
expect(config.matcher).toContain("/api/spreadsheet-adapter/:path*");
|
||||
expect(config.matcher).toContain("/selecionar-workspace");
|
||||
});
|
||||
|
||||
it("redireciona a página protegida ao login preservando o destino", () => {
|
||||
const response = proxy(request("/adaptador/operacao"));
|
||||
expect(response.status).toBe(307);
|
||||
const location = new URL(response.headers.get("location") as string);
|
||||
expect(location.pathname).toBe("/login");
|
||||
expect(location.searchParams.get("next")).toBe("/adaptador/operacao");
|
||||
});
|
||||
|
||||
it("não propaga query da requisição original para o login", () => {
|
||||
const next = new NextRequest(new URL("http://localhost/adaptador?returnTo=https://evil.example"));
|
||||
const response = proxy(next);
|
||||
const location = new URL(response.headers.get("location") as string);
|
||||
expect(location.searchParams.get("returnTo")).toBeNull();
|
||||
expect(location.searchParams.get("next")).toBe("/adaptador");
|
||||
});
|
||||
|
||||
it("responde 401 em JSON na API sem sessão", async () => {
|
||||
const response = proxy(request("/api/spreadsheet-adapter/models"));
|
||||
expect(response.status).toBe(401);
|
||||
const body = (await response.json()) as { error: { code: string } };
|
||||
expect(body.error.code).toBe("UNAUTHENTICATED");
|
||||
});
|
||||
|
||||
it("deixa passar com cookie do Supabase e com a sessão local", () => {
|
||||
expect(proxy(request("/adaptador", { "sb-projeto-auth-token": "abc" })).status).toBe(200);
|
||||
expect(proxy(request("/adaptador", { adapter_dev_session: "abc" })).status).toBe(200);
|
||||
});
|
||||
|
||||
it("não interfere em rota pública nem no health check", () => {
|
||||
expect(proxy(request("/")).status).toBe(200);
|
||||
expect(proxy(request("/api/health/live")).status).toBe(200);
|
||||
expect(proxy(request("/login")).status).toBe(200);
|
||||
});
|
||||
|
||||
it("protege a tela de escolha de workspace", () => {
|
||||
expect(proxy(request("/selecionar-workspace")).status).toBe(307);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user