useServiceHealthMetrics.utils.test.ts350 lines · main
1import dayjs from 'dayjs'
2import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
3
4import type { LogsBarChartDatum } from '../ProjectHome/ProjectUsage.metrics'
5import {
6 calculateAggregatedMetrics,
7 calculateDateRange,
8 calculateHealthMetrics,
9 transformToBarChartData,
10} from './useServiceHealthMetrics.utils'
11
12describe('calculateDateRange', () => {
13 beforeEach(() => {
14 // Mock the current time to ensure consistent test results
15 vi.useFakeTimers()
16 vi.setSystemTime(new Date('2024-01-15T12:00:00Z'))
17 })
18
19 afterEach(() => {
20 vi.useRealTimers()
21 })
22
23 it('calculates correct date range for 1hr interval', () => {
24 const result = calculateDateRange('1hr')
25
26 expect(result.startDate).toBe('2024-01-15T11:00:00.000Z')
27 expect(result.endDate).toBe('2024-01-15T12:00:00.000Z')
28 })
29
30 it('calculates correct date range for 1day interval', () => {
31 const result = calculateDateRange('1day')
32
33 expect(result.startDate).toBe('2024-01-14T12:00:00.000Z')
34 expect(result.endDate).toBe('2024-01-15T12:00:00.000Z')
35 })
36
37 it('calculates correct date range for 7day interval', () => {
38 const result = calculateDateRange('7day')
39
40 expect(result.startDate).toBe('2024-01-08T12:00:00.000Z')
41 expect(result.endDate).toBe('2024-01-15T12:00:00.000Z')
42 })
43
44 it('returns ISO format strings', () => {
45 const result = calculateDateRange('1hr')
46
47 expect(dayjs(result.startDate).isValid()).toBe(true)
48 expect(dayjs(result.endDate).isValid()).toBe(true)
49 })
50
51 it('end date is always after start date', () => {
52 const intervals: Array<'1hr' | '1day' | '7day'> = ['1hr', '1day', '7day']
53
54 intervals.forEach((interval) => {
55 const result = calculateDateRange(interval)
56 expect(dayjs(result.endDate).isAfter(dayjs(result.startDate))).toBe(true)
57 })
58 })
59})
60
61describe('transformToBarChartData', () => {
62 it('transforms raw data to LogsBarChartDatum format', () => {
63 const rawData = [
64 { timestamp: '2024-01-15T12:00:00Z', ok_count: 10, warning_count: 2, error_count: 1 },
65 { timestamp: '2024-01-15T12:01:00Z', ok_count: 15, warning_count: 0, error_count: 0 },
66 ]
67
68 const result = transformToBarChartData(rawData)
69
70 expect(result).toEqual([
71 { timestamp: '2024-01-15T12:00:00Z', ok_count: 10, warning_count: 2, error_count: 1 },
72 { timestamp: '2024-01-15T12:01:00Z', ok_count: 15, warning_count: 0, error_count: 0 },
73 ])
74 })
75
76 it('handles missing count fields by setting them to 0', () => {
77 const rawData = [
78 { timestamp: '2024-01-15T12:00:00Z' },
79 { timestamp: '2024-01-15T12:01:00Z', ok_count: 5 },
80 ]
81
82 const result = transformToBarChartData(rawData)
83
84 expect(result).toEqual([
85 { timestamp: '2024-01-15T12:00:00Z', ok_count: 0, warning_count: 0, error_count: 0 },
86 { timestamp: '2024-01-15T12:01:00Z', ok_count: 5, warning_count: 0, error_count: 0 },
87 ])
88 })
89
90 it('handles null values by converting to 0', () => {
91 const rawData = [
92 {
93 timestamp: '2024-01-15T12:00:00Z',
94 ok_count: null,
95 warning_count: null,
96 error_count: null,
97 },
98 ]
99
100 const result = transformToBarChartData(rawData)
101
102 expect(result).toEqual([
103 { timestamp: '2024-01-15T12:00:00Z', ok_count: 0, warning_count: 0, error_count: 0 },
104 ])
105 })
106
107 it('handles empty array', () => {
108 const result = transformToBarChartData([])
109
110 expect(result).toEqual([])
111 })
112
113 it('preserves timestamp values', () => {
114 const rawData = [
115 { timestamp: '2024-01-15T12:00:00Z', ok_count: 10, warning_count: 0, error_count: 0 },
116 ]
117
118 const result = transformToBarChartData(rawData)
119
120 expect(result[0].timestamp).toBe('2024-01-15T12:00:00Z')
121 })
122})
123
124describe('calculateHealthMetrics', () => {
125 it('calculates metrics correctly with only ok requests', () => {
126 const eventChartData: LogsBarChartDatum[] = [
127 { timestamp: '2024-01-15T12:00:00Z', ok_count: 100, warning_count: 0, error_count: 0 },
128 { timestamp: '2024-01-15T12:01:00Z', ok_count: 50, warning_count: 0, error_count: 0 },
129 ]
130
131 const result = calculateHealthMetrics(eventChartData)
132
133 expect(result).toEqual({
134 total: 150,
135 errorRate: 0,
136 successRate: 100,
137 errorCount: 0,
138 warningCount: 0,
139 okCount: 150,
140 })
141 })
142
143 it('calculates metrics correctly with errors', () => {
144 const eventChartData: LogsBarChartDatum[] = [
145 { timestamp: '2024-01-15T12:00:00Z', ok_count: 80, warning_count: 10, error_count: 10 },
146 ]
147
148 const result = calculateHealthMetrics(eventChartData)
149
150 expect(result).toEqual({
151 total: 100,
152 errorRate: 10,
153 successRate: 80,
154 errorCount: 10,
155 warningCount: 10,
156 okCount: 80,
157 })
158 })
159
160 it('calculates metrics correctly with warnings', () => {
161 const eventChartData: LogsBarChartDatum[] = [
162 { timestamp: '2024-01-15T12:00:00Z', ok_count: 70, warning_count: 30, error_count: 0 },
163 ]
164
165 const result = calculateHealthMetrics(eventChartData)
166
167 expect(result).toEqual({
168 total: 100,
169 errorRate: 0,
170 successRate: 70,
171 errorCount: 0,
172 warningCount: 30,
173 okCount: 70,
174 })
175 })
176
177 it('returns 0 error rate when total is 0', () => {
178 const eventChartData: LogsBarChartDatum[] = [
179 { timestamp: '2024-01-15T12:00:00Z', ok_count: 0, warning_count: 0, error_count: 0 },
180 ]
181
182 const result = calculateHealthMetrics(eventChartData)
183
184 expect(result).toEqual({
185 total: 0,
186 errorRate: 0,
187 successRate: 0,
188 errorCount: 0,
189 warningCount: 0,
190 okCount: 0,
191 })
192 })
193
194 it('handles empty array', () => {
195 const result = calculateHealthMetrics([])
196
197 expect(result).toEqual({
198 total: 0,
199 errorRate: 0,
200 successRate: 0,
201 errorCount: 0,
202 warningCount: 0,
203 okCount: 0,
204 })
205 })
206
207 it('aggregates metrics across multiple time periods', () => {
208 const eventChartData: LogsBarChartDatum[] = [
209 { timestamp: '2024-01-15T12:00:00Z', ok_count: 50, warning_count: 5, error_count: 5 },
210 { timestamp: '2024-01-15T12:01:00Z', ok_count: 30, warning_count: 5, error_count: 5 },
211 { timestamp: '2024-01-15T12:02:00Z', ok_count: 20, warning_count: 0, error_count: 0 },
212 ]
213
214 const result = calculateHealthMetrics(eventChartData)
215
216 expect(result).toEqual({
217 total: 120,
218 errorRate: (10 / 120) * 100,
219 successRate: (100 / 120) * 100,
220 errorCount: 10,
221 warningCount: 10,
222 okCount: 100,
223 })
224 })
225
226 it('calculates correct error rate for high error scenario', () => {
227 const eventChartData: LogsBarChartDatum[] = [
228 { timestamp: '2024-01-15T12:00:00Z', ok_count: 10, warning_count: 10, error_count: 80 },
229 ]
230
231 const result = calculateHealthMetrics(eventChartData)
232
233 expect(result.errorRate).toBe(80)
234 expect(result.successRate).toBe(10)
235 })
236})
237
238describe('calculateAggregatedMetrics', () => {
239 it('aggregates metrics from multiple services', () => {
240 const services = [
241 { total: 100, errorCount: 10, warningCount: 5 },
242 { total: 200, errorCount: 20, warningCount: 10 },
243 { total: 50, errorCount: 5, warningCount: 2 },
244 ]
245
246 const result = calculateAggregatedMetrics(services)
247
248 expect(result).toEqual({
249 totalRequests: 350,
250 totalErrors: 35,
251 totalWarnings: 17,
252 overallErrorRate: ((35 + 17) / 350) * 100,
253 overallSuccessRate: ((350 - 35 - 17) / 350) * 100,
254 })
255 })
256
257 it('handles empty services array', () => {
258 const result = calculateAggregatedMetrics([])
259
260 expect(result).toEqual({
261 totalRequests: 0,
262 totalErrors: 0,
263 totalWarnings: 0,
264 overallErrorRate: 0,
265 overallSuccessRate: 0,
266 })
267 })
268
269 it('handles single service', () => {
270 const services = [{ total: 100, errorCount: 10, warningCount: 5 }]
271
272 const result = calculateAggregatedMetrics(services)
273
274 expect(result).toEqual({
275 totalRequests: 100,
276 totalErrors: 10,
277 totalWarnings: 5,
278 overallErrorRate: 15,
279 overallSuccessRate: 85,
280 })
281 })
282
283 it('handles services with zero metrics', () => {
284 const services = [
285 { total: 0, errorCount: 0, warningCount: 0 },
286 { total: 100, errorCount: 10, warningCount: 5 },
287 ]
288
289 const result = calculateAggregatedMetrics(services)
290
291 expect(result).toEqual({
292 totalRequests: 100,
293 totalErrors: 10,
294 totalWarnings: 5,
295 overallErrorRate: 15,
296 overallSuccessRate: 85,
297 })
298 })
299
300 it('calculates correct rates when all requests are successful', () => {
301 const services = [
302 { total: 100, errorCount: 0, warningCount: 0 },
303 { total: 200, errorCount: 0, warningCount: 0 },
304 ]
305
306 const result = calculateAggregatedMetrics(services)
307
308 expect(result).toEqual({
309 totalRequests: 300,
310 totalErrors: 0,
311 totalWarnings: 0,
312 overallErrorRate: 0,
313 overallSuccessRate: 100,
314 })
315 })
316
317 it('calculates correct rates when all requests fail', () => {
318 const services = [
319 { total: 100, errorCount: 100, warningCount: 0 },
320 { total: 200, errorCount: 200, warningCount: 0 },
321 ]
322
323 const result = calculateAggregatedMetrics(services)
324
325 expect(result).toEqual({
326 totalRequests: 300,
327 totalErrors: 300,
328 totalWarnings: 0,
329 overallErrorRate: 100,
330 overallSuccessRate: 0,
331 })
332 })
333
334 it('handles mixed success/warning/error scenarios', () => {
335 const services = [
336 { total: 100, errorCount: 20, warningCount: 30 }, // 50% success
337 { total: 100, errorCount: 10, warningCount: 10 }, // 80% success
338 { total: 100, errorCount: 0, warningCount: 0 }, // 100% success
339 ]
340
341 const result = calculateAggregatedMetrics(services)
342
343 expect(result.totalRequests).toBe(300)
344 expect(result.totalErrors).toBe(30)
345 expect(result.totalWarnings).toBe(40)
346 // Overall: 230 success, 40 warnings, 30 errors out of 300
347 expect(result.overallSuccessRate).toBeCloseTo((230 / 300) * 100, 2)
348 expect(result.overallErrorRate).toBeCloseTo((70 / 300) * 100, 2)
349 })
350})