test(notify): assert per-channel results on decrypt-fail and unknown-type

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-04 16:01:14 +07:00
parent f14916396c
commit 41844d49a0
+32 -2
View File
@@ -3,6 +3,7 @@ package notify
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"net" "net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@@ -400,9 +401,13 @@ func TestDispatcherSkipsUnknownChannelType(t *testing.T) {
{ID: uuid.New(), ProjectID: projectID, Type: "carrier-pigeon", Config: json.RawMessage(`{}`), Enabled: true}, {ID: uuid.New(), ProjectID: projectID, Type: "carrier-pigeon", Config: json.RawMessage(`{}`), Enabled: true},
} }
d := NewDispatcher(&mockChannelStore{channels: channels}, &mockDecryptor{}) d := NewDispatcher(&mockChannelStore{channels: channels}, &mockDecryptor{})
if _, err := d.Send(context.Background(), projectID, Event{Project: "p", Domain: "d", Status: "drift"}); err != nil { results, err := d.Send(context.Background(), projectID, Event{Project: "p", Domain: "d", Status: "drift"})
if err != nil {
t.Fatalf("unexpected error for unknown channel type: %v", err) t.Fatalf("unexpected error for unknown channel type: %v", err)
} }
if len(results) != 0 {
t.Fatalf("results = %d, want 0: unknown channel type must not produce a result", len(results))
}
} }
func TestDispatcherDecryptFailureIsAggregatedNotFatal(t *testing.T) { func TestDispatcherDecryptFailureIsAggregatedNotFatal(t *testing.T) {
@@ -423,13 +428,38 @@ func TestDispatcherDecryptFailureIsAggregatedNotFatal(t *testing.T) {
// default; swap in an allowPrivate webhook so this test can still hit it. // default; swap in an allowPrivate webhook so this test can still hit it.
d.byType["webhook"] = &Webhook{HTTP: whSrv.Client(), allowPrivate: true} d.byType["webhook"] = &Webhook{HTTP: whSrv.Client(), allowPrivate: true}
_, err := d.Send(context.Background(), projectID, Event{Project: "p", Domain: "d", Status: "drift"}) results, err := d.Send(context.Background(), projectID, Event{Project: "p", Domain: "d", Status: "drift"})
if err == nil { if err == nil {
t.Fatal("expected error due to decrypt failure") t.Fatal("expected error due to decrypt failure")
} }
if !whCalled { if !whCalled {
t.Error("expected webhook channel to still be attempted after telegram decrypt failure") t.Error("expected webhook channel to still be attempted after telegram decrypt failure")
} }
if len(results) != 2 {
t.Fatalf("results = %d, want 2", len(results))
}
byType := make(map[string]ChannelResult, len(results))
for _, r := range results {
byType[r.Type] = r
}
tg, ok := byType["telegram"]
if !ok {
t.Fatal("expected a telegram result")
}
if tg.Err == nil {
t.Fatalf("telegram result = %+v, want decrypt error", tg)
}
if !errors.Is(tg.Err, errBoom) {
t.Fatalf("telegram result err = %v, want errBoom", tg.Err)
}
wh, ok := byType["webhook"]
if !ok {
t.Fatal("expected a webhook result")
}
if wh.Err != nil {
t.Fatalf("webhook result = %+v, want ok result (decrypt failure on telegram must not fail webhook)", wh)
}
} }
// notifierFunc adapts a function to the Notifier interface for tests. // notifierFunc adapts a function to the Notifier interface for tests.