Openstatus
www.openstatus.dev
1import { describe, expect, it } from "bun:test";
2import type { Incident } from "@openstatus/db/src/schema";
3import { getIncidentDuration } from "./incident";
4
5// Helper to create a partial incident object for testing
6function createIncident(overrides: Partial<Incident>): Incident {
7 return {
8 id: 1,
9 monitorId: 1,
10 workspaceId: 1,
11 startedAt: null,
12 resolvedAt: null,
13 autoResolved: false,
14 acknowledgedAt: null,
15 acknowledgedBy: null,
16 ...overrides,
17 } as Incident;
18}
19
20describe("getIncidentDuration", () => {
21 it("returns null when incident.startedAt is missing", () => {
22 const incident = createIncident({
23 startedAt: null,
24 resolvedAt: new Date("2026-01-22T12:00:00Z"),
25 });
26 expect(getIncidentDuration(incident)).toBe(null);
27 });
28
29 it("returns null when incident.resolvedAt is null (ongoing incident)", () => {
30 const incident = createIncident({
31 startedAt: new Date("2026-01-22T10:00:00Z"),
32 resolvedAt: null,
33 });
34 expect(getIncidentDuration(incident)).toBe(null);
35 });
36
37 it("calculates correct duration for resolved incident", () => {
38 // 2h 15m 30s = 8130000ms
39 const incident = createIncident({
40 startedAt: new Date("2026-01-22T10:00:00Z"),
41 resolvedAt: new Date("2026-01-22T12:15:30Z"),
42 });
43 expect(getIncidentDuration(incident)).toBe("2h 15m 30s");
44 });
45
46 it("calculates duration with Date objects", () => {
47 const incident = createIncident({
48 startedAt: new Date("2026-01-22T10:00:00Z"),
49 resolvedAt: new Date("2026-01-22T10:30:00Z"),
50 });
51 expect(getIncidentDuration(incident)).toBe("30m");
52 });
53
54 it("calculates duration with timestamp numbers", () => {
55 // 5 minutes = 300000ms
56 const startTime = new Date("2026-01-22T10:00:00Z").getTime();
57 const endTime = new Date("2026-01-22T10:05:00Z").getTime();
58 const incident = createIncident({
59 startedAt: startTime as unknown as Date,
60 resolvedAt: endTime as unknown as Date,
61 });
62 expect(getIncidentDuration(incident)).toBe("5m");
63 });
64
65 it("handles very short durations (less than a minute)", () => {
66 const incident = createIncident({
67 startedAt: new Date("2026-01-22T10:00:00Z"),
68 resolvedAt: new Date("2026-01-22T10:00:45Z"),
69 });
70 expect(getIncidentDuration(incident)).toBe("45s");
71 });
72
73 it("handles very long durations (over a day)", () => {
74 // 1 day, 2 hours = 26 hours = 93600000ms
75 const incident = createIncident({
76 startedAt: new Date("2026-01-21T10:00:00Z"),
77 resolvedAt: new Date("2026-01-22T12:00:00Z"),
78 });
79 expect(getIncidentDuration(incident)).toBe("1d 2h");
80 });
81
82 it("handles same start and end time (0 duration)", () => {
83 const timestamp = new Date("2026-01-22T10:00:00Z");
84 const incident = createIncident({
85 startedAt: timestamp,
86 resolvedAt: timestamp,
87 });
88 expect(getIncidentDuration(incident)).toBe("0s");
89 });
90
91 it("returns null when resolvedAt is before startedAt (negative duration)", () => {
92 const incident = createIncident({
93 startedAt: new Date("2026-01-22T12:00:00Z"),
94 resolvedAt: new Date("2026-01-22T10:00:00Z"),
95 });
96 expect(getIncidentDuration(incident)).toBe(null);
97 });
98});