Skip to content

Commit 2dc98a3

Browse files
committed
fix(testing): make dbChainMock .for('update') chainable with .limit()
1 parent 9281be3 commit 2dc98a3

File tree

2 files changed

+23
-6
lines changed

2 files changed

+23
-6
lines changed

.claude/rules/sim-testing.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,12 @@ it('reads a row', async () => {
224224
- `db.execute()` resolves `[]`
225225
- `db.transaction(cb)` calls cb with `dbChainMock.db`
226226

227-
`.for('update')` (Postgres row-level locking) is supported as a terminal on
228-
`where` builders. Override with `dbChainMockFns.for.mockResolvedValueOnce([...])`.
227+
`.for('update')` (Postgres row-level locking) is supported on `where`
228+
builders. It returns a thenable with `.limit` / `.orderBy` / `.returning` /
229+
`.groupBy` attached, so both `await .where().for('update')` (terminal) and
230+
`await .where().for('update').limit(1)` (chained) work. Override the terminal
231+
result with `dbChainMockFns.for.mockResolvedValueOnce([...])`; for the chained
232+
form, mock the downstream terminal (e.g. `dbChainMockFns.limit.mockResolvedValueOnce([...])`).
229233

230234
All terminals default to `Promise.resolve([])`. Override per-test with `dbChainMockFns.<terminal>.mockResolvedValueOnce(...)`.
231235

packages/testing/src/mocks/database.mock.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,12 @@ export function createMockSqlOperators() {
7171
* Terminals (`limit`, `orderBy`, `returning`, `groupBy`, `for`, `values`)
7272
* default to resolving `[]` (or `undefined` for `values`). Override per-test
7373
* with `dbChainMockFns.limit.mockResolvedValueOnce([...])`. `for` mirrors
74-
* drizzle's `.for('update')` row-lock terminal — override with
75-
* `dbChainMockFns.for.mockResolvedValueOnce([...])`.
74+
* drizzle's `.for('update')` — it returns a Promise with `.limit` / `.orderBy`
75+
* / `.returning` / `.groupBy` attached, so both `await .where().for('update')`
76+
* (terminal) and `await .where().for('update').limit(1)` (chained) work.
77+
* Override the terminal result with `dbChainMockFns.for.mockResolvedValueOnce(
78+
* [...])`; override the chained result by mocking the downstream terminal
79+
* (e.g. `dbChainMockFns.limit.mockResolvedValueOnce([...])`).
7680
*
7781
* `vi.clearAllMocks()` clears call history but preserves default wiring. Tests
7882
* that replace a wiring with `mockReturnValue(...)` (not `...Once`) must re-wire
@@ -95,7 +99,16 @@ const orderBy = vi.fn(() => Promise.resolve([] as unknown[]))
9599
const returning = vi.fn(() => Promise.resolve([] as unknown[]))
96100
const groupBy = vi.fn(() => Promise.resolve([] as unknown[]))
97101
const execute = vi.fn(() => Promise.resolve([] as unknown[]))
98-
const forClause = vi.fn(() => Promise.resolve([] as unknown[]))
102+
103+
const forBuilder = () => {
104+
const thenable: any = Promise.resolve([] as unknown[])
105+
thenable.limit = limit
106+
thenable.orderBy = orderBy
107+
thenable.returning = returning
108+
thenable.groupBy = groupBy
109+
return thenable
110+
}
111+
const forClause = vi.fn(forBuilder)
99112

100113
const onConflictDoUpdate = vi.fn(() => ({ returning }) as unknown as Promise<void>)
101114
const onConflictDoNothing = vi.fn(() => ({ returning }) as unknown as Promise<void>)
@@ -177,7 +190,7 @@ export function resetDbChainMock(): void {
177190
returning.mockImplementation(() => Promise.resolve([] as unknown[]))
178191
groupBy.mockImplementation(() => Promise.resolve([] as unknown[]))
179192
execute.mockImplementation(() => Promise.resolve([] as unknown[]))
180-
forClause.mockImplementation(() => Promise.resolve([] as unknown[]))
193+
forClause.mockImplementation(forBuilder)
181194
transaction.mockImplementation(async (cb: (tx: typeof dbChainMock.db) => unknown) =>
182195
cb(dbChainMock.db)
183196
)

0 commit comments

Comments
 (0)