Skip to content

Commit bc859c7

Browse files
committed
test(knex): add query-count checks for listSchedules and deleteSchedule
1 parent 681d7b6 commit bc859c7

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { Knex } from 'knex'
2+
3+
interface WithKnexQuerySpyOptions<T> {
4+
connection: Knex
5+
run: () => Promise<T>
6+
}
7+
8+
export async function withKnexQuerySpy<T>({
9+
connection,
10+
run,
11+
}: WithKnexQuerySpyOptions<T>): Promise<{ result: T; queries: string[] }> {
12+
const queries: string[] = []
13+
const onQuery = (query: { sql: string }) => {
14+
queries.push(query.sql.toLowerCase())
15+
}
16+
17+
connection.on('query', onQuery)
18+
try {
19+
const result = await run()
20+
return { result, queries }
21+
} finally {
22+
connection.off('query', onQuery)
23+
}
24+
}

tests/adapter.spec.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { KnexAdapter } from '../src/drivers/knex_adapter.js'
77
import { QueueSchemaService } from '../src/services/queue_schema.js'
88
import { registerDriverTestSuite } from './_utils/register_driver_test_suite.js'
99
import { withRedisWriteSpy } from './_utils/with_redis_write_spy.js'
10+
import { withKnexQuerySpy } from './_utils/with_knex_query_spy.js'
1011

1112
const KEY_PREFIX = 'boringnode::queue::test::'
1213

@@ -146,6 +147,67 @@ test.group('Adapter | Knex (SQLite)', (group) => {
146147
return adapter
147148
},
148149
})
150+
151+
test('listSchedules should execute a single SQL query in Knex adapter', async ({ assert }) => {
152+
const knexAdapter = new KnexAdapter({ connection })
153+
154+
for (let i = 0; i < 20; i++) {
155+
await knexAdapter.upsertSchedule({
156+
id: `knex-list-${i}`,
157+
name: 'KnexPerfJob',
158+
payload: { i },
159+
everyMs: 60_000,
160+
timezone: 'UTC',
161+
})
162+
}
163+
164+
const { result: schedules, queries } = await withKnexQuerySpy({
165+
connection,
166+
run: () => knexAdapter.listSchedules(),
167+
})
168+
assert.lengthOf(schedules, 20)
169+
170+
const scheduleSelectQueries = queries.filter(
171+
(sql) => sql.includes('select') && sql.includes('queue_schedules')
172+
)
173+
174+
assert.lengthOf(
175+
scheduleSelectQueries,
176+
1,
177+
`Expected a single schedule SELECT query, got ${scheduleSelectQueries.length}`
178+
)
179+
})
180+
181+
test('deleteSchedule should execute a single SQL query in Knex adapter', async ({ assert }) => {
182+
const knexAdapter = new KnexAdapter({ connection })
183+
const id = 'knex-delete-atomicity'
184+
185+
await knexAdapter.upsertSchedule({
186+
id,
187+
name: 'KnexDeleteJob',
188+
payload: {},
189+
everyMs: 60_000,
190+
timezone: 'UTC',
191+
})
192+
193+
const { queries } = await withKnexQuerySpy({
194+
connection,
195+
run: () => knexAdapter.deleteSchedule(id),
196+
})
197+
198+
const schedule = await knexAdapter.getSchedule(id)
199+
assert.isNull(schedule)
200+
201+
const scheduleDeleteQueries = queries.filter(
202+
(sql) => sql.includes('delete') && sql.includes('queue_schedules')
203+
)
204+
205+
assert.lengthOf(
206+
scheduleDeleteQueries,
207+
1,
208+
`Expected a single schedule DELETE query, got ${scheduleDeleteQueries.length}`
209+
)
210+
})
149211
})
150212

151213
test.group('Adapter | Knex (PostgreSQL)', (group) => {
@@ -192,4 +254,69 @@ test.group('Adapter | Knex (PostgreSQL)', (group) => {
192254
return adapter
193255
},
194256
})
257+
258+
test('listSchedules should execute a single SQL query in Knex PostgreSQL adapter', async ({
259+
assert,
260+
}) => {
261+
const knexAdapter = new KnexAdapter({ connection, tableName, schedulesTableName })
262+
263+
for (let i = 0; i < 20; i++) {
264+
await knexAdapter.upsertSchedule({
265+
id: `pg-list-${i}`,
266+
name: 'PgPerfJob',
267+
payload: { i },
268+
everyMs: 60_000,
269+
timezone: 'UTC',
270+
})
271+
}
272+
273+
const { result: schedules, queries } = await withKnexQuerySpy({
274+
connection,
275+
run: () => knexAdapter.listSchedules(),
276+
})
277+
assert.lengthOf(schedules, 20)
278+
279+
const scheduleSelectQueries = queries.filter(
280+
(sql) => sql.includes('select') && sql.includes(schedulesTableName)
281+
)
282+
283+
assert.lengthOf(
284+
scheduleSelectQueries,
285+
1,
286+
`Expected a single schedule SELECT query, got ${scheduleSelectQueries.length}`
287+
)
288+
})
289+
290+
test('deleteSchedule should execute a single SQL query in Knex PostgreSQL adapter', async ({
291+
assert,
292+
}) => {
293+
const knexAdapter = new KnexAdapter({ connection, tableName, schedulesTableName })
294+
const id = 'pg-delete-atomicity'
295+
296+
await knexAdapter.upsertSchedule({
297+
id,
298+
name: 'PgDeleteJob',
299+
payload: {},
300+
everyMs: 60_000,
301+
timezone: 'UTC',
302+
})
303+
304+
const { queries } = await withKnexQuerySpy({
305+
connection,
306+
run: () => knexAdapter.deleteSchedule(id),
307+
})
308+
309+
const schedule = await knexAdapter.getSchedule(id)
310+
assert.isNull(schedule)
311+
312+
const scheduleDeleteQueries = queries.filter(
313+
(sql) => sql.includes('delete') && sql.includes(schedulesTableName)
314+
)
315+
316+
assert.lengthOf(
317+
scheduleDeleteQueries,
318+
1,
319+
`Expected a single schedule DELETE query, got ${scheduleDeleteQueries.length}`
320+
)
321+
})
195322
})

0 commit comments

Comments
 (0)