@@ -42,13 +42,13 @@ describe('AlertManager Webhook Functionality', () => {
42
42
} ,
43
43
guest : {
44
44
name : 'test-vm' ,
45
- id : '100' ,
45
+ vmid : '100' ,
46
46
type : 'qemu' ,
47
47
node : 'test-node' ,
48
48
status : 'running'
49
49
} ,
50
- value : 92 ,
51
- threshold : 85 ,
50
+ currentValue : 92 ,
51
+ effectiveThreshold : 85 ,
52
52
triggeredAt : 1640995200000 , // Valid timestamp
53
53
lastUpdate : 1640995260000 // Valid timestamp
54
54
} ;
@@ -72,14 +72,12 @@ describe('AlertManager Webhook Functionality', () => {
72
72
expect ( mockAxios . post ) . toHaveBeenCalledTimes ( 1 ) ;
73
73
const payload = mockAxios . post . mock . calls [ 0 ] [ 1 ] ;
74
74
75
- // Check main timestamp field
76
- expect ( payload . timestamp ) . toBe ( new Date ( mockAlert . triggeredAt ) . toISOString ( ) ) ;
77
-
78
- // Check embed timestamp
79
- expect ( payload . embeds [ 0 ] . timestamp ) . toBe ( new Date ( mockAlert . triggeredAt ) . toISOString ( ) ) ;
80
-
81
- // Check Slack timestamp (Unix timestamp)
75
+ // For Slack webhooks, check the timestamp in attachments
82
76
expect ( payload . attachments [ 0 ] . ts ) . toBe ( Math . floor ( mockAlert . triggeredAt / 1000 ) ) ;
77
+
78
+ // Slack webhooks don't have top-level timestamp or embeds
79
+ expect ( payload . timestamp ) . toBeUndefined ( ) ;
80
+ expect ( payload . embeds ) . toBeUndefined ( ) ;
83
81
} ) ;
84
82
85
83
test ( 'should fallback to lastUpdate when triggeredAt is missing' , async ( ) => {
@@ -94,9 +92,7 @@ describe('AlertManager Webhook Functionality', () => {
94
92
expect ( mockAxios . post ) . toHaveBeenCalledTimes ( 1 ) ;
95
93
const payload = mockAxios . post . mock . calls [ 0 ] [ 1 ] ;
96
94
97
- // Should use lastUpdate timestamp
98
- expect ( payload . timestamp ) . toBe ( new Date ( mockAlert . lastUpdate ) . toISOString ( ) ) ;
99
- expect ( payload . embeds [ 0 ] . timestamp ) . toBe ( new Date ( mockAlert . lastUpdate ) . toISOString ( ) ) ;
95
+ // Should use lastUpdate timestamp in Slack format
100
96
expect ( payload . attachments [ 0 ] . ts ) . toBe ( Math . floor ( mockAlert . lastUpdate / 1000 ) ) ;
101
97
} ) ;
102
98
@@ -115,10 +111,11 @@ describe('AlertManager Webhook Functionality', () => {
115
111
expect ( mockAxios . post ) . toHaveBeenCalledTimes ( 1 ) ;
116
112
const payload = mockAxios . post . mock . calls [ 0 ] [ 1 ] ;
117
113
118
- // Should use current time (within reasonable range)
119
- const timestamp = new Date ( payload . timestamp ) . getTime ( ) ;
120
- expect ( timestamp ) . toBeGreaterThanOrEqual ( beforeTime ) ;
121
- expect ( timestamp ) . toBeLessThanOrEqual ( afterTime ) ;
114
+ // Should use current time (within reasonable range) for Slack format
115
+ // Note: Unix timestamps lose millisecond precision, so allow for some tolerance
116
+ const timestamp = payload . attachments [ 0 ] . ts * 1000 ; // Convert Unix timestamp back to milliseconds
117
+ expect ( timestamp ) . toBeGreaterThanOrEqual ( Math . floor ( beforeTime / 1000 ) * 1000 ) ;
118
+ expect ( timestamp ) . toBeLessThanOrEqual ( Math . ceil ( afterTime / 1000 ) * 1000 ) ;
122
119
} ) ;
123
120
124
121
test ( 'should handle invalid timestamp values gracefully' , async ( ) => {
@@ -138,10 +135,11 @@ describe('AlertManager Webhook Functionality', () => {
138
135
expect ( mockAxios . post ) . toHaveBeenCalledTimes ( 1 ) ;
139
136
const payload = mockAxios . post . mock . calls [ 0 ] [ 1 ] ;
140
137
141
- // Should fallback to current time when timestamps are invalid
142
- const timestamp = new Date ( payload . timestamp ) . getTime ( ) ;
143
- expect ( timestamp ) . toBeGreaterThanOrEqual ( beforeTime ) ;
144
- expect ( timestamp ) . toBeLessThanOrEqual ( afterTime ) ;
138
+ // Should fallback to current time when timestamps are invalid (Slack format)
139
+ // Note: Unix timestamps lose millisecond precision, so allow for some tolerance
140
+ const timestamp = payload . attachments [ 0 ] . ts * 1000 ;
141
+ expect ( timestamp ) . toBeGreaterThanOrEqual ( Math . floor ( beforeTime / 1000 ) * 1000 ) ;
142
+ expect ( timestamp ) . toBeLessThanOrEqual ( Math . ceil ( afterTime / 1000 ) * 1000 ) ;
145
143
} ) ;
146
144
} ) ;
147
145
@@ -154,26 +152,19 @@ describe('AlertManager Webhook Functionality', () => {
154
152
expect ( mockAxios . post ) . toHaveBeenCalledTimes ( 1 ) ;
155
153
const payload = mockAxios . post . mock . calls [ 0 ] [ 1 ] ;
156
154
157
- // Check main structure
158
- expect ( payload ) . toHaveProperty ( 'timestamp' ) ;
159
- expect ( payload ) . toHaveProperty ( 'alert' ) ;
160
- expect ( payload ) . toHaveProperty ( 'embeds' ) ;
155
+ // Check Slack webhook structure (based on URL)
161
156
expect ( payload ) . toHaveProperty ( 'text' ) ;
162
157
expect ( payload ) . toHaveProperty ( 'attachments' ) ;
163
-
164
- // Check Discord embed structure
165
- expect ( payload . embeds ) . toHaveLength ( 1 ) ;
166
- expect ( payload . embeds [ 0 ] ) . toHaveProperty ( 'title' ) ;
167
- expect ( payload . embeds [ 0 ] ) . toHaveProperty ( 'description' ) ;
168
- expect ( payload . embeds [ 0 ] ) . toHaveProperty ( 'color' ) ;
169
- expect ( payload . embeds [ 0 ] ) . toHaveProperty ( 'fields' ) ;
170
- expect ( payload . embeds [ 0 ] ) . toHaveProperty ( 'footer' ) ;
171
- expect ( payload . embeds [ 0 ] ) . toHaveProperty ( 'timestamp' ) ;
158
+
159
+ // Slack webhooks don't have these properties
160
+ expect ( payload ) . not . toHaveProperty ( 'timestamp' ) ;
161
+ expect ( payload ) . not . toHaveProperty ( 'alert' ) ;
162
+ expect ( payload ) . not . toHaveProperty ( 'embeds' ) ;
172
163
173
164
// Check Slack attachment structure
174
165
expect ( payload . attachments ) . toHaveLength ( 1 ) ;
175
- expect ( payload . attachments [ 0 ] ) . toHaveProperty ( 'color' ) ;
176
166
expect ( payload . attachments [ 0 ] ) . toHaveProperty ( 'fields' ) ;
167
+ expect ( payload . attachments [ 0 ] ) . toHaveProperty ( 'color' ) ;
177
168
expect ( payload . attachments [ 0 ] ) . toHaveProperty ( 'footer' ) ;
178
169
expect ( payload . attachments [ 0 ] ) . toHaveProperty ( 'ts' ) ;
179
170
} ) ;
@@ -185,38 +176,34 @@ describe('AlertManager Webhook Functionality', () => {
185
176
186
177
const payload = mockAxios . post . mock . calls [ 0 ] [ 1 ] ;
187
178
188
- // Check alert fields
189
- expect ( payload . alert . id ) . toBe ( mockAlert . id ) ;
190
- expect ( payload . alert . rule . name ) . toBe ( mockAlert . rule . name ) ;
191
- expect ( payload . alert . rule . severity ) . toBe ( mockAlert . rule . severity ) ;
192
- expect ( payload . alert . guest . name ) . toBe ( mockAlert . guest . name ) ;
193
- expect ( payload . alert . value ) . toBe ( mockAlert . value ) ;
194
- expect ( payload . alert . threshold ) . toBe ( mockAlert . threshold ) ;
179
+ // Check Slack format fields (data is in text and attachments)
180
+ expect ( payload . text ) . toContain ( mockAlert . rule . name ) ;
181
+ expect ( payload . attachments [ 0 ] . fields [ 0 ] . value ) . toContain ( mockAlert . guest . name ) ;
182
+ expect ( payload . attachments [ 0 ] . fields [ 1 ] . value ) . toBe ( mockAlert . guest . node ) ;
183
+ expect ( payload . attachments [ 0 ] . fields [ 2 ] . value ) . toContain ( '92%' ) ; // formatted value
184
+ expect ( payload . attachments [ 0 ] . fields [ 2 ] . value ) . toContain ( '85%' ) ; // formatted threshold
195
185
} ) ;
196
186
197
187
test ( 'should set correct colors based on severity' , async ( ) => {
198
188
mockAxios . post . mockResolvedValue ( { status : 200 , data : { success : true } } ) ;
199
189
200
- // Test warning severity
190
+ // Test warning severity (Slack format only has attachments)
201
191
await alertManager . sendWebhookNotification ( mockWebhookChannel , mockAlert ) ;
202
192
let payload = mockAxios . post . mock . calls [ 0 ] [ 1 ] ;
203
- expect ( payload . embeds [ 0 ] . color ) . toBe ( 15844367 ) ; // Orange
204
193
expect ( payload . attachments [ 0 ] . color ) . toBe ( 'warning' ) ;
205
194
206
195
// Test critical severity
207
196
mockAxios . post . mockClear ( ) ;
208
197
const criticalAlert = { ...mockAlert , rule : { ...mockAlert . rule , severity : 'critical' } } ;
209
198
await alertManager . sendWebhookNotification ( mockWebhookChannel , criticalAlert ) ;
210
199
payload = mockAxios . post . mock . calls [ 0 ] [ 1 ] ;
211
- expect ( payload . embeds [ 0 ] . color ) . toBe ( 15158332 ) ; // Red
212
200
expect ( payload . attachments [ 0 ] . color ) . toBe ( 'danger' ) ;
213
201
214
202
// Test info severity
215
203
mockAxios . post . mockClear ( ) ;
216
204
const infoAlert = { ...mockAlert , rule : { ...mockAlert . rule , severity : 'info' } } ;
217
205
await alertManager . sendWebhookNotification ( mockWebhookChannel , infoAlert ) ;
218
206
payload = mockAxios . post . mock . calls [ 0 ] [ 1 ] ;
219
- expect ( payload . embeds [ 0 ] . color ) . toBe ( 3447003 ) ; // Blue
220
207
expect ( payload . attachments [ 0 ] . color ) . toBe ( 'good' ) ;
221
208
} ) ;
222
209
} ) ;
0 commit comments