Changeset 0.28.6 (#192)
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
"git.gobha.me/xcaliber/chat-switchboard/database"
|
||||
"git.gobha.me/xcaliber/chat-switchboard/middleware"
|
||||
"git.gobha.me/xcaliber/chat-switchboard/models"
|
||||
"git.gobha.me/xcaliber/chat-switchboard/notifications"
|
||||
"git.gobha.me/xcaliber/chat-switchboard/store"
|
||||
postgres "git.gobha.me/xcaliber/chat-switchboard/store/postgres"
|
||||
sqlite "git.gobha.me/xcaliber/chat-switchboard/store/sqlite"
|
||||
@@ -61,6 +62,16 @@ func setupNotifHarness(t *testing.T) *notifHarness {
|
||||
protected.PUT("/notifications/preferences/:type", notifH.SetPreference)
|
||||
protected.DELETE("/notifications/preferences/:type", notifH.DeletePreference)
|
||||
|
||||
// Admin broadcast route (v0.28.6)
|
||||
admin := api.Group("/admin")
|
||||
admin.Use(middleware.Auth(cfg, stores.Users, userCache))
|
||||
admin.Use(middleware.RequireAdmin())
|
||||
admin.POST("/notifications/broadcast", notifH.Broadcast)
|
||||
|
||||
// Set up notification service singleton for Broadcast handler
|
||||
notifSvc := notifications.NewService(stores.Notifications, nil)
|
||||
notifications.SetDefault(notifSvc)
|
||||
|
||||
// Seed admin
|
||||
adminID := database.SeedTestUser(t, "notif-admin", "notif-admin@test.com")
|
||||
database.TestDB.Exec(dialectSQL("UPDATE users SET role = 'admin', is_active = true WHERE id = $1"), adminID)
|
||||
@@ -548,6 +559,110 @@ func TestNotifications_Unauthenticated(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Admin Broadcast (v0.28.6) ────────────
|
||||
|
||||
func TestBroadcast_Success(t *testing.T) {
|
||||
h := setupNotifHarness(t)
|
||||
|
||||
resp := h.request("POST", "/api/v1/admin/notifications/broadcast", h.adminToken, map[string]interface{}{
|
||||
"title": "Maintenance Tonight",
|
||||
"message": "Servers will be down from 2-4 AM.",
|
||||
"level": "warning",
|
||||
})
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Fatalf("broadcast: got %d, body: %s", resp.Code, resp.Body.String())
|
||||
}
|
||||
|
||||
var body map[string]interface{}
|
||||
json.NewDecoder(resp.Body).Decode(&body)
|
||||
if body["message"] != "broadcast sent" {
|
||||
t.Errorf("expected 'broadcast sent', got %v", body["message"])
|
||||
}
|
||||
count := body["count"].(float64)
|
||||
if count < 2 {
|
||||
t.Errorf("expected at least 2 recipients (admin+user), got %v", count)
|
||||
}
|
||||
|
||||
// Verify notification created for regular user
|
||||
resp = h.request("GET", "/api/v1/notifications", h.userToken, nil)
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Fatalf("list notifications: got %d", resp.Code)
|
||||
}
|
||||
var listBody map[string]interface{}
|
||||
json.NewDecoder(resp.Body).Decode(&listBody)
|
||||
items := listBody["data"].([]interface{})
|
||||
if len(items) == 0 {
|
||||
t.Fatal("user should have received the broadcast notification")
|
||||
}
|
||||
n := items[0].(map[string]interface{})
|
||||
if n["type"] != "system.announcement" {
|
||||
t.Errorf("notification type: got %v, want system.announcement", n["type"])
|
||||
}
|
||||
if n["title"] != "Maintenance Tonight" {
|
||||
t.Errorf("notification title: got %v", n["title"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestBroadcast_NonAdmin403(t *testing.T) {
|
||||
h := setupNotifHarness(t)
|
||||
|
||||
resp := h.request("POST", "/api/v1/admin/notifications/broadcast", h.userToken, map[string]interface{}{
|
||||
"title": "Unauthorized",
|
||||
"message": "Should fail.",
|
||||
})
|
||||
if resp.Code != http.StatusForbidden {
|
||||
t.Fatalf("non-admin broadcast: want 403, got %d", resp.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBroadcast_MissingTitle(t *testing.T) {
|
||||
h := setupNotifHarness(t)
|
||||
|
||||
resp := h.request("POST", "/api/v1/admin/notifications/broadcast", h.adminToken, map[string]interface{}{
|
||||
"message": "No title.",
|
||||
})
|
||||
if resp.Code != http.StatusBadRequest {
|
||||
t.Fatalf("missing title: want 400, got %d", resp.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBroadcast_MissingMessage(t *testing.T) {
|
||||
h := setupNotifHarness(t)
|
||||
|
||||
resp := h.request("POST", "/api/v1/admin/notifications/broadcast", h.adminToken, map[string]interface{}{
|
||||
"title": "No message.",
|
||||
})
|
||||
if resp.Code != http.StatusBadRequest {
|
||||
t.Fatalf("missing message: want 400, got %d", resp.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBroadcast_InvalidLevel(t *testing.T) {
|
||||
h := setupNotifHarness(t)
|
||||
|
||||
resp := h.request("POST", "/api/v1/admin/notifications/broadcast", h.adminToken, map[string]interface{}{
|
||||
"title": "Bad level",
|
||||
"message": "Test",
|
||||
"level": "extreme",
|
||||
})
|
||||
if resp.Code != http.StatusBadRequest {
|
||||
t.Fatalf("invalid level: want 400, got %d", resp.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBroadcast_DefaultLevel(t *testing.T) {
|
||||
h := setupNotifHarness(t)
|
||||
|
||||
// Omit level — should default to info and succeed
|
||||
resp := h.request("POST", "/api/v1/admin/notifications/broadcast", h.adminToken, map[string]interface{}{
|
||||
"title": "Info broadcast",
|
||||
"message": "Default level test.",
|
||||
})
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Fatalf("default level: want 200, got %d, body: %s", resp.Code, resp.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
// ── helpers ────────────────────────────────
|
||||
|
||||
func keys(m map[string]json.RawMessage) []string {
|
||||
|
||||
Reference in New Issue
Block a user