package notifications import ( "bytes" "context" "io" "net/http" "testing" "github.com/gofrs/uuid/v5" "github.com/saveinmed/backend-go/internal/domain" ) type roundTripperFunc func(*http.Request) (*http.Response, error) func (f roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) } func TestRegisterAndUnregisterToken(t *testing.T) { svc := NewFCMService("") ctx := context.Background() userID := uuid.Must(uuid.NewV7()) if err := svc.RegisterToken(ctx, userID, ""); err == nil { t.Fatal("expected error for empty token") } if err := svc.RegisterToken(ctx, userID, "token-1"); err != nil { t.Fatalf("unexpected error: %v", err) } if err := svc.RegisterToken(ctx, userID, "token-1"); err != nil { t.Fatalf("unexpected error on duplicate token: %v", err) } if len(svc.tokens[userID]) != 1 { t.Fatalf("expected 1 token, got %d", len(svc.tokens[userID])) } if err := svc.UnregisterToken(ctx, userID, "token-1"); err != nil { t.Fatalf("unexpected error unregistering: %v", err) } if len(svc.tokens[userID]) != 0 { t.Fatalf("expected no tokens after unregister, got %d", len(svc.tokens[userID])) } } func TestSendPushSkipsWhenNoTokens(t *testing.T) { svc := NewFCMService("") ctx := context.Background() if err := svc.SendPush(ctx, uuid.Must(uuid.NewV7()), "title", "body", nil); err != nil { t.Fatalf("unexpected error: %v", err) } } func TestSendToFCMWithServerKey(t *testing.T) { svc := NewFCMService("server-key") ctx := context.Background() var capturedAuth string svc.httpClient = &http.Client{ Transport: roundTripperFunc(func(req *http.Request) (*http.Response, error) { capturedAuth = req.Header.Get("Authorization") return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBufferString("ok"))}, nil }), } err := svc.sendToFCM(ctx, FCMMessage{ To: "token", Notification: &FCMNotification{ Title: "Hello", Body: "World", }, }) if err != nil { t.Fatalf("unexpected error: %v", err) } if capturedAuth != "key=server-key" { t.Fatalf("expected auth header to include server key, got %q", capturedAuth) } } func TestSendToFCMRejectsNonOK(t *testing.T) { svc := NewFCMService("server-key") ctx := context.Background() svc.httpClient = &http.Client{ Transport: roundTripperFunc(func(req *http.Request) (*http.Response, error) { return &http.Response{StatusCode: http.StatusBadRequest, Body: io.NopCloser(bytes.NewBufferString("bad"))}, nil }), } if err := svc.sendToFCM(ctx, FCMMessage{To: "token"}); err == nil { t.Fatal("expected error for non-OK response") } } func TestNotifyOrderStatusChangedUsesDefaultEmoji(t *testing.T) { svc := NewFCMService("") ctx := context.Background() buyerID := uuid.Must(uuid.NewV7()) _ = svc.RegisterToken(ctx, buyerID, "token-1") order := &domain.Order{ID: uuid.Must(uuid.NewV7()), Status: domain.OrderStatus("Em anĂ¡lise")} buyer := &domain.User{ID: buyerID} if err := svc.NotifyOrderStatusChanged(ctx, order, buyer); err != nil { t.Fatalf("unexpected error: %v", err) } }