Files
unified-media-manager/internal/service/notification_test.go
2026-04-24 10:45:19 -07:00

217 lines
6.0 KiB
Go

package service
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
)
func TestNotificationChannelCRUD(t *testing.T) {
t.Skip("requires database")
}
func TestNotificationChannelTelegram(t *testing.T) {
t.Skip("requires database")
}
func TestNotificationUpdateSubscriptions(t *testing.T) {
t.Skip("requires database")
}
func TestDeliverWebhook_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
t.Errorf("expected POST, got %s", r.Method)
}
if r.Header.Get("Content-Type") != "application/json" {
t.Errorf("expected application/json content type")
}
var body map[string]interface{}
json.NewDecoder(r.Body).Decode(&body)
if body["title"] != "Test Event" {
t.Errorf("expected title 'Test Event', got %v", body["title"])
}
w.WriteHeader(200)
}))
defer server.Close()
svc := NewNotificationService(nil) // nil DB is fine for delivery tests
err := svc.DeliverWebhook(context.Background(), server.URL, map[string]interface{}{
"title": "Test Event",
})
if err != nil {
t.Fatalf("expected nil error, got: %v", err)
}
}
func TestDeliverWebhook_Non200(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500)
}))
defer server.Close()
svc := NewNotificationService(nil)
err := svc.DeliverWebhook(context.Background(), server.URL, map[string]interface{}{})
if err == nil {
t.Fatal("expected error on 500 response, got nil")
}
}
func TestDeliverWebhook_Timeout(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(15 * time.Second) // exceed client timeout
}))
defer server.Close()
svc := NewNotificationService(nil)
err := svc.DeliverWebhook(context.Background(), server.URL, map[string]interface{}{})
if err == nil {
t.Fatal("expected error on timeout, got nil")
}
}
func TestDeliverTelegram_Success(t *testing.T) {
var receivedPath string
var receivedBody map[string]interface{}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
receivedPath = r.URL.Path
json.NewDecoder(r.Body).Decode(&receivedBody)
w.WriteHeader(200)
json.NewEncoder(w).Encode(map[string]interface{}{"ok": true})
}))
defer server.Close()
svc := NewNotificationService(nil)
svc.telegramBaseURL = server.URL // override for testing
err := svc.DeliverTelegram(context.Background(), "123456:ABC-DEF", "987654321", "<b>Test Message</b>")
if err != nil {
t.Fatalf("expected nil error, got: %v", err)
}
expectedPath := "/bot123456:ABC-DEF/sendMessage"
if receivedPath != expectedPath {
t.Errorf("expected path %s, got %s", expectedPath, receivedPath)
}
if receivedBody["chat_id"] != "987654321" {
t.Errorf("expected chat_id 987654321, got %v", receivedBody["chat_id"])
}
if receivedBody["parse_mode"] != "HTML" {
t.Errorf("expected parse_mode HTML, got %v", receivedBody["parse_mode"])
}
}
func TestDeliverTelegram_Non200(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(400)
json.NewEncoder(w).Encode(map[string]interface{}{"ok": false, "description": "Bad Request"})
}))
defer server.Close()
svc := NewNotificationService(nil)
svc.telegramBaseURL = server.URL
err := svc.DeliverTelegram(context.Background(), "token", "chat", "text")
if err == nil {
t.Fatal("expected error on 400 response, got nil")
}
}
func TestCalculateBackoff(t *testing.T) {
expected := []time.Duration{
30 * time.Second, // attempt 0: 30s
60 * time.Second, // attempt 1: 60s
120 * time.Second, // attempt 2: 120s
240 * time.Second, // attempt 3: 240s
480 * time.Second, // attempt 4: 480s (capped)
480 * time.Second, // attempt 5: still capped
}
for i, want := range expected {
got := calculateBackoff(i)
if got != want {
t.Errorf("calculateBackoff(%d) = %v, want %v", i, got, want)
}
}
}
func TestMaskTelegramConfig(t *testing.T) {
raw := json.RawMessage(`{"bot_token":"secret123","chat_id":"999"}`)
masked := maskConfig("telegram", raw)
var m map[string]interface{}
json.Unmarshal(masked, &m)
if m["bot_token"] != "***" {
t.Errorf("expected bot_token masked as ***, got %v", m["bot_token"])
}
if m["chat_id"] != "999" {
t.Errorf("expected chat_id preserved as 999, got %v", m["chat_id"])
}
}
func TestMaskWebhookConfig(t *testing.T) {
raw := json.RawMessage(`{"url":"https://example.com/hook"}`)
masked := maskConfig("webhook", raw)
var m map[string]interface{}
json.Unmarshal(masked, &m)
if m["url"] != "https://example.com/hook" {
t.Errorf("expected url preserved, got %v", m["url"])
}
}
func TestValidateChannelConfig_Webhook(t *testing.T) {
svc := NewNotificationService(nil)
tests := []struct {
name string
config string
wantErr bool
}{
{"valid", `{"url":"https://example.com/hook"}`, false},
{"http scheme", `{"url":"http://example.com/hook"}`, false},
{"missing url", `{"url":""}`, true},
{"no url field", `{}`, true},
{"invalid scheme", `{"url":"ftp://example.com"}`, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := svc.ValidateChannelConfig("webhook", json.RawMessage(tt.config))
if (err != nil) != tt.wantErr {
t.Errorf("ValidateChannelConfig() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestValidateChannelConfig_Telegram(t *testing.T) {
svc := NewNotificationService(nil)
tests := []struct {
name string
config string
wantErr bool
}{
{"valid", `{"bot_token":"123:ABC","chat_id":"999"}`, false},
{"missing bot_token", `{"bot_token":"","chat_id":"999"}`, true},
{"missing chat_id", `{"bot_token":"123:ABC","chat_id":""}`, true},
{"both missing", `{}`, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := svc.ValidateChannelConfig("telegram", json.RawMessage(tt.config))
if (err != nil) != tt.wantErr {
t.Errorf("ValidateChannelConfig() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}