@@ -17,6 +17,7 @@ package webhook
1717import (
1818 "context"
1919 "encoding/json"
20+ "fmt"
2021 "io"
2122 "net/http"
2223 "strings"
@@ -160,10 +161,6 @@ func TestSender_SendWebhook_Success(t *testing.T) {
160161 t .Fatalf ("SendWebhook failed: %v" , err )
161162 }
162163
163- // Verify the payload text contains the link
164- // The mock doFunc already checked basic text, but let's verify exact format if needed.
165- // (Actual check is inside the mock doFunc redefined above if we want to be strict)
166-
167164 if len (mockState .successCalls ) != 1 {
168165 t .Errorf ("expected 1 success call, got %d" , len (mockState .successCalls ))
169166 }
@@ -252,3 +249,167 @@ func TestSender_SendWebhook_HTTPFailure(t *testing.T) {
252249 t .Error ("expected permanent failure for 404" )
253250 }
254251}
252+
253+ func TestSender_SendWebhook_UnsupportedType (t * testing.T ) {
254+ mockState := & mockChannelStateManager {
255+ successCalls : nil ,
256+ failureCalls : nil ,
257+ }
258+ sender := NewSender (nil , mockState , "https://webstatus.dev" )
259+
260+ job := workertypes.IncomingWebhookDeliveryJob {
261+ WebhookDeliveryJob : workertypes.WebhookDeliveryJob {
262+ ChannelID : "chan-1" ,
263+ WebhookURL : "https://example.com/webhook" ,
264+ WebhookType : "unknown" ,
265+ SummaryRaw : nil ,
266+ SubscriptionID : "sub-1" ,
267+ Triggers : nil ,
268+ Metadata : workertypes.DeliveryMetadata {
269+ EventID : "evt-1" ,
270+ SearchID : "search-1" ,
271+ SearchName : "" ,
272+ Query : "group:css" ,
273+ Frequency : workertypes .FrequencyImmediate ,
274+ GeneratedAt : time.Time {},
275+ },
276+ },
277+ WebhookEventID : "evt-1" ,
278+ }
279+
280+ err := sender .SendWebhook (context .Background (), job )
281+ if err == nil {
282+ t .Fatal ("expected error, got nil" )
283+ }
284+ if ! strings .Contains (err .Error (), "unsupported webhook type" ) {
285+ t .Errorf ("unexpected error message: %v" , err )
286+ }
287+
288+ if len (mockState .failureCalls ) != 1 {
289+ t .Errorf ("expected 1 failure call, got %d" , len (mockState .failureCalls ))
290+ }
291+ if ! mockState .failureCalls [0 ].isPermanent {
292+ t .Error ("expected permanent failure for unsupported type" )
293+ }
294+ }
295+
296+ func TestSender_SendWebhook_InvalidSlackURL (t * testing.T ) {
297+ mockState := & mockChannelStateManager {
298+ successCalls : nil ,
299+ failureCalls : nil ,
300+ }
301+ sender := NewSender (nil , mockState , "https://webstatus.dev" )
302+
303+ job := workertypes.IncomingWebhookDeliveryJob {
304+ WebhookDeliveryJob : workertypes.WebhookDeliveryJob {
305+ ChannelID : "chan-1" ,
306+ WebhookURL : "https://not-slack.com/hook" ,
307+ WebhookType : workertypes .WebhookTypeSlack ,
308+ SummaryRaw : nil ,
309+ SubscriptionID : "sub-1" ,
310+ Triggers : nil ,
311+ Metadata : workertypes.DeliveryMetadata {
312+ EventID : "evt-1" ,
313+ SearchID : "search-1" ,
314+ SearchName : "" ,
315+ Query : "group:css" ,
316+ Frequency : workertypes .FrequencyImmediate ,
317+ GeneratedAt : time.Time {},
318+ },
319+ },
320+ WebhookEventID : "evt-1" ,
321+ }
322+
323+ err := sender .SendWebhook (context .Background (), job )
324+ if err == nil {
325+ t .Fatal ("expected error, got nil" )
326+ }
327+
328+ if len (mockState .failureCalls ) != 1 {
329+ t .Errorf ("expected 1 failure call, got %d" , len (mockState .failureCalls ))
330+ }
331+ if ! mockState .failureCalls [0 ].isPermanent {
332+ t .Error ("expected permanent failure for invalid URL" )
333+ }
334+ }
335+
336+ func TestSender_SendWebhook_NetworkError (t * testing.T ) {
337+ mockHTTP := & mockHTTPClient {
338+ doFunc : func (_ * http.Request ) (* http.Response , error ) {
339+ return nil , fmt .Errorf ("network error" )
340+ },
341+ }
342+ mockState := & mockChannelStateManager {
343+ successCalls : nil ,
344+ failureCalls : nil ,
345+ }
346+ sender := NewSender (mockHTTP , mockState , "https://webstatus.dev" )
347+
348+ job := workertypes.IncomingWebhookDeliveryJob {
349+ WebhookDeliveryJob : workertypes.WebhookDeliveryJob {
350+ ChannelID : "chan-1" ,
351+ WebhookURL : "https://hooks.slack.com/services/123" ,
352+ WebhookType : workertypes .WebhookTypeSlack ,
353+ SummaryRaw : []byte (`{"text":"test"}` ),
354+ SubscriptionID : "sub-1" ,
355+ Triggers : nil ,
356+ Metadata : workertypes.DeliveryMetadata {
357+ EventID : "evt-1" ,
358+ SearchID : "search-1" ,
359+ SearchName : "" ,
360+ Query : "group:css" ,
361+ Frequency : workertypes .FrequencyImmediate ,
362+ GeneratedAt : time.Time {},
363+ },
364+ },
365+ WebhookEventID : "evt-1" ,
366+ }
367+
368+ err := sender .SendWebhook (context .Background (), job )
369+ if err == nil {
370+ t .Fatal ("expected error, got nil" )
371+ }
372+
373+ if len (mockState .failureCalls ) != 1 {
374+ t .Errorf ("expected 1 failure call, got %d" , len (mockState .failureCalls ))
375+ }
376+ if mockState .failureCalls [0 ].isPermanent {
377+ t .Error ("expected transient failure for network error" )
378+ }
379+ }
380+
381+ func TestSender_SendWebhook_InvalidSummary (t * testing.T ) {
382+ mockState := & mockChannelStateManager {
383+ successCalls : nil ,
384+ failureCalls : nil ,
385+ }
386+ sender := NewSender (nil , mockState , "https://webstatus.dev" )
387+
388+ job := workertypes.IncomingWebhookDeliveryJob {
389+ WebhookDeliveryJob : workertypes.WebhookDeliveryJob {
390+ ChannelID : "chan-1" ,
391+ WebhookURL : "https://hooks.slack.com/services/123" ,
392+ WebhookType : workertypes .WebhookTypeSlack ,
393+ SummaryRaw : []byte (`invalid json` ),
394+ SubscriptionID : "sub-1" ,
395+ Triggers : nil ,
396+ Metadata : workertypes.DeliveryMetadata {
397+ EventID : "evt-1" ,
398+ SearchID : "search-1" ,
399+ SearchName : "" ,
400+ Query : "group:css" ,
401+ Frequency : workertypes .FrequencyImmediate ,
402+ GeneratedAt : time.Time {},
403+ },
404+ },
405+ WebhookEventID : "evt-1" ,
406+ }
407+
408+ err := sender .SendWebhook (context .Background (), job )
409+ if err == nil {
410+ t .Fatal ("expected error, got nil" )
411+ }
412+ if ! strings .Contains (err .Error (), "failed to unmarshal summary" ) {
413+ t .Errorf ("unexpected error message: %v" , err )
414+ }
415+ }
0 commit comments