Skip to content

Commit bdd7ebe

Browse files
committed
test: improve coverage
1 parent 5c69b03 commit bdd7ebe

4 files changed

Lines changed: 210 additions & 0 deletions

File tree

src/colorful-logger.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import assert from 'node:assert/strict'
22
import test from 'node:test'
33
import { createQueryEndEvent, createQueryErrorEvent } from '../test/helpers/events.ts'
44
import { colorfulLogger } from './colorful-logger.ts'
5+
import type { QueryFormatterInput } from './types.ts'
56

67
test('colorfulLogger writes successful queries', () => {
78
const calls: string[] = []
@@ -36,3 +37,42 @@ test('colorfulLogger writes failed queries', () => {
3637
assert.equal(calls[0]?.includes('\u001B[35m'), true)
3738
assert.equal(calls[0]?.includes('\u001B[31m'), true)
3839
})
40+
41+
test('colorfulLogger supports custom formatters', () => {
42+
const calls: string[] = []
43+
const logger = colorfulLogger({
44+
formatter(event) {
45+
return `${event.sql} -- custom`
46+
},
47+
write(message) {
48+
calls.push(message)
49+
},
50+
})
51+
52+
logger.onEnd?.(createQueryEndEvent())
53+
54+
assert.equal(calls.length, 1)
55+
assert.match(calls[0], /select \? -- custom/)
56+
})
57+
58+
test('colorfulLogger warns when bindings and formatter are provided together', () => {
59+
const warnings: unknown[][] = []
60+
const originalWarn = console.warn
61+
62+
console.warn = (...args: unknown[]) => {
63+
warnings.push(args)
64+
}
65+
66+
try {
67+
colorfulLogger({
68+
bindings: false,
69+
formatter(event: QueryFormatterInput) {
70+
return event.sql
71+
},
72+
} as unknown as Parameters<typeof colorfulLogger>[0])
73+
} finally {
74+
console.warn = originalWarn
75+
}
76+
77+
assert.deepEqual(warnings, [['knex-tiny-logger: "bindings" is ignored when "formatter" is provided.']])
78+
})

src/formatter.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,58 @@ test('defaultQueryFormatter formats bindings through knex raw queries', () => {
3030
)
3131
})
3232

33+
test('defaultQueryFormatter returns raw SQL when bindings are empty', () => {
34+
const formatter = defaultQueryFormatter()
35+
const knex = createFakeKnex()
36+
37+
assert.equal(
38+
formatter({
39+
knex,
40+
sql: 'select 1',
41+
bindings: null,
42+
}),
43+
'select 1',
44+
)
45+
assert.equal(
46+
formatter({
47+
knex,
48+
sql: 'select 1',
49+
bindings: [],
50+
}),
51+
'select 1',
52+
)
53+
assert.equal(
54+
formatter({
55+
knex,
56+
sql: 'select 1',
57+
bindings: {},
58+
}),
59+
'select 1',
60+
)
61+
})
62+
63+
test('defaultQueryFormatter formats named and scalar bindings', () => {
64+
const formatter = defaultQueryFormatter()
65+
const knex = createFakeKnex()
66+
67+
assert.equal(
68+
formatter({
69+
knex,
70+
sql: 'select :id',
71+
bindings: { id: 1 },
72+
}),
73+
'select :id -- {"id":1}',
74+
)
75+
assert.equal(
76+
formatter({
77+
knex,
78+
sql: 'select ?',
79+
bindings: 1,
80+
}),
81+
'select ? -- 1',
82+
)
83+
})
84+
3385
test('defaultQueryFormatter falls back to raw SQL when formatting fails', () => {
3486
const formatter = defaultQueryFormatter()
3587

src/tracer.test.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import assert from 'node:assert/strict'
2+
import { EventEmitter } from 'node:events'
23
import test from 'node:test'
4+
import type { Knex } from 'knex'
35
import { createFakeKnex } from '../test/helpers/fake-knex.ts'
46
import { createTracer } from './tracer.ts'
57
import type { QueryStartEvent, TracerQueryEndEvent, TracerQueryErrorEvent } from './types.ts'
@@ -104,3 +106,82 @@ test('createTracer ignores tracer errors by default', () => {
104106
knex.emit('query-response', [], { __knexQueryUid: 'query-1' })
105107
})
106108
})
109+
110+
test('createTracer supports queries without knex ids', () => {
111+
const knex = createFakeKnex()
112+
const query = { sql: 'select ?', bindings: [1] }
113+
const startEvents: QueryStartEvent[] = []
114+
const endEvents: TracerQueryEndEvent[] = []
115+
116+
createTracer(knex, {
117+
onStart(event) {
118+
startEvents.push(event)
119+
},
120+
onEnd(event) {
121+
endEvents.push(event)
122+
},
123+
})
124+
125+
knex.emit('query', query)
126+
knex.emit('query-response', [], query)
127+
128+
assert.equal(startEvents.length, 1)
129+
assert.equal(startEvents[0].queryId, 'anonymous:1')
130+
assert.equal(endEvents.length, 1)
131+
assert.equal(endEvents[0].queryId, 'anonymous:1')
132+
})
133+
134+
test('createTracer normalizes non-object query payloads', () => {
135+
const knex = createFakeKnex()
136+
const startEvents: QueryStartEvent[] = []
137+
138+
createTracer(knex, {
139+
onStart(event) {
140+
startEvents.push(event)
141+
},
142+
})
143+
144+
knex.emit('query', 'select 1')
145+
146+
assert.equal(startEvents.length, 1)
147+
assert.equal(startEvents[0].queryId, 'anonymous:1')
148+
assert.equal(startEvents[0].sql, '')
149+
})
150+
151+
test('createTracer swallows tracer error handler failures', () => {
152+
const knex = createFakeKnex()
153+
154+
createTracer(knex, {
155+
onEnd() {
156+
throw new Error('logger failed')
157+
},
158+
onTracerError() {
159+
throw new Error('tracer failed')
160+
},
161+
})
162+
163+
assert.doesNotThrow(() => {
164+
knex.emit('query', { __knexQueryUid: 'query-1', sql: 'select 1' })
165+
knex.emit('query-response', [], { __knexQueryUid: 'query-1' })
166+
})
167+
})
168+
169+
test('createTracer disposes with removeListener when off is unavailable', () => {
170+
const emitter = new EventEmitter()
171+
const knex = {
172+
emit: emitter.emit.bind(emitter),
173+
on: emitter.on.bind(emitter),
174+
removeListener: emitter.removeListener.bind(emitter),
175+
} as unknown as Knex & Pick<EventEmitter, 'emit'>
176+
const startEvents: QueryStartEvent[] = []
177+
const tracer = createTracer(knex, {
178+
onStart(event) {
179+
startEvents.push(event)
180+
},
181+
})
182+
183+
tracer.dispose()
184+
knex.emit('query', { __knexQueryUid: 'query-1', sql: 'select 1' })
185+
186+
assert.equal(startEvents.length, 0)
187+
})

test/integration/knex-tiny-logger.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,43 @@ test('knexTinyLogger reports logger errors', () => {
113113
assert.deepEqual(loggerErrors, [loggerError])
114114
})
115115

116+
test('knexTinyLogger wires start, error, and logger error fallback hooks', () => {
117+
const knex = createFakeKnex()
118+
const loggerError = new Error('logger failed')
119+
const handlerError = new Error('handler failed')
120+
const startEvents: unknown[] = []
121+
const consoleErrors: unknown[][] = []
122+
const originalError = console.error
123+
124+
console.error = (...args: unknown[]) => {
125+
consoleErrors.push(args)
126+
}
127+
128+
try {
129+
knexTinyLogger(knex, {
130+
logger: {
131+
onStart(event) {
132+
startEvents.push(event)
133+
},
134+
onError() {
135+
throw loggerError
136+
},
137+
},
138+
onLoggerError() {
139+
throw handlerError
140+
},
141+
})
142+
143+
knex.emit('query', { __knexQueryUid: 'query-1', sql: 'select broken' })
144+
knex.emit('query-error', new Error('query failed'), { __knexQueryUid: 'query-1' })
145+
} finally {
146+
console.error = originalError
147+
}
148+
149+
assert.equal(startEvents.length, 1)
150+
assert.deepEqual(consoleErrors, [[handlerError]])
151+
})
152+
116153
test('createTracer receives real knex query responses', async () => {
117154
const knex = createKnex({
118155
client: 'sqlite3',

0 commit comments

Comments
 (0)