Skip to content

Commit 05fb42b

Browse files
Refactoring test code to use same function to create connections
1 parent c8ece83 commit 05fb42b

File tree

9 files changed

+139
-130
lines changed

9 files changed

+139
-130
lines changed

jest.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module.exports = {
22
preset: 'ts-jest',
33
testEnvironment: 'node',
44
testMatch: ['**/test/**/*.test.ts'],
5+
// longer timeout when using a debugger:
56
testTimeout: 30 * 1000,
67
maxWorkers: 8,
78
collectCoverageFrom: ['<rootDir>/src/**/*.ts', '!<rootDir>/src/index.ts', '!<rootDir>/src/types/**/*.ts'],

src/connection.ts

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -162,37 +162,45 @@ export class SQLiteCloudConnection {
162162
}
163163

164164
this.operations.enqueue(done => {
165+
// clear all listeners and call done in the operations queue
166+
const finish: ResultsCallback = (error, result) => {
167+
if (this.socket) {
168+
this.socket.removeAllListeners('data')
169+
this.socket.removeAllListeners('error')
170+
this.socket.removeAllListeners('close')
171+
}
172+
if (callback) {
173+
callback?.call(this, error)
174+
callback = undefined
175+
}
176+
if (error) {
177+
this.close()
178+
}
179+
// process next operation in the queue
180+
done?.call(this, error ? error : undefined)
181+
}
182+
165183
// connect to tls socket, initialize connection, setup event handlers
166184
const client: tls.TLSSocket = tls.connect(this.config.port as number, this.config.host, this.config.tlsOptions, () => {
167185
if (!client.authorized) {
168186
const anonimizedError = anonimizeError(client.authorizationError)
169187
this.log('Connection was not authorized', anonimizedError)
170188
this.close()
171-
const error = new SQLiteCloudError('Connection was not authorized', { cause: anonimizedError })
172-
callback?.call(this, error)
173-
done?.call(this, error)
189+
finish(new SQLiteCloudError('Connection was not authorized', { cause: anonimizedError }))
174190
} else {
175191
this.socket = client
176-
callback?.call(this, null)
177-
done?.call(this)
192+
finish(null)
178193
}
179194
})
180195

181196
client.on('close', () => {
182-
if (this.socket) {
183-
// no loggin if already disposed
184-
this.log('Connection closed')
185-
this.socket.destroy()
186-
this.socket = undefined
187-
}
197+
this.log('Connection closed')
198+
finish(new SQLiteCloudError('Connection was closed'))
188199
})
189200

190201
client.once('error', (error: any) => {
191-
error = new SQLiteCloudError('Connection error', { cause: error })
192202
this.log('Connection error', error)
193-
this.close()
194-
callback?.call(this, error)
195-
done?.call(this, error)
203+
finish(new SQLiteCloudError('Connection error', { cause: error }))
196204
})
197205
})
198206

@@ -225,13 +233,19 @@ export class SQLiteCloudConnection {
225233
let socketTimeout: number
226234

227235
// clear all listeners and call done in the operations queue
228-
const finish = (error?: Error) => {
236+
const finish: ResultsCallback = (error, result) => {
229237
clearTimeout(socketTimeout)
230238
if (this.socket) {
231239
this.socket.removeAllListeners('data')
232240
this.socket.removeAllListeners('error')
241+
this.socket.removeAllListeners('close')
233242
}
234-
done?.call(this, error)
243+
if (callback) {
244+
callback?.call(this, error, result)
245+
callback = undefined
246+
}
247+
// process next operation in the queue
248+
done?.call(this, error ? error : undefined)
235249
}
236250

237251
// define the Promise that waits for the server response
@@ -263,15 +277,13 @@ export class SQLiteCloudConnection {
263277
if (dataType !== CMD_ROWSET_CHUNK && compressedDataType !== CMD_ROWSET_CHUNK) {
264278
this.socket?.off('data', readData)
265279
const { data } = popData(buffer)
266-
callback?.call(this, null, data)
267-
finish?.call(this)
280+
finish(null, data)
268281
} else {
269282
// @ts-expect-error
270283
// check if rowset received the ending chunk
271284
if (data.subarray(data.indexOf(' ') + 1, data.length).toString() === '0 0 0 ') {
272285
const parsedData = parseRowsetChunks(rowsetChunks)
273-
callback?.call(this, null, parsedData)
274-
finish?.call(this)
286+
finish?.call(this, null, parsedData)
275287
} else {
276288
// no ending string? ask server for another chunk
277289
rowsetChunks.push(buffer)
@@ -285,28 +297,32 @@ export class SQLiteCloudConnection {
285297
const lastChar = buffer.subarray(buffer.length - 1, buffer.length).toString('utf8')
286298
if (lastChar == ' ') {
287299
const { data } = popData(buffer)
288-
callback?.call(this, null, data)
289-
finish?.call(this)
300+
finish(null, data)
290301
}
291302
}
292303
} catch (error) {
293-
this.close()
294-
callback?.call(this, error as Error)
295-
finish?.call(this, error as Error)
304+
console.assert(error instanceof Error)
305+
if (error instanceof Error) {
306+
finish(error)
307+
}
296308
}
297309
}
298310

311+
this.socket?.once('close', () => {
312+
finish(new SQLiteCloudError('Connection was closed', { cause: anonimizeCommand(commands) }))
313+
})
314+
299315
this.socket?.write(commands, 'utf8', () => {
300316
socketTimeout = setTimeout(() => {
301-
finish?.call(this, new SQLiteCloudError('Request timed out', { cause: anonimizeCommand(commands) }))
317+
finish(new SQLiteCloudError('Request timed out', { cause: anonimizeCommand(commands) }))
302318
}, this.config.timeout)
303319
this.socket?.on('data', readData)
304320
})
305321

306322
this.socket?.once('error', (error: any) => {
307323
this.log('Socket error', error)
308324
this.close()
309-
finish?.call(this, new SQLiteCloudError('Socket error', { cause: anonimizeError(error) }))
325+
finish(new SQLiteCloudError('Socket error', { cause: anonimizeError(error) }))
310326
})
311327
})
312328

src/database.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ import { Statement } from './statement'
1818
import { ErrorCallback, ResultsCallback, RowCallback, RowsCallback } from './types'
1919
import EventEmitter from 'eventemitter3'
2020

21+
// Uses eventemitter3 instead of node events for browser compatibility
22+
// https://github.com/primus/eventemitter3
23+
2124
/**
2225
* Creating a Database object automatically opens a connection to the SQLite database.
2326
* When the connection is established the Database object emits an open event and calls
@@ -110,9 +113,6 @@ export class Database extends EventEmitter {
110113
/** Emits given event with optional arguments on the next tick so callbacks can complete first */
111114
private emitEvent(event: string, ...args: any[]): void {
112115
process.nextTick(() => {
113-
if (this.config.verbose) {
114-
console.log(`Database.emitEvent - emitted '${event}'`, ...args)
115-
}
116116
this.emit(event, ...args)
117117
})
118118
}

test/compare.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
* compare.test.ts - test driver api against sqlite3 equivalents
33
*/
44

5-
import { Database } from '../src/index'
65
import { CHINOOK_DATABASE_FILE, CHINOOK_DATABASE_URL, CHINOOK_FIRST_TRACK, LONG_TIMEOUT, TESTING_SQL } from './shared'
76
import { getChinookDatabase, getTestingDatabase } from './shared'
87

@@ -44,7 +43,7 @@ describe('Database.on', () => {
4443
})
4544

4645
it('sqlitecloud: should emit open event', done => {
47-
const chinook = new Database(CHINOOK_DATABASE_URL, error => {
46+
const chinook = getChinookDatabase(error => {
4847
expect(chinook).toBeDefined()
4948
expect(error).toBeNull()
5049
chinook.once('open', () => {

test/connection.test.ts

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,17 @@
55
import { SQLiteCloudError } from '../src/index'
66
import { SQLiteCloudConnection, anonimizeCommand } from '../src/connection'
77
import { parseConnectionString } from '../src/utilities'
8-
import { CHINOOK_DATABASE_URL, TESTING_DATABASE_URL, LONG_TIMEOUT, getTestingConfig, getChinoookConfig } from './shared'
8+
import { CHINOOK_DATABASE_URL, TESTING_DATABASE_URL, LONG_TIMEOUT, getTestingConfig, getChinookConfig, getChinookConnection } from './shared'
99

1010
describe('connection', () => {
1111
let chinook: SQLiteCloudConnection
1212

13-
beforeAll(() => {
14-
expect(CHINOOK_DATABASE_URL).toBeDefined()
15-
expect(TESTING_DATABASE_URL).toBeDefined()
16-
})
17-
18-
beforeEach(done => {
19-
chinook = new SQLiteCloudConnection(CHINOOK_DATABASE_URL + '?nonlinearizable=1', error => {
20-
expect(chinook).toBeDefined()
21-
done()
22-
})
23-
// connection.verbose()
13+
beforeEach(() => {
14+
chinook = getChinookConnection()
2415
})
2516

2617
afterEach(() => {
27-
if (chinook) {
28-
chinook.close()
29-
}
18+
chinook?.close()
3019
// @ts-ignore
3120
chinook = undefined
3221
})
@@ -37,11 +26,11 @@ describe('connection', () => {
3726
})
3827

3928
it('should add self signed certificate for localhost connections', () => {
40-
const localConfig = getChinoookConfig('sqlitecloud://admin:xxx@localhost:8850/chinook.db')
29+
const localConfig = getChinookConfig('sqlitecloud://admin:xxx@localhost:8850/chinook.db')
4130
expect(localConfig.host).toBe('localhost')
4231
expect(localConfig.tlsOptions?.ca).toBeTruthy()
4332

44-
const remoteConfig = getChinoookConfig('sqlitecloud://admin:xxx@sqlitecloud.io:8850/chinook.db')
33+
const remoteConfig = getChinookConfig('sqlitecloud://admin:xxx@sqlitecloud.io:8850/chinook.db')
4534
expect(remoteConfig.host).toBe('sqlitecloud.io')
4635
expect(remoteConfig.tlsOptions).toBeFalsy()
4736
})

test/database.test.ts

Lines changed: 54 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,43 @@
33
*/
44

55
import { SQLiteCloudRowset, SQLiteCloudRow, SQLiteCloudError } from '../src/index'
6-
import { Database } from '../src/database'
7-
import { getTestingDatabase, getTestingDatabaseAsync, getChinookDatabase, removeDatabase, removeDatabaseAsync } from './shared'
8-
import { CHINOOK_DATABASE_URL, LONG_TIMEOUT } from './shared'
6+
import { getTestingDatabase, getTestingDatabaseAsync, getChinookDatabase, removeDatabase, removeDatabaseAsync, LONG_TIMEOUT } from './shared'
97
import { RowCountCallback } from '../src/types'
108

119
//
1210
// utility methods to setup and destroy temporary test databases
1311
//
1412

1513
describe('Database.run', () => {
16-
it('simple update', done => {
17-
const updateSql = "UPDATE people SET name = 'Charlie Brown' WHERE id = 3; UPDATE people SET name = 'David Bowie' WHERE id = 4; "
14+
it(
15+
'simple update',
16+
done => {
17+
const updateSql = "UPDATE people SET name = 'Charlie Brown' WHERE id = 3; UPDATE people SET name = 'David Bowie' WHERE id = 4; "
1818

19-
// lambda callback would "hide" this
20-
function plainCallbackNotALambda(err: Error, results: any) {
21-
expect(err).toBeNull()
22-
expect(results).toBeUndefined()
19+
// lambda callback would "hide" this
20+
function plainCallbackNotALambda(err: Error, results: any) {
21+
expect(err).toBeNull()
22+
expect(results).toBeUndefined()
2323

24-
// Database.run should return number of rows modified and lastID
25-
// @ts-expect-error
26-
const context = this as any
27-
expect(context.lastID).toBe(20)
28-
expect(context.changes).toBe(1) // should this be 2?
29-
expect(context.totalChanges).toBe(22)
30-
expect(context.finalized).toBe(1)
24+
// Database.run should return number of rows modified and lastID
25+
// @ts-expect-error
26+
const context = this as any
27+
expect(context.lastID).toBe(20)
28+
expect(context.changes).toBe(1) // should this be 2?
29+
expect(context.totalChanges).toBe(22)
30+
expect(context.finalized).toBe(1)
3131

32-
removeDatabase(database, error => {
33-
expect(error).toBeNull()
34-
done()
35-
})
36-
}
32+
removeDatabase(database, error => {
33+
expect(error).toBeNull()
34+
done()
35+
})
36+
}
3737

38-
const database = getTestingDatabase()
39-
database.run(updateSql, plainCallbackNotALambda)
40-
})
38+
const database = getTestingDatabase()
39+
database.run(updateSql, plainCallbackNotALambda)
40+
},
41+
LONG_TIMEOUT
42+
)
4143

4244
it(
4345
'insert with parameter value',
@@ -90,8 +92,8 @@ describe('Database.all', () => {
9092
it(
9193
'simple select',
9294
done => {
93-
const db = new Database(CHINOOK_DATABASE_URL)
94-
db.all('SELECT * FROM tracks', (err: Error, rows: SQLiteCloudRowset) => {
95+
const chinook = getChinookDatabase()
96+
chinook.all('SELECT * FROM tracks', (err: Error, rows: SQLiteCloudRowset) => {
9597
expect(err).toBeNull()
9698
expect(rows).toBeDefined()
9799
expect(rows).toHaveLength(3503)
@@ -107,7 +109,7 @@ describe('Database.all', () => {
107109
UnitPrice: 0.99
108110
})
109111

110-
db.close(error => {
112+
chinook.close(error => {
111113
expect(error).toBeNull()
112114
done()
113115
})
@@ -205,27 +207,32 @@ describe('Database.exec', () => {
205207
})
206208
})
207209

208-
it('execute statement with errors', done => {
209-
// sqlitecloud-js / fix problem with jest tests of sendCommands error conditions #24
210-
const chinook = getChinookDatabase()
211-
try {
212-
chinook.exec('SET BOGUS STATEMENT TO 1;', error => {
213-
expect(error).toBeInstanceOf(SQLiteCloudError)
214-
expect(error).toMatchObject({
215-
errorCode: '10002',
216-
externalErrorCode: '0',
217-
name: 'SQLiteCloudError',
218-
offsetCode: -1,
219-
message: 'Unable to find command SET BOGUS STATEMENT TO 1;.'
210+
it(
211+
'execute statement with errors',
212+
done => {
213+
// sqlitecloud-js / fix problem with jest tests of sendCommands error conditions #24
214+
const chinook = getChinookDatabase()
215+
try {
216+
chinook.exec('SET BOGUS STATEMENT TO 1;', error => {
217+
expect(error).toBeDefined()
218+
expect(error).toBeInstanceOf(SQLiteCloudError)
219+
220+
const sqliteCloudError = error as SQLiteCloudError
221+
expect(sqliteCloudError.errorCode).toBe('10002')
222+
expect(sqliteCloudError.externalErrorCode).toBe('0')
223+
expect(sqliteCloudError.name).toBe('SQLiteCloudError')
224+
expect(sqliteCloudError.offsetCode).toBe(-1)
225+
expect(sqliteCloudError.message.includes('Unable to find command SET BOGUS STATEMENT TO 1;')).toBe(true)
226+
227+
chinook.close()
228+
done()
220229
})
221-
222-
chinook.close()
223-
done()
224-
})
225-
} catch (error) {
226-
done(error)
227-
}
228-
})
230+
} catch (error) {
231+
done(error)
232+
}
233+
},
234+
LONG_TIMEOUT
235+
)
229236
})
230237

231238
describe('Database.sql (async)', () => {

0 commit comments

Comments
 (0)