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?.(); } }); }); });