Skip to content

Commit b291afe

Browse files
committed
improvement(testing): consolidate @sim/db mocks into one table-aware chain mock
- back databaseMock and dbChainMock with the SAME db instance so a module bound to either export hits identical chain fns — rival-mock divergence between the two @sim/testing db mocks is structurally impossible now - add queueTableRows(table, rows): FIFO per-table select routing keyed by schema-mock table identity, consumed at where() materialization and resolved by every downstream terminal (limit/orderBy/groupBy/for/joins) - delete createMockDb (duplicate chain implementation, no external users) - migrate the five suites that hand-rolled table routing + databaseMock delegation (billing plan/usage/usage-log, admin dashboard-organizations, workspaces/utils) onto queueTableRows; net -295 lines - add a contract test for the mock itself and a test script to @sim/testing so its tests actually run under turbo
1 parent fad9728 commit b291afe

9 files changed

Lines changed: 305 additions & 498 deletions

File tree

Lines changed: 35 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
/** @vitest-environment node */
22

3-
import { databaseMock, dbChainMock, dbChainMockFns, resetDbChainMock } from '@sim/testing'
4-
import type { Mock } from 'vitest'
3+
import { member, organization, permissions, subscription } from '@sim/db/schema'
4+
import { dbChainMock, dbChainMockFns, queueTableRows, resetDbChainMock } from '@sim/testing'
55
import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'
66

77
vi.unmock('drizzle-orm')
88

99
const mocks = vi.hoisted(() => ({
10-
queryRows: [] as unknown[][],
11-
selectCalls: 0,
12-
selectDistinctOnCalls: 0,
1310
provisionings: new Map(),
1411
}))
1512

@@ -65,78 +62,8 @@ vi.mock('@/lib/core/outbox/service', () => ({ enqueueOutboxEvent: vi.fn() }))
6562

6663
import { listDashboardOrganizations, toDashboardConfigurationUpdate } from '@/lib/admin/dashboard'
6764

68-
/**
69-
* `@sim/db` behavior is driven through the SHARED `dbChainMockFns` instances
70-
* instead of a file-local factory object. This file mocks `@sim/db` with
71-
* `dbChainMock` and installs the queued-rows select implementation in
72-
* `beforeEach`; the setup-level `databaseMock` entry points are mirrored onto
73-
* the same chain fns. Under `isolate: false` the module under test may have
74-
* been loaded by an earlier suite in this worker with `@sim/db` bound to
75-
* `databaseMock` — configuring both shared instances keeps either binding
76-
* correct.
77-
*/
78-
function queryChain(rows: unknown[]) {
79-
const chain: Record<string, unknown> = {}
80-
for (const method of ['from', 'innerJoin', 'leftJoin', 'where', 'orderBy', 'limit']) {
81-
chain[method] = () => chain
82-
}
83-
chain.offset = () => Promise.resolve(rows)
84-
chain.groupBy = () => Promise.resolve(rows)
85-
chain.then = (resolve: (value: unknown[]) => unknown, reject: (error: unknown) => unknown) =>
86-
Promise.resolve(rows).then(resolve, reject)
87-
return chain
88-
}
89-
90-
function queuedSelect() {
91-
const rows = mocks.queryRows[mocks.selectCalls] ?? []
92-
mocks.selectCalls += 1
93-
return queryChain(rows)
94-
}
95-
96-
function queuedSelectDistinctOn() {
97-
const rows = mocks.queryRows[mocks.selectCalls] ?? []
98-
mocks.selectCalls += 1
99-
mocks.selectDistinctOnCalls += 1
100-
return queryChain(rows)
101-
}
102-
103-
const GLOBAL_DB_KEYS = [
104-
'select',
105-
'selectDistinct',
106-
'selectDistinctOn',
107-
'insert',
108-
'update',
109-
'delete',
110-
'transaction',
111-
] as const
112-
113-
const globalDb = databaseMock.db as unknown as Record<(typeof GLOBAL_DB_KEYS)[number], Mock>
114-
const savedGlobalDbImpls = new Map<
115-
(typeof GLOBAL_DB_KEYS)[number],
116-
((...args: unknown[]) => unknown) | undefined
117-
>()
118-
119-
/** Mirrors the setup-level databaseMock entry points onto the shared chain fns. */
120-
function delegateGlobalDbToChainMocks(): void {
121-
for (const key of GLOBAL_DB_KEYS) {
122-
const fn = globalDb[key]
123-
if (typeof fn?.mockImplementation !== 'function') continue
124-
if (!savedGlobalDbImpls.has(key)) savedGlobalDbImpls.set(key, fn.getMockImplementation())
125-
fn.mockImplementation((...args: unknown[]) => (dbChainMockFns[key] as Mock)(...args))
126-
}
127-
}
128-
129-
/** Restores the databaseMock entry points captured before this suite ran. */
130-
function restoreGlobalDb(): void {
131-
for (const [key, impl] of savedGlobalDbImpls) {
132-
if (impl) globalDb[key].mockImplementation(impl)
133-
else globalDb[key].mockReset()
134-
}
135-
}
136-
13765
afterAll(() => {
13866
resetDbChainMock()
139-
restoreGlobalDb()
14067
})
14168

14269
describe('toDashboardConfigurationUpdate', () => {
@@ -173,48 +100,41 @@ describe('listDashboardOrganizations', () => {
173100
beforeEach(() => {
174101
vi.clearAllMocks()
175102
resetDbChainMock()
176-
dbChainMockFns.select.mockImplementation(queuedSelect)
177-
dbChainMockFns.selectDistinctOn.mockImplementation(queuedSelectDistinctOn)
178-
delegateGlobalDbToChainMocks()
179-
mocks.selectCalls = 0
180-
mocks.selectDistinctOnCalls = 0
181103
mocks.provisionings = new Map()
182104
})
183105

184106
it('loads a page with a fixed batch of queries instead of querying once per organization', async () => {
185-
mocks.queryRows = [
186-
[{ total: 2 }],
187-
[
188-
{ id: 'org-1', name: 'One', orgUsageLimit: '10', creditBalance: '1' },
189-
{ id: 'org-2', name: 'Two', orgUsageLimit: '20', creditBalance: '2' },
190-
],
191-
[
192-
{
193-
organizationId: 'org-1',
194-
memberCount: 2,
195-
ownerId: 'owner-1',
196-
ownerName: 'Owner One',
197-
ownerEmail: 'one@example.com',
198-
},
199-
{
200-
organizationId: 'org-2',
201-
memberCount: 1,
202-
ownerId: 'owner-2',
203-
ownerName: 'Owner Two',
204-
ownerEmail: 'two@example.com',
205-
},
206-
],
207-
[{ organizationId: 'org-1', externalCollaboratorCount: 3 }],
208-
[
209-
{
210-
id: 'sub-1',
211-
referenceId: 'org-1',
212-
plan: 'team_6000',
213-
status: 'active',
214-
metadata: null,
215-
},
216-
],
217-
]
107+
queueTableRows(organization, [{ total: 2 }])
108+
queueTableRows(organization, [
109+
{ id: 'org-1', name: 'One', orgUsageLimit: '10', creditBalance: '1' },
110+
{ id: 'org-2', name: 'Two', orgUsageLimit: '20', creditBalance: '2' },
111+
])
112+
queueTableRows(member, [
113+
{
114+
organizationId: 'org-1',
115+
memberCount: 2,
116+
ownerId: 'owner-1',
117+
ownerName: 'Owner One',
118+
ownerEmail: 'one@example.com',
119+
},
120+
{
121+
organizationId: 'org-2',
122+
memberCount: 1,
123+
ownerId: 'owner-2',
124+
ownerName: 'Owner Two',
125+
ownerEmail: 'two@example.com',
126+
},
127+
])
128+
queueTableRows(permissions, [{ organizationId: 'org-1', externalCollaboratorCount: 3 }])
129+
queueTableRows(subscription, [
130+
{
131+
id: 'sub-1',
132+
referenceId: 'org-1',
133+
plan: 'team_6000',
134+
status: 'active',
135+
metadata: null,
136+
},
137+
])
218138

219139
const result = await listDashboardOrganizations({ search: '', limit: 50, offset: 0 })
220140

@@ -231,7 +151,7 @@ describe('listDashboardOrganizations', () => {
231151
externalCollaboratorCount: 0,
232152
planLabel: 'No plan',
233153
})
234-
expect(mocks.selectCalls).toBe(5)
235-
expect(mocks.selectDistinctOnCalls).toBe(1)
154+
expect(dbChainMockFns.select).toHaveBeenCalledTimes(4)
155+
expect(dbChainMockFns.selectDistinctOn).toHaveBeenCalledTimes(1)
236156
})
237157
})

0 commit comments

Comments
 (0)