Skip to content

Commit f542637

Browse files
committed
address bugbot
1 parent af621cf commit f542637

2 files changed

Lines changed: 117 additions & 6 deletions

File tree

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import {
5+
authMockFns,
6+
dbChainMock,
7+
dbChainMockFns,
8+
resetDbChainMock,
9+
workflowsApiUtilsMock,
10+
workflowsApiUtilsMockFns,
11+
workflowsOrchestrationMock,
12+
workflowsOrchestrationMockFns,
13+
} from '@sim/testing'
14+
import { NextRequest } from 'next/server'
15+
import { beforeEach, describe, expect, it, vi } from 'vitest'
16+
17+
const { mockCheckWorkflowAccessForFormCreation } = vi.hoisted(() => ({
18+
mockCheckWorkflowAccessForFormCreation: vi.fn(),
19+
}))
20+
21+
const mockCreateErrorResponse = workflowsApiUtilsMockFns.mockCreateErrorResponse
22+
const mockPerformFullDeploy = workflowsOrchestrationMockFns.mockPerformFullDeploy
23+
24+
vi.mock('@sim/db', () => dbChainMock)
25+
26+
vi.mock('@sim/utils/id', () => ({
27+
generateId: vi.fn(() => 'form-123'),
28+
}))
29+
30+
vi.mock('@/app/api/form/utils', () => ({
31+
checkWorkflowAccessForFormCreation: mockCheckWorkflowAccessForFormCreation,
32+
DEFAULT_FORM_CUSTOMIZATIONS: {},
33+
}))
34+
35+
vi.mock('@/app/api/workflows/utils', () => workflowsApiUtilsMock)
36+
37+
vi.mock('@/lib/core/config/feature-flags', () => ({
38+
isDev: true,
39+
}))
40+
41+
vi.mock('@/lib/core/utils/urls', () => ({
42+
getEmailDomain: vi.fn(() => 'localhost:3000'),
43+
}))
44+
45+
vi.mock('@/lib/workflows/orchestration', () => workflowsOrchestrationMock)
46+
47+
import { POST } from '@/app/api/form/route'
48+
49+
describe('Form API Route', () => {
50+
beforeEach(() => {
51+
vi.clearAllMocks()
52+
resetDbChainMock()
53+
54+
authMockFns.mockGetSession.mockResolvedValue({
55+
user: {
56+
id: 'user-123',
57+
email: 'user@example.com',
58+
name: 'Test User',
59+
},
60+
})
61+
mockCreateErrorResponse.mockImplementation((message, status = 500) => {
62+
return new Response(JSON.stringify({ error: message }), {
63+
status,
64+
headers: { 'Content-Type': 'application/json' },
65+
})
66+
})
67+
mockCheckWorkflowAccessForFormCreation.mockResolvedValue({
68+
hasAccess: true,
69+
workflow: {
70+
id: 'workflow-123',
71+
isDeployed: false,
72+
workspaceId: 'workspace-123',
73+
},
74+
})
75+
dbChainMockFns.limit.mockResolvedValue([])
76+
})
77+
78+
it('cleans up inserted form when deploy throws', async () => {
79+
mockPerformFullDeploy.mockRejectedValue(new Error('Deploy exploded'))
80+
81+
const request = new NextRequest('http://localhost:3000/api/form', {
82+
method: 'POST',
83+
body: JSON.stringify({
84+
workflowId: 'workflow-123',
85+
identifier: 'test-form',
86+
title: 'Test Form',
87+
}),
88+
})
89+
90+
const response = await POST(request)
91+
92+
expect(response.status).toBe(500)
93+
expect(dbChainMockFns.insert).toHaveBeenCalled()
94+
expect(dbChainMockFns.delete).toHaveBeenCalled()
95+
expect(mockCreateErrorResponse).toHaveBeenCalledWith('Deploy exploded', 500)
96+
})
97+
})

apps/sim/app/api/form/route.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ function getErrorMessage(error: unknown, fallback: string): string {
2626
return error instanceof Error ? error.message : fallback
2727
}
2828

29+
async function cleanupFormAfterDeployFailure(formId: string) {
30+
try {
31+
await db.delete(form).where(eq(form.id, formId))
32+
} catch (cleanupError) {
33+
logger.error('Failed to clean up form after deploy failure:', cleanupError)
34+
}
35+
}
36+
2937
export const GET = withRouteHandler(async (request: NextRequest) => {
3038
try {
3139
const session = await getSession()
@@ -146,14 +154,20 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
146154
updatedAt: new Date(),
147155
})
148156

149-
const result = await performFullDeploy({
150-
workflowId,
151-
userId: session.user.id,
152-
request,
153-
})
157+
let result: Awaited<ReturnType<typeof performFullDeploy>>
158+
try {
159+
result = await performFullDeploy({
160+
workflowId,
161+
userId: session.user.id,
162+
request,
163+
})
164+
} catch (error) {
165+
await cleanupFormAfterDeployFailure(id)
166+
throw error
167+
}
154168

155169
if (!result.success) {
156-
await db.delete(form).where(eq(form.id, id))
170+
await cleanupFormAfterDeployFailure(id)
157171
const status =
158172
result.errorCode === 'validation' ? 400 : result.errorCode === 'not_found' ? 404 : 500
159173
return createErrorResponse(result.error || 'Failed to deploy workflow', status)

0 commit comments

Comments
 (0)