mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 10:35:51 +00:00
439c1a6195
Recovery handlers retained the startup monitor after router reload, unlike normal notification handlers. Stopping that monitor cleared its queue and left Retry/Dismiss returning 503 even when the replacement notifier was available. Refresh the stable handler in both replacement paths and synchronise its owner reads. Add stopped-owner regression, concurrent replacement and scope proofs; keep queue persistence semantics unchanged. Change-source: pulse-maintainer
157 lines
5.1 KiB
Go
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", ¬ifications.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", ¬ifications.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", ¬ifications.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", ¬ifications.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
|
|
}
|