Files
gridpilot.gg/apps/website/tests/view-data/health.test.ts
Marc Mintel fb1221701d
Some checks failed
Contract Testing / contract-tests (push) Failing after 6m7s
Contract Testing / contract-snapshot (push) Failing after 4m46s
add tests
2026-01-22 11:52:42 +01:00

1066 lines
36 KiB
TypeScript

/**
* View Data Layer Tests - Health Functionality
*
* This test file covers the view data layer for health functionality.
*
* The view data layer is responsible for:
* - DTO → UI model mapping
* - Formatting, sorting, and grouping
* - Derived fields and defaults
* - UI-specific semantics
*
* This layer isolates the UI from API churn by providing a stable interface
* between the API layer and the presentation layer.
*
* Test coverage includes:
* - Health status data transformation and aggregation
* - System metrics and performance view models
* - Health check data formatting and validation
* - Derived health fields (status indicators, alerts, etc.)
* - Default values and fallbacks for health views
* - Health-specific formatting (uptime, response times, error rates, etc.)
* - Data grouping and categorization for health components
* - Real-time health monitoring data updates
* - Health alert and notification view models
*/
import { HealthViewDataBuilder, HealthDTO } from '@/lib/builders/view-data/HealthViewDataBuilder';
import { HealthStatusDisplay } from '@/lib/display-objects/HealthStatusDisplay';
import { HealthMetricDisplay } from '@/lib/display-objects/HealthMetricDisplay';
import { HealthComponentDisplay } from '@/lib/display-objects/HealthComponentDisplay';
import { HealthAlertDisplay } from '@/lib/display-objects/HealthAlertDisplay';
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('99.999%');
expect(result.metrics.responseTime).toBe('5.00s');
expect(result.metrics.errorRate).toBe('0.001%');
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');
});
});
});
describe('HealthStatusDisplay', () => {
describe('happy paths', () => {
it('should format status labels correctly', () => {
expect(HealthStatusDisplay.formatStatusLabel('ok')).toBe('Healthy');
expect(HealthStatusDisplay.formatStatusLabel('degraded')).toBe('Degraded');
expect(HealthStatusDisplay.formatStatusLabel('error')).toBe('Error');
expect(HealthStatusDisplay.formatStatusLabel('unknown')).toBe('Unknown');
});
it('should format status colors correctly', () => {
expect(HealthStatusDisplay.formatStatusColor('ok')).toBe('#10b981');
expect(HealthStatusDisplay.formatStatusColor('degraded')).toBe('#f59e0b');
expect(HealthStatusDisplay.formatStatusColor('error')).toBe('#ef4444');
expect(HealthStatusDisplay.formatStatusColor('unknown')).toBe('#6b7280');
});
it('should format status icons correctly', () => {
expect(HealthStatusDisplay.formatStatusIcon('ok')).toBe('✓');
expect(HealthStatusDisplay.formatStatusIcon('degraded')).toBe('⚠');
expect(HealthStatusDisplay.formatStatusIcon('error')).toBe('✕');
expect(HealthStatusDisplay.formatStatusIcon('unknown')).toBe('?');
});
it('should format timestamp correctly', () => {
const timestamp = '2024-01-15T10:30:45.123Z';
const result = HealthStatusDisplay.formatTimestamp(timestamp);
expect(result).toMatch(/Jan 15, 2024, 10:30:45/);
});
it('should format relative time correctly', () => {
const now = new Date();
const oneMinuteAgo = new Date(now.getTime() - 60 * 1000);
const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
expect(HealthStatusDisplay.formatRelativeTime(oneMinuteAgo.toISOString())).toBe('1m ago');
expect(HealthStatusDisplay.formatRelativeTime(oneHourAgo.toISOString())).toBe('1h ago');
expect(HealthStatusDisplay.formatRelativeTime(oneDayAgo.toISOString())).toBe('1d ago');
});
});
describe('edge cases', () => {
it('should handle unknown status', () => {
expect(HealthStatusDisplay.formatStatusLabel('unknown' as any)).toBe('Unknown');
expect(HealthStatusDisplay.formatStatusColor('unknown' as any)).toBe('#6b7280');
expect(HealthStatusDisplay.formatStatusIcon('unknown' as any)).toBe('?');
});
it('should handle just now relative time', () => {
const now = new Date();
const justNow = new Date(now.getTime() - 30 * 1000);
expect(HealthStatusDisplay.formatRelativeTime(justNow.toISOString())).toBe('Just now');
});
it('should handle weeks ago relative time', () => {
const now = new Date();
const twoWeeksAgo = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1000);
expect(HealthStatusDisplay.formatRelativeTime(twoWeeksAgo.toISOString())).toBe('2w ago');
});
});
});
describe('HealthMetricDisplay', () => {
describe('happy paths', () => {
it('should format uptime correctly', () => {
expect(HealthMetricDisplay.formatUptime(99.95)).toBe('99.95%');
expect(HealthMetricDisplay.formatUptime(100)).toBe('100.00%');
expect(HealthMetricDisplay.formatUptime(0)).toBe('0.00%');
});
it('should format response time correctly', () => {
expect(HealthMetricDisplay.formatResponseTime(150)).toBe('150ms');
expect(HealthMetricDisplay.formatResponseTime(1500)).toBe('1.50s');
expect(HealthMetricDisplay.formatResponseTime(90000)).toBe('1.50m');
});
it('should format error rate correctly', () => {
expect(HealthMetricDisplay.formatErrorRate(0.05)).toBe('0.05%');
expect(HealthMetricDisplay.formatErrorRate(5.5)).toBe('5.50%');
expect(HealthMetricDisplay.formatErrorRate(100)).toBe('100.00%');
});
it('should format timestamp correctly', () => {
const timestamp = '2024-01-15T10:30:45.123Z';
const result = HealthMetricDisplay.formatTimestamp(timestamp);
expect(result).toMatch(/Jan 15, 2024, 10:30:45/);
});
it('should format success rate correctly', () => {
expect(HealthMetricDisplay.formatSuccessRate(90, 10)).toBe('90.0%');
expect(HealthMetricDisplay.formatSuccessRate(100, 0)).toBe('100.0%');
expect(HealthMetricDisplay.formatSuccessRate(0, 100)).toBe('0.0%');
});
});
describe('edge cases', () => {
it('should handle null/undefined values', () => {
expect(HealthMetricDisplay.formatUptime(null as any)).toBe('N/A');
expect(HealthMetricDisplay.formatUptime(undefined)).toBe('N/A');
expect(HealthMetricDisplay.formatResponseTime(null as any)).toBe('N/A');
expect(HealthMetricDisplay.formatResponseTime(undefined)).toBe('N/A');
expect(HealthMetricDisplay.formatErrorRate(null as any)).toBe('N/A');
expect(HealthMetricDisplay.formatErrorRate(undefined)).toBe('N/A');
});
it('should handle negative values', () => {
expect(HealthMetricDisplay.formatUptime(-1)).toBe('N/A');
expect(HealthMetricDisplay.formatResponseTime(-100)).toBe('N/A');
expect(HealthMetricDisplay.formatErrorRate(-0.5)).toBe('N/A');
});
it('should handle zero checks', () => {
expect(HealthMetricDisplay.formatSuccessRate(0, 0)).toBe('N/A');
});
it('should handle decimal response times', () => {
expect(HealthMetricDisplay.formatResponseTime(1234.56)).toBe('1.23s');
});
});
});
describe('HealthComponentDisplay', () => {
describe('happy paths', () => {
it('should format component status labels correctly', () => {
expect(HealthComponentDisplay.formatStatusLabel('ok')).toBe('Healthy');
expect(HealthComponentDisplay.formatStatusLabel('degraded')).toBe('Degraded');
expect(HealthComponentDisplay.formatStatusLabel('error')).toBe('Error');
expect(HealthComponentDisplay.formatStatusLabel('unknown')).toBe('Unknown');
});
it('should format component status colors correctly', () => {
expect(HealthComponentDisplay.formatStatusColor('ok')).toBe('#10b981');
expect(HealthComponentDisplay.formatStatusColor('degraded')).toBe('#f59e0b');
expect(HealthComponentDisplay.formatStatusColor('error')).toBe('#ef4444');
expect(HealthComponentDisplay.formatStatusColor('unknown')).toBe('#6b7280');
});
it('should format component status icons correctly', () => {
expect(HealthComponentDisplay.formatStatusIcon('ok')).toBe('✓');
expect(HealthComponentDisplay.formatStatusIcon('degraded')).toBe('⚠');
expect(HealthComponentDisplay.formatStatusIcon('error')).toBe('✕');
expect(HealthComponentDisplay.formatStatusIcon('unknown')).toBe('?');
});
it('should format timestamp correctly', () => {
const timestamp = '2024-01-15T10:30:45.123Z';
const result = HealthComponentDisplay.formatTimestamp(timestamp);
expect(result).toMatch(/Jan 15, 2024, 10:30:45/);
});
});
describe('edge cases', () => {
it('should handle unknown status', () => {
expect(HealthComponentDisplay.formatStatusLabel('unknown' as any)).toBe('Unknown');
expect(HealthComponentDisplay.formatStatusColor('unknown' as any)).toBe('#6b7280');
expect(HealthComponentDisplay.formatStatusIcon('unknown' as any)).toBe('?');
});
});
});
describe('HealthAlertDisplay', () => {
describe('happy paths', () => {
it('should format alert severities correctly', () => {
expect(HealthAlertDisplay.formatSeverity('critical')).toBe('Critical');
expect(HealthAlertDisplay.formatSeverity('warning')).toBe('Warning');
expect(HealthAlertDisplay.formatSeverity('info')).toBe('Info');
});
it('should format alert severity colors correctly', () => {
expect(HealthAlertDisplay.formatSeverityColor('critical')).toBe('#ef4444');
expect(HealthAlertDisplay.formatSeverityColor('warning')).toBe('#f59e0b');
expect(HealthAlertDisplay.formatSeverityColor('info')).toBe('#3b82f6');
});
it('should format timestamp correctly', () => {
const timestamp = '2024-01-15T10:30:45.123Z';
const result = HealthAlertDisplay.formatTimestamp(timestamp);
expect(result).toMatch(/Jan 15, 2024, 10:30:45/);
});
it('should format relative time correctly', () => {
const now = new Date();
const oneMinuteAgo = new Date(now.getTime() - 60 * 1000);
const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
expect(HealthAlertDisplay.formatRelativeTime(oneMinuteAgo.toISOString())).toBe('1m ago');
expect(HealthAlertDisplay.formatRelativeTime(oneHourAgo.toISOString())).toBe('1h ago');
expect(HealthAlertDisplay.formatRelativeTime(oneDayAgo.toISOString())).toBe('1d ago');
});
});
describe('edge cases', () => {
it('should handle unknown type', () => {
expect(HealthAlertDisplay.formatSeverity('unknown' as any)).toBe('Info');
expect(HealthAlertDisplay.formatSeverityColor('unknown' as any)).toBe('#3b82f6');
});
it('should handle just now relative time', () => {
const now = new Date();
const justNow = new Date(now.getTime() - 30 * 1000);
expect(HealthAlertDisplay.formatRelativeTime(justNow.toISOString())).toBe('Just now');
});
it('should handle weeks ago relative time', () => {
const now = new Date();
const twoWeeksAgo = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1000);
expect(HealthAlertDisplay.formatRelativeTime(twoWeeksAgo.toISOString())).toBe('2w ago');
});
});
});
describe('Health View Data - Cross-Component Consistency', () => {
describe('common patterns', () => {
it('should all use consistent formatting for numeric values', () => {
const healthDTO: HealthDTO = {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: 99.95,
responseTime: 150,
errorRate: 0.05,
checksPassed: 995,
checksFailed: 5,
components: [
{
name: 'Database',
status: 'ok',
lastCheck: new Date().toISOString(),
responseTime: 50,
errorRate: 0.01,
},
],
alerts: [
{
id: 'alert-1',
type: 'info',
title: 'Test',
message: 'Test message',
timestamp: new Date().toISOString(),
},
],
};
const result = HealthViewDataBuilder.build(healthDTO);
// All numeric values should be formatted as strings
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');
expect(typeof result.components[0].responseTime).toBe('string');
expect(typeof result.components[0].errorRate).toBe('string');
});
it('should all handle missing data gracefully', () => {
const healthDTO: HealthDTO = {
status: 'ok',
timestamp: new Date().toISOString(),
};
const result = HealthViewDataBuilder.build(healthDTO);
// All fields should have safe defaults
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.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 all preserve ISO timestamps for serialization', () => {
const now = new Date();
const timestamp = now.toISOString();
const healthDTO: HealthDTO = {
status: 'ok',
timestamp: timestamp,
lastCheck: timestamp,
components: [
{
name: 'Database',
status: 'ok',
lastCheck: timestamp,
},
],
alerts: [
{
id: 'alert-1',
type: 'info',
title: 'Test',
message: 'Test message',
timestamp: timestamp,
},
],
};
const result = HealthViewDataBuilder.build(healthDTO);
// All timestamps should be preserved as ISO strings
expect(result.overallStatus.timestamp).toBe(timestamp);
expect(result.metrics.lastCheck).toBe(timestamp);
expect(result.components[0].lastCheck).toBe(timestamp);
expect(result.alerts[0].timestamp).toBe(timestamp);
});
it('should all handle boolean flags correctly', () => {
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(),
},
{
name: 'Component 3',
status: 'error',
lastCheck: 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);
expect(result.hasDegradedComponents).toBe(true);
expect(result.hasErrorComponents).toBe(true);
});
});
describe('data integrity', () => {
it('should maintain data consistency across transformations', () => {
const healthDTO: HealthDTO = {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: 99.95,
responseTime: 150,
errorRate: 0.05,
checksPassed: 995,
checksFailed: 5,
components: [
{
name: 'Database',
status: 'ok',
lastCheck: new Date().toISOString(),
},
{
name: 'API',
status: 'degraded',
lastCheck: new Date().toISOString(),
},
],
alerts: [
{
id: 'alert-1',
type: 'info',
title: 'Test',
message: 'Test message',
timestamp: new Date().toISOString(),
},
],
};
const result = HealthViewDataBuilder.build(healthDTO);
// Verify derived fields match their source data
expect(result.hasAlerts).toBe(healthDTO.alerts!.length > 0);
expect(result.hasDegradedComponents).toBe(
healthDTO.components!.some((c) => c.status === 'degraded')
);
expect(result.hasErrorComponents).toBe(
healthDTO.components!.some((c) => c.status === 'error')
);
expect(result.metrics.totalChecks).toBe(
(healthDTO.checksPassed || 0) + (healthDTO.checksFailed || 0)
);
});
it('should handle complex real-world scenarios', () => {
const now = new Date();
const timestamp = now.toISOString();
const healthDTO: HealthDTO = {
status: 'degraded',
timestamp: timestamp,
uptime: 98.5,
responseTime: 350,
errorRate: 1.5,
lastCheck: timestamp,
checksPassed: 985,
checksFailed: 15,
components: [
{
name: 'Database',
status: 'ok',
lastCheck: timestamp,
responseTime: 50,
errorRate: 0.01,
},
{
name: 'API',
status: 'degraded',
lastCheck: timestamp,
responseTime: 200,
errorRate: 2.0,
},
{
name: 'Cache',
status: 'error',
lastCheck: timestamp,
responseTime: 1000,
errorRate: 10.0,
},
],
alerts: [
{
id: 'alert-1',
type: 'critical',
title: 'Cache Failure',
message: 'Cache service is down',
timestamp: timestamp,
},
{
id: 'alert-2',
type: 'warning',
title: 'High Response Time',
message: 'API response time is elevated',
timestamp: timestamp,
},
],
};
const result = HealthViewDataBuilder.build(healthDTO);
// Verify all transformations
expect(result.overallStatus.status).toBe('degraded');
expect(result.overallStatus.statusLabel).toBe('Degraded');
expect(result.metrics.uptime).toBe('98.50%');
expect(result.metrics.responseTime).toBe('350ms');
expect(result.metrics.errorRate).toBe('1.50%');
expect(result.metrics.checksPassed).toBe(985);
expect(result.metrics.checksFailed).toBe(15);
expect(result.metrics.totalChecks).toBe(1000);
expect(result.metrics.successRate).toBe('98.5%');
expect(result.components).toHaveLength(3);
expect(result.components[0].statusLabel).toBe('Healthy');
expect(result.components[1].statusLabel).toBe('Degraded');
expect(result.components[2].statusLabel).toBe('Error');
expect(result.alerts).toHaveLength(2);
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.hasAlerts).toBe(true);
expect(result.hasDegradedComponents).toBe(true);
expect(result.hasErrorComponents).toBe(true);
});
});
});