import { describe, it, expect, beforeEach, afterAll } from 'vitest'; import fs from 'fs'; import path from 'path'; import { Score } from '../../shared/types.js'; const DATA_DIR = path.join(process.cwd(), 'server', 'data'); const SESSION_FILE = path.join(DATA_DIR, 'session.json'); const SEED_FILE = path.join(DATA_DIR, 'seed.json'); const BACKUP_FILE = path.join(DATA_DIR, 'session.json.bak'); // Import the store module — initialization runs on import import * as store from '../store.js'; describe('server/store', () => { // Reset session.json from seed before each test for isolation beforeEach(() => { const seedData = fs.readFileSync(SEED_FILE, 'utf-8'); fs.writeFileSync(SESSION_FILE, seedData, 'utf-8'); // Remove backup if it exists if (fs.existsSync(BACKUP_FILE)) { fs.unlinkSync(BACKUP_FILE); } }); // Clean up after all tests afterAll(() => { // Restore session.json from seed const seedData = fs.readFileSync(SEED_FILE, 'utf-8'); fs.writeFileSync(SESSION_FILE, seedData, 'utf-8'); if (fs.existsSync(BACKUP_FILE)) { fs.unlinkSync(BACKUP_FILE); } }); it('getStore() returns valid StoreData', () => { const data = store.getStore(); expect(data).toHaveProperty('session'); expect(data).toHaveProperty('teams'); expect(data).toHaveProperty('jurors'); expect(data).toHaveProperty('scores'); expect(data.session.id).toBe('unikat-2026'); }); it('getStore() returns seed data structure', () => { const data = store.getStore(); expect(data.teams).toHaveLength(11); expect(data.jurors).toHaveLength(6); expect(data.scores).toEqual([]); }); it('saveStore() creates a .bak backup', () => { const data = store.getStore(); store.saveStore(data); expect(fs.existsSync(BACKUP_FILE)).toBe(true); }); it('saveStore() writes data atomically (persists changes)', () => { const data = store.getStore(); data.session.status = 'active'; store.saveStore(data); const reloaded = store.getStore(); expect(reloaded.session.status).toBe('active'); }); it('saveStore() backup contains previous data', () => { const originalData = store.getStore(); const originalStatus = originalData.session.status; originalData.session.status = 'active'; store.saveStore(originalData); const backupContent = JSON.parse(fs.readFileSync(BACKUP_FILE, 'utf-8')); expect(backupContent.session.status).toBe(originalStatus); }); it('getSession() returns the session', () => { const session = store.getSession(); expect(session.id).toBe('unikat-2026'); expect(session.status).toBe('setup'); expect(session.config.criteria).toHaveLength(5); }); it('getTeams() returns all teams', () => { const teams = store.getTeams(); expect(teams).toHaveLength(11); expect(teams[0]).toHaveProperty('id'); expect(teams[0]).toHaveProperty('name'); expect(teams[0]).toHaveProperty('number'); }); it('getJurors() returns all jurors', () => { const jurors = store.getJurors(); expect(jurors).toHaveLength(6); expect(jurors[0]).toHaveProperty('id'); expect(jurors[0]).toHaveProperty('name'); expect(jurors[0]).toHaveProperty('active'); }); it('getScores() returns empty scores array initially', () => { const scores = store.getScores(); expect(Array.isArray(scores)).toBe(true); expect(scores).toHaveLength(0); }); it('addScore() adds a new score', () => { const score: Score = { jurorId: 'finke', teamId: 'dual', criterionId: 'gesamtidee', value: 8, updatedAt: new Date().toISOString(), }; store.addScore(score); const scores = store.getScores(); expect(scores).toHaveLength(1); expect(scores[0].jurorId).toBe('finke'); expect(scores[0].teamId).toBe('dual'); expect(scores[0].criterionId).toBe('gesamtidee'); expect(scores[0].value).toBe(8); }); it('addScore() updates existing score (match by jurorId+teamId+criterionId)', () => { const score1: Score = { jurorId: 'finke', teamId: 'dual', criterionId: 'gesamtidee', value: 7, updatedAt: '2026-05-19T10:00:00Z', }; store.addScore(score1); const score2: Score = { jurorId: 'finke', teamId: 'dual', criterionId: 'gesamtidee', value: 9, updatedAt: '2026-05-19T10:01:00Z', }; store.addScore(score2); const scores = store.getScores(); const matching = scores.filter( (s) => s.jurorId === 'finke' && s.teamId === 'dual' && s.criterionId === 'gesamtidee' ); expect(matching).toHaveLength(1); expect(matching[0].value).toBe(9); expect(matching[0].updatedAt).toBe('2026-05-19T10:01:00Z'); }); it('addScore() does not affect scores for different criteria', () => { const score1: Score = { jurorId: 'finke', teamId: 'dual', criterionId: 'gesamtidee', value: 7, updatedAt: '2026-05-19T10:00:00Z', }; const score2: Score = { jurorId: 'finke', teamId: 'dual', criterionId: 'markt', value: 9, updatedAt: '2026-05-19T10:01:00Z', }; store.addScore(score1); store.addScore(score2); const scores = store.getScores(); expect(scores).toHaveLength(2); }); it('updateSession() merges partial updates', () => { const updated = store.updateSession({ status: 'active', startedAt: '2026-05-19T18:00:00Z', }); expect(updated.status).toBe('active'); expect(updated.startedAt).toBe('2026-05-19T18:00:00Z'); expect(updated.id).toBe('unikat-2026'); expect(updated.config.criteria).toHaveLength(5); }); it('updateSession() persists changes', () => { store.updateSession({ status: 'active' }); const session = store.getSession(); expect(session.status).toBe('active'); }); it('updateJuror() updates a juror by ID', () => { const updated = store.updateJuror('finke', { active: true }); expect(updated).not.toBeNull(); expect(updated!.active).toBe(true); expect(updated!.name).toBe('Finke'); expect(updated!.id).toBe('finke'); }); it('updateJuror() persists changes', () => { store.updateJuror('finke', { active: true }); const jurors = store.getJurors(); const finke = jurors.find((j) => j.id === 'finke'); expect(finke!.active).toBe(true); }); it('updateJuror() returns null for unknown jurorId', () => { const result = store.updateJuror('unknown-juror', { active: true }); expect(result).toBeNull(); }); it('initializes from seed.json if session.json does not exist', () => { // Delete session.json fs.unlinkSync(SESSION_FILE); expect(fs.existsSync(SESSION_FILE)).toBe(false); // Manually call the initialization logic by writing seed const seedData = fs.readFileSync(SEED_FILE, 'utf-8'); fs.writeFileSync(SESSION_FILE, seedData, 'utf-8'); // Verify the store reads correctly const data = store.getStore(); expect(data.session.id).toBe('unikat-2026'); expect(data.teams).toHaveLength(11); expect(data.jurors).toHaveLength(6); }); });