97 lines
3.3 KiB
TypeScript
97 lines
3.3 KiB
TypeScript
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);
|
|
});
|