Skip to content

Commit 52659d4

Browse files
authored
improvement(tests): db-mock migration tranche 2 — copilot, mothership, workspaces, connectors, mcp (#5863)
* improvement(tests): db-mock migration tranche 2 — copilot, mothership, workspaces, connectors, mcp * fix(testing): drain unconsumed ...Once overrides in resetDbChainMock vi.clearAllMocks clears call history only — a ...Once override queued by a previous test but never consumed survived into the next test. resetDbChainMock now mockReset()s every shared spy and stable wrapper, which restores the original implementation AND drains once-queues.
1 parent 62a8ce4 commit 52659d4

29 files changed

Lines changed: 538 additions & 1065 deletions

File tree

apps/sim/app/api/copilot/api-keys/validate/route.test.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
/**
22
* @vitest-environment node
33
*/
4-
import { createMockRequest } from '@sim/testing'
5-
import { beforeEach, describe, expect, it, vi } from 'vitest'
4+
import { createMockRequest, queueTableRows, resetDbChainMock, schemaMock } from '@sim/testing'
5+
import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'
66

77
const {
8-
mockDbLimit,
98
mockCheckInternalApiKey,
109
mockCheckAttributedUsageLimits,
1110
mockCheckServerSideUsageLimits,
@@ -21,7 +20,6 @@ const {
2120
mockGetWorkspaceBillingSettings,
2221
mockFlags,
2322
} = vi.hoisted(() => ({
24-
mockDbLimit: vi.fn(),
2523
mockCheckInternalApiKey: vi.fn(),
2624
mockCheckAttributedUsageLimits: vi.fn(),
2725
mockCheckServerSideUsageLimits: vi.fn(),
@@ -77,12 +75,6 @@ const OLD_GO_OPAQUE_WORKSPACE_VALIDATE_BODY = {
7775
workspaceId: 'local-self-hosted-workspace',
7876
} as const
7977

80-
vi.mock('@sim/db', () => ({
81-
db: {
82-
select: () => ({ from: () => ({ where: () => ({ limit: mockDbLimit }) }) }),
83-
},
84-
}))
85-
8678
vi.mock('@/lib/billing/core/billing-attribution', () => ({
8779
BILLING_ACCOUNT_DECISION_HEADER: 'x-sim-billing-account-decision',
8880
BILLING_ATTRIBUTION_HEADER: 'x-sim-billing-attribution',
@@ -151,9 +143,10 @@ function request(body: Record<string, unknown>, headers: Record<string, string>
151143
describe('POST /api/copilot/api-keys/validate billing protocols', () => {
152144
beforeEach(() => {
153145
vi.clearAllMocks()
146+
resetDbChainMock()
154147
mockFlags.isCopilotBillingProtocolRequired = false
155148
mockCheckInternalApiKey.mockReturnValue({ success: true })
156-
mockDbLimit.mockResolvedValue([{ id: 'user-1' }])
149+
queueTableRows(schemaMock.user, [{ id: 'user-1' }])
157150
mockResolveBillingAttribution.mockResolvedValue(ATTRIBUTION)
158151
mockResolveLegacyV0BillingAttribution.mockResolvedValue(ATTRIBUTION)
159152
mockSerializeBillingAttributionHeader.mockReturnValue('serialized-attribution')
@@ -193,6 +186,10 @@ describe('POST /api/copilot/api-keys/validate billing protocols', () => {
193186
})
194187
})
195188

189+
afterAll(() => {
190+
resetDbChainMock()
191+
})
192+
196193
it('keeps the exact old-Go validate bodies contract-compatible', () => {
197194
expect(validateCopilotApiKeyBodySchema.safeParse(OLD_GO_HOSTED_VALIDATE_BODY).success).toBe(
198195
true

apps/sim/app/api/copilot/chat/update-messages/route.test.ts

Lines changed: 30 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3,52 +3,24 @@
33
*
44
* @vitest-environment node
55
*/
6-
import { authMockFns } from '@sim/testing'
6+
import {
7+
authMockFns,
8+
dbChainMockFns,
9+
queueTableRows,
10+
resetDbChainMock,
11+
schemaMock,
12+
} from '@sim/testing'
713
import { NextRequest } from 'next/server'
8-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
9-
10-
const {
11-
mockSelect,
12-
mockFrom,
13-
mockWhere,
14-
mockLimit,
15-
mockUpdate,
16-
mockSet,
17-
mockUpdateWhere,
18-
mockReturning,
19-
mockReplaceCopilotChatMessages,
20-
} = vi.hoisted(() => ({
21-
mockSelect: vi.fn(),
22-
mockFrom: vi.fn(),
23-
mockWhere: vi.fn(),
24-
mockLimit: vi.fn(),
25-
mockUpdate: vi.fn(),
26-
mockSet: vi.fn(),
27-
mockUpdateWhere: vi.fn(),
28-
mockReturning: vi.fn(),
29-
mockReplaceCopilotChatMessages: vi.fn(),
30-
}))
14+
import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'
3115

32-
vi.mock('@sim/db', () => ({
33-
db: {
34-
select: mockSelect,
35-
update: mockUpdate,
36-
transaction: async (
37-
cb: (tx: { update: typeof mockUpdate; select: typeof mockSelect }) => unknown
38-
) => cb({ update: mockUpdate, select: mockSelect }),
39-
},
16+
const { mockReplaceCopilotChatMessages } = vi.hoisted(() => ({
17+
mockReplaceCopilotChatMessages: vi.fn(),
4018
}))
4119

4220
vi.mock('@/lib/copilot/chat/messages-store', () => ({
4321
replaceCopilotChatMessages: mockReplaceCopilotChatMessages,
4422
}))
4523

46-
vi.mock('drizzle-orm', () => ({
47-
and: vi.fn((...conditions: unknown[]) => ({ conditions, type: 'and' })),
48-
eq: vi.fn((field: unknown, value: unknown) => ({ field, value, type: 'eq' })),
49-
isNull: vi.fn((field: unknown) => ({ field, type: 'isNull' })),
50-
}))
51-
5224
import { POST } from '@/app/api/copilot/chat/update-messages/route'
5325

5426
function createMockRequest(method: string, body: Record<string, unknown>): NextRequest {
@@ -62,21 +34,15 @@ function createMockRequest(method: string, body: Record<string, unknown>): NextR
6234
describe('Copilot Chat Update Messages API Route', () => {
6335
beforeEach(() => {
6436
vi.clearAllMocks()
37+
resetDbChainMock()
6538

6639
authMockFns.mockGetSession.mockResolvedValue(null)
6740

68-
mockSelect.mockReturnValue({ from: mockFrom })
69-
mockFrom.mockReturnValue({ where: mockWhere })
70-
mockWhere.mockReturnValue({ limit: mockLimit })
71-
mockLimit.mockResolvedValue([])
72-
mockUpdate.mockReturnValue({ set: mockSet })
73-
mockSet.mockReturnValue({ where: mockUpdateWhere })
74-
mockUpdateWhere.mockReturnValue({ returning: mockReturning })
75-
mockReturning.mockResolvedValue([{ model: 'gpt-4' }])
41+
dbChainMockFns.returning.mockResolvedValue([{ model: 'gpt-4' }])
7642
})
7743

78-
afterEach(() => {
79-
vi.restoreAllMocks()
44+
afterAll(() => {
45+
resetDbChainMock()
8046
})
8147

8248
describe('POST', () => {
@@ -181,7 +147,7 @@ describe('Copilot Chat Update Messages API Route', () => {
181147
it('should return 404 when chat is not found', async () => {
182148
authMockFns.mockGetSession.mockResolvedValue({ user: { id: 'user-123' } })
183149

184-
mockLimit.mockResolvedValueOnce([])
150+
queueTableRows(schemaMock.copilotChats, [])
185151

186152
const req = createMockRequest('POST', {
187153
chatId: 'non-existent-chat',
@@ -205,7 +171,7 @@ describe('Copilot Chat Update Messages API Route', () => {
205171
it('should return 404 when chat belongs to different user', async () => {
206172
authMockFns.mockGetSession.mockResolvedValue({ user: { id: 'user-123' } })
207173

208-
mockLimit.mockResolvedValueOnce([])
174+
queueTableRows(schemaMock.copilotChats, [])
209175

210176
const req = createMockRequest('POST', {
211177
chatId: 'other-user-chat',
@@ -234,7 +200,7 @@ describe('Copilot Chat Update Messages API Route', () => {
234200
userId: 'user-123',
235201
messages: [],
236202
}
237-
mockLimit.mockResolvedValueOnce([existingChat])
203+
queueTableRows(schemaMock.copilotChats, [existingChat])
238204

239205
const messages = [
240206
{
@@ -265,9 +231,9 @@ describe('Copilot Chat Update Messages API Route', () => {
265231
messageCount: 2,
266232
})
267233

268-
expect(mockSelect).toHaveBeenCalled()
269-
expect(mockUpdate).toHaveBeenCalled()
270-
expect(mockSet).toHaveBeenCalledWith({ updatedAt: expect.any(Date) })
234+
expect(dbChainMockFns.select).toHaveBeenCalled()
235+
expect(dbChainMockFns.update).toHaveBeenCalled()
236+
expect(dbChainMockFns.set).toHaveBeenCalledWith({ updatedAt: expect.any(Date) })
271237
expect(mockReplaceCopilotChatMessages).toHaveBeenCalledWith(
272238
'chat-123',
273239
messages,
@@ -284,7 +250,7 @@ describe('Copilot Chat Update Messages API Route', () => {
284250
userId: 'user-123',
285251
messages: [],
286252
}
287-
mockLimit.mockResolvedValueOnce([existingChat])
253+
queueTableRows(schemaMock.copilotChats, [existingChat])
288254

289255
const messages = [
290256
{
@@ -328,7 +294,7 @@ describe('Copilot Chat Update Messages API Route', () => {
328294
messageCount: 2,
329295
})
330296

331-
expect(mockSet).toHaveBeenCalledWith({ updatedAt: expect.any(Date) })
297+
expect(dbChainMockFns.set).toHaveBeenCalledWith({ updatedAt: expect.any(Date) })
332298
expect(mockReplaceCopilotChatMessages).toHaveBeenCalledWith(
333299
'chat-456',
334300
[
@@ -373,7 +339,7 @@ describe('Copilot Chat Update Messages API Route', () => {
373339
userId: 'user-123',
374340
messages: [],
375341
}
376-
mockLimit.mockResolvedValueOnce([existingChat])
342+
queueTableRows(schemaMock.copilotChats, [existingChat])
377343

378344
const req = createMockRequest('POST', {
379345
chatId: 'chat-789',
@@ -389,7 +355,7 @@ describe('Copilot Chat Update Messages API Route', () => {
389355
messageCount: 0,
390356
})
391357

392-
expect(mockSet).toHaveBeenCalledWith({ updatedAt: expect.any(Date) })
358+
expect(dbChainMockFns.set).toHaveBeenCalledWith({ updatedAt: expect.any(Date) })
393359
expect(mockReplaceCopilotChatMessages).toHaveBeenCalledWith(
394360
'chat-789',
395361
[],
@@ -401,7 +367,7 @@ describe('Copilot Chat Update Messages API Route', () => {
401367
it('should handle database errors during chat lookup', async () => {
402368
authMockFns.mockGetSession.mockResolvedValue({ user: { id: 'user-123' } })
403369

404-
mockLimit.mockRejectedValueOnce(new Error('Database connection failed'))
370+
dbChainMockFns.limit.mockRejectedValueOnce(new Error('Database connection failed'))
405371

406372
const req = createMockRequest('POST', {
407373
chatId: 'chat-123',
@@ -430,11 +396,9 @@ describe('Copilot Chat Update Messages API Route', () => {
430396
userId: 'user-123',
431397
messages: [],
432398
}
433-
mockLimit.mockResolvedValueOnce([existingChat])
399+
queueTableRows(schemaMock.copilotChats, [existingChat])
434400

435-
mockSet.mockReturnValueOnce({
436-
where: vi.fn().mockRejectedValue(new Error('Update operation failed')),
437-
})
401+
dbChainMockFns.returning.mockRejectedValueOnce(new Error('Update operation failed'))
438402

439403
const req = createMockRequest('POST', {
440404
chatId: 'chat-123',
@@ -481,7 +445,7 @@ describe('Copilot Chat Update Messages API Route', () => {
481445
userId: 'user-123',
482446
messages: [],
483447
}
484-
mockLimit.mockResolvedValueOnce([existingChat])
448+
queueTableRows(schemaMock.copilotChats, [existingChat])
485449

486450
const messages = Array.from({ length: 100 }, (_, i) => ({
487451
id: `msg-${i + 1}`,
@@ -504,7 +468,7 @@ describe('Copilot Chat Update Messages API Route', () => {
504468
messageCount: 100,
505469
})
506470

507-
expect(mockSet).toHaveBeenCalledWith({ updatedAt: expect.any(Date) })
471+
expect(dbChainMockFns.set).toHaveBeenCalledWith({ updatedAt: expect.any(Date) })
508472
expect(mockReplaceCopilotChatMessages).toHaveBeenCalledWith(
509473
'chat-large',
510474
messages,
@@ -521,7 +485,7 @@ describe('Copilot Chat Update Messages API Route', () => {
521485
userId: 'user-123',
522486
messages: [],
523487
}
524-
mockLimit.mockResolvedValueOnce([existingChat])
488+
queueTableRows(schemaMock.copilotChats, [existingChat])
525489

526490
const messages = [
527491
{

apps/sim/app/api/copilot/chats/route.test.ts

Lines changed: 20 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,15 @@
33
*
44
* @vitest-environment node
55
*/
6-
import { copilotHttpMock, copilotHttpMockFns } from '@sim/testing'
7-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
8-
9-
const { mockSelectDistinctOn, mockFrom, mockLeftJoin, mockWhere, mockOrderBy } = vi.hoisted(() => ({
10-
mockSelectDistinctOn: vi.fn(),
11-
mockFrom: vi.fn(),
12-
mockLeftJoin: vi.fn(),
13-
mockWhere: vi.fn(),
14-
mockOrderBy: vi.fn(),
15-
}))
16-
17-
vi.mock('@sim/db', () => ({
18-
db: {
19-
selectDistinctOn: mockSelectDistinctOn,
20-
},
21-
}))
22-
23-
vi.mock('drizzle-orm', () => ({
24-
and: vi.fn((...conditions: unknown[]) => ({ conditions, type: 'and' })),
25-
eq: vi.fn((field: unknown, value: unknown) => ({ field, value, type: 'eq' })),
26-
or: vi.fn((...conditions: unknown[]) => ({ conditions, type: 'or' })),
27-
inArray: vi.fn((field: unknown, values: unknown) => ({ field, values, type: 'inArray' })),
28-
isNull: vi.fn((field: unknown) => ({ field, type: 'isNull' })),
29-
desc: vi.fn((field: unknown) => ({ field, type: 'desc' })),
30-
sql: vi.fn(),
31-
}))
6+
import {
7+
copilotHttpMock,
8+
copilotHttpMockFns,
9+
dbChainMockFns,
10+
queueTableRows,
11+
resetDbChainMock,
12+
schemaMock,
13+
} from '@sim/testing'
14+
import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'
3215

3316
vi.mock('@/lib/copilot/request/http', () => copilotHttpMock)
3417

@@ -45,16 +28,11 @@ import { GET } from '@/app/api/copilot/chats/route'
4528
describe('Copilot Chats List API Route', () => {
4629
beforeEach(() => {
4730
vi.clearAllMocks()
48-
49-
mockSelectDistinctOn.mockReturnValue({ from: mockFrom })
50-
mockFrom.mockReturnValue({ leftJoin: mockLeftJoin })
51-
mockLeftJoin.mockReturnValue({ leftJoin: mockLeftJoin, where: mockWhere })
52-
mockWhere.mockReturnValue({ orderBy: mockOrderBy })
53-
mockOrderBy.mockResolvedValue([])
31+
resetDbChainMock()
5432
})
5533

56-
afterEach(() => {
57-
vi.restoreAllMocks()
34+
afterAll(() => {
35+
resetDbChainMock()
5836
})
5937

6038
describe('GET', () => {
@@ -78,7 +56,7 @@ describe('Copilot Chats List API Route', () => {
7856
isAuthenticated: true,
7957
})
8058

81-
mockOrderBy.mockResolvedValueOnce([])
59+
queueTableRows(schemaMock.copilotChats, [])
8260

8361
const request = new Request('http://localhost:3000/api/copilot/chats')
8462
const response = await GET(request as any)
@@ -111,7 +89,7 @@ describe('Copilot Chats List API Route', () => {
11189
updatedAt: new Date('2024-01-01'),
11290
},
11391
]
114-
mockOrderBy.mockResolvedValueOnce(mockChats)
92+
queueTableRows(schemaMock.copilotChats, mockChats)
11593

11694
const request = new Request('http://localhost:3000/api/copilot/chats')
11795
const response = await GET(request as any)
@@ -151,7 +129,7 @@ describe('Copilot Chats List API Route', () => {
151129
updatedAt: new Date('2024-01-01'),
152130
},
153131
]
154-
mockOrderBy.mockResolvedValueOnce(mockChats)
132+
queueTableRows(schemaMock.copilotChats, mockChats)
155133

156134
const request = new Request('http://localhost:3000/api/copilot/chats')
157135
const response = await GET(request as any)
@@ -176,7 +154,7 @@ describe('Copilot Chats List API Route', () => {
176154
updatedAt: new Date('2024-01-01'),
177155
},
178156
]
179-
mockOrderBy.mockResolvedValueOnce(mockChats)
157+
queueTableRows(schemaMock.copilotChats, mockChats)
180158

181159
const request = new Request('http://localhost:3000/api/copilot/chats')
182160
const response = await GET(request as any)
@@ -192,7 +170,7 @@ describe('Copilot Chats List API Route', () => {
192170
isAuthenticated: true,
193171
})
194172

195-
mockOrderBy.mockRejectedValueOnce(new Error('Database connection failed'))
173+
dbChainMockFns.orderBy.mockRejectedValueOnce(new Error('Database connection failed'))
196174

197175
const request = new Request('http://localhost:3000/api/copilot/chats')
198176
const response = await GET(request as any)
@@ -216,13 +194,13 @@ describe('Copilot Chats List API Route', () => {
216194
updatedAt: new Date('2024-01-01'),
217195
},
218196
]
219-
mockOrderBy.mockResolvedValueOnce(mockChats)
197+
queueTableRows(schemaMock.copilotChats, mockChats)
220198

221199
const request = new Request('http://localhost:3000/api/copilot/chats')
222200
await GET(request as any)
223201

224-
expect(mockSelectDistinctOn).toHaveBeenCalled()
225-
expect(mockWhere).toHaveBeenCalled()
202+
expect(dbChainMockFns.selectDistinctOn).toHaveBeenCalled()
203+
expect(dbChainMockFns.where).toHaveBeenCalled()
226204
})
227205

228206
it('should return 401 when userId is null despite isAuthenticated being true', async () => {

0 commit comments

Comments
 (0)