diff --git a/clients/go/mxgateway/client_session_test.go b/clients/go/mxgateway/client_session_test.go index 343aed7..5cffc29 100644 --- a/clients/go/mxgateway/client_session_test.go +++ b/clients/go/mxgateway/client_session_test.go @@ -363,6 +363,89 @@ func TestBulkMethodsShortCircuitOnEmptySliceWithoutRoundTrip(t *testing.T) { } } +func TestWrite2BuildsCommandWithTimestampAndReturnsNoError(t *testing.T) { + fake := &fakeGatewayServer{ + invokeReply: &pb.MxCommandReply{ + SessionId: "session-1", + Kind: pb.MxCommandKind_MX_COMMAND_KIND_WRITE2, + ProtocolStatus: &pb.ProtocolStatus{ + Code: pb.ProtocolStatusCode_PROTOCOL_STATUS_CODE_OK, + }, + }, + } + client, cleanup := newBufconnClient(t, fake) + defer cleanup() + session := NewSessionForID(client, "session-1") + + val := Int32Value(99) + ts := Int32Value(77) + err := session.Write2(context.Background(), 12, 34, val, ts, 100) + if err != nil { + t.Fatalf("Write2() error = %v", err) + } + + req := fake.invokeRequest + if req.GetCommand().GetKind() != pb.MxCommandKind_MX_COMMAND_KIND_WRITE2 { + t.Fatalf("command kind = %s, want WRITE2", req.GetCommand().GetKind()) + } + w2 := req.GetCommand().GetWrite2() + if w2.GetServerHandle() != 12 { + t.Fatalf("server handle = %d, want 12", w2.GetServerHandle()) + } + if w2.GetItemHandle() != 34 { + t.Fatalf("item handle = %d, want 34", w2.GetItemHandle()) + } + if w2.GetValue().GetInt32Value() != 99 { + t.Fatalf("value int32 = %d, want 99", w2.GetValue().GetInt32Value()) + } + if w2.GetTimestampValue().GetInt32Value() != 77 { + t.Fatalf("timestamp value int32 = %d, want 77", w2.GetTimestampValue().GetInt32Value()) + } + if w2.GetUserId() != 100 { + t.Fatalf("user id = %d, want 100", w2.GetUserId()) + } +} + +func TestWrite2RawReturnsRawReply(t *testing.T) { + fake := &fakeGatewayServer{ + invokeReply: &pb.MxCommandReply{ + SessionId: "session-1", + Kind: pb.MxCommandKind_MX_COMMAND_KIND_WRITE2, + ProtocolStatus: &pb.ProtocolStatus{ + Code: pb.ProtocolStatusCode_PROTOCOL_STATUS_CODE_OK, + }, + }, + } + client, cleanup := newBufconnClient(t, fake) + defer cleanup() + session := NewSessionForID(client, "session-1") + + reply, err := session.Write2Raw(context.Background(), 12, 34, Int32Value(1), Int32Value(0), 0) + if err != nil { + t.Fatalf("Write2Raw() error = %v", err) + } + if reply == nil { + t.Fatal("Write2Raw() returned nil reply") + } + if reply.GetKind() != pb.MxCommandKind_MX_COMMAND_KIND_WRITE2 { + t.Fatalf("reply kind = %s, want WRITE2", reply.GetKind()) + } +} + +func TestWrite2RejectsNilValue(t *testing.T) { + fake := &fakeGatewayServer{} + client, cleanup := newBufconnClient(t, fake) + defer cleanup() + session := NewSessionForID(client, "session-1") + + if err := session.Write2(context.Background(), 12, 34, nil, Int32Value(0), 0); err == nil { + t.Fatal("Write2(nil value) returned no error") + } + if err := session.Write2(context.Background(), 12, 34, Int32Value(1), nil, 0); err == nil { + t.Fatal("Write2(nil timestampValue) returned no error") + } +} + func TestReadBulkForwardsTimeoutAndUnpacksCachedFlag(t *testing.T) { fake := &fakeGatewayServer{ invokeReply: &pb.MxCommandReply{ diff --git a/clients/go/mxgateway/session.go b/clients/go/mxgateway/session.go index f9e2393..e2b9a46 100644 --- a/clients/go/mxgateway/session.go +++ b/clients/go/mxgateway/session.go @@ -580,6 +580,35 @@ func (s *Session) WriteRaw(ctx context.Context, serverHandle, itemHandle int32, }) } +// Write2 invokes MXAccess Write2 (timestamped single-item write). +func (s *Session) Write2(ctx context.Context, serverHandle, itemHandle int32, value, timestampValue *MxValue, userID int32) error { + _, err := s.Write2Raw(ctx, serverHandle, itemHandle, value, timestampValue, userID) + return err +} + +// Write2Raw invokes MXAccess Write2 (timestamped single-item write) and returns the raw reply. +func (s *Session) Write2Raw(ctx context.Context, serverHandle, itemHandle int32, value, timestampValue *MxValue, userID int32) (*MxCommandReply, error) { + if value == nil { + return nil, errors.New("mxgateway: write2 value is required") + } + if timestampValue == nil { + return nil, errors.New("mxgateway: write2 timestamp value is required") + } + + return s.invokeCommand(ctx, &pb.MxCommand{ + Kind: pb.MxCommandKind_MX_COMMAND_KIND_WRITE2, + Payload: &pb.MxCommand_Write2{ + Write2: &pb.Write2Command{ + ServerHandle: serverHandle, + ItemHandle: itemHandle, + Value: value, + TimestampValue: timestampValue, + UserId: userID, + }, + }, + }) +} + // Events streams ordered session events until the server ends the stream, // context cancellation stops Recv, or a terminal error is sent. func (s *Session) Events(ctx context.Context) (<-chan EventResult, error) {