Repository: ably/ably-go
Pull request #588 (2023-03-06 18:31:18)

Run unit tests based on msgpack_test_fixtures
Run unit tests using fixtures from  https://github.com/ably/ably-common/blob/main/test-resources/messages-encoding.json and https://github.com/ably/ably-common/blob/main/test-resources/msgpack_test_fixtures.json
Ensure that the json and msgpack messages are decoded correctly, and that re-encoding the decoded message 
gets you back to the original fixture.


Patch:
diff --git a/common b/common
index 4b3ecbd..e4a5c76 160000
--- a/common
+++ b/common
@@ -1,1 +1,1 @@
-Subproject commit 4b3ecbde6b03fdc4a2e056007598997aeb8ce167
+Subproject commit e4a5c7692044807e3011b959426868b5075c998e
diff --git a/go.mod b/go.mod
index 9ff9d62..e51cf50 100644
--- a/go.mod
+++ b/go.mod
@@ -1,5 +1,7 @@
 module github.com/ably/ably-go
 
+go 1.18
+
 require (
 	github.com/stretchr/testify v1.7.1
 	github.com/ugorji/go/codec v1.1.9
@@ -13,5 +15,3 @@ require (
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
 )
-
-go 1.17
diff --git a/go.sum b/go.sum
index 59a3d2b..0ba7b45 100644
--- a/go.sum
+++ b/go.sum
@@ -46,7 +46,6 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
 github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
-github.com/ugorji/go v1.1.9 h1:SObrQTaSuP8WOv2WNCj8gECiNSJIUvk3Q7N26c96Gws=
 github.com/ugorji/go v1.1.9/go.mod h1:chLrngdsg43geAaeId+nXO57YsDdl5OZqd/QtBiD19g=
 github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
 github.com/ugorji/go/codec v1.1.9 h1:J/7hhpkQwgypRNvaeh/T5gzJ2gEI/l8S3qyRrdEa1fA=


Test patch:
diff --git a/ably/proto_message_decoding_test.go b/ably/proto_message_decoding_test.go
new file mode 100644
index 0000000..060e740
--- /dev/null
+++ b/ably/proto_message_decoding_test.go
@@ -0,0 +1,152 @@
+package ably
+
+import (
+	"bytes"
+	"encoding/base64"
+	"encoding/hex"
+	"encoding/json"
+	"os"
+	"strings"
+	"testing"
+
+	"github.com/ably/ably-go/ably/internal/ablyutil"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+type fixture struct {
+	Data             string `json:"data"`
+	Encoding         string `json:"encoding"`
+	ExpectedType     string `json:"expectedType"`
+	ExpectedValue    any    `json:"expectedValue"`
+	ExpectedHexValue string `json:"expectedHexValue"`
+}
+
+func loadFixtures() ([]fixture, error) {
+	var dec struct {
+		Messages []fixture
+	}
+
+	// We can't embed the fixtures as go:embed forbids embedding of resources above the current directory.
+	text, err := os.ReadFile("../common/test-resources/messages-encoding.json")
+	if err != nil {
+		return nil, err
+	}
+
+	err = json.Unmarshal(text, &dec)
+	return dec.Messages, err
+}
+
+func Test_decodeMessage(t *testing.T) {
+	fixtures, err := loadFixtures()
+	require.NoError(t, err, "failed to load test fixtures")
+	for _, f := range fixtures {
+		t.Run(f.Data, func(t *testing.T) {
+			msg := Message{
+				Data:     f.Data,
+				Encoding: f.Encoding,
+			}
+			decodedMsg, err := msg.withDecodedData(nil)
+			require.NoError(t, err)
+			switch f.ExpectedType {
+			case "string":
+				assert.IsType(t, "string", decodedMsg.Data)
+				assert.Equal(t, f.ExpectedValue, decodedMsg.Data)
+			case "jsonObject":
+				assert.IsType(t, map[string]any{}, decodedMsg.Data)
+				assert.Equal(t, f.ExpectedValue, decodedMsg.Data)
+
+			case "jsonArray":
+				assert.IsType(t, []any{}, decodedMsg.Data)
+				assert.Equal(t, f.ExpectedValue, decodedMsg.Data)
+			case "binary":
+				assert.IsType(t, []byte{}, decodedMsg.Data)
+				if f.ExpectedHexValue != "" {
+					value, err := hex.DecodeString(f.ExpectedHexValue)
+					require.NoError(t, err)
+					assert.Equal(t, value, decodedMsg.Data)
+				} else if f.ExpectedValue != nil {
+					assert.Equal(t, []byte(f.ExpectedValue.(string)), decodedMsg.Data)
+				}
+			}
+
+			// Test that the re-encoding of the decoded message gives us back the original fixture.
+			reEncoded, err := decodedMsg.withEncodedData(nil)
+			require.NoError(t, err)
+			assert.Equal(t, f.Encoding, reEncoded.Encoding)
+			if f.Encoding == "json" {
+				require.IsType(t, "", reEncoded.Data)
+				// json fields could be re-ordered so assert.Equal could fail.
+				assert.JSONEq(t, f.Data, reEncoded.Data.(string))
+			} else {
+				assert.Equal(t, f.Data, reEncoded.Data)
+			}
+		})
+	}
+}
+
+type MsgpackTestFixture struct {
+	Name      string `json:"name"`
+	Data      any    `json:"data"`
+	Encoding  string `json:"encoding"`
+	NumRepeat int    `json:"numRepeat"`
+	Type      string `json:"type"`
+	MsgPack   string `json:"msgpack"`
+}
+
+func loadMsgpackFixtures() ([]MsgpackTestFixture, error) {
+	var o []MsgpackTestFixture
+
+	// We can't embed the fixtures as go:embed forbids embedding of resources above the current directory.
+	text, err := os.ReadFile("../common/test-resources/msgpack_test_fixtures.json")
+	if err != nil {
+		return nil, err
+	}
+
+	err = json.Unmarshal(text, &o)
+	return o, err
+}
+
+func TestMsgpackDecoding(t *testing.T) {
+	msgpackTestFixtures, err := loadMsgpackFixtures()
+	require.NoError(t, err)
+
+	for _, f := range msgpackTestFixtures {
+		t.Run(f.Name, func(t *testing.T) {
+			msgpackData := make([]byte, len(f.MsgPack))
+			n, err := base64.StdEncoding.Decode(msgpackData, []byte(f.MsgPack))
+			require.NoError(t, err)
+			msgpackData = msgpackData[:n]
+
+			var protoMsg ProtocolMessage
+			err = ablyutil.UnmarshalMsgpack(msgpackData, &protoMsg)
+			require.NoError(t, err)
+
+			msg := protoMsg.Messages[0]
+			decodedMsg, err := msg.withDecodedData(nil)
+			switch f.Type {
+			case "string":
+				require.IsType(t, "string", decodedMsg.Data)
+				assert.Equal(t, f.NumRepeat, len(decodedMsg.Data.(string)))
+				assert.Equal(t, strings.Repeat(f.Data.(string), f.NumRepeat), decodedMsg.Data.(string))
+			case "binary":
+				require.IsType(t, []byte{}, decodedMsg.Data)
+				assert.Equal(t, f.NumRepeat, len(decodedMsg.Data.([]byte)))
+				assert.Equal(t, bytes.Repeat([]byte(f.Data.(string)), f.NumRepeat), decodedMsg.Data.([]byte))
+			case "jsonObject", "jsonArray":
+				assert.Equal(t, f.Data, decodedMsg.Data)
+			}
+
+			// Now re-encode and check that we get back the original message.
+			reencodedMsg, err := msg.withEncodedData(nil)
+			require.NoError(t, err)
+			newMsg := ProtocolMessage{
+				Messages: []*Message{&reencodedMsg},
+			}
+			newBytes, err := ablyutil.MarshalMsgpack(newMsg)
+			require.NoError(t, err)
+			assert.Equal(t, msgpackData, newBytes)
+
+		})
+	}
+}