Files
pulse/internal/api/alerting/notification_queue_error_test.go
pulse-triage[bot] b84906eb1d fix(notifications): backport recovery owner refresh to release line
Backport Core 439c1a6195 API repair for the candidate recovery-control regression. Preserve registered handlers while refreshing their synchronised owner in both monitor replacement paths. Include stopped-owner, concurrency and scope tests and matching release-line contracts; exclude unrelated main changes.

Change-source: pulse-maintainer
2026-09-08 12:22:47 +01:00

157 lines
5.1 KiB
Go

package alerting
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/rcourtman/pulse-go-rewrite/internal/config"
"github.com/rcourtman/pulse-go-rewrite/internal/monitoring"
"github.com/rcourtman/pulse-go-rewrite/internal/notifications"
internalauth "github.com/rcourtman/pulse-go-rewrite/pkg/auth"
)
func TestNotificationQueueHandlers_GetDLQ_MissingScope(t *testing.T) {
handler := &NotificationQueueHandlers{}
req := httptest.NewRequest(http.MethodGet, "/api/notifications/dlq", nil)
record := &config.APITokenRecord{Scopes: []string{config.ScopeMonitoringWrite}}
req = req.WithContext(internalauth.WithAPIToken(req.Context(), record))
rec := httptest.NewRecorder()
handler.GetDLQ(rec, req)
if rec.Code != http.StatusForbidden {
t.Fatalf("expected 403, got %d", rec.Code)
}
}
func TestNotificationQueueHandlers_GetDLQ_QueueNil(t *testing.T) {
monitor := &monitoring.Monitor{}
setUnexportedField(t, monitor, "notificationMgr", &notifications.NotificationManager{})
handler := NewNotificationQueueHandlers(monitor)
req := httptest.NewRequest(http.MethodGet, "/api/notifications/dlq", nil)
rec := httptest.NewRecorder()
handler.GetDLQ(rec, req)
if rec.Code != http.StatusServiceUnavailable {
t.Fatalf("expected 503, got %d", rec.Code)
}
}
func TestNotificationQueueHandlers_GetQueueStats_QueueNil(t *testing.T) {
monitor := &monitoring.Monitor{}
setUnexportedField(t, monitor, "notificationMgr", &notifications.NotificationManager{})
handler := NewNotificationQueueHandlers(monitor)
req := httptest.NewRequest(http.MethodGet, "/api/notifications/queue/stats", nil)
rec := httptest.NewRecorder()
handler.GetQueueStats(rec, req)
if rec.Code != http.StatusServiceUnavailable {
t.Fatalf("expected 503, got %d", rec.Code)
}
}
func TestNotificationQueueHandlers_RetryDLQItem_Errors(t *testing.T) {
handler := &NotificationQueueHandlers{}
req := httptest.NewRequest(http.MethodPost, "/api/notifications/dlq/retry", bytes.NewReader([]byte("{bad")))
rec := httptest.NewRecorder()
handler.RetryDLQItem(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("expected 400, got %d", rec.Code)
}
req = httptest.NewRequest(http.MethodPost, "/api/notifications/dlq/retry", bytes.NewReader([]byte(`{"id":""}`)))
rec = httptest.NewRecorder()
handler.RetryDLQItem(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("expected 400, got %d", rec.Code)
}
}
func TestNotificationQueueHandlers_RetryDLQItem_QueueNil(t *testing.T) {
monitor := &monitoring.Monitor{}
setUnexportedField(t, monitor, "notificationMgr", &notifications.NotificationManager{})
handler := NewNotificationQueueHandlers(monitor)
req := httptest.NewRequest(http.MethodPost, "/api/notifications/dlq/retry", bytes.NewReader([]byte(`{"id":"missing"}`)))
rec := httptest.NewRecorder()
handler.RetryDLQItem(rec, req)
if rec.Code != http.StatusServiceUnavailable {
t.Fatalf("expected 503, got %d", rec.Code)
}
}
func TestNotificationQueueHandlers_DeleteDLQItem_Errors(t *testing.T) {
handler := &NotificationQueueHandlers{}
req := httptest.NewRequest(http.MethodPost, "/api/notifications/dlq/delete", bytes.NewReader([]byte("{bad")))
rec := httptest.NewRecorder()
handler.DeleteDLQItem(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("expected 400, got %d", rec.Code)
}
req = httptest.NewRequest(http.MethodPost, "/api/notifications/dlq/delete", bytes.NewReader([]byte(`{"id":""}`)))
rec = httptest.NewRecorder()
handler.DeleteDLQItem(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("expected 400, got %d", rec.Code)
}
}
func TestNotificationQueueHandlers_DeleteDLQItem_QueueNil(t *testing.T) {
monitor := &monitoring.Monitor{}
setUnexportedField(t, monitor, "notificationMgr", &notifications.NotificationManager{})
handler := NewNotificationQueueHandlers(monitor)
req := httptest.NewRequest(http.MethodPost, "/api/notifications/dlq/delete", bytes.NewReader([]byte(`{"id":"missing"}`)))
rec := httptest.NewRecorder()
handler.DeleteDLQItem(rec, req)
if rec.Code != http.StatusServiceUnavailable {
t.Fatalf("expected 503, got %d", rec.Code)
}
}
func TestNotificationQueueHandlers_GetDLQ_InvalidLimit(t *testing.T) {
handler, _ := newNotificationQueueHandlers(t)
req := httptest.NewRequest(http.MethodGet, "/api/notifications/dlq?limit=invalid", nil)
rec := httptest.NewRecorder()
handler.GetDLQ(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", rec.Code)
}
var dlq []notifications.QueuedNotification
if err := json.Unmarshal(rec.Body.Bytes(), &dlq); err != nil {
t.Fatalf("decode dlq: %v", err)
}
}
func TestNotificationQueueHandlers_MonitorReplacementConcurrent(t *testing.T) {
h := NewNotificationQueueHandlers(nil)
m := &monitoring.Monitor{}
done := make(chan struct{})
go func() {
defer close(done)
for i := 0; i < 100; i++ {
h.SetMonitor(m)
h.SetMonitor(nil)
}
}()
for i := 0; i < 100; i++ {
rec := httptest.NewRecorder()
h.GetQueueStats(rec, httptest.NewRequest(http.MethodGet, "/", nil))
if rec.Code != http.StatusServiceUnavailable {
t.Errorf("unavailable monitor: got %d", rec.Code)
}
}
<-done
}