import { describe, it, expect, beforeEach } from 'vitest'; import { InMemorySessionRepository } from '../../../apps/companion/main/automation/infrastructure/repositories/InMemorySessionRepository'; import { AutomationSession } from 'apps/companion/main/automation/domain/entities/AutomationSession'; import { StepId } from 'apps/companion/main/automation/domain/value-objects/StepId'; describe('InMemorySessionRepository Integration Tests', () => { let repository: InMemorySessionRepository; beforeEach(() => { repository = new InMemorySessionRepository(); }); describe('save', () => { it('should persist a new session', async () => { const session = AutomationSession.create({ sessionName: 'Test Race', trackId: 'spa', carIds: ['dallara-f3'], }); await repository.save(session); const retrieved = await repository.findById(session.id); expect(retrieved).toBeDefined(); expect(retrieved?.id).toBe(session.id); }); it('should update existing session on duplicate save', async () => { const session = AutomationSession.create({ sessionName: 'Test Race', trackId: 'spa', carIds: ['dallara-f3'], }); await repository.save(session); session.start(); session.transitionToStep(StepId.create(2)); await repository.save(session); const retrieved = await repository.findById(session.id); expect(retrieved?.currentStep.value).toBe(2); expect(retrieved?.state.isInProgress()).toBe(true); }); it('should preserve all session properties', async () => { const session = AutomationSession.create({ sessionName: 'Test Race Session', trackId: 'spa-francorchamps', carIds: ['dallara-f3', 'porsche-911-gt3'], }); await repository.save(session); const retrieved = await repository.findById(session.id); expect(retrieved?.config.sessionName).toBe('Test Race Session'); expect(retrieved?.config.trackId).toBe('spa-francorchamps'); expect(retrieved?.config.carIds).toEqual(['dallara-f3', 'porsche-911-gt3']); }); }); describe('findById', () => { it('should return null for non-existent session', async () => { const result = await repository.findById('non-existent-id'); expect(result).toBeNull(); }); it('should retrieve existing session by ID', async () => { const session = AutomationSession.create({ sessionName: 'Test Race', trackId: 'spa', carIds: ['dallara-f3'], }); await repository.save(session); const retrieved = await repository.findById(session.id); expect(retrieved).toBeDefined(); expect(retrieved?.id).toBe(session.id); }); it('should return domain entity not DTO', async () => { const session = AutomationSession.create({ sessionName: 'Test Race', trackId: 'spa', carIds: ['dallara-f3'], }); await repository.save(session); const retrieved = await repository.findById(session.id); expect(retrieved).toBeInstanceOf(AutomationSession); }); it('should retrieve session with correct state', async () => { const session = AutomationSession.create({ sessionName: 'Test Race', trackId: 'spa', carIds: ['dallara-f3'], }); session.start(); await repository.save(session); const retrieved = await repository.findById(session.id); expect(retrieved?.state.isInProgress()).toBe(true); expect(retrieved?.startedAt).toBeDefined(); }); }); describe('update', () => { it('should update existing session', async () => { const session = AutomationSession.create({ sessionName: 'Test Race', trackId: 'spa', carIds: ['dallara-f3'], }); await repository.save(session); session.start(); session.transitionToStep(StepId.create(2)); await repository.update(session); const retrieved = await repository.findById(session.id); expect(retrieved?.currentStep.value).toBe(2); }); it('should throw error when updating non-existent session', async () => { const session = AutomationSession.create({ sessionName: 'Test Race', trackId: 'spa', carIds: ['dallara-f3'], }); await expect(repository.update(session)).rejects.toThrow('Session not found'); }); it('should preserve unchanged properties', async () => { const session = AutomationSession.create({ sessionName: 'Original Name', trackId: 'spa', carIds: ['dallara-f3'], }); await repository.save(session); session.start(); await repository.update(session); const retrieved = await repository.findById(session.id); expect(retrieved?.config.sessionName).toBe('Original Name'); expect(retrieved?.state.isInProgress()).toBe(true); }); it('should update session state correctly', async () => { const session = AutomationSession.create({ sessionName: 'Test Race', trackId: 'spa', carIds: ['dallara-f3'], }); await repository.save(session); session.start(); session.pause(); await repository.update(session); const retrieved = await repository.findById(session.id); expect(retrieved?.state.value).toBe('PAUSED'); }); }); describe('delete', () => { it('should remove session from storage', async () => { const session = AutomationSession.create({ sessionName: 'Test Race', trackId: 'spa', carIds: ['dallara-f3'], }); await repository.save(session); await repository.delete(session.id); const retrieved = await repository.findById(session.id); expect(retrieved).toBeNull(); }); it('should not throw when deleting non-existent session', async () => { await expect(repository.delete('non-existent-id')).resolves.not.toThrow(); }); it('should only delete specified session', async () => { const session1 = AutomationSession.create({ sessionName: 'Race 1', trackId: 'spa', carIds: ['dallara-f3'], }); const session2 = AutomationSession.create({ sessionName: 'Race 2', trackId: 'monza', carIds: ['porsche-911-gt3'], }); await repository.save(session1); await repository.save(session2); await repository.delete(session1.id); const retrieved1 = await repository.findById(session1.id); const retrieved2 = await repository.findById(session2.id); expect(retrieved1).toBeNull(); expect(retrieved2).toBeDefined(); }); }); describe('findAll', () => { it('should return empty array when no sessions exist', async () => { const sessions = await repository.findAll(); expect(sessions).toEqual([]); }); it('should return all saved sessions', async () => { const session1 = AutomationSession.create({ sessionName: 'Race 1', trackId: 'spa', carIds: ['dallara-f3'], }); const session2 = AutomationSession.create({ sessionName: 'Race 2', trackId: 'monza', carIds: ['porsche-911-gt3'], }); await repository.save(session1); await repository.save(session2); const sessions = await repository.findAll(); expect(sessions).toHaveLength(2); expect(sessions.map(s => s.id)).toContain(session1.id); expect(sessions.map(s => s.id)).toContain(session2.id); }); it('should return domain entities not DTOs', async () => { const session = AutomationSession.create({ sessionName: 'Test Race', trackId: 'spa', carIds: ['dallara-f3'], }); await repository.save(session); const sessions = await repository.findAll(); expect(sessions[0]).toBeInstanceOf(AutomationSession); }); }); describe('findByState', () => { it('should return sessions matching state', async () => { const session1 = AutomationSession.create({ sessionName: 'Race 1', trackId: 'spa', carIds: ['dallara-f3'], }); session1.start(); const session2 = AutomationSession.create({ sessionName: 'Race 2', trackId: 'monza', carIds: ['porsche-911-gt3'], }); await repository.save(session1); await repository.save(session2); const inProgressSessions = await repository.findByState('IN_PROGRESS'); expect(inProgressSessions).toHaveLength(1); expect(inProgressSessions[0]!.id).toBe(session1.id); }); it('should return empty array when no sessions match state', async () => { const session = AutomationSession.create({ sessionName: 'Test Race', trackId: 'spa', carIds: ['dallara-f3'], }); await repository.save(session); const completedSessions = await repository.findByState('COMPLETED'); expect(completedSessions).toEqual([]); }); it('should handle multiple sessions with same state', async () => { const session1 = AutomationSession.create({ sessionName: 'Race 1', trackId: 'spa', carIds: ['dallara-f3'], }); const session2 = AutomationSession.create({ sessionName: 'Race 2', trackId: 'monza', carIds: ['porsche-911-gt3'], }); await repository.save(session1); await repository.save(session2); const pendingSessions = await repository.findByState('PENDING'); expect(pendingSessions).toHaveLength(2); }); }); describe('concurrent operations', () => { it('should handle concurrent saves', async () => { const sessions = Array.from({ length: 10 }, (_, i) => AutomationSession.create({ sessionName: `Race ${i}`, trackId: 'spa', carIds: ['dallara-f3'], }) ); await Promise.all(sessions.map(s => repository.save(s))); const allSessions = await repository.findAll(); expect(allSessions).toHaveLength(10); }); it('should handle concurrent updates', async () => { const session = AutomationSession.create({ sessionName: 'Test Race', trackId: 'spa', carIds: ['dallara-f3'], }); await repository.save(session); session.start(); await Promise.all([ repository.update(session), repository.update(session), repository.update(session), ]); const retrieved = await repository.findById(session.id); expect(retrieved?.state.isInProgress()).toBe(true); }); }); });