node/test/parallel/test-sqlite-database-sync.js
Colin Ihrig 1d01ad6773
Some checks are pending
Coverage Linux (without intl) / coverage-linux-without-intl (push) Waiting to run
Coverage Linux / coverage-linux (push) Waiting to run
Coverage Windows / coverage-windows (push) Waiting to run
Test and upload documentation to artifacts / build-docs (push) Waiting to run
Linters / lint-addon-docs (push) Waiting to run
Linters / lint-cpp (push) Waiting to run
Linters / format-cpp (push) Waiting to run
Linters / lint-js-and-md (push) Waiting to run
Linters / lint-py (push) Waiting to run
Linters / lint-yaml (push) Waiting to run
Linters / lint-sh (push) Waiting to run
Linters / lint-codeowners (push) Waiting to run
Linters / lint-pr-url (push) Waiting to run
Linters / lint-readme (push) Waiting to run
Notify on Push / Notify on Force Push on `main` (push) Waiting to run
Notify on Push / Notify on Push on `main` that lacks metadata (push) Waiting to run
Scorecard supply-chain security / Scorecard analysis (push) Waiting to run
doc,lib,src,test: unflag sqlite module
This commit allows the node:sqlite module to be used without
starting Node with a CLI flag. The module is still experimental.

Fixes: https://github.com/nodejs/node/issues/55854
PR-URL: https://github.com/nodejs/node/pull/55890
Reviewed-By: Jake Yuesong Li <jake.yuesong@gmail.com>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
2024-11-19 03:23:18 +00:00

282 lines
7.8 KiB
JavaScript

'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/,
});
});
test('throws if options.readOnly is provided but is not a boolean', (t) => {
t.assert.throws(() => {
new DatabaseSync('foo', { readOnly: 5 });
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: /The "options\.readOnly" argument must be a boolean/,
});
});
test('is not read-only by default', (t) => {
const dbPath = nextDb();
const db = new DatabaseSync(dbPath);
db.exec('CREATE TABLE foo (id INTEGER PRIMARY KEY)');
});
test('is read-only if readOnly is set', (t) => {
const dbPath = nextDb();
{
const db = new DatabaseSync(dbPath);
db.exec('CREATE TABLE foo (id INTEGER PRIMARY KEY)');
db.close();
}
{
const db = new DatabaseSync(dbPath, { readOnly: true });
t.assert.throws(() => {
db.exec('CREATE TABLE bar (id INTEGER PRIMARY KEY)');
}, {
code: 'ERR_SQLITE_ERROR',
message: /attempt to write a readonly database/,
});
}
});
test('throws if options.enableForeignKeyConstraints is provided but is not a boolean', (t) => {
t.assert.throws(() => {
new DatabaseSync('foo', { enableForeignKeyConstraints: 5 });
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: /The "options\.enableForeignKeyConstraints" argument must be a boolean/,
});
});
test('enables foreign key constraints by default', (t) => {
const dbPath = nextDb();
const db = new DatabaseSync(dbPath);
db.exec(`
CREATE TABLE foo (id INTEGER PRIMARY KEY);
CREATE TABLE bar (foo_id INTEGER REFERENCES foo(id));
`);
t.after(() => { db.close(); });
t.assert.throws(() => {
db.exec('INSERT INTO bar (foo_id) VALUES (1)');
}, {
code: 'ERR_SQLITE_ERROR',
message: 'FOREIGN KEY constraint failed',
});
});
test('allows disabling foreign key constraints', (t) => {
const dbPath = nextDb();
const db = new DatabaseSync(dbPath, { enableForeignKeyConstraints: false });
db.exec(`
CREATE TABLE foo (id INTEGER PRIMARY KEY);
CREATE TABLE bar (foo_id INTEGER REFERENCES foo(id));
`);
t.after(() => { db.close(); });
db.exec('INSERT INTO bar (foo_id) VALUES (1)');
});
test('throws if options.enableDoubleQuotedStringLiterals is provided but is not a boolean', (t) => {
t.assert.throws(() => {
new DatabaseSync('foo', { enableDoubleQuotedStringLiterals: 5 });
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: /The "options\.enableDoubleQuotedStringLiterals" argument must be a boolean/,
});
});
test('disables double-quoted string literals by default', (t) => {
const dbPath = nextDb();
const db = new DatabaseSync(dbPath);
t.after(() => { db.close(); });
t.assert.throws(() => {
db.exec('SELECT "foo";');
}, {
code: 'ERR_SQLITE_ERROR',
message: /no such column: "foo"/,
});
});
test('allows enabling double-quoted string literals', (t) => {
const dbPath = nextDb();
const db = new DatabaseSync(dbPath, { enableDoubleQuotedStringLiterals: true });
t.after(() => { db.close(); });
db.exec('SELECT "foo";');
});
});
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(), [
{ __proto__: null, key: 1, val: 2 },
{ __proto__: null, 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/,
});
});
});