import { ERROR_CODES } from '@insforge/shared-schemas'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; // End-to-end through the REAL DenoSubhostingProvider running the REAL // `deno check` (the provider is intentionally NOT mocked here). Proves the // wired createFunction path actually rejects the #1594 bug — a duplicate // top-level declaration — before any DB write. Only DB/secret/logger are // mocked, so this exercises the full service -> provider -> deno check chain. const mockClient = { query: vi.fn(), release: vi.fn() }; const mockPool = { query: vi.fn(), connect: vi.fn().mockResolvedValue(mockClient) }; vi.mock('../../src/infra/database/database.manager.js', () => ({ DatabaseManager: { getInstance: () => ({ getPool: () => mockPool }), }, })); vi.mock('../../src/services/secrets/secret.service.js', () => ({ SecretService: { getInstance: () => ({}) }, })); vi.mock('../../src/utils/logger.js', () => ({ default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn() }, })); const DUPLICATE_DECL = [ 'const KILL_SWITCH_DOC_ID = "global";', 'export default function (_req: Request): Response {', ' return new Response(KILL_SWITCH_DOC_ID);', '}', 'var KILL_SWITCH_DOC_ID = "global";', ].join('\n'); describe('Pre-deploy check — end to end through real deno check (#1594)', () => { beforeEach(() => { vi.resetModules(); vi.clearAllMocks(); process.env.DENO_DEPLOY_TOKEN = 't'; process.env.DENO_DEPLOY_ORG_ID = 'o'; }); afterEach(() => { delete process.env.DENO_DEPLOY_TOKEN; delete process.env.DENO_DEPLOY_ORG_ID; }); it('rejects the real duplicate-declaration function with a 400 before any DB write', async () => { const { FunctionService } = await import('../../src/services/functions/function.service.js'); const service = FunctionService.getInstance(); await expect( service.createFunction({ slug: 'dpo_agent', name: 'DPO Agent', code: DUPLICATE_DECL, status: 'active', }) ).rejects.toMatchObject({ statusCode: 400, code: ERROR_CODES.INVALID_INPUT }); // The real `deno check` names the offending identifier, and the bad // function must never reach the database. await expect( service.createFunction({ slug: 'dpo_agent', name: 'DPO Agent', code: DUPLICATE_DECL, status: 'active', }) ).rejects.toThrow(/KILL_SWITCH_DOC_ID/); expect(mockPool.connect).not.toHaveBeenCalled(); }, 60_000); it('does NOT block the same code when it is uploaded inactive (no deploy => no check)', async () => { const { FunctionService } = await import('../../src/services/functions/function.service.js'); const service = FunctionService.getInstance(); // Inactive functions are not deployed, so the static check is skipped and // the DB write proceeds (mocked). Guards the create-time gating. mockClient.query .mockResolvedValueOnce({}) .mockResolvedValueOnce({ rows: [{ id: '1', slug: 'dpo_agent' }] }); await expect( service.createFunction({ slug: 'dpo_agent', name: 'DPO Agent', code: DUPLICATE_DECL, status: 'inactive', }) ).resolves.toBeDefined(); expect(mockPool.connect).toHaveBeenCalled(); }, 60_000); });