import { Database } from 'bun:sqlite'; import { initializeDatabase } from '../../src/db'; import { describe, test, expect, beforeEach, afterAll, beforeAll } from 'bun:test'; describe('Database', () => { let testDb: Database; // Create an in-memory database before any tests run beforeAll(() => { testDb = new Database(':memory:'); }); // Close the database connection after all tests have run afterAll(() => { testDb.close(); }); beforeEach(() => { // Reset database schema between tests by dropping tables and re-initializing testDb.exec('DROP TABLE IF EXISTS tasks'); testDb.exec('DROP TABLE IF EXISTS task_assignments'); testDb.exec('DROP TABLE IF EXISTS users'); testDb.exec('DROP TABLE IF EXISTS groups'); // Initialize schema on the test database instance initializeDatabase(testDb); }); describe('Table Creation', () => { test('should create all required tables', () => { const tables = testDb .query("SELECT name FROM sqlite_master WHERE type='table'") .all() .map((t: any) => t.name); const expectedTables = ['groups', 'task_assignments', 'tasks', 'users']; const userTables = tables.filter(t => !t.startsWith('sqlite_')); expect(userTables.sort()).toEqual(expectedTables.sort()); }); }); describe('Table Schemas', () => { test('users table should have correct columns', () => { const columns = testDb .query("PRAGMA table_info(users)") .all() .map((c: any) => c.name); expect(columns).toEqual(['id', 'first_seen', 'last_seen']); }); test('tasks table should have required columns', () => { const columns = testDb .query("PRAGMA table_info(tasks)") .all() .map((c: any) => c.name); expect(columns).toContain('description'); expect(columns).toContain('due_date'); expect(columns).toContain('group_id'); expect(columns).toContain('created_by'); }); test('groups table should have active flag default to true', () => { testDb.exec(` INSERT INTO groups (id, community_id, name) VALUES ('test-group', 'test-community', 'Test Group') `); const group = testDb.query("SELECT active FROM groups WHERE id = 'test-group'").get(); expect(group.active).toBe(1); // SQLite uses 1 for TRUE }); }); describe('Foreign Keys', () => { test('task_assignments should reference tasks', () => { const fkInfo = testDb .query("PRAGMA foreign_key_list(task_assignments)") .all(); expect(fkInfo.length).toBe(1); expect(fkInfo[0].from).toBe('task_id'); expect(fkInfo[0].to).toBe('id'); expect(fkInfo[0].table).toBe('tasks'); }); test('tasks should reference users via created_by', () => { const fkInfo = testDb .query("PRAGMA foreign_key_list(tasks)") .all(); expect(fkInfo.length).toBe(1); expect(fkInfo[0].from).toBe('created_by'); expect(fkInfo[0].to).toBe('id'); expect(fkInfo[0].table).toBe('users'); }); }); describe('User Operations', () => { test('should reject duplicate user IDs', () => { // First insert should succeed const firstInsert = testDb.prepare(` INSERT INTO users (id) VALUES ('34650112233') `).run(); expect(firstInsert.changes).toBe(1); // Second insert with same ID should fail due to PRIMARY KEY constraint expect(() => { testDb.prepare(` INSERT INTO users (id) VALUES ('34650112233') `).run(); }).toThrow(); // Bun's SQLite driver throws an error on constraint violation // Verify only one record exists const countResult = testDb.prepare(` SELECT COUNT(*) as count FROM users WHERE id = '34650112233' `).get(); expect(countResult.count).toBe(1); }); }); describe('Data Operations', () => { beforeEach(() => { // Seed necessary data for these tests into the test database testDb.exec(` INSERT INTO users (id) VALUES ('34650112233'); INSERT INTO groups (id, community_id, name) VALUES ('test-group', 'test-community', 'Test Group') `); }); test('should allow inserting group tasks', () => { const result = testDb.prepare(` INSERT INTO tasks (description, created_by, group_id) VALUES ('Test task', '34650112233', 'test-group') `).run(); expect(result.changes).toBe(1); }); test('should allow inserting private tasks', () => { const result = testDb.prepare(` INSERT INTO tasks (description, created_by) VALUES ('Private task', '34650112233') `).run(); expect(result.changes).toBe(1); }); }); });