import { describe, it, expect } from 'vitest'; import { HealthViewDataBuilder, HealthDTO } from './HealthViewDataBuilder'; describe('HealthViewDataBuilder', () => { describe('happy paths', () => { it('should transform HealthDTO to HealthViewData correctly', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), uptime: 99.95, responseTime: 150, errorRate: 0.05, lastCheck: new Date().toISOString(), checksPassed: 995, checksFailed: 5, components: [ { name: 'Database', status: 'ok', lastCheck: new Date().toISOString(), responseTime: 50, errorRate: 0.01, }, { name: 'API', status: 'ok', lastCheck: new Date().toISOString(), responseTime: 100, errorRate: 0.02, }, ], alerts: [ { id: 'alert-1', type: 'info', title: 'System Update', message: 'System updated successfully', timestamp: new Date().toISOString(), }, ], }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.overallStatus.status).toBe('ok'); expect(result.overallStatus.statusLabel).toBe('Healthy'); expect(result.overallStatus.statusColor).toBe('#10b981'); expect(result.overallStatus.statusIcon).toBe('✓'); expect(result.metrics.uptime).toBe('99.95%'); expect(result.metrics.responseTime).toBe('150ms'); expect(result.metrics.errorRate).toBe('0.05%'); expect(result.metrics.checksPassed).toBe(995); expect(result.metrics.checksFailed).toBe(5); expect(result.metrics.totalChecks).toBe(1000); expect(result.metrics.successRate).toBe('99.5%'); expect(result.components).toHaveLength(2); expect(result.components[0].name).toBe('Database'); expect(result.components[0].status).toBe('ok'); expect(result.components[0].statusLabel).toBe('Healthy'); expect(result.alerts).toHaveLength(1); expect(result.alerts[0].id).toBe('alert-1'); expect(result.alerts[0].type).toBe('info'); expect(result.hasAlerts).toBe(true); expect(result.hasDegradedComponents).toBe(false); expect(result.hasErrorComponents).toBe(false); }); it('should handle missing optional fields gracefully', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.overallStatus.status).toBe('ok'); expect(result.metrics.uptime).toBe('N/A'); expect(result.metrics.responseTime).toBe('N/A'); expect(result.metrics.errorRate).toBe('N/A'); expect(result.metrics.checksPassed).toBe(0); expect(result.metrics.checksFailed).toBe(0); expect(result.metrics.totalChecks).toBe(0); expect(result.metrics.successRate).toBe('N/A'); expect(result.components).toEqual([]); expect(result.alerts).toEqual([]); expect(result.hasAlerts).toBe(false); expect(result.hasDegradedComponents).toBe(false); expect(result.hasErrorComponents).toBe(false); }); it('should handle degraded status correctly', () => { const healthDTO: HealthDTO = { status: 'degraded', timestamp: new Date().toISOString(), uptime: 95.5, responseTime: 500, errorRate: 4.5, components: [ { name: 'Database', status: 'degraded', lastCheck: new Date().toISOString(), responseTime: 200, errorRate: 2.0, }, ], }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.overallStatus.status).toBe('degraded'); expect(result.overallStatus.statusLabel).toBe('Degraded'); expect(result.overallStatus.statusColor).toBe('#f59e0b'); expect(result.overallStatus.statusIcon).toBe('⚠'); expect(result.metrics.uptime).toBe('95.50%'); expect(result.metrics.responseTime).toBe('500ms'); expect(result.metrics.errorRate).toBe('4.50%'); expect(result.hasDegradedComponents).toBe(true); }); it('should handle error status correctly', () => { const healthDTO: HealthDTO = { status: 'error', timestamp: new Date().toISOString(), uptime: 85.2, responseTime: 2000, errorRate: 14.8, components: [ { name: 'Database', status: 'error', lastCheck: new Date().toISOString(), responseTime: 1500, errorRate: 10.0, }, ], }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.overallStatus.status).toBe('error'); expect(result.overallStatus.statusLabel).toBe('Error'); expect(result.overallStatus.statusColor).toBe('#ef4444'); expect(result.overallStatus.statusIcon).toBe('✕'); expect(result.metrics.uptime).toBe('85.20%'); expect(result.metrics.responseTime).toBe('2.00s'); expect(result.metrics.errorRate).toBe('14.80%'); expect(result.hasErrorComponents).toBe(true); }); it('should handle multiple components with mixed statuses', () => { const healthDTO: HealthDTO = { status: 'degraded', timestamp: new Date().toISOString(), components: [ { name: 'Database', status: 'ok', lastCheck: new Date().toISOString(), }, { name: 'API', status: 'degraded', lastCheck: new Date().toISOString(), }, { name: 'Cache', status: 'error', lastCheck: new Date().toISOString(), }, ], }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.components).toHaveLength(3); expect(result.hasDegradedComponents).toBe(true); expect(result.hasErrorComponents).toBe(true); expect(result.components[0].statusLabel).toBe('Healthy'); expect(result.components[1].statusLabel).toBe('Degraded'); expect(result.components[2].statusLabel).toBe('Error'); }); it('should handle multiple alerts with different severities', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), alerts: [ { id: 'alert-1', type: 'critical', title: 'Critical Alert', message: 'Critical issue detected', timestamp: new Date().toISOString(), }, { id: 'alert-2', type: 'warning', title: 'Warning Alert', message: 'Warning message', timestamp: new Date().toISOString(), }, { id: 'alert-3', type: 'info', title: 'Info Alert', message: 'Informational message', timestamp: new Date().toISOString(), }, ], }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.alerts).toHaveLength(3); expect(result.hasAlerts).toBe(true); expect(result.alerts[0].severity).toBe('Critical'); expect(result.alerts[0].severityColor).toBe('#ef4444'); expect(result.alerts[1].severity).toBe('Warning'); expect(result.alerts[1].severityColor).toBe('#f59e0b'); expect(result.alerts[2].severity).toBe('Info'); expect(result.alerts[2].severityColor).toBe('#3b82f6'); }); }); describe('data transformation', () => { it('should preserve all DTO fields in the output', () => { const now = new Date(); const healthDTO: HealthDTO = { status: 'ok', timestamp: now.toISOString(), uptime: 99.99, responseTime: 100, errorRate: 0.01, lastCheck: now.toISOString(), checksPassed: 9999, checksFailed: 1, components: [ { name: 'Test Component', status: 'ok', lastCheck: now.toISOString(), responseTime: 50, errorRate: 0.005, }, ], alerts: [ { id: 'test-alert', type: 'info', title: 'Test Alert', message: 'Test message', timestamp: now.toISOString(), }, ], }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.overallStatus.status).toBe(healthDTO.status); expect(result.overallStatus.timestamp).toBe(healthDTO.timestamp); expect(result.metrics.uptime).toBe('99.99%'); expect(result.metrics.responseTime).toBe('100ms'); expect(result.metrics.errorRate).toBe('0.01%'); expect(result.metrics.lastCheck).toBe(healthDTO.lastCheck); expect(result.metrics.checksPassed).toBe(healthDTO.checksPassed); expect(result.metrics.checksFailed).toBe(healthDTO.checksFailed); expect(result.components[0].name).toBe(healthDTO.components![0].name); expect(result.components[0].status).toBe(healthDTO.components![0].status); expect(result.alerts[0].id).toBe(healthDTO.alerts![0].id); expect(result.alerts[0].type).toBe(healthDTO.alerts![0].type); }); it('should not modify the input DTO', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), uptime: 99.95, responseTime: 150, errorRate: 0.05, components: [ { name: 'Database', status: 'ok', lastCheck: new Date().toISOString(), }, ], }; const originalDTO = JSON.parse(JSON.stringify(healthDTO)); HealthViewDataBuilder.build(healthDTO); expect(healthDTO).toEqual(originalDTO); }); it('should transform all numeric fields to formatted strings', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), uptime: 99.95, responseTime: 150, errorRate: 0.05, checksPassed: 995, checksFailed: 5, }; const result = HealthViewDataBuilder.build(healthDTO); expect(typeof result.metrics.uptime).toBe('string'); expect(typeof result.metrics.responseTime).toBe('string'); expect(typeof result.metrics.errorRate).toBe('string'); expect(typeof result.metrics.successRate).toBe('string'); }); it('should handle large numbers correctly', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), uptime: 99.999, responseTime: 5000, errorRate: 0.001, checksPassed: 999999, checksFailed: 1, }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.metrics.uptime).toBe('100.00%'); expect(result.metrics.responseTime).toBe('5.00s'); expect(result.metrics.errorRate).toBe('0.00%'); expect(result.metrics.successRate).toBe('100.0%'); }); }); describe('edge cases', () => { it('should handle null/undefined numeric fields', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), uptime: null as any, responseTime: undefined, errorRate: null as any, }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.metrics.uptime).toBe('N/A'); expect(result.metrics.responseTime).toBe('N/A'); expect(result.metrics.errorRate).toBe('N/A'); }); it('should handle negative numeric values', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), uptime: -1, responseTime: -100, errorRate: -0.5, }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.metrics.uptime).toBe('N/A'); expect(result.metrics.responseTime).toBe('N/A'); expect(result.metrics.errorRate).toBe('N/A'); }); it('should handle empty components and alerts arrays', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), components: [], alerts: [], }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.components).toEqual([]); expect(result.alerts).toEqual([]); expect(result.hasAlerts).toBe(false); expect(result.hasDegradedComponents).toBe(false); expect(result.hasErrorComponents).toBe(false); }); it('should handle component with missing optional fields', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), components: [ { name: 'Test Component', status: 'ok', }, ], }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.components[0].lastCheck).toBeDefined(); expect(result.components[0].formattedLastCheck).toBeDefined(); expect(result.components[0].responseTime).toBe('N/A'); expect(result.components[0].errorRate).toBe('N/A'); }); it('should handle alert with missing optional fields', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), alerts: [ { id: 'alert-1', type: 'info', title: 'Test Alert', message: 'Test message', timestamp: new Date().toISOString(), }, ], }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.alerts[0].id).toBe('alert-1'); expect(result.alerts[0].type).toBe('info'); expect(result.alerts[0].title).toBe('Test Alert'); expect(result.alerts[0].message).toBe('Test message'); expect(result.alerts[0].timestamp).toBeDefined(); expect(result.alerts[0].formattedTimestamp).toBeDefined(); expect(result.alerts[0].relativeTime).toBeDefined(); }); it('should handle unknown status', () => { const healthDTO: HealthDTO = { status: 'unknown', timestamp: new Date().toISOString(), }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.overallStatus.status).toBe('unknown'); expect(result.overallStatus.statusLabel).toBe('Unknown'); expect(result.overallStatus.statusColor).toBe('#6b7280'); expect(result.overallStatus.statusIcon).toBe('?'); }); }); describe('derived fields', () => { it('should correctly calculate hasAlerts', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), alerts: [ { id: 'alert-1', type: 'info', title: 'Test', message: 'Test message', timestamp: new Date().toISOString(), }, ], }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.hasAlerts).toBe(true); }); it('should correctly calculate hasDegradedComponents', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), components: [ { name: 'Component 1', status: 'ok', lastCheck: new Date().toISOString(), }, { name: 'Component 2', status: 'degraded', lastCheck: new Date().toISOString(), }, ], }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.hasDegradedComponents).toBe(true); }); it('should correctly calculate hasErrorComponents', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), components: [ { name: 'Component 1', status: 'ok', lastCheck: new Date().toISOString(), }, { name: 'Component 2', status: 'error', lastCheck: new Date().toISOString(), }, ], }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.hasErrorComponents).toBe(true); }); it('should correctly calculate totalChecks', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), checksPassed: 100, checksFailed: 20, }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.metrics.totalChecks).toBe(120); }); it('should correctly calculate successRate', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), checksPassed: 90, checksFailed: 10, }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.metrics.successRate).toBe('90.0%'); }); it('should handle zero checks correctly', () => { const healthDTO: HealthDTO = { status: 'ok', timestamp: new Date().toISOString(), checksPassed: 0, checksFailed: 0, }; const result = HealthViewDataBuilder.build(healthDTO); expect(result.metrics.totalChecks).toBe(0); expect(result.metrics.successRate).toBe('N/A'); }); }); });