/** * SqliteAdapter Unit Tests – init, CRUD for all entities, close */ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { SqliteAdapter } from '../src/database/SqliteAdapter.js'; let idCounter = 0; function uniqueId(prefix: string): string { idCounter++; return `${prefix}-${Date.now()}-${idCounter}`; } describe('SqliteAdapter', () => { let db: SqliteAdapter; beforeAll(async () => { db = new SqliteAdapter(':memory:'); await db.init(); }); afterAll(() => { db.close(); }); // ─── Init & Close ─────────────────────────────────── describe('init() and close()', () => { it('should initialize the database without errors', async () => { const testDb = new SqliteAdapter(':memory:'); await testDb.init(); const users = testDb.listUsers(); expect(Array.isArray(users)).toBe(true); testDb.close(); }); it('should seed a default user on init', async () => { const defaultUser = db.getUser('user-default'); expect(defaultUser).not.toBeNull(); expect(defaultUser!.email).toBe('default@webcad.local'); expect(defaultUser!.role).toBe('admin'); }); }); // ─── Users CRUD ───────────────────────────────────── describe('Users CRUD', () => { it('should create and get a user', () => { const uid = uniqueId('user'); const user = db.createUser({ id: uid, email: `${uid}@example.com`, password_hash: 'hash123', name: 'CRUD User', role: 'planer', }); expect(user.id).toBe(uid); expect(user.email).toBe(`${uid}@example.com`); const fetched = db.getUser(user.id); expect(fetched).not.toBeNull(); expect(fetched!.email).toBe(`${uid}@example.com`); }); it('should get user by email', () => { const uid = uniqueId('user'); db.createUser({ id: uid, email: `${uid}@example.com`, password_hash: 'hash', name: 'Email User', role: 'planer' }); const user = db.getUserByEmail(`${uid}@example.com`); expect(user).not.toBeNull(); expect(user!.name).toBe('Email User'); }); it('should return null for non-existent user', () => { expect(db.getUser('non-existent-id')).toBeNull(); expect(db.getUserByEmail('nobody@nowhere.com')).toBeNull(); }); it('should update a user', () => { const uid = uniqueId('user'); db.createUser({ id: uid, email: `${uid}@example.com`, password_hash: 'hash', name: 'Original Name', role: 'planer' }); const updated = db.updateUser(uid, { name: 'Updated Name', role: 'admin' }); expect(updated).not.toBeNull(); expect(updated!.name).toBe('Updated Name'); expect(updated!.role).toBe('admin'); }); it('should list users', () => { const users = db.listUsers(); expect(users.length).toBeGreaterThanOrEqual(2); }); it('should delete a user', () => { const uid = uniqueId('user'); db.createUser({ id: uid, email: `${uid}@example.com`, password_hash: 'hash', name: 'Delete Me', role: 'planer' }); const ok = db.deleteUser(uid); expect(ok).toBe(true); expect(db.getUser(uid)).toBeNull(); }); it('should return false when deleting non-existent user', () => { expect(db.deleteUser('non-existent-id')).toBe(false); }); }); // ─── Projects CRUD ────────────────────────────────── describe('Projects CRUD', () => { it('should create and get a project', () => { const pid = uniqueId('proj'); const project = db.createProject({ id: pid, name: 'Test Project', description: 'Test desc' }); expect(project.id).toBe(pid); expect(project.name).toBe('Test Project'); const fetched = db.getProject(pid); expect(fetched).not.toBeNull(); expect(fetched!.name).toBe('Test Project'); }); it('should return null for non-existent project', () => { expect(db.getProject('non-existent-id')).toBeNull(); }); it('should update a project', () => { const pid = uniqueId('proj'); db.createProject({ id: pid, name: 'Update Project' }); const updated = db.updateProject(pid, { name: 'Updated Project', description: 'New desc' }); expect(updated).not.toBeNull(); expect(updated!.name).toBe('Updated Project'); expect(updated!.description).toBe('New desc'); }); it('should list projects', () => { const projects = db.listProjects(); expect(projects.length).toBeGreaterThanOrEqual(1); }); it('should delete a project', () => { const pid = uniqueId('proj'); db.createProject({ id: pid, name: 'Delete Project' }); const ok = db.deleteProject(pid); expect(ok).toBe(true); expect(db.getProject(pid)).toBeNull(); }); it('should cascade delete drawings when project is deleted', () => { const pid = uniqueId('proj'); const did = uniqueId('draw'); db.createProject({ id: pid, name: 'Cascade Project' }); db.createDrawing({ id: did, project_id: pid, name: 'Cascade Drawing' }); db.deleteProject(pid); expect(db.getDrawing(did)).toBeNull(); }); }); // ─── Drawings CRUD ────────────────────────────────── describe('Drawings CRUD', () => { let projectId: string; it('should create and get a drawing', () => { projectId = uniqueId('proj'); const did = uniqueId('draw'); db.createProject({ id: projectId, name: 'Drawing Project' }); const drawing = db.createDrawing({ id: did, project_id: projectId, name: 'Floor 1' }); expect(drawing.id).toBe(did); expect(drawing.name).toBe('Floor 1'); expect(drawing.project_id).toBe(projectId); const fetched = db.getDrawing(did); expect(fetched).not.toBeNull(); expect(fetched!.name).toBe('Floor 1'); }); it('should list drawings by project', () => { const did1 = uniqueId('draw'); const did2 = uniqueId('draw'); db.createDrawing({ id: did1, project_id: projectId, name: 'Drawing A' }); db.createDrawing({ id: did2, project_id: projectId, name: 'Drawing B' }); const drawings = db.listDrawings(projectId); expect(drawings.length).toBeGreaterThanOrEqual(3); }); it('should update a drawing', () => { const did = uniqueId('draw'); db.createDrawing({ id: did, project_id: projectId, name: 'Update Me' }); const updated = db.updateDrawing(did, { name: 'Updated Drawing', data_json: '{"x":1}' }); expect(updated).not.toBeNull(); expect(updated!.name).toBe('Updated Drawing'); expect(updated!.data_json).toBe('{"x":1}'); }); it('should delete a drawing', () => { const did = uniqueId('draw'); db.createDrawing({ id: did, project_id: projectId, name: 'Delete Me' }); const ok = db.deleteDrawing(did); expect(ok).toBe(true); expect(db.getDrawing(did)).toBeNull(); }); it('should cascade delete layers when drawing is deleted', () => { const did = uniqueId('draw'); const lid = uniqueId('layer'); db.createDrawing({ id: did, project_id: projectId, name: 'Cascade Drawing' }); db.createLayer({ id: lid, drawing_id: did, name: 'Cascade Layer' }); db.deleteDrawing(did); const layers = db.listLayers(did); expect(layers.find((l) => l.id === lid)).toBeUndefined(); }); }); // ─── Layers CRUD ──────────────────────────────────── describe('Layers CRUD', () => { let drawingId: string; it('should create and get a layer', () => { const pid = uniqueId('proj'); drawingId = uniqueId('draw'); const lid = uniqueId('layer'); db.createProject({ id: pid, name: 'Layer Project' }); db.createDrawing({ id: drawingId, project_id: pid, name: 'Layer Drawing' }); const layer = db.createLayer({ id: lid, drawing_id: drawingId, name: 'Layer 1', color: '#ff0000' }); expect(layer.id).toBe(lid); expect(layer.name).toBe('Layer 1'); expect(layer.color).toBe('#ff0000'); expect(layer.visible).toBe(1); expect(layer.locked).toBe(0); }); it('should list layers by drawing', () => { const lid2 = uniqueId('layer'); db.createLayer({ id: lid2, drawing_id: drawingId, name: 'Layer 2' }); const layers = db.listLayers(drawingId); expect(layers.length).toBeGreaterThanOrEqual(2); }); it('should update a layer', () => { const lid = uniqueId('layer'); db.createLayer({ id: lid, drawing_id: drawingId, name: 'Update Layer' }); const updated = db.updateLayer(lid, { name: 'Updated Layer', visible: 0, locked: 1 }); expect(updated).not.toBeNull(); expect(updated!.name).toBe('Updated Layer'); expect(updated!.visible).toBe(0); expect(updated!.locked).toBe(1); }); it('should delete a layer', () => { const lid = uniqueId('layer'); db.createLayer({ id: lid, drawing_id: drawingId, name: 'Delete Layer' }); const ok = db.deleteLayer(lid); expect(ok).toBe(true); const layers = db.listLayers(drawingId); expect(layers.find((l) => l.id === lid)).toBeUndefined(); }); }); // ─── Elements CRUD ────────────────────────────────── describe('Elements CRUD', () => { let drawingId: string; let layerId: string; it('should create and get an element', () => { const pid = uniqueId('proj'); drawingId = uniqueId('draw'); layerId = uniqueId('layer'); const eid = uniqueId('elem'); db.createProject({ id: pid, name: 'Element Project' }); db.createDrawing({ id: drawingId, project_id: pid, name: 'Element Drawing' }); db.createLayer({ id: layerId, drawing_id: drawingId, name: 'Element Layer' }); const element = db.createElement({ id: eid, drawing_id: drawingId, layer_id: layerId, type: 'rect', x: 10, y: 20, width: 100, height: 50, }); expect(element.id).toBe(eid); expect(element.type).toBe('rect'); expect(element.x).toBe(10); expect(element.y).toBe(20); }); it('should list elements by drawing', () => { const eid2 = uniqueId('elem'); db.createElement({ id: eid2, drawing_id: drawingId, layer_id: layerId, type: 'circle' }); const elements = db.listElements(drawingId); expect(elements.length).toBeGreaterThanOrEqual(2); }); it('should update an element', () => { const eid = uniqueId('elem'); db.createElement({ id: eid, drawing_id: drawingId, layer_id: layerId, type: 'line' }); const updated = db.updateElement(eid, { x: 500, y: 600, width: 200 }); expect(updated).not.toBeNull(); expect(updated!.x).toBe(500); expect(updated!.y).toBe(600); expect(updated!.width).toBe(200); }); it('should delete an element', () => { const eid = uniqueId('elem'); db.createElement({ id: eid, drawing_id: drawingId, layer_id: layerId, type: 'text' }); const ok = db.deleteElement(eid); expect(ok).toBe(true); const elements = db.listElements(drawingId); expect(elements.find((e) => e.id === eid)).toBeUndefined(); }); }); // ─── Blocks CRUD ──────────────────────────────────── describe('Blocks CRUD', () => { let drawingId: string; it('should create and get a block', () => { const pid = uniqueId('proj'); drawingId = uniqueId('draw'); const bid = uniqueId('block'); db.createProject({ id: pid, name: 'Block Project' }); db.createDrawing({ id: drawingId, project_id: pid, name: 'Block Drawing' }); const block = db.createBlock({ id: bid, drawing_id: drawingId, name: 'Table Block', category: 'Mobiliar' }); expect(block.id).toBe(bid); expect(block.name).toBe('Table Block'); expect(block.category).toBe('Mobiliar'); expect(block.elements_json).toBe('[]'); }); it('should list blocks by drawing', () => { const bid2 = uniqueId('block'); db.createBlock({ id: bid2, drawing_id: drawingId, name: 'Chair Block' }); const blocks = db.listBlocks(drawingId); expect(blocks.length).toBeGreaterThanOrEqual(2); }); it('should update a block', () => { const bid = uniqueId('block'); db.createBlock({ id: bid, drawing_id: drawingId, name: 'Update Block' }); const updated = db.updateBlock(bid, { name: 'Updated Block', category: 'Bühne' }); expect(updated).not.toBeNull(); expect(updated!.name).toBe('Updated Block'); expect(updated!.category).toBe('Bühne'); }); it('should delete a block', () => { const bid = uniqueId('block'); db.createBlock({ id: bid, drawing_id: drawingId, name: 'Delete Block' }); const ok = db.deleteBlock(bid); expect(ok).toBe(true); const blocks = db.listBlocks(drawingId); expect(blocks.find((b) => b.id === bid)).toBeUndefined(); }); }); // ─── Settings CRUD ────────────────────────────────── describe('Settings CRUD', () => { it('should set and get a setting', () => { db.setSetting('test-key', 'test-value'); const setting = db.getSetting('test-key'); expect(setting).not.toBeNull(); expect(setting!.key).toBe('test-key'); expect(setting!.value).toBe('test-value'); expect(setting!.updated_at).toBeDefined(); }); it('should return null for non-existent setting', () => { expect(db.getSetting('non-existent-key')).toBeNull(); }); it('should upsert (update existing setting)', () => { db.setSetting('upsert-key', 'initial'); db.setSetting('upsert-key', 'updated'); const setting = db.getSetting('upsert-key'); expect(setting).not.toBeNull(); expect(setting!.value).toBe('updated'); }); }); });