Repository: gopcua/opcua
Pull request #509 (2021-11-22 12:45:16)

client: add test for ReadRequest defaults
This patch factors out the code for setting defaults of a ReadRequest
and adds a test for it.


Patch:
diff --git a/client.go b/client.go
index 9c64ce6..d2f125f 100644
--- a/client.go
+++ b/client.go
@@ -783,13 +783,7 @@ func (c *Client) GetEndpoints() (*ua.GetEndpointsResponse, error) {
 	return res, err
 }
 
-// Read executes a synchronous read request.
-//
-// By default, the function requests the value of the nodes
-// in the default encoding of the server.
-func (c *Client) Read(req *ua.ReadRequest) (*ua.ReadResponse, error) {
-	// clone the request and the ReadValueIDs to set defaults without
-	// manipulating them in-place.
+func cloneReadRequest(req *ua.ReadRequest) *ua.ReadRequest {
 	rvs := make([]*ua.ReadValueID, len(req.NodesToRead))
 	for i, rv := range req.NodesToRead {
 		rc := &ua.ReadValueID{}
@@ -802,11 +796,21 @@ func (c *Client) Read(req *ua.ReadRequest) (*ua.ReadResponse, error) {
 		}
 		rvs[i] = rc
 	}
-	req = &ua.ReadRequest{
+	return &ua.ReadRequest{
 		MaxAge:             req.MaxAge,
 		TimestampsToReturn: req.TimestampsToReturn,
 		NodesToRead:        rvs,
 	}
+}
+
+// Read executes a synchronous read request.
+//
+// By default, the function requests the value of the nodes
+// in the default encoding of the server.
+func (c *Client) Read(req *ua.ReadRequest) (*ua.ReadResponse, error) {
+	// clone the request and the ReadValueIDs to set defaults without
+	// manipulating them in-place.
+	req = cloneReadRequest(req)
 
 	var res *ua.ReadResponse
 	err := c.Send(req, func(v interface{}) error {


Test patch:
diff --git a/client_test.go b/client_test.go
index d7ba3d8..85c8bfb 100644
--- a/client_test.go
+++ b/client_test.go
@@ -1,9 +1,10 @@
 package opcua
 
 import (
+	"testing"
+
 	"github.com/gopcua/opcua/ua"
 	"github.com/pascaldekloe/goe/verify"
-	"testing"
 )
 
 func TestClient_Send_DoesNotPanicWhenDisconnected(t *testing.T) {
@@ -13,3 +14,87 @@ func TestClient_Send_DoesNotPanicWhenDisconnected(t *testing.T) {
 	})
 	verify.Values(t, "", err, ua.StatusBadServerNotConnected)
 }
+
+func TestCloneReadRequest(t *testing.T) {
+	tests := []struct {
+		name      string
+		req, want *ua.ReadRequest
+	}{
+		{
+			name: "empty",
+			req:  &ua.ReadRequest{},
+			want: &ua.ReadRequest{
+				NodesToRead: []*ua.ReadValueID{},
+			},
+		},
+		{
+			name: "keep values",
+			req: &ua.ReadRequest{
+				MaxAge:             1,
+				TimestampsToReturn: 5,
+			},
+			want: &ua.ReadRequest{
+				MaxAge:             1,
+				TimestampsToReturn: 5,
+				NodesToRead:        []*ua.ReadValueID{},
+			},
+		},
+		{
+			name: "set ReadValueID defaults",
+			req: &ua.ReadRequest{
+				NodesToRead: []*ua.ReadValueID{
+					{
+						NodeID:     ua.MustParseNodeID("i=85"),
+						IndexRange: "abc",
+					},
+				},
+			},
+			want: &ua.ReadRequest{
+				NodesToRead: []*ua.ReadValueID{
+					{
+						NodeID:       ua.MustParseNodeID("i=85"),
+						AttributeID:  ua.AttributeIDValue,
+						IndexRange:   "abc",
+						DataEncoding: &ua.QualifiedName{},
+					},
+				},
+			},
+		},
+		{
+			name: "keep ReadValueID values",
+			req: &ua.ReadRequest{
+				NodesToRead: []*ua.ReadValueID{
+					{
+						NodeID:      ua.MustParseNodeID("i=85"),
+						AttributeID: 15,
+						IndexRange:  "abc",
+						DataEncoding: &ua.QualifiedName{
+							NamespaceIndex: 5,
+							Name:           "xxx",
+						},
+					},
+				},
+			},
+			want: &ua.ReadRequest{
+				NodesToRead: []*ua.ReadValueID{
+					{
+						NodeID:      ua.MustParseNodeID("i=85"),
+						AttributeID: 15,
+						IndexRange:  "abc",
+						DataEncoding: &ua.QualifiedName{
+							NamespaceIndex: 5,
+							Name:           "xxx",
+						},
+					},
+				},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := cloneReadRequest(tt.req)
+			verify.Values(t, "", got, tt.want)
+		})
+	}
+}