Skip to content

Commit

Permalink
sqlite: split up large test file
Browse files Browse the repository at this point in the history
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
cjihrig authored and targos committed Aug 14, 2024
1 parent 94e2ea6 commit 7f1bf1c
Show file tree
Hide file tree
Showing 6 changed files with 765 additions and 662 deletions.
157 changes: 157 additions & 0 deletions test/parallel/test-sqlite-data-types.js
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 }],
);
});
});
185 changes: 185 additions & 0 deletions test/parallel/test-sqlite-database-sync.js
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/,
});
});
});
Loading

0 comments on commit 7f1bf1c

Please sign in to comment.