Repository navigation
Expand file tree
/
Copy pathwebhook-handler.test.ts
More file actions
203 lines (186 loc) · 6.03 KB
/
Copy pathwebhook-handler.test.ts
File metadata and controls
203 lines (186 loc) · 6.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
parseWebhookEvent,
cacheEvent,
evaluateAlerts,
setAlertConfig,
generateCIReport,
} from './webhook-handler';
vi.mock('@/lib/cache', () => {
const store = new Map<string, unknown>();
class DistributedCache<T> {
async get(key: string): Promise<T | null> {
return (store.get(key) as T) ?? null;
}
async set(key: string, value: T): Promise<void> {
store.set(key, value);
}
async delete(key: string): Promise<boolean> {
return store.delete(key);
}
}
return { DistributedCache };
});
// Use a recent timestamp so report-window assertions stay valid regardless of
// when the suite runs (the weekly window is 7 days).
const recentIso = new Date(Date.now() - 60 * 60 * 1000).toISOString();
const sampleWorkflowPayload = {
workflow_run: {
id: 42,
name: 'CI',
status: 'completed',
conclusion: 'success',
created_at: recentIso,
updated_at: recentIso,
run_number: 7,
repository: {
name: 'commitpulse',
full_name: 'JhaSourav07/commitpulse',
owner: { login: 'JhaSourav07', type: 'User' },
},
head_branch: 'main',
head_commit: {
id: 'abc1234567890',
message: 'fix: update tests',
timestamp: '2026-06-16T09:58:00Z',
},
},
};
describe('parseWebhookEvent', () => {
it('parses a workflow_run success event', () => {
const event = parseWebhookEvent(sampleWorkflowPayload);
expect(event).not.toBeNull();
expect(event?.type).toBe('workflow_run');
expect(event?.status).toBe('success');
expect(event?.repository).toBe('JhaSourav07/commitpulse');
});
it('parses a workflow_run failure event', () => {
const payload = {
workflow_run: {
...sampleWorkflowPayload.workflow_run,
conclusion: 'failure',
},
};
const event = parseWebhookEvent(payload);
expect(event?.status).toBe('failure');
});
it('parses in_progress status as pending', () => {
const payload = {
workflow_run: {
...sampleWorkflowPayload.workflow_run,
status: 'in_progress',
conclusion: null,
},
};
const event = parseWebhookEvent(payload);
expect(event?.status).toBe('pending');
});
it('parses a check_run event with root-level repository', () => {
const payload = {
repository: {
name: 'commitpulse',
full_name: 'JhaSourav07/commitpulse',
owner: { login: 'JhaSourav07', type: 'User' },
},
check_run: {
id: 12345,
name: 'test-check',
status: 'completed',
conclusion: 'success',
started_at: recentIso,
completed_at: recentIso,
},
};
const event = parseWebhookEvent(payload);
expect(event).not.toBeNull();
expect(event?.type).toBe('check_run');
expect(event?.status).toBe('success');
expect(event?.repository).toBe('JhaSourav07/commitpulse');
expect(event?.details.name).toBe('test-check');
});
it('returns null when no workflow_run or check_run in payload', () => {
const event = parseWebhookEvent({});
expect(event).toBeNull();
});
});
describe('cacheEvent', () => {
it('stores event without throwing', async () => {
const event = parseWebhookEvent(sampleWorkflowPayload)!;
await expect(cacheEvent(event)).resolves.toBeUndefined();
});
});
describe('setAlertConfig and evaluateAlerts', () => {
beforeEach(async () => {
await setAlertConfig('JhaSourav07/commitpulse', {
enabled: true,
onFailure: true,
onSuccess: false,
});
});
it('does not trigger alert for success when onSuccess is false', async () => {
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
const event = parseWebhookEvent(sampleWorkflowPayload)!;
await evaluateAlerts(event);
consoleSpy.mockRestore();
});
it('triggers email alert for failure when onFailure is true', async () => {
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
const failPayload = {
workflow_run: {
...sampleWorkflowPayload.workflow_run,
conclusion: 'failure',
},
};
const event = parseWebhookEvent(failPayload)!;
await setAlertConfig('JhaSourav07/commitpulse', {
enabled: true,
onFailure: true,
onSuccess: false,
});
await evaluateAlerts(event);
consoleSpy.mockRestore();
});
});
describe('generateCIReport', () => {
it('returns a report with correct structure for daily period', () => {
const events = [parseWebhookEvent(sampleWorkflowPayload)!];
const report = generateCIReport(events, 'daily');
expect(report.period).toBe('daily');
expect(typeof report.totalEvents).toBe('number');
expect(report.repositories).toBeDefined();
});
it('counts success and failure correctly', () => {
const successEvent = parseWebhookEvent(sampleWorkflowPayload)!;
const failureEvent = parseWebhookEvent({
workflow_run: {
...sampleWorkflowPayload.workflow_run,
conclusion: 'failure',
},
})!;
const report = generateCIReport([successEvent, failureEvent], 'weekly') as {
repositories: Record<
string,
{ total: number; success: number; failure: number; successRate: string }
>;
};
const repo = report.repositories['JhaSourav07/commitpulse'];
expect(repo).toBeDefined();
expect(repo.total).toBe(2);
expect(repo.success).toBe(1);
expect(repo.failure).toBe(1);
expect(repo.successRate).toBe('50.0%');
});
it('returns zero counts for events outside the period window', () => {
const oldEvent: Parameters<typeof generateCIReport>[0][0] = {
type: 'workflow_run',
repository: 'JhaSourav07/commitpulse',
timestamp: new Date(Date.now() - 31 * 24 * 60 * 60 * 1000).toISOString(),
status: 'success',
details: { id: 1 },
};
const report = generateCIReport([oldEvent], 'daily') as { totalEvents: number };
expect(report.totalEvents).toBe(0);
});
});