better-sqlite3/test/42.integrity.js
2024-10-16 09:39:46 -07:00

505 lines
18 KiB
JavaScript

'use strict';
const fs = require('fs');
const path = require('path');
const Database = require('../.');
describe('integrity checks', function () {
beforeEach(function () {
let func = () => {};
this.useFunc = fn => { func = fn; };
this.db = new Database(util.next());
this.db.prepare("CREATE TABLE entries (a TEXT, b INTEGER, c REAL, d BLOB, e TEXT)").run();
this.db.prepare("INSERT INTO entries WITH RECURSIVE temp(a, b, c, d, e) AS (SELECT 'foo', 1, 3.14, x'dddddddd', NULL UNION ALL SELECT a, b + 1, c, d, e FROM temp LIMIT 5) SELECT * FROM temp").run();
this.db.function('func', x => (func(), x));
this.iterator = this.db.prepare("SELECT func(b) from entries ORDER BY rowid");
this.reader = this.db.prepare("SELECT func(b) from entries ORDER BY rowid");
this.writer = this.db.prepare("UPDATE entries SET c = c + 2.718")
});
afterEach(function () {
this.db.close();
});
const allowed = fn => () => expect(fn).to.not.throw();
const blocked = fn => () => expect(fn).to.throw(TypeError);
const normally = fn => fn();
const whileIterating = (self, fn) => {
let count = 0;
for (const _ of self.iterator.iterate()) { count += 1; fn(); }
expect(count).to.equal(5);
};
const whileBusy = (self, fn) => {
let count = 0;
self.useFunc(() => { count += 1; fn(); });
self.iterator.all();
expect(count).to.equal(5);
};
const whileClosed = (self, fn) => {
self.db.close();
fn();
};
describe('Database#prepare()', function () {
specify('while iterating (allowed)', function () {
whileIterating(this, allowed(() => this.db.prepare('SELECT 555')));
whileIterating(this, allowed(() => this.db.prepare('DELETE FROM entries')));
normally(allowed(() => this.db.prepare('SELECT 555')));
normally(allowed(() => this.db.prepare('DELETE FROM entries')));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => this.db.prepare('SELECT 555')));
whileBusy(this, blocked(() => this.db.prepare('DELETE FROM entries')));
normally(allowed(() => this.db.prepare('SELECT 555')));
normally(allowed(() => this.db.prepare('DELETE FROM entries')));
});
specify('while closed (blocked)', function () {
whileClosed(this, blocked(() => this.db.prepare('SELECT 555')));
whileClosed(this, blocked(() => this.db.prepare('DELETE FROM entries')));
});
});
describe('Database#exec()', function () {
specify('while iterating (blocked)', function () {
whileIterating(this, blocked(() => this.db.exec('SELECT 555')));
whileIterating(this, blocked(() => this.db.exec('DELETE FROM entries')));
normally(allowed(() => this.db.exec('SELECT 555')));
normally(allowed(() => this.db.exec('DELETE FROM entries')));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => this.db.exec('SELECT 555')));
whileBusy(this, blocked(() => this.db.exec('DELETE FROM entries')));
normally(allowed(() => this.db.exec('SELECT 555')));
normally(allowed(() => this.db.exec('DELETE FROM entries')));
});
specify('while closed (blocked)', function () {
whileClosed(this, blocked(() => this.db.exec('SELECT 555')));
whileClosed(this, blocked(() => this.db.exec('DELETE FROM entries')));
});
});
describe('Database#pragma()', function () {
specify('while iterating (blocked)', function () {
whileIterating(this, blocked(() => this.db.pragma('cache_size')));
normally(allowed(() => this.db.pragma('cache_size')));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => this.db.pragma('cache_size')));
normally(allowed(() => this.db.pragma('cache_size')));
});
specify('while closed (blocked)', function () {
whileClosed(this, blocked(() => this.db.pragma('cache_size')));
});
});
describe('Database#pragma(\'wal_checkpoint(RESTART)\')', function () {
specify('while iterating (blocked)', function () {
whileIterating(this, blocked(() => this.db.pragma('wal_checkpoint(RESTART)')));
normally(allowed(() => this.db.pragma('wal_checkpoint(RESTART)')));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => this.db.pragma('wal_checkpoint(RESTART)')));
normally(allowed(() => this.db.pragma('wal_checkpoint(RESTART)')));
});
specify('while closed (blocked)', function () {
whileClosed(this, blocked(() => this.db.pragma('wal_checkpoint(RESTART)')));
});
});
describe('Database#backup()', function () {
specify('while iterating (allowed)', async function () {
const promises = [];
whileIterating(this, allowed(() => promises.push(this.db.backup(util.next()))));
expect(promises.length).to.equal(5);
return Promise.all(promises);
});
specify('while busy (allowed)', async function () {
const promises = [];
whileBusy(this, allowed(() => promises.push(this.db.backup(util.next()))));
expect(promises.length).to.equal(5);
return Promise.all(promises);
});
specify('while closed (blocked)', async function () {
const promises = [];
whileClosed(this, allowed(() => promises.push(this.db.backup(util.next()))));
expect(promises.length).to.equal(1);
return Promise.all(promises.map(p =>
p.then(() => { throw new Error('Promise should have been rejected'); }, () => {})
));
});
});
describe('Database#function()', function () {
specify('while iterating (blocked)', function () {
let i = 0;
whileIterating(this, blocked(() => this.db.function(`fn_${++i}`, () => {})));
expect(i).to.equal(5);
normally(allowed(() => this.db.function(`fn_${++i}`, () => {})));
});
specify('while busy (blocked)', function () {
let i = 0;
whileBusy(this, blocked(() => this.db.function(`fn_${++i}`, () => {})));
expect(i).to.equal(5);
normally(allowed(() => this.db.function(`fn_${++i}`, () => {})));
});
specify('while closed (blocked)', function () {
let i = 0;
whileClosed(this, blocked(() => this.db.function(`fn_${++i}`, () => {})));
expect(i).to.equal(1);
});
});
describe('Database#aggregate()', function () {
specify('while iterating (blocked)', function () {
let i = 0;
whileIterating(this, blocked(() => this.db.aggregate(`agg_${++i}`, { step: () => {} })));
expect(i).to.equal(5);
normally(allowed(() => this.db.aggregate(`agg_${++i}`, { step: () => {} })));
});
specify('while busy (blocked)', function () {
let i = 0;
whileBusy(this, blocked(() => this.db.aggregate(`agg_${++i}`, { step: () => {} })));
expect(i).to.equal(5);
normally(allowed(() => this.db.aggregate(`agg_${++i}`, { step: () => {} })));
});
specify('while closed (blocked)', function () {
let i = 0;
whileClosed(this, blocked(() => this.db.aggregate(`agg_${++i}`, { step: () => {} })));
expect(i).to.equal(1);
});
});
describe('Database#table()', function () {
specify('while iterating (blocked)', function () {
let i = 0;
whileIterating(this, blocked(() => this.db.table(`tbl_${++i}`, { columns: ['x'], *rows() {} })));
expect(i).to.equal(5);
normally(allowed(() => this.db.table(`tbl_${++i}`, { columns: ['x'], *rows() {} })));
});
specify('while busy (blocked)', function () {
let i = 0;
whileBusy(this, blocked(() => this.db.table(`tbl_${++i}`, { columns: ['x'], *rows() {} })));
expect(i).to.equal(5);
normally(allowed(() => this.db.table(`tbl_${++i}`, { columns: ['x'], *rows() {} })));
});
specify('while closed (blocked)', function () {
let i = 0;
whileClosed(this, blocked(() => this.db.table(`tbl_${++i}`, { columns: ['x'], *rows() {} })));
expect(i).to.equal(1);
});
});
describe('Database#close()', function () {
specify('while iterating (blocked)', function () {
whileIterating(this, blocked(() => this.db.close()));
normally(allowed(() => this.db.close()));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => this.db.close()));
normally(allowed(() => this.db.close()));
});
specify('while closed (allowed)', function () {
whileClosed(this, allowed(() => this.db.close()));
});
});
describe('Database#defaultSafeIntegers()', function () {
specify('while iterating (allowed)', function () {
let bool = true;
whileIterating(this, allowed(() => this.db.defaultSafeIntegers(bool = !bool)));
});
specify('while busy (allowed)', function () {
let bool = true;
whileBusy(this, allowed(() => this.db.defaultSafeIntegers(bool = !bool)));
});
specify('while closed (allowed)', function () {
let bool = true;
whileClosed(this, allowed(() => this.db.defaultSafeIntegers(bool = !bool)));
});
});
describe('Database#open', function () {
specify('while iterating (allowed)', function () {
whileIterating(this, allowed(() => expect(this.db.open).to.be.true));
});
specify('while busy (allowed)', function () {
whileBusy(this, allowed(() => expect(this.db.open).to.be.true));
});
specify('while closed (allowed)', function () {
whileClosed(this, allowed(() => expect(this.db.open).to.be.false));
});
});
describe('Database#inTransaction', function () {
specify('while iterating (allowed)', function () {
this.db.exec('BEGIN');
whileIterating(this, allowed(() => expect(this.db.inTransaction).to.be.true));
});
specify('while busy (allowed)', function () {
this.db.exec('BEGIN');
whileBusy(this, allowed(() => expect(this.db.inTransaction).to.be.true));
});
specify('while closed (allowed)', function () {
this.db.exec('BEGIN');
whileClosed(this, allowed(() => expect(this.db.inTransaction).to.be.false));
});
});
describe('Statement#run()', function () {
specify('while iterating (blocked)', function () {
whileIterating(this, blocked(() => this.writer.run()));
normally(allowed(() => this.writer.run()));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => this.writer.run()));
normally(allowed(() => this.writer.run()));
});
specify('while closed (blocked)', function () {
whileClosed(this, blocked(() => this.writer.run()));
});
});
describe('Statement#get()', function () {
specify('while iterating (allowed)', function () {
whileIterating(this, allowed(() => this.reader.get()));
normally(allowed(() => this.reader.get()));
});
specify('while self-iterating (blocked)', function () {
whileIterating(this, blocked(() => this.iterator.get()));
normally(allowed(() => this.iterator.get()));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => this.reader.get()));
normally(allowed(() => this.reader.get()));
});
specify('while closed (blocked)', function () {
whileClosed(this, blocked(() => this.reader.get()));
});
});
describe('Statement#all()', function () {
specify('while iterating (allowed)', function () {
whileIterating(this, allowed(() => this.reader.all()));
normally(allowed(() => this.reader.all()));
});
specify('while self-iterating (blocked)', function () {
whileIterating(this, blocked(() => this.iterator.all()));
normally(allowed(() => this.iterator.all()));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => this.reader.all()));
normally(allowed(() => this.reader.all()));
});
specify('while closed (blocked)', function () {
whileClosed(this, blocked(() => this.reader.all()));
});
});
describe('Statement#iterate()', function () {
specify('while iterating (allowed)', function () {
whileIterating(this, allowed(() => Array.from(this.reader.iterate())));
normally(allowed(() => Array.from(this.reader.iterate())));
});
specify('while self-iterating (blocked)', function () {
whileIterating(this, blocked(() => Array.from(this.iterator.iterate())));
normally(allowed(() => Array.from(this.iterator.iterate())));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => Array.from(this.reader.iterate())));
normally(allowed(() => Array.from(this.reader.iterate())));
});
specify('while closed (blocked)', function () {
whileClosed(this, blocked(() => Array.from(this.reader.iterate())));
});
});
describe('Statement#bind()', function () {
const bind = (stmt) => {
if (!stmt.__bound) {
stmt.bind();
stmt.__bound = true;
}
};
specify('while iterating (allowed)', function () {
whileIterating(this, allowed(() => bind(this.reader)));
});
specify('while self-iterating (blocked)', function () {
whileIterating(this, blocked(() => bind(this.iterator)));
normally(allowed(() => bind(this.iterator)));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => bind(this.reader)));
normally(allowed(() => bind(this.reader)));
});
specify('while closed (blocked)', function () {
whileClosed(this, blocked(() => bind(this.reader)));
});
});
describe('Statement#pluck()', function () {
specify('while iterating (allowed)', function () {
whileIterating(this, allowed(() => this.reader.pluck()));
normally(allowed(() => this.reader.pluck()));
});
specify('while self-iterating (blocked)', function () {
whileIterating(this, blocked(() => this.iterator.pluck()));
normally(allowed(() => this.iterator.pluck()));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => this.reader.pluck()));
normally(allowed(() => this.reader.pluck()));
});
specify('while closed (allowed)', function () {
whileClosed(this, allowed(() => this.reader.pluck()));
});
});
describe('Statement#expand()', function () {
specify('while iterating (allowed)', function () {
whileIterating(this, allowed(() => this.reader.expand()));
normally(allowed(() => this.reader.expand()));
});
specify('while self-iterating (blocked)', function () {
whileIterating(this, blocked(() => this.iterator.expand()));
normally(allowed(() => this.iterator.expand()));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => this.reader.expand()));
normally(allowed(() => this.reader.expand()));
});
specify('while closed (allowed)', function () {
whileClosed(this, allowed(() => this.reader.expand()));
});
});
describe('Statement#raw()', function () {
specify('while iterating (allowed)', function () {
whileIterating(this, allowed(() => this.reader.raw()));
normally(allowed(() => this.reader.raw()));
});
specify('while self-iterating (blocked)', function () {
whileIterating(this, blocked(() => this.iterator.raw()));
normally(allowed(() => this.iterator.raw()));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => this.reader.raw()));
normally(allowed(() => this.reader.raw()));
});
specify('while closed (allowed)', function () {
whileClosed(this, allowed(() => this.reader.raw()));
});
});
describe('Statement#safeIntegers()', function () {
specify('while iterating (allowed)', function () {
whileIterating(this, allowed(() => this.reader.safeIntegers()));
normally(allowed(() => this.reader.safeIntegers()));
});
specify('while self-iterating (blocked)', function () {
whileIterating(this, blocked(() => this.iterator.safeIntegers()));
normally(allowed(() => this.iterator.safeIntegers()));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => this.reader.safeIntegers()));
normally(allowed(() => this.reader.safeIntegers()));
});
specify('while closed (allowed)', function () {
whileClosed(this, allowed(() => this.reader.safeIntegers()));
});
});
describe('Statement#columns()', function () {
specify('while iterating (allowed)', function () {
whileIterating(this, allowed(() => this.reader.columns()));
normally(allowed(() => this.reader.columns()));
});
specify('while self-iterating (allowed)', function () {
whileIterating(this, allowed(() => this.iterator.columns()));
normally(allowed(() => this.iterator.columns()));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => this.reader.columns()));
normally(allowed(() => this.reader.columns()));
});
specify('while closed (blocked)', function () {
whileClosed(this, blocked(() => this.reader.columns()));
});
});
describe('StatementIterator#next()', function () {
specify('while iterating (allowed)', function () {
const iterator = this.reader.iterate();
try {
whileIterating(this, allowed(() => iterator.next()));
normally(allowed(() => iterator.next()));
} finally {
iterator.return();
}
});
specify('while self-iterating (allowed)', function () {
const iterator = this.iterator.iterate();
try {
let count = 0;
for (const _ of iterator) {
count += 1;
iterator.next();
}
expect(count).to.equal(3);
} finally {
iterator.return();
}
});
specify('while busy (blocked)', function () {
const iterator = this.reader.iterate();
try {
whileBusy(this, blocked(() => iterator.next()));
normally(allowed(() => iterator.next()));
} finally {
iterator.return();
}
});
specify('while closed (allowed)', function () {
const iterator = this.reader.iterate();
iterator.return();
whileClosed(this, allowed(() => iterator.next()));
});
});
describe('StatementIterator#return()', function () {
specify('while iterating (allowed)', function () {
const iterator = this.reader.iterate();
try {
whileIterating(this, allowed(() => iterator.return()));
normally(allowed(() => iterator.return()));
} finally {
iterator.return();
}
});
specify('while self-iterating (allowed)', function () {
const iterator = this.iterator.iterate();
try {
let count = 0;
for (const _ of iterator) {
count += 1;
iterator.return();
}
expect(count).to.equal(1);
} finally {
iterator.return();
}
});
specify('while busy (blocked)', function () {
const iterator = this.reader.iterate();
try {
whileBusy(this, blocked(() => iterator.return()));
normally(allowed(() => iterator.return()));
} finally {
iterator.return();
}
});
specify('while closed (allowed)', function () {
const iterator = this.reader.iterate();
iterator.return();
whileClosed(this, allowed(() => iterator.return()));
});
});
});