package discord import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "time" "git.daviestechlabs.io/daviestechlabs/ntfy-discord/internal/ntfy" ) func TestClient_Send(t *testing.T) { tests := []struct { name string msg ntfy.Message wantStatus int wantErr bool }{ { name: "successful send", msg: ntfy.Message{ ID: "test-id", Topic: "alerts", Title: "Test Alert", Message: "This is a test message", Priority: 3, Time: time.Now().Unix(), }, wantStatus: http.StatusNoContent, wantErr: false, }, { name: "high priority message", msg: ntfy.Message{ ID: "high-priority", Topic: "urgent", Title: "Urgent Alert", Message: "Critical issue detected", Priority: 5, Tags: []string{"warning", "fire"}, }, wantStatus: http.StatusNoContent, wantErr: false, }, { name: "server error", msg: ntfy.Message{ ID: "error-test", Topic: "test", }, wantStatus: http.StatusInternalServerError, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var receivedPayload WebhookPayload server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { t.Errorf("expected POST, got %s", r.Method) } if r.Header.Get("Content-Type") != "application/json" { t.Errorf("expected application/json, got %s", r.Header.Get("Content-Type")) } if err := json.NewDecoder(r.Body).Decode(&receivedPayload); err != nil { t.Errorf("failed to decode payload: %v", err) } w.WriteHeader(tt.wantStatus) })) defer server.Close() client := NewClient() err := client.Send(context.Background(), server.URL, tt.msg) if (err != nil) != tt.wantErr { t.Errorf("Send() error = %v, wantErr %v", err, tt.wantErr) } if !tt.wantErr && len(receivedPayload.Embeds) != 1 { t.Errorf("expected 1 embed, got %d", len(receivedPayload.Embeds)) } }) } } func TestClient_Send_NoWebhookURL(t *testing.T) { client := NewClient() err := client.Send(context.Background(), "", ntfy.Message{}) if err == nil { t.Error("expected error for empty webhook URL") } } func TestClient_buildEmbed(t *testing.T) { client := NewClient() tests := []struct { name string msg ntfy.Message wantColor int wantEmoji string }{ { name: "default priority", msg: ntfy.Message{ Title: "Test", Message: "Hello", Priority: 3, }, wantColor: 3066993, // Blue wantEmoji: "", }, { name: "max priority with warning tag", msg: ntfy.Message{ Title: "Alert", Message: "Critical", Priority: 5, Tags: []string{"warning"}, }, wantColor: 15158332, // Red wantEmoji: "⚠️", }, { name: "low priority", msg: ntfy.Message{ Title: "Info", Message: "Low priority", Priority: 2, }, wantColor: 9807270, // Gray wantEmoji: "", }, { name: "with check tag", msg: ntfy.Message{ Title: "Success", Message: "Completed", Priority: 3, Tags: []string{"check", "success"}, }, wantColor: 3066993, wantEmoji: "✅", }, { name: "no title uses topic", msg: ntfy.Message{ Topic: "alerts", Message: "No title", }, wantColor: 3066993, wantEmoji: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { embed := client.buildEmbed(tt.msg) if embed.Color != tt.wantColor { t.Errorf("Color = %d, want %d", embed.Color, tt.wantColor) } if tt.wantEmoji != "" { if len(embed.Title) < 2 || embed.Title[:len(tt.wantEmoji)] != tt.wantEmoji { t.Errorf("Title should start with emoji %s, got %s", tt.wantEmoji, embed.Title) } } if embed.Description != tt.msg.Message { t.Errorf("Description = %s, want %s", embed.Description, tt.msg.Message) } }) } } func TestClient_extractEmoji(t *testing.T) { client := NewClient() tests := []struct { name string tags []string want string }{ {"warning tag", []string{"warning"}, "⚠️"}, {"check tag", []string{"check"}, "✅"}, {"fire tag", []string{"fire"}, "🔥"}, {"rocket tag", []string{"rocket"}, "🚀"}, {"unknown tag", []string{"unknown"}, ""}, {"empty tags", []string{}, ""}, {"multiple tags first match", []string{"unknown", "fire", "warning"}, "🔥"}, {"case insensitive", []string{"WARNING"}, "⚠️"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := client.extractEmoji(tt.tags) if got != tt.want { t.Errorf("extractEmoji(%v) = %s, want %s", tt.tags, got, tt.want) } }) } } func TestPriorityColors(t *testing.T) { expected := map[int]int{ 1: 12370112, // Light Gray 2: 9807270, // Gray 3: 3066993, // Blue 4: 15105570, // Orange 5: 15158332, // Red } for priority, color := range expected { if priorityColors[priority] != color { t.Errorf("priorityColors[%d] = %d, want %d", priority, priorityColors[priority], color) } } } func TestWebhookPayload_JSON(t *testing.T) { payload := WebhookPayload{ Embeds: []Embed{ { Title: "Test", Description: "Hello World", Color: 3066993, Fields: []Field{ {Name: "Topic", Value: "alerts", Inline: true}, }, Footer: &Footer{Text: "ntfy"}, }, }, } data, err := json.Marshal(payload) if err != nil { t.Fatalf("failed to marshal: %v", err) } var decoded WebhookPayload if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal: %v", err) } if len(decoded.Embeds) != 1 { t.Errorf("expected 1 embed, got %d", len(decoded.Embeds)) } if decoded.Embeds[0].Title != "Test" { t.Errorf("Title = %s, want Test", decoded.Embeds[0].Title) } }