Files
electric-cherry/tests/unit/db-statements.test.ts
T
2026-10-09 17:05:06 -04:00

122 lines
5.4 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { newFixture, type Fixture } from '../helpers/fixture.js';
let fixture: Fixture;
beforeEach(async () => {
vi.resetModules();
fixture = await newFixture([]);
});
afterEach(async () => {
await fixture.cleanup();
vi.unstubAllEnvs();
});
describe('database statements', () => {
it('tunes the controller connection and stops periodic optimization when closed', async () => {
const { db, closeDb } = await import('../../src/lib/server/db/index.js');
vi.useFakeTimers({ toFake: ['setInterval', 'clearInterval'] });
try {
const handle = db();
expect(handle.pragma('cache_size', { simple: true })).toBe(-131072);
expect(handle.pragma('temp_store', { simple: true })).toBe(1);
expect(handle.pragma('journal_size_limit', { simple: true })).toBe(67108864);
const pragma = vi.spyOn(handle, 'pragma');
vi.advanceTimersByTime(60 * 60_000);
expect(pragma).toHaveBeenCalledWith('optimize');
pragma.mockClear();
closeDb();
vi.advanceTimersByTime(60 * 60_000);
expect(pragma).not.toHaveBeenCalled();
} finally {
vi.useRealTimers();
}
});
it('keeps caller-owned statements reusable with fresh parameters after rollback', async () => {
const { db } = await import('../../src/lib/server/db/index.js');
const insertSql = 'INSERT INTO settings (key, value) VALUES (?, ?)';
const selectSql = 'SELECT value FROM settings WHERE key = ?';
const insert = db().prepare(insertSql);
const select = db().prepare(selectSql);
insert.run('cache-first', 'one');
expect(() =>
db().transaction(() => {
db().prepare(insertSql).run('cache-rollback', 'discarded');
throw new Error('rollback');
})()
).toThrow('rollback');
db().prepare(insertSql).run('cache-second', 'two');
expect(db().prepare(insertSql)).not.toBe(insert);
expect(db().prepare(selectSql)).not.toBe(select);
expect(select.get('cache-first')).toEqual({ value: 'one' });
expect(select.get('cache-second')).toEqual({ value: 'two' });
expect(select.get('cache-rollback')).toBeUndefined();
});
it('prepares fresh statements when the connection is reopened', async () => {
const { db, closeDb } = await import('../../src/lib/server/db/index.js');
const sql = 'SELECT value FROM settings WHERE key = ?';
const oldStatement = db().prepare(sql);
db()
.prepare('INSERT INTO settings (key, value) VALUES (?, ?)')
.run('cache-persisted', 'saved');
closeDb();
const statement = db().prepare(sql);
expect(statement).not.toBe(oldStatement);
expect(statement.database).not.toBe(oldStatement.database);
expect(statement.get('cache-persisted')).toEqual({ value: 'saved' });
expect(db().prepare(sql)).not.toBe(statement);
});
describe.each([0, 20])('statement isolation with EC_SLOW_SQL_MS=%i', (slowMs) => {
beforeEach(() => vi.stubEnv('EC_SLOW_SQL_MS', String(slowMs)));
it.each([
['raw', [1]],
['pluck', 1],
['expand', { $: { value: 1 } }],
['safeIntegers', { value: 1n }]
] as const)('keeps %s mode local to its statement', async (mode, expected) => {
const { db } = await import('../../src/lib/server/db/index.js');
const sql = 'SELECT 1 AS value';
const customized = db().prepare(sql)[mode]();
const standard = db().prepare(sql);
expect(standard.get()).toEqual({ value: 1 });
expect(customized.get()).toEqual(expected);
customized[mode](false);
standard[mode]();
expect(customized.get()).toEqual({ value: 1 });
expect(standard.get()).toEqual(expected);
expect(db().prepare(sql).get()).toEqual({ value: 1 });
});
it('keeps permanent bindings local to their statement', async () => {
const { db } = await import('../../src/lib/server/db/index.js');
const sql = 'SELECT ? AS value';
const bound = db().prepare(sql).bind('first');
expect(db().prepare(sql).get('second')).toEqual({ value: 'second' });
expect(bound.get()).toEqual({ value: 'first' });
expect(db().prepare(sql).bind('third').get()).toEqual({ value: 'third' });
expect(bound.get()).toEqual({ value: 'first' });
});
it('allows identical queries while another statement is being iterated', async () => {
const { db } = await import('../../src/lib/server/db/index.js');
const sql = 'SELECT 1 AS value UNION ALL SELECT 2 AS value';
const iterator = db().prepare(sql).iterate();
try {
expect(iterator.next()).toEqual({ value: { value: 1 }, done: false });
expect(db().prepare(sql).get()).toEqual({ value: 1 });
expect([...db().prepare(sql).iterate()]).toEqual([{ value: 1 }, { value: 2 }]);
expect(iterator.next()).toEqual({ value: { value: 2 }, done: false });
expect(iterator.next().done).toBe(true);
} finally {
iterator.return?.();
}
});
});
});