Files
insforge--insforge/backend/tests/unit/function-predeploy-check-real.test.ts
wehub-resource-sync 3a28426bf4
Lint and Format Check / lint-and-format (push) Failing after 0s
Check Migrations / Check for duplicate migration numbers (push) Failing after 1s
CI Pre-merge Check / CI Pre-merge Check (push) Failing after 2m17s
chore: import upstream snapshot with attribution
2026-07-13 12:23:40 +08:00

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);
});