-
Notifications
You must be signed in to change notification settings - Fork 30.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The original test/parallel/test-sqlite.js test appears to time out in the CI occasionally. This commit splits the test into several smaller test files. Fixes: #54006 PR-URL: #54014 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Michaël Zasso <targos@protonmail.com>
- Loading branch information
Showing
6 changed files
with
765 additions
and
662 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
// Flags: --experimental-sqlite | ||
'use strict'; | ||
require('../common'); | ||
const tmpdir = require('../common/tmpdir'); | ||
const { join } = require('node:path'); | ||
const { DatabaseSync } = require('node:sqlite'); | ||
const { suite, test } = require('node:test'); | ||
let cnt = 0; | ||
|
||
tmpdir.refresh(); | ||
|
||
function nextDb() { | ||
return join(tmpdir.path, `database-${cnt++}.db`); | ||
} | ||
|
||
suite('data binding and mapping', () => { | ||
test('supported data types', (t) => { | ||
const u8a = new TextEncoder().encode('a☃b☃c'); | ||
const db = new DatabaseSync(nextDb()); | ||
t.after(() => { db.close(); }); | ||
const setup = db.exec(` | ||
CREATE TABLE types( | ||
key INTEGER PRIMARY KEY, | ||
int INTEGER, | ||
double REAL, | ||
text TEXT, | ||
buf BLOB | ||
) STRICT; | ||
`); | ||
t.assert.strictEqual(setup, undefined); | ||
const stmt = db.prepare('INSERT INTO types (key, int, double, text, buf) ' + | ||
'VALUES (?, ?, ?, ?, ?)'); | ||
t.assert.deepStrictEqual( | ||
stmt.run(1, 42, 3.14159, 'foo', u8a), | ||
{ changes: 1, lastInsertRowid: 1 }, | ||
); | ||
t.assert.deepStrictEqual( | ||
stmt.run(2, null, null, null, null), | ||
{ changes: 1, lastInsertRowid: 2 } | ||
); | ||
t.assert.deepStrictEqual( | ||
stmt.run(3, Number(8), Number(2.718), String('bar'), Buffer.from('x☃y☃')), | ||
{ changes: 1, lastInsertRowid: 3 }, | ||
); | ||
t.assert.deepStrictEqual( | ||
stmt.run(4, 99n, 0xf, '', new Uint8Array()), | ||
{ changes: 1, lastInsertRowid: 4 }, | ||
); | ||
|
||
const query = db.prepare('SELECT * FROM types WHERE key = ?'); | ||
t.assert.deepStrictEqual(query.get(1), { | ||
key: 1, | ||
int: 42, | ||
double: 3.14159, | ||
text: 'foo', | ||
buf: u8a, | ||
}); | ||
t.assert.deepStrictEqual(query.get(2), { | ||
key: 2, | ||
int: null, | ||
double: null, | ||
text: null, | ||
buf: null, | ||
}); | ||
t.assert.deepStrictEqual(query.get(3), { | ||
key: 3, | ||
int: 8, | ||
double: 2.718, | ||
text: 'bar', | ||
buf: new TextEncoder().encode('x☃y☃'), | ||
}); | ||
t.assert.deepStrictEqual(query.get(4), { | ||
key: 4, | ||
int: 99, | ||
double: 0xf, | ||
text: '', | ||
buf: new Uint8Array(), | ||
}); | ||
}); | ||
|
||
test('unsupported data types', (t) => { | ||
const db = new DatabaseSync(nextDb()); | ||
t.after(() => { db.close(); }); | ||
const setup = db.exec( | ||
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' | ||
); | ||
t.assert.strictEqual(setup, undefined); | ||
|
||
[ | ||
undefined, | ||
() => {}, | ||
Symbol(), | ||
/foo/, | ||
Promise.resolve(), | ||
new Map(), | ||
new Set(), | ||
].forEach((val) => { | ||
t.assert.throws(() => { | ||
db.prepare('INSERT INTO types (key, val) VALUES (?, ?)').run(1, val); | ||
}, { | ||
code: 'ERR_INVALID_ARG_TYPE', | ||
message: /Provided value cannot be bound to SQLite parameter 2/, | ||
}); | ||
}); | ||
|
||
t.assert.throws(() => { | ||
const stmt = db.prepare('INSERT INTO types (key, val) VALUES ($k, $v)'); | ||
stmt.run({ $k: 1, $v: () => {} }); | ||
}, { | ||
code: 'ERR_INVALID_ARG_TYPE', | ||
message: /Provided value cannot be bound to SQLite parameter 2/, | ||
}); | ||
}); | ||
|
||
test('throws when binding a BigInt that is too large', (t) => { | ||
const max = 9223372036854775807n; // Largest 64-bit signed integer value. | ||
const db = new DatabaseSync(nextDb()); | ||
t.after(() => { db.close(); }); | ||
const setup = db.exec( | ||
'CREATE TABLE types(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' | ||
); | ||
t.assert.strictEqual(setup, undefined); | ||
const stmt = db.prepare('INSERT INTO types (key, val) VALUES (?, ?)'); | ||
t.assert.deepStrictEqual( | ||
stmt.run(1, max), | ||
{ changes: 1, lastInsertRowid: 1 }, | ||
); | ||
t.assert.throws(() => { | ||
stmt.run(1, max + 1n); | ||
}, { | ||
code: 'ERR_INVALID_ARG_VALUE', | ||
message: /BigInt value is too large to bind/, | ||
}); | ||
}); | ||
|
||
test('statements are unbound on each call', (t) => { | ||
const db = new DatabaseSync(nextDb()); | ||
t.after(() => { db.close(); }); | ||
const setup = db.exec( | ||
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;' | ||
); | ||
t.assert.strictEqual(setup, undefined); | ||
const stmt = db.prepare('INSERT INTO data (key, val) VALUES (?, ?)'); | ||
t.assert.deepStrictEqual( | ||
stmt.run(1, 5), | ||
{ changes: 1, lastInsertRowid: 1 }, | ||
); | ||
t.assert.deepStrictEqual( | ||
stmt.run(), | ||
{ changes: 1, lastInsertRowid: 2 }, | ||
); | ||
t.assert.deepStrictEqual( | ||
db.prepare('SELECT * FROM data ORDER BY key').all(), | ||
[{ key: 1, val: 5 }, { key: 2, val: null }], | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
// Flags: --experimental-sqlite | ||
'use strict'; | ||
require('../common'); | ||
const tmpdir = require('../common/tmpdir'); | ||
const { existsSync } = require('node:fs'); | ||
const { join } = require('node:path'); | ||
const { DatabaseSync, StatementSync } = require('node:sqlite'); | ||
const { suite, test } = require('node:test'); | ||
let cnt = 0; | ||
|
||
tmpdir.refresh(); | ||
|
||
function nextDb() { | ||
return join(tmpdir.path, `database-${cnt++}.db`); | ||
} | ||
|
||
suite('DatabaseSync() constructor', () => { | ||
test('throws if called without new', (t) => { | ||
t.assert.throws(() => { | ||
DatabaseSync(); | ||
}, { | ||
code: 'ERR_CONSTRUCT_CALL_REQUIRED', | ||
message: /Cannot call constructor without `new`/, | ||
}); | ||
}); | ||
|
||
test('throws if database path is not a string', (t) => { | ||
t.assert.throws(() => { | ||
new DatabaseSync(); | ||
}, { | ||
code: 'ERR_INVALID_ARG_TYPE', | ||
message: /The "path" argument must be a string/, | ||
}); | ||
}); | ||
|
||
test('throws if options is provided but is not an object', (t) => { | ||
t.assert.throws(() => { | ||
new DatabaseSync('foo', null); | ||
}, { | ||
code: 'ERR_INVALID_ARG_TYPE', | ||
message: /The "options" argument must be an object/, | ||
}); | ||
}); | ||
|
||
test('throws if options.open is provided but is not a boolean', (t) => { | ||
t.assert.throws(() => { | ||
new DatabaseSync('foo', { open: 5 }); | ||
}, { | ||
code: 'ERR_INVALID_ARG_TYPE', | ||
message: /The "options\.open" argument must be a boolean/, | ||
}); | ||
}); | ||
}); | ||
|
||
suite('DatabaseSync.prototype.open()', () => { | ||
test('opens a database connection', (t) => { | ||
const dbPath = nextDb(); | ||
const db = new DatabaseSync(dbPath, { open: false }); | ||
t.after(() => { db.close(); }); | ||
|
||
t.assert.strictEqual(existsSync(dbPath), false); | ||
t.assert.strictEqual(db.open(), undefined); | ||
t.assert.strictEqual(existsSync(dbPath), true); | ||
}); | ||
|
||
test('throws if database is already open', (t) => { | ||
const db = new DatabaseSync(nextDb(), { open: false }); | ||
t.after(() => { db.close(); }); | ||
|
||
db.open(); | ||
t.assert.throws(() => { | ||
db.open(); | ||
}, { | ||
code: 'ERR_INVALID_STATE', | ||
message: /database is already open/, | ||
}); | ||
}); | ||
}); | ||
|
||
suite('DatabaseSync.prototype.close()', () => { | ||
test('closes an open database connection', (t) => { | ||
const db = new DatabaseSync(nextDb()); | ||
|
||
t.assert.strictEqual(db.close(), undefined); | ||
}); | ||
|
||
test('throws if database is not open', (t) => { | ||
const db = new DatabaseSync(nextDb(), { open: false }); | ||
|
||
t.assert.throws(() => { | ||
db.close(); | ||
}, { | ||
code: 'ERR_INVALID_STATE', | ||
message: /database is not open/, | ||
}); | ||
}); | ||
}); | ||
|
||
suite('DatabaseSync.prototype.prepare()', () => { | ||
test('returns a prepared statement', (t) => { | ||
const db = new DatabaseSync(nextDb()); | ||
t.after(() => { db.close(); }); | ||
const stmt = db.prepare('CREATE TABLE webstorage(key TEXT)'); | ||
t.assert.ok(stmt instanceof StatementSync); | ||
}); | ||
|
||
test('throws if database is not open', (t) => { | ||
const db = new DatabaseSync(nextDb(), { open: false }); | ||
|
||
t.assert.throws(() => { | ||
db.prepare(); | ||
}, { | ||
code: 'ERR_INVALID_STATE', | ||
message: /database is not open/, | ||
}); | ||
}); | ||
|
||
test('throws if sql is not a string', (t) => { | ||
const db = new DatabaseSync(nextDb()); | ||
t.after(() => { db.close(); }); | ||
|
||
t.assert.throws(() => { | ||
db.prepare(); | ||
}, { | ||
code: 'ERR_INVALID_ARG_TYPE', | ||
message: /The "sql" argument must be a string/, | ||
}); | ||
}); | ||
}); | ||
|
||
suite('DatabaseSync.prototype.exec()', () => { | ||
test('executes SQL', (t) => { | ||
const db = new DatabaseSync(nextDb()); | ||
t.after(() => { db.close(); }); | ||
const result = db.exec(` | ||
CREATE TABLE data( | ||
key INTEGER PRIMARY KEY, | ||
val INTEGER | ||
) STRICT; | ||
INSERT INTO data (key, val) VALUES (1, 2); | ||
INSERT INTO data (key, val) VALUES (8, 9); | ||
`); | ||
t.assert.strictEqual(result, undefined); | ||
const stmt = db.prepare('SELECT * FROM data ORDER BY key'); | ||
t.assert.deepStrictEqual(stmt.all(), [ | ||
{ key: 1, val: 2 }, | ||
{ key: 8, val: 9 }, | ||
]); | ||
}); | ||
|
||
test('reports errors from SQLite', (t) => { | ||
const db = new DatabaseSync(nextDb()); | ||
t.after(() => { db.close(); }); | ||
|
||
t.assert.throws(() => { | ||
db.exec('CREATE TABLEEEE'); | ||
}, { | ||
code: 'ERR_SQLITE_ERROR', | ||
message: /syntax error/, | ||
}); | ||
}); | ||
|
||
test('throws if database is not open', (t) => { | ||
const db = new DatabaseSync(nextDb(), { open: false }); | ||
|
||
t.assert.throws(() => { | ||
db.exec(); | ||
}, { | ||
code: 'ERR_INVALID_STATE', | ||
message: /database is not open/, | ||
}); | ||
}); | ||
|
||
test('throws if sql is not a string', (t) => { | ||
const db = new DatabaseSync(nextDb()); | ||
t.after(() => { db.close(); }); | ||
|
||
t.assert.throws(() => { | ||
db.exec(); | ||
}, { | ||
code: 'ERR_INVALID_ARG_TYPE', | ||
message: /The "sql" argument must be a string/, | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.