Skip to content

Commit 2842bf5

Browse files
committed
test suite prep.
1 parent 9f456b9 commit 2842bf5

3 files changed

Lines changed: 108 additions & 0 deletions

File tree

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ export * from './util/column-type.js'
224224
export * from './util/compilable.js'
225225
export * from './util/explainable.js'
226226
export * from './util/streamable.js'
227+
export * from './util/executable.js'
227228
export * from './util/log.js'
228229
export {
229230
AnyAliasedColumn,

src/query-executor/query-executor.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,10 @@ export interface ExecuteQueryOptions {
104104
*/
105105
abortSignal?: AbortSignal
106106
}
107+
108+
export class AbortError extends Error {
109+
constructor() {
110+
super('The operation was aborted.')
111+
this.name = 'AbortError'
112+
}
113+
}

test/node/src/cancellation.test.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { expect } from 'chai'
2+
import { AbortError, Executable } from '../../..'
3+
import {
4+
clearDatabase,
5+
destroyTest,
6+
DIALECTS,
7+
initTest,
8+
insertDefaultDataSet,
9+
TestContext,
10+
} from './test-setup.js'
11+
12+
for (const dialect of DIALECTS) {
13+
describe(`${dialect}: query cancellation`, () => {
14+
const cancellableBuilders: Executable<any>[] = []
15+
let ctx: TestContext
16+
17+
before(async function () {
18+
ctx = await initTest(this, dialect)
19+
20+
const { db } = ctx
21+
22+
cancellableBuilders.push(db.selectFrom('person').selectAll())
23+
})
24+
25+
beforeEach(async () => {
26+
await insertDefaultDataSet(ctx)
27+
})
28+
29+
afterEach(async () => {
30+
await clearDatabase(ctx)
31+
})
32+
33+
after(async () => {
34+
await destroyTest(ctx)
35+
})
36+
37+
it('should execute queries normally when not aborted', async () => {
38+
const abortSignal = new AbortController().signal
39+
40+
const results = await executeQueries(cancellableBuilders, abortSignal)
41+
42+
expect(
43+
results.filter(
44+
(result) =>
45+
result.status === 'rejected' && result.reason instanceof AbortError,
46+
),
47+
).to.be.empty
48+
})
49+
50+
it.skip('should cancel the query when aborted after execution start and throw an abort error', async () => {
51+
const abortController = new AbortController()
52+
const abortSignal = abortController.signal
53+
54+
const results = await executeQueries(cancellableBuilders, abortSignal, [
55+
setTimeout(() => {
56+
abortController.abort()
57+
}, 0),
58+
])
59+
60+
expect(
61+
results.filter(
62+
(result) =>
63+
result.status === 'rejected' && result.reason instanceof AbortError,
64+
),
65+
).to.have.lengthOf(cancellableBuilders.length)
66+
// TODO: assert all queries were executed and cancelled.
67+
})
68+
69+
it.skip('should not execute the query when aborted before and throw an abort error', async () => {
70+
const abortController = new AbortController()
71+
const abortSignal = abortController.signal
72+
abortController.abort()
73+
74+
const results = await executeQueries(cancellableBuilders, abortSignal)
75+
76+
expect(
77+
results.filter(
78+
(result) =>
79+
result.status === 'rejected' && result.reason instanceof AbortError,
80+
),
81+
).to.have.lengthOf(cancellableBuilders.length)
82+
// TODO: assert no queries were executed internally.
83+
})
84+
})
85+
}
86+
87+
async function executeQueries(
88+
queries: Executable<any>[],
89+
abortSignal: AbortSignal,
90+
moreItems?: any[],
91+
) {
92+
return await Promise.allSettled([
93+
...queries.flatMap((query) => [
94+
query.execute({ abortSignal }),
95+
query.executeTakeFirst({ abortSignal }),
96+
query.executeTakeFirstOrThrow({ abortSignal }),
97+
]),
98+
...(moreItems ?? []),
99+
])
100+
}

0 commit comments

Comments
 (0)