import { describe, expect, it } from "vitest"; import { createModel } from "@/server/spreadsheet-adapter/service"; import { ctxOf, createReadyDb, depsOf, IDS } from "../helpers/world"; describe("RLS e permissões", () => { it("impede leitura e escrita entre workspaces", async () => { const db = await createReadyDb(); const deps = depsOf(db); const ana = ctxOf("ana", "alpha", "owner", true); const diego = ctxOf("diego", "beta", "admin", false); await createModel(deps, ana, { name: "Somente Alpha", filename: "a.csv", bytes: Buffer.from("A\n1\n") }); const asDiego = await db.withUser(IDS.diego, (query) => query.query(`select name from public.spreadsheet_models`)); expect(asDiego).toHaveLength(0); const asAna = await db.withUser(IDS.ana, (query) => query.query(`select name from public.spreadsheet_models where workspace_id = $1`, [IDS.beta])); expect(asAna).toHaveLength(0); await expect( db.withUser(IDS.ana, (query) => query.query( `insert into public.spreadsheet_models (workspace_id, name, original_filename, format, storage_key, content_sha256, header_row, columns_schema, created_by) values ($1,'Invasao','a.csv','csv','workspaces/x/models/y/original','abc',1,'[]'::jsonb,$2)`, [IDS.beta, IDS.ana], ), ), ).rejects.toThrow(/row-level security/i); await expect(createModel(deps, { ...diego, moduleEnabled: true }, { name: "Beta", filename: "a.csv", bytes: Buffer.from("A\n1\n") })).rejects.toMatchObject({ code: "FEATURE_DISABLED", }); const eva = ctxOf("eva", "beta", "agent", true, true); await expect(createModel(deps, eva, { name: "Super", filename: "a.csv", bytes: Buffer.from("A\n1\n") })).rejects.toThrow(); await db.close(); }); it("agent não altera configuração e owner altera", async () => { const db = await createReadyDb(); const ana = ctxOf("ana", "alpha", "owner", true); const bruno = ctxOf("bruno", "alpha", "agent", true); await expect( db.withUser(bruno.userId, (query) => query.query(`insert into public.spreadsheet_adapter_settings (workspace_id, allow_non_admin_model_upload) values ($1, true)`, [IDS.alpha]), ), ).rejects.toThrow(/row-level security/i); await db.withUser(ana.userId, (query) => query.query(`insert into public.spreadsheet_adapter_settings (workspace_id, allow_non_admin_model_upload) values ($1, true)`, [IDS.alpha]), ); const seen = await db.withUser(bruno.userId, (query) => query.query<{ allow_non_admin_model_upload: boolean }>(`select allow_non_admin_model_upload from public.spreadsheet_adapter_settings`), ); expect(seen[0].allow_non_admin_model_upload).toBe(true); await db.close(); }); it("não altera auditoria pelo papel da aplicação", async () => { const db = await createReadyDb(); const ana = ctxOf("ana", "alpha", "owner", true); await db.withUser(ana.userId, (query) => query.query( `insert into public.audit_log (workspace_id, actor_id, action, resource_type, resource_id, result) values ($1, $2, 'model.uploaded', 'model', 'm1', 'success')`, [IDS.alpha, IDS.ana], ), ); await expect( db.withUser(ana.userId, (query) => query.query(`update public.audit_log set action = 'tamper' where workspace_id = $1`, [IDS.alpha]), ), ).rejects.toThrow(/permission denied/i); const rows = await db.withService((query) => query.query<{ action: string }>(`select action from public.audit_log where workspace_id = $1`, [IDS.alpha]), ); expect(rows.every((row) => row.action !== "tamper")).toBe(true); await db.close(); }); });