Browse Source

Test Firebase stuff

Philipp Heckel 3 years ago
parent
commit
a77f89d302
4 changed files with 213 additions and 81 deletions
  1. 24 0
      server/server_firebase.go
  2. 170 0
      server/server_firebase_test.go
  3. 0 25
      server/util.go
  4. 19 56
      server/util_test.go

+ 24 - 0
server/server_firebase.go

@@ -2,6 +2,7 @@ package server
 
 import (
 	"context"
+	"encoding/json"
 	firebase "firebase.google.com/go"
 	"firebase.google.com/go/messaging"
 	"fmt"
@@ -10,6 +11,29 @@ import (
 	"strings"
 )
 
+const (
+	fcmMessageLimit = 4000
+)
+
+// maybeTruncateFCMMessage performs best-effort truncation of FCM messages.
+// The docs say the limit is 4000 characters, but during testing it wasn't quite clear
+// what fields matter; so we're just capping the serialized JSON to 4000 bytes.
+func maybeTruncateFCMMessage(m *messaging.Message) *messaging.Message {
+	s, err := json.Marshal(m)
+	if err != nil {
+		return m
+	}
+	if len(s) > fcmMessageLimit {
+		over := len(s) - fcmMessageLimit + 16 // = len("truncated":"1",), sigh ...
+		message, ok := m.Data["message"]
+		if ok && len(message) > over {
+			m.Data["truncated"] = "1"
+			m.Data["message"] = message[:len(message)-over]
+		}
+	}
+	return m
+}
+
 func createFirebaseSubscriber(credentialsFile string, auther auth.Auther) (subscriber, error) {
 	fb, err := firebase.NewApp(context.Background(), nil, option.WithCredentialsFile(credentialsFile))
 	if err != nil {

+ 170 - 0
server/server_firebase_test.go

@@ -0,0 +1,170 @@
+package server
+
+import (
+	"encoding/json"
+	"errors"
+	"firebase.google.com/go/messaging"
+	"fmt"
+	"github.com/stretchr/testify/require"
+	"heckel.io/ntfy/auth"
+	"strings"
+	"testing"
+)
+
+type testAuther struct {
+	Allow bool
+}
+
+func (t testAuther) Authenticate(username, password string) (*auth.User, error) {
+	return nil, errors.New("not used")
+}
+
+func (t testAuther) Authorize(user *auth.User, topic string, perm auth.Permission) error {
+	if t.Allow {
+		return nil
+	}
+	return errors.New("unauthorized")
+}
+
+func TestToFirebaseMessage_Keepalive(t *testing.T) {
+	m := newKeepaliveMessage("mytopic")
+	fbm, err := toFirebaseMessage(m, nil)
+	require.Nil(t, err)
+	require.Equal(t, "mytopic", fbm.Topic)
+	require.Nil(t, fbm.Android)
+	require.Equal(t, map[string]string{
+		"id":    m.ID,
+		"time":  fmt.Sprintf("%d", m.Time),
+		"event": m.Event,
+		"topic": m.Topic,
+	}, fbm.Data)
+}
+
+func TestToFirebaseMessage_Open(t *testing.T) {
+	m := newOpenMessage("mytopic")
+	fbm, err := toFirebaseMessage(m, nil)
+	require.Nil(t, err)
+	require.Equal(t, "mytopic", fbm.Topic)
+	require.Nil(t, fbm.Android)
+	require.Equal(t, map[string]string{
+		"id":    m.ID,
+		"time":  fmt.Sprintf("%d", m.Time),
+		"event": m.Event,
+		"topic": m.Topic,
+	}, fbm.Data)
+}
+
+func TestToFirebaseMessage_Message_Normal_Allowed(t *testing.T) {
+	m := newDefaultMessage("mytopic", "this is a message")
+	m.Priority = 4
+	m.Tags = []string{"tag 1", "tag2"}
+	m.Click = "https://google.com"
+	m.Title = "some title"
+	m.Attachment = &attachment{
+		Name:    "some file.jpg",
+		Type:    "image/jpeg",
+		Size:    12345,
+		Expires: 98765543,
+		URL:     "https://example.com/file.jpg",
+		Owner:   "some-owner",
+	}
+	fbm, err := toFirebaseMessage(m, &testAuther{Allow: true})
+	require.Nil(t, err)
+	require.Equal(t, "mytopic", fbm.Topic)
+	require.Equal(t, &messaging.AndroidConfig{
+		Priority: "high",
+	}, fbm.Android)
+	require.Equal(t, map[string]string{
+		"id":                 m.ID,
+		"time":               fmt.Sprintf("%d", m.Time),
+		"event":              "message",
+		"topic":              "mytopic",
+		"priority":           "4",
+		"tags":               strings.Join(m.Tags, ","),
+		"click":              "https://google.com",
+		"title":              "some title",
+		"message":            "this is a message",
+		"encoding":           "",
+		"attachment_name":    "some file.jpg",
+		"attachment_type":    "image/jpeg",
+		"attachment_size":    "12345",
+		"attachment_expires": "98765543",
+		"attachment_url":     "https://example.com/file.jpg",
+	}, fbm.Data)
+}
+
+func TestToFirebaseMessage_Message_Normal_Not_Allowed(t *testing.T) {
+	m := newDefaultMessage("mytopic", "this is a message")
+	m.Priority = 5
+	fbm, err := toFirebaseMessage(m, &testAuther{Allow: false}) // Not allowed!
+	require.Nil(t, err)
+	require.Equal(t, "mytopic", fbm.Topic)
+	require.Equal(t, &messaging.AndroidConfig{
+		Priority: "high",
+	}, fbm.Android)
+	require.Equal(t, "", fbm.Data["message"])
+	require.Equal(t, "", fbm.Data["priority"])
+	require.Equal(t, map[string]string{
+		"id":    m.ID,
+		"time":  fmt.Sprintf("%d", m.Time),
+		"event": "poll_request",
+		"topic": "mytopic",
+	}, fbm.Data)
+}
+
+func TestMaybeTruncateFCMMessage(t *testing.T) {
+	origMessage := strings.Repeat("this is a long string", 300)
+	origFCMMessage := &messaging.Message{
+		Topic: "mytopic",
+		Data: map[string]string{
+			"id":       "abcdefg",
+			"time":     "1641324761",
+			"event":    "message",
+			"topic":    "mytopic",
+			"priority": "0",
+			"tags":     "",
+			"title":    "",
+			"message":  origMessage,
+		},
+		Android: &messaging.AndroidConfig{
+			Priority: "high",
+		},
+	}
+	origMessageLength := len(origFCMMessage.Data["message"])
+	serializedOrigFCMMessage, _ := json.Marshal(origFCMMessage)
+	require.Greater(t, len(serializedOrigFCMMessage), fcmMessageLimit) // Pre-condition
+
+	truncatedFCMMessage := maybeTruncateFCMMessage(origFCMMessage)
+	truncatedMessageLength := len(truncatedFCMMessage.Data["message"])
+	serializedTruncatedFCMMessage, _ := json.Marshal(truncatedFCMMessage)
+	require.Equal(t, fcmMessageLimit, len(serializedTruncatedFCMMessage))
+	require.Equal(t, "1", truncatedFCMMessage.Data["truncated"])
+	require.NotEqual(t, origMessageLength, truncatedMessageLength)
+}
+
+func TestMaybeTruncateFCMMessage_NotTooLong(t *testing.T) {
+	origMessage := "not really a long string"
+	origFCMMessage := &messaging.Message{
+		Topic: "mytopic",
+		Data: map[string]string{
+			"id":       "abcdefg",
+			"time":     "1641324761",
+			"event":    "message",
+			"topic":    "mytopic",
+			"priority": "0",
+			"tags":     "",
+			"title":    "",
+			"message":  origMessage,
+		},
+	}
+	origMessageLength := len(origFCMMessage.Data["message"])
+	serializedOrigFCMMessage, _ := json.Marshal(origFCMMessage)
+	require.LessOrEqual(t, len(serializedOrigFCMMessage), fcmMessageLimit) // Pre-condition
+
+	notTruncatedFCMMessage := maybeTruncateFCMMessage(origFCMMessage)
+	notTruncatedMessageLength := len(notTruncatedFCMMessage.Data["message"])
+	serializedNotTruncatedFCMMessage, _ := json.Marshal(notTruncatedFCMMessage)
+	require.Equal(t, origMessageLength, notTruncatedMessageLength)
+	require.Equal(t, len(serializedOrigFCMMessage), len(serializedNotTruncatedFCMMessage))
+	require.Equal(t, "", notTruncatedFCMMessage.Data["truncated"])
+}

+ 0 - 25
server/util.go

@@ -1,35 +1,10 @@
 package server
 
 import (
-	"encoding/json"
-	"firebase.google.com/go/messaging"
 	"net/http"
 	"strings"
 )
 
-const (
-	fcmMessageLimit = 4000
-)
-
-// maybeTruncateFCMMessage performs best-effort truncation of FCM messages.
-// The docs say the limit is 4000 characters, but during testing it wasn't quite clear
-// what fields matter; so we're just capping the serialized JSON to 4000 bytes.
-func maybeTruncateFCMMessage(m *messaging.Message) *messaging.Message {
-	s, err := json.Marshal(m)
-	if err != nil {
-		return m
-	}
-	if len(s) > fcmMessageLimit {
-		over := len(s) - fcmMessageLimit + 16 // = len("truncated":"1",), sigh ...
-		message, ok := m.Data["message"]
-		if ok && len(message) > over {
-			m.Data["truncated"] = "1"
-			m.Data["message"] = message[:len(message)-over]
-		}
-	}
-	return m
-}
-
 func readBoolParam(r *http.Request, defaultValue bool, names ...string) bool {
 	value := strings.ToLower(readParam(r, names...))
 	if value == "" {

+ 19 - 56
server/util_test.go

@@ -1,66 +1,29 @@
 package server
 
 import (
-	"encoding/json"
-	"firebase.google.com/go/messaging"
 	"github.com/stretchr/testify/require"
-	"strings"
+	"net/http"
 	"testing"
 )
 
-func TestMaybeTruncateFCMMessage(t *testing.T) {
-	origMessage := strings.Repeat("this is a long string", 300)
-	origFCMMessage := &messaging.Message{
-		Topic: "mytopic",
-		Data: map[string]string{
-			"id":       "abcdefg",
-			"time":     "1641324761",
-			"event":    "message",
-			"topic":    "mytopic",
-			"priority": "0",
-			"tags":     "",
-			"title":    "",
-			"message":  origMessage,
-		},
-		Android: &messaging.AndroidConfig{
-			Priority: "high",
-		},
-	}
-	origMessageLength := len(origFCMMessage.Data["message"])
-	serializedOrigFCMMessage, _ := json.Marshal(origFCMMessage)
-	require.Greater(t, len(serializedOrigFCMMessage), fcmMessageLimit) // Pre-condition
+func TestReadBoolParam(t *testing.T) {
+	r, _ := http.NewRequest("GET", "https://ntfy.sh/mytopic?up=1&firebase=no", nil)
+	up := readBoolParam(r, false, "x-up", "up")
+	firebase := readBoolParam(r, true, "x-firebase", "firebase")
+	require.Equal(t, true, up)
+	require.Equal(t, false, firebase)
 
-	truncatedFCMMessage := maybeTruncateFCMMessage(origFCMMessage)
-	truncatedMessageLength := len(truncatedFCMMessage.Data["message"])
-	serializedTruncatedFCMMessage, _ := json.Marshal(truncatedFCMMessage)
-	require.Equal(t, fcmMessageLimit, len(serializedTruncatedFCMMessage))
-	require.Equal(t, "1", truncatedFCMMessage.Data["truncated"])
-	require.NotEqual(t, origMessageLength, truncatedMessageLength)
-}
-
-func TestMaybeTruncateFCMMessage_NotTooLong(t *testing.T) {
-	origMessage := "not really a long string"
-	origFCMMessage := &messaging.Message{
-		Topic: "mytopic",
-		Data: map[string]string{
-			"id":       "abcdefg",
-			"time":     "1641324761",
-			"event":    "message",
-			"topic":    "mytopic",
-			"priority": "0",
-			"tags":     "",
-			"title":    "",
-			"message":  origMessage,
-		},
-	}
-	origMessageLength := len(origFCMMessage.Data["message"])
-	serializedOrigFCMMessage, _ := json.Marshal(origFCMMessage)
-	require.LessOrEqual(t, len(serializedOrigFCMMessage), fcmMessageLimit) // Pre-condition
+	r, _ = http.NewRequest("GET", "https://ntfy.sh/mytopic", nil)
+	r.Header.Set("X-Up", "yes")
+	r.Header.Set("X-Firebase", "0")
+	up = readBoolParam(r, false, "x-up", "up")
+	firebase = readBoolParam(r, true, "x-firebase", "firebase")
+	require.Equal(t, true, up)
+	require.Equal(t, false, firebase)
 
-	notTruncatedFCMMessage := maybeTruncateFCMMessage(origFCMMessage)
-	notTruncatedMessageLength := len(notTruncatedFCMMessage.Data["message"])
-	serializedNotTruncatedFCMMessage, _ := json.Marshal(notTruncatedFCMMessage)
-	require.Equal(t, origMessageLength, notTruncatedMessageLength)
-	require.Equal(t, len(serializedOrigFCMMessage), len(serializedNotTruncatedFCMMessage))
-	require.Equal(t, "", notTruncatedFCMMessage.Data["truncated"])
+	r, _ = http.NewRequest("GET", "https://ntfy.sh/mytopic", nil)
+	up = readBoolParam(r, false, "x-up", "up")
+	firebase = readBoolParam(r, true, "x-up", "up")
+	require.Equal(t, false, up)
+	require.Equal(t, true, firebase)
 }