import { beforeEach, describe, expect, it, vi } from "vitest"; import { getRlsExecutionContext, runWithRlsContext } from "@hivegrid/db/rls-context.server"; const mocks = vi.hoisted(() => ({ findScopes: vi.fn(), deleteForTenant: vi.fn(), })); vi.mock("@hivegrid/db/accounts.server", () => ({ adminGetAllLegacyTenantSummaries: vi.fn(async () => []), })); vi.mock("@hivegrid/db/api-keys.server", () => ({ countLegacyApiKeyLogs: vi.fn(), deleteTenantLegacyApiKeyLogsByIds: mocks.deleteForTenant, findLegacyApiKeyLogTenantScopesByIds: mocks.findScopes, getLegacyApiKeyLogAdminDetails: vi.fn(), groupLegacyApiKeyLogs: vi.fn(), tenantLegacyApiKeyLogScope: (tenantId: string) => ({ tenantId }), })); import ApiKeyLogService from "./ApiKeyLogService"; describe("ApiKeyLogService admin RLS deletion", () => { beforeEach(() => { vi.clearAllMocks(); mocks.deleteForTenant.mockImplementation(async ({ tenantId }: { tenantId: string }) => { expect(getRlsExecutionContext()).toMatchObject({ userId: "operator_1", tenantId }); return { count: 1 }; }); }); it("deletes each selected log only inside its owning tenant context", async () => { mocks.findScopes.mockResolvedValue([ { id: "log_a", tenantId: "tenant_a" }, { id: "log_b", tenantId: "tenant_b" }, ]); const result = await runWithRlsContext({ userId: "operator_1" }, () => ApiKeyLogService.adminDeleteMany(["log_a", "log_b"])); expect(result).toEqual({ deletedCount: 2, rejectedTenantlessIds: [] }); expect(mocks.deleteForTenant).toHaveBeenCalledTimes(2); expect(mocks.deleteForTenant).toHaveBeenCalledWith({ ids: ["log_a"], tenantId: "tenant_a" }); expect(mocks.deleteForTenant).toHaveBeenCalledWith({ ids: ["log_b"], tenantId: "tenant_b" }); }); it("rejects the whole batch when it contains a tenantless platform log", async () => { mocks.findScopes.mockResolvedValue([ { id: "log_a", tenantId: "tenant_a" }, { id: "log_platform", tenantId: null }, ]); const result = await runWithRlsContext({ userId: "operator_1" }, () => ApiKeyLogService.adminDeleteMany(["log_a", "log_platform"])); expect(result).toEqual({ deletedCount: 0, rejectedTenantlessIds: ["log_platform"] }); expect(mocks.deleteForTenant).not.toHaveBeenCalled(); }); it("fails closed outside an unscoped authenticated operator request", async () => { await expect(ApiKeyLogService.adminDeleteMany(["log_a"])).rejects.toThrow("unscoped authenticated operator context"); await expect(runWithRlsContext({ userId: "operator_1", tenantId: "tenant_a" }, () => ApiKeyLogService.adminDeleteMany(["log_a"]))).rejects.toThrow( "unscoped authenticated operator context" ); expect(mocks.findScopes).not.toHaveBeenCalled(); }); });