From 297caeaa8d8672e9d406b377bf32f240d0bb8a51 Mon Sep 17 00:00:00 2001 From: Niharika Bhavaraju Date: Thu, 16 Jan 2025 19:22:49 +0000 Subject: [PATCH 1/9] Implemented Time Command Signed-off-by: Niharika Bhavaraju --- go/api/base_client.go | 8 ++++++ go/api/server_management_commands.go | 14 +++++++++ go/integTest/standalone_commands_test.go | 36 ++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/go/api/base_client.go b/go/api/base_client.go index 1a67892934..60344e6f87 100644 --- a/go/api/base_client.go +++ b/go/api/base_client.go @@ -1905,3 +1905,11 @@ func (client *baseClient) ZScanWithOptions( } return handleScanResponse(result) } + +func (client *baseClient) Time() ([]Result[string], error) { + result, err := client.executeCommand(C.Time, []string{}) + if err != nil { + return nil, err + } + return handleStringArrayResponse(result) +} diff --git a/go/api/server_management_commands.go b/go/api/server_management_commands.go index 37954f543a..c4c3c8d5e4 100644 --- a/go/api/server_management_commands.go +++ b/go/api/server_management_commands.go @@ -61,4 +61,18 @@ type ServerManagementCommands interface { // // [valkey.io]: https://valkey.io/commands/config-set/ ConfigSet(parameters map[string]string) (string, error) + + // Returns the server time. + // + // Return value: + // The current server time as a String array with two elements: + // A UNIX TIME and the amount of microseconds already elapsed in the current second. + // The returned array is in a [UNIX TIME, Microseconds already elapsed] format. + // + // For example: + // result, err := client.Time() + // result: [{1737051660 false} {994688 false}] + // + // [valkey.io]: https://valkey.io/commands/time/ + Time() ([]Result[string], error) } diff --git a/go/integTest/standalone_commands_test.go b/go/integTest/standalone_commands_test.go index 063e884a5d..e3c78ce391 100644 --- a/go/integTest/standalone_commands_test.go +++ b/go/integTest/standalone_commands_test.go @@ -4,6 +4,7 @@ package integTest import ( "fmt" + "strconv" "strings" "github.com/google/uuid" @@ -273,3 +274,38 @@ func (suite *GlideTestSuite) TestSelect_SwitchBetweenDatabases() { assert.Nil(suite.T(), err) assert.Equal(suite.T(), value2, result.Value()) } + +func (suite *GlideTestSuite) TestTime_Success() { + client := suite.defaultClient() + + results, err := client.Time() + assert.Nil(suite.T(), err) + assert.Len(suite.T(), results, 2) + + timestamp := results[0].Value() + microseconds := results[1].Value() + + // Validate Unix timestamp + if _, err := strconv.ParseInt(timestamp, 10, 64); err != nil { + suite.T().Fatalf("Expected a valid Unix timestamp but got %s", timestamp) + } + + // Validate microseconds + microsecondsInt, err := strconv.ParseInt(microseconds, 10, 64) + if err != nil || microsecondsInt < 0 || microsecondsInt >= 1000000 { + suite.T().Fatalf("Expected a valid microseconds value between 0 and 999999 but got %s", microseconds) + } +} + +func (suite *GlideTestSuite) TestTime_Error() { + client := suite.defaultClient() + + // Disconnect the client or simulate an error condition + client.Close() + + results, err := client.Time() + + assert.NotNil(suite.T(), err) + assert.Nil(suite.T(), results) + assert.IsType(suite.T(), &api.ClosingError{}, err) +} From 1244741493d8e844d85045837d189dcbe1bcb390 Mon Sep 17 00:00:00 2001 From: Niharika Bhavaraju Date: Fri, 17 Jan 2025 11:56:23 +0000 Subject: [PATCH 2/9] Fixed code review changes Signed-off-by: Niharika Bhavaraju --- go/api/base_client.go | 17 ++++++++++++++-- go/api/response_handlers.go | 26 ++++++++++++++++++++++++ go/api/server_management_commands.go | 14 +------------ go/integTest/standalone_commands_test.go | 21 ++++++++----------- 4 files changed, 51 insertions(+), 27 deletions(-) diff --git a/go/api/base_client.go b/go/api/base_client.go index ad24f84890..69bb58c238 100644 --- a/go/api/base_client.go +++ b/go/api/base_client.go @@ -2033,10 +2033,23 @@ func (client *baseClient) ObjectEncoding(key string) (Result[string], error) { return handleStringOrNilResponse(result) } -func (client *baseClient) Time() ([]Result[string], error) { +// Returns the server time. +// +// Return value: +// The current server time as a String array with two elements: +// A UNIX TIME and the amount of microseconds already elapsed in the current second. +// The returned array is in a [UNIX TIME, Microseconds already elapsed] format. +// +// For example: +// +// result, err := client.Time() +// result: [{1737051660} {994688}] +// +// [valkey.io]: https://valkey.io/commands/time/ +func (client *baseClient) Time() ([]string, error) { result, err := client.executeCommand(C.Time, []string{}) if err != nil { return nil, err } - return handleStringArrayResponse(result) + return handleRawStringArrayResponse(result) } diff --git a/go/api/response_handlers.go b/go/api/response_handlers.go index 07ab7e1a09..96fb37d0b0 100644 --- a/go/api/response_handlers.go +++ b/go/api/response_handlers.go @@ -693,3 +693,29 @@ func handleXPendingDetailResponse(response *C.struct_CommandResponse) ([]XPendin return pendingDetails, nil } + +func handleRawStringArrayResponse(response *C.struct_CommandResponse) ([]string, error) { + defer C.free_command_response(response) + + typeErr := checkResponseType(response, C.Array, false) + if typeErr != nil { + return nil, typeErr + } + + slice := make([]string, 0, response.array_value_len) + for _, v := range unsafe.Slice(response.array_value, response.array_value_len) { + err := checkResponseType(&v, C.String, false) + if err != nil { + return nil, err + } + + if v.string_value == nil { + return nil, &RequestError{"Unexpected nil string in array"} + } + + byteSlice := C.GoBytes(unsafe.Pointer(v.string_value), C.int(int64(v.string_value_len))) + slice = append(slice, string(byteSlice)) + } + + return slice, nil +} diff --git a/go/api/server_management_commands.go b/go/api/server_management_commands.go index c4c3c8d5e4..6dbcdb3084 100644 --- a/go/api/server_management_commands.go +++ b/go/api/server_management_commands.go @@ -62,17 +62,5 @@ type ServerManagementCommands interface { // [valkey.io]: https://valkey.io/commands/config-set/ ConfigSet(parameters map[string]string) (string, error) - // Returns the server time. - // - // Return value: - // The current server time as a String array with two elements: - // A UNIX TIME and the amount of microseconds already elapsed in the current second. - // The returned array is in a [UNIX TIME, Microseconds already elapsed] format. - // - // For example: - // result, err := client.Time() - // result: [{1737051660 false} {994688 false}] - // - // [valkey.io]: https://valkey.io/commands/time/ - Time() ([]Result[string], error) + Time() ([]string, error) } diff --git a/go/integTest/standalone_commands_test.go b/go/integTest/standalone_commands_test.go index e3c78ce391..0b4e976f35 100644 --- a/go/integTest/standalone_commands_test.go +++ b/go/integTest/standalone_commands_test.go @@ -6,6 +6,7 @@ import ( "fmt" "strconv" "strings" + "time" "github.com/google/uuid" "github.com/valkey-io/valkey-glide/go/glide/api" @@ -277,24 +278,20 @@ func (suite *GlideTestSuite) TestSelect_SwitchBetweenDatabases() { func (suite *GlideTestSuite) TestTime_Success() { client := suite.defaultClient() - results, err := client.Time() + assert.Nil(suite.T(), err) assert.Len(suite.T(), results, 2) - timestamp := results[0].Value() - microseconds := results[1].Value() + now := time.Now().Unix() - 1 - // Validate Unix timestamp - if _, err := strconv.ParseInt(timestamp, 10, 64); err != nil { - suite.T().Fatalf("Expected a valid Unix timestamp but got %s", timestamp) - } + timestamp, err := strconv.ParseInt(results[0], 10, 64) + assert.Nil(suite.T(), err) + assert.Greater(suite.T(), timestamp, now) - // Validate microseconds - microsecondsInt, err := strconv.ParseInt(microseconds, 10, 64) - if err != nil || microsecondsInt < 0 || microsecondsInt >= 1000000 { - suite.T().Fatalf("Expected a valid microseconds value between 0 and 999999 but got %s", microseconds) - } + microseconds, err := strconv.ParseInt(results[1], 10, 64) + assert.Nil(suite.T(), err) + assert.Less(suite.T(), microseconds, int64(1000000)) } func (suite *GlideTestSuite) TestTime_Error() { From 3716426ff2fbdaab8f385b9901f54407e7ff713f Mon Sep 17 00:00:00 2001 From: Niharika Bhavaraju Date: Sun, 19 Jan 2025 18:29:13 +0000 Subject: [PATCH 3/9] Added Time command (cluster) Signed-off-by: Niharika Bhavaraju --- go/api/glide_cluster_client.go | 58 ++++++++++++++++++++ go/api/response_handlers.go | 38 +++++++++++++ go/api/server_management_cluster_commands.go | 12 ++++ go/integTest/cluster_commands_test.go | 41 ++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 go/api/server_management_cluster_commands.go diff --git a/go/api/glide_cluster_client.go b/go/api/glide_cluster_client.go index cc672a91b5..00ebb96545 100644 --- a/go/api/glide_cluster_client.go +++ b/go/api/glide_cluster_client.go @@ -13,6 +13,7 @@ var _ GlideClusterClient = (*glideClusterClient)(nil) type GlideClusterClient interface { BaseClient GenericClusterCommands + ServerManagementClusterCommands } // glideClusterClient implements cluster mode operations by extending baseClient functionality. @@ -41,3 +42,60 @@ func (client *glideClusterClient) CustomCommand(args []string) (ClusterValue[int } return CreateClusterValue(data), nil } + +// Returns the server time. +// +// See [valkey.io] for details. +// +// Parameters: +// +// route - Specifies the routing configuration for the command. The client will route the +// command to the nodes defined by route. +// +// Return value: +// +// The current server time as a String array with two elements: A UNIX TIME and the amount +// of microseconds already elapsed in the current second. +// The returned array is in a [UNIX TIME, Microseconds already elapsed] format. +// +// Example: +// +// route := api.SimpleNodeRoute(api.RandomRoute) +// result, err := client.Time(route) +// +// fmt.Println(result.Value()) // Output: [1737285074 67888] +// +// [valkey.io]: https://valkey.io/commands/time/ +func (client *glideClusterClient) Time(route route) (ClusterValue[[]string], error) { + res, err := client.executeCommandWithRoute(C.Time, []string{}, route) + if err != nil { + return ClusterValue[[]string]{ + value: Result[[]string]{isNil: true}, + }, err + } + + if err := checkResponseType(res, C.Map, true); err == nil { + + // Multi-node response + mapData, err := handleRawStringArrayMapResponse(res) + if err != nil { + return ClusterValue[[]string]{ + value: Result[[]string]{isNil: true}, + }, err + } + var times []string + for _, nodeTimes := range mapData { + times = append(times, nodeTimes...) + } + return CreateClusterMultiValue(times), nil + } + + // Single node response + data, err := handleRawStringArrayResponse(res) + if err != nil { + return ClusterValue[[]string]{ + value: Result[[]string]{isNil: true}, + }, err + } + return CreateClusterSingleValue(data), nil +} diff --git a/go/api/response_handlers.go b/go/api/response_handlers.go index 33ba00d78a..9394945560 100644 --- a/go/api/response_handlers.go +++ b/go/api/response_handlers.go @@ -881,3 +881,41 @@ func handleRawStringArrayResponse(response *C.struct_CommandResponse) ([]string, return slice, nil } + +func handleRawStringArrayMapResponse(response *C.struct_CommandResponse) (map[string][]string, error) { + defer C.free_command_response(response) + typeErr := checkResponseType(response, C.Map, false) + if typeErr != nil { + return nil, typeErr + } + + result := make(map[string][]string) + for _, v := range unsafe.Slice(response.array_value, response.array_value_len) { + key, err := convertCharArrayToString(v.map_key, true) + if err != nil { + return nil, err + } + + err = checkResponseType(v.map_value, C.Array, false) + if err != nil { + return nil, err + } + + timeStrings := make([]string, 0, v.map_value.array_value_len) + for _, strVal := range unsafe.Slice(v.map_value.array_value, v.map_value.array_value_len) { + err := checkResponseType(&strVal, C.String, false) + if err != nil { + return nil, err + } + if strVal.string_value == nil { + return nil, &RequestError{"Unexpected nil string in array"} + } + byteSlice := C.GoBytes(unsafe.Pointer(strVal.string_value), C.int(int64(strVal.string_value_len))) + timeStrings = append(timeStrings, string(byteSlice)) + } + + result[key.Value()] = timeStrings + } + + return result, nil +} diff --git a/go/api/server_management_cluster_commands.go b/go/api/server_management_cluster_commands.go new file mode 100644 index 0000000000..c7bfa39214 --- /dev/null +++ b/go/api/server_management_cluster_commands.go @@ -0,0 +1,12 @@ +// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 + +package api + +// ServerManagementClusterCommands supports commands for the "Server Management Commands" group for cluster client. +// +// See [valkey.io] for details. +// +// [valkey.io]: https://valkey.io/commands/#server +type ServerManagementClusterCommands interface { + Time(route route) (ClusterValue[[]string], error) +} diff --git a/go/integTest/cluster_commands_test.go b/go/integTest/cluster_commands_test.go index 142f0cf273..6241bd79d4 100644 --- a/go/integTest/cluster_commands_test.go +++ b/go/integTest/cluster_commands_test.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/stretchr/testify/assert" + "github.com/valkey-io/valkey-glide/go/glide/api" ) func (suite *GlideTestSuite) TestClusterCustomCommandInfo() { @@ -27,3 +28,43 @@ func (suite *GlideTestSuite) TestClusterCustomCommandEcho() { // ECHO is routed to a single random node assert.Equal(suite.T(), "GO GLIDE GO", result.Value().(string)) } + +func (suite *GlideTestSuite) TestTime_RandomRoute() { + client := suite.defaultClusterClient() + route := api.RandomRoute + + result, err := client.Time(route) + + assert.NoError(suite.T(), err) + assert.NotNil(suite.T(), result) + assert.NotEmpty(suite.T(), result.Value()) + assert.IsType(suite.T(), "", result.Value()[0]) + assert.Equal(suite.T(), 2, len(result.Value())) +} + +func (suite *GlideTestSuite) TestTime_AllNodes_MultipleValues() { + client := suite.defaultClusterClient() + route := api.AllNodes + + result, err := client.Time(route) + + assert.NoError(suite.T(), err) + assert.NotNil(suite.T(), result) + assert.NotEmpty(suite.T(), result.Value()) + + assert.Greater(suite.T(), len(result.Value()), 1) + + for _, timeStr := range result.Value() { + assert.IsType(suite.T(), "", timeStr) + } +} + +func (suite *GlideTestSuite) TestTime_ErrorHandling() { + client := suite.defaultClusterClient() + invalidRoute := api.NewByAddressRoute("invalidHost", 9999) + + result, err := client.Time(invalidRoute) + + assert.NotNil(suite.T(), err) + assert.Empty(suite.T(), result.Value()) +} From bcc00a7fd46f78306d74a2b88f18baed3074b038 Mon Sep 17 00:00:00 2001 From: Niharika Bhavaraju Date: Wed, 22 Jan 2025 18:31:07 +0000 Subject: [PATCH 4/9] Fixed code review comments Signed-off-by: Niharika Bhavaraju --- go/api/base_client.go | 27 ++- go/api/command_options.go | 11 +- go/api/{ => config}/request_routing_config.go | 35 +-- go/api/{ => errors}/errors.go | 28 ++- go/api/glide_cluster_client.go | 42 +--- go/api/options/time_options.go | 16 ++ go/api/response_handlers.go | 80 ++++--- go/api/response_types.go | 7 + go/api/server_management_cluster_commands.go | 4 +- go/integTest/cluster_commands_test.go | 21 +- go/integTest/connection_test.go | 3 +- .../request_routing_config_test.go | 27 +-- go/integTest/shared_commands_test.go | 219 +++++++++--------- go/integTest/standalone_commands_test.go | 15 +- 14 files changed, 284 insertions(+), 251 deletions(-) rename go/api/{ => config}/request_routing_config.go (85%) rename go/api/{ => errors}/errors.go (70%) create mode 100644 go/api/options/time_options.go rename go/{api => integTest}/request_routing_config_test.go (69%) diff --git a/go/api/base_client.go b/go/api/base_client.go index c633af232a..cdadc4cd14 100644 --- a/go/api/base_client.go +++ b/go/api/base_client.go @@ -10,12 +10,13 @@ package api import "C" import ( - "errors" "fmt" "math" "strconv" "unsafe" + "github.com/valkey-io/valkey-glide/go/glide/api/config" + "github.com/valkey-io/valkey-glide/go/glide/api/errors" "github.com/valkey-io/valkey-glide/go/glide/api/options" "github.com/valkey-io/valkey-glide/go/glide/protobuf" "github.com/valkey-io/valkey-glide/go/glide/utils" @@ -54,8 +55,10 @@ func successCallback(channelPtr unsafe.Pointer, cResponse *C.struct_CommandRespo //export failureCallback func failureCallback(channelPtr unsafe.Pointer, cErrorMessage *C.char, cErrorType C.RequestErrorType) { + defer C.free_error_message(cErrorMessage) + msg := C.GoString(cErrorMessage) resultChannel := *(*chan payload)(channelPtr) - resultChannel <- payload{value: nil, error: goError(cErrorType, cErrorMessage)} + resultChannel <- payload{value: nil, error: errors.GoError(uint32(cErrorType), msg)} } type clientConfiguration interface { @@ -92,7 +95,7 @@ func createClient(config clientConfiguration) (*baseClient, error) { cErr := cResponse.connection_error_message if cErr != nil { message := C.GoString(cErr) - return nil, &ConnectionError{message} + return nil, &errors.ConnectionError{Msg: message} } return &baseClient{cResponse.conn_ptr}, nil @@ -115,10 +118,10 @@ func (client *baseClient) executeCommand(requestType C.RequestType, args []strin func (client *baseClient) executeCommandWithRoute( requestType C.RequestType, args []string, - route route, + route config.Route, ) (*C.struct_CommandResponse, error) { if client.coreClient == nil { - return nil, &ClosingError{"ExecuteCommand failed. The client is closed."} + return nil, &errors.ClosingError{Msg: "ExecuteCommand failed. The client is closed."} } var cArgsPtr *C.uintptr_t = nil var argLengthsPtr *C.ulong = nil @@ -134,9 +137,9 @@ func (client *baseClient) executeCommandWithRoute( var routeBytesPtr *C.uchar = nil var routeBytesCount C.uintptr_t = 0 if route != nil { - routeProto, err := route.toRoutesProtobuf() + routeProto, err := route.ToRoutesProtobuf() if err != nil { - return nil, &RequestError{"ExecuteCommand failed due to invalid route"} + return nil, &errors.RequestError{Msg: "ExecuteCommand failed due to invalid route"} } msg, err := proto.Marshal(routeProto) if err != nil { @@ -357,7 +360,7 @@ func (client *baseClient) LCS(key1 string, key2 string) (string, error) { func (client *baseClient) GetDel(key string) (Result[string], error) { if key == "" { - return CreateNilStringResult(), errors.New("key is required") + return CreateNilStringResult(), &errors.RequestError{Msg: "key is required"} } result, err := client.executeCommand(C.GetDel, []string{key}) @@ -1134,7 +1137,7 @@ func (client *baseClient) LMPop(keys []string, listDirection ListDirection) (map // Check for potential length overflow. if len(keys) > math.MaxInt-2 { - return nil, &RequestError{"Length overflow for the provided keys"} + return nil, &errors.RequestError{Msg: "Length overflow for the provided keys"} } // args slice will have 2 more arguments with the keys provided. @@ -1162,7 +1165,7 @@ func (client *baseClient) LMPopCount( // Check for potential length overflow. if len(keys) > math.MaxInt-4 { - return nil, &RequestError{"Length overflow for the provided keys"} + return nil, &errors.RequestError{Msg: "Length overflow for the provided keys"} } // args slice will have 4 more arguments with the keys provided. @@ -1190,7 +1193,7 @@ func (client *baseClient) BLMPop( // Check for potential length overflow. if len(keys) > math.MaxInt-3 { - return nil, &RequestError{"Length overflow for the provided keys"} + return nil, &errors.RequestError{Msg: "Length overflow for the provided keys"} } // args slice will have 3 more arguments with the keys provided. @@ -1219,7 +1222,7 @@ func (client *baseClient) BLMPopCount( // Check for potential length overflow. if len(keys) > math.MaxInt-5 { - return nil, &RequestError{"Length overflow for the provided keys"} + return nil, &errors.RequestError{Msg: "Length overflow for the provided keys"} } // args slice will have 5 more arguments with the keys provided. diff --git a/go/api/command_options.go b/go/api/command_options.go index dcf17446bc..29065582bf 100644 --- a/go/api/command_options.go +++ b/go/api/command_options.go @@ -5,6 +5,7 @@ package api import ( "strconv" + "github.com/valkey-io/valkey-glide/go/glide/api/errors" "github.com/valkey-io/valkey-glide/go/glide/utils" ) @@ -63,7 +64,7 @@ func (opts *SetOptions) toArgs() ([]string, error) { case KeepExisting: args = append(args, string(opts.Expiry.Type)) default: - err = &RequestError{"Invalid expiry type"} + err = &errors.RequestError{Msg: "Invalid expiry type"} } } @@ -101,7 +102,7 @@ func (opts *GetExOptions) toArgs() ([]string, error) { case Persist: args = append(args, string(opts.Expiry.Type)) default: - err = &RequestError{"Invalid expiry type"} + err = &errors.RequestError{Msg: "Invalid expiry type"} } } @@ -144,7 +145,7 @@ func (expireCondition ExpireCondition) toString() (string, error) { case NewExpiryLessThanCurrent: return string(NewExpiryLessThanCurrent), nil default: - return "", &RequestError{"Invalid expire condition"} + return "", &errors.RequestError{Msg: "Invalid expire condition"} } } @@ -254,7 +255,7 @@ func (insertPosition InsertPosition) toString() (string, error) { case After: return string(After), nil default: - return "", &RequestError{"Invalid insert position"} + return "", &errors.RequestError{Msg: "Invalid insert position"} } } @@ -275,7 +276,7 @@ func (listDirection ListDirection) toString() (string, error) { case Right: return string(Right), nil default: - return "", &RequestError{"Invalid list direction"} + return "", &errors.RequestError{Msg: "Invalid list direction"} } } diff --git a/go/api/request_routing_config.go b/go/api/config/request_routing_config.go similarity index 85% rename from go/api/request_routing_config.go rename to go/api/config/request_routing_config.go index 3a1c53b124..1d0acc27d3 100644 --- a/go/api/request_routing_config.go +++ b/go/api/config/request_routing_config.go @@ -1,22 +1,23 @@ // Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 -package api +package config import ( "fmt" "strconv" "strings" + "github.com/valkey-io/valkey-glide/go/glide/api/errors" "github.com/valkey-io/valkey-glide/go/glide/protobuf" ) // Request routing basic interface. Please use one of the following: -// - [api.SimpleNodeRoute] -// - [api.SlotIdRoute] -// - [api.SlotKeyRoute] -// - [api.ByAddressRoute] -type route interface { - toRoutesProtobuf() (*protobuf.Routes, error) +// - [config.SimpleNodeRoute] +// - [config.SlotIdRoute] +// - [config.SlotKeyRoute] +// - [config.ByAddressRoute] +type Route interface { + ToRoutesProtobuf() (*protobuf.Routes, error) } type SimpleNodeRoute int @@ -32,7 +33,7 @@ const ( RandomRoute ) -func (simpleNodeRoute SimpleNodeRoute) toRoutesProtobuf() (*protobuf.Routes, error) { +func (simpleNodeRoute SimpleNodeRoute) ToRoutesProtobuf() (*protobuf.Routes, error) { simpleRouteProto, err := mapSimpleNodeRoute(simpleNodeRoute) if err != nil { return nil, err @@ -55,7 +56,7 @@ func mapSimpleNodeRoute(simpleNodeRoute SimpleNodeRoute) (protobuf.SimpleRoutes, case RandomRoute: return protobuf.SimpleRoutes_Random, nil default: - return protobuf.SimpleRoutes_Random, &RequestError{"Invalid simple node route"} + return protobuf.SimpleRoutes_Random, &errors.RequestError{Msg: "Invalid simple node route"} } } @@ -76,7 +77,7 @@ func mapSlotType(slotType SlotType) (protobuf.SlotTypes, error) { case SlotTypeReplica: return protobuf.SlotTypes_Replica, nil default: - return protobuf.SlotTypes_Primary, &RequestError{"Invalid slot type"} + return protobuf.SlotTypes_Primary, &errors.RequestError{Msg: "Invalid slot type"} } } @@ -94,7 +95,7 @@ func NewSlotIdRoute(slotType SlotType, slotId int32) *SlotIdRoute { return &SlotIdRoute{slotType: slotType, slotID: slotId} } -func (slotIdRoute *SlotIdRoute) toRoutesProtobuf() (*protobuf.Routes, error) { +func (slotIdRoute *SlotIdRoute) ToRoutesProtobuf() (*protobuf.Routes, error) { slotType, err := mapSlotType(slotIdRoute.slotType) if err != nil { return nil, err @@ -124,7 +125,7 @@ func NewSlotKeyRoute(slotType SlotType, slotKey string) *SlotKeyRoute { return &SlotKeyRoute{slotType: slotType, slotKey: slotKey} } -func (slotKeyRoute *SlotKeyRoute) toRoutesProtobuf() (*protobuf.Routes, error) { +func (slotKeyRoute *SlotKeyRoute) ToRoutesProtobuf() (*protobuf.Routes, error) { slotType, err := mapSlotType(slotKeyRoute.slotType) if err != nil { return nil, err @@ -159,8 +160,8 @@ func NewByAddressRoute(host string, port int32) *ByAddressRoute { func NewByAddressRouteWithHost(host string) (*ByAddressRoute, error) { split := strings.Split(host, ":") if len(split) != 2 { - return nil, &RequestError{ - fmt.Sprintf( + return nil, &errors.RequestError{ + Msg: fmt.Sprintf( "no port provided, or host is not in the expected format 'hostname:port'. Received: %s", host, ), } @@ -168,8 +169,8 @@ func NewByAddressRouteWithHost(host string) (*ByAddressRoute, error) { port, err := strconv.ParseInt(split[1], 10, 32) if err != nil { - return nil, &RequestError{ - fmt.Sprintf( + return nil, &errors.RequestError{ + Msg: fmt.Sprintf( "port must be a valid integer. Received: %s", split[1], ), } @@ -178,7 +179,7 @@ func NewByAddressRouteWithHost(host string) (*ByAddressRoute, error) { return &ByAddressRoute{host: split[0], port: int32(port)}, nil } -func (byAddressRoute *ByAddressRoute) toRoutesProtobuf() (*protobuf.Routes, error) { +func (byAddressRoute *ByAddressRoute) ToRoutesProtobuf() (*protobuf.Routes, error) { request := &protobuf.Routes{ Value: &protobuf.Routes_ByAddressRoute{ ByAddressRoute: &protobuf.ByAddressRoute{ diff --git a/go/api/errors.go b/go/api/errors/errors.go similarity index 70% rename from go/api/errors.go rename to go/api/errors/errors.go index 4fa4fad92a..634d6b0688 100644 --- a/go/api/errors.go +++ b/go/api/errors/errors.go @@ -1,25 +1,25 @@ // Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 -package api +package errors // #cgo LDFLAGS: -L../target/release -lglide_rs -// #include "../lib.h" +// #include "../../lib.h" import "C" // ConnectionError is a client error that occurs when there is an error while connecting or when a connection // disconnects. type ConnectionError struct { - msg string + Msg string } -func (e *ConnectionError) Error() string { return e.msg } +func (e *ConnectionError) Error() string { return e.Msg } // RequestError is a client error that occurs when an error is reported during a request. type RequestError struct { - msg string + Msg string } -func (e *RequestError) Error() string { return e.msg } +func (e *RequestError) Error() string { return e.Msg } // ExecAbortError is a client error that occurs when a transaction is aborted. type ExecAbortError struct { @@ -44,22 +44,20 @@ func (e *DisconnectError) Error() string { return e.msg } // ClosingError is a client error that indicates that the client has closed and is no longer usable. type ClosingError struct { - msg string + Msg string } -func (e *ClosingError) Error() string { return e.msg } +func (e *ClosingError) Error() string { return e.Msg } -func goError(cErrorType C.RequestErrorType, cErrorMessage *C.char) error { - defer C.free_error_message(cErrorMessage) - msg := C.GoString(cErrorMessage) +func GoError(cErrorType uint32, errorMessage string) error { switch cErrorType { case C.ExecAbort: - return &ExecAbortError{msg} + return &ExecAbortError{errorMessage} case C.Timeout: - return &TimeoutError{msg} + return &TimeoutError{errorMessage} case C.Disconnect: - return &DisconnectError{msg} + return &DisconnectError{errorMessage} default: - return &RequestError{msg} + return &RequestError{errorMessage} } } diff --git a/go/api/glide_cluster_client.go b/go/api/glide_cluster_client.go index 00ebb96545..3a7981b8ca 100644 --- a/go/api/glide_cluster_client.go +++ b/go/api/glide_cluster_client.go @@ -5,6 +5,7 @@ package api // #cgo LDFLAGS: -L../target/release -lglide_rs // #include "../lib.h" import "C" +import "github.com/valkey-io/valkey-glide/go/glide/api/options" // GlideClusterClient interface compliance check. var _ GlideClusterClient = (*glideClusterClient)(nil) @@ -49,8 +50,7 @@ func (client *glideClusterClient) CustomCommand(args []string) (ClusterValue[int // // Parameters: // -// route - Specifies the routing configuration for the command. The client will route the -// command to the nodes defined by route. +// options - The TimeOptions type. // // Return value: // @@ -61,41 +61,15 @@ func (client *glideClusterClient) CustomCommand(args []string) (ClusterValue[int // Example: // // route := api.SimpleNodeRoute(api.RandomRoute) -// result, err := client.Time(route) -// +// options := options.NewTimeOptionsBuilder().SetRoute(route) +// result, err := client.TimeWithOptions(route) // fmt.Println(result.Value()) // Output: [1737285074 67888] // // [valkey.io]: https://valkey.io/commands/time/ -func (client *glideClusterClient) Time(route route) (ClusterValue[[]string], error) { - res, err := client.executeCommandWithRoute(C.Time, []string{}, route) - if err != nil { - return ClusterValue[[]string]{ - value: Result[[]string]{isNil: true}, - }, err - } - - if err := checkResponseType(res, C.Map, true); err == nil { - - // Multi-node response - mapData, err := handleRawStringArrayMapResponse(res) - if err != nil { - return ClusterValue[[]string]{ - value: Result[[]string]{isNil: true}, - }, err - } - var times []string - for _, nodeTimes := range mapData { - times = append(times, nodeTimes...) - } - return CreateClusterMultiValue(times), nil - } - - // Single node response - data, err := handleRawStringArrayResponse(res) +func (client *glideClusterClient) TimeWithOptions(opts *options.TimeOptions) (ClusterValue[[]string], error) { + result, err := client.executeCommandWithRoute(C.Time, []string{}, opts.Route) if err != nil { - return ClusterValue[[]string]{ - value: Result[[]string]{isNil: true}, - }, err + return CreateEmptyStringArrayClusterValue(), err } - return CreateClusterSingleValue(data), nil + return handleTimeClusterResponse(result) } diff --git a/go/api/options/time_options.go b/go/api/options/time_options.go new file mode 100644 index 0000000000..fdbd326ccb --- /dev/null +++ b/go/api/options/time_options.go @@ -0,0 +1,16 @@ +package options + +import "github.com/valkey-io/valkey-glide/go/glide/api/config" + +type TimeOptions struct { + Route config.Route +} + +func NewTimeOptionsBuilder() *TimeOptions { + return &TimeOptions{} +} + +func (timeOptions *TimeOptions) SetRoute(route config.Route) *TimeOptions { + timeOptions.Route = route + return timeOptions +} diff --git a/go/api/response_handlers.go b/go/api/response_handlers.go index 21a4108c22..5f67147482 100644 --- a/go/api/response_handlers.go +++ b/go/api/response_handlers.go @@ -11,6 +11,8 @@ import ( "reflect" "strconv" "unsafe" + + "github.com/valkey-io/valkey-glide/go/glide/api/errors" ) func checkResponseType(response *C.struct_CommandResponse, expectedType C.ResponseType, isNilable bool) error { @@ -18,8 +20,8 @@ func checkResponseType(response *C.struct_CommandResponse, expectedType C.Respon expectedTypeStr := C.get_response_type_string(expectedTypeInt) if !isNilable && response == nil { - return &RequestError{ - fmt.Sprintf( + return &errors.RequestError{ + Msg: fmt.Sprintf( "Unexpected return type from Valkey: got nil, expected %s", C.GoString(expectedTypeStr), ), @@ -35,8 +37,8 @@ func checkResponseType(response *C.struct_CommandResponse, expectedType C.Respon } actualTypeStr := C.get_response_type_string(response.response_type) - return &RequestError{ - fmt.Sprintf( + return &errors.RequestError{ + Msg: fmt.Sprintf( "Unexpected return type from Valkey: got %s, expected %s", C.GoString(actualTypeStr), C.GoString(expectedTypeStr), @@ -89,7 +91,7 @@ func parseInterface(response *C.struct_CommandResponse) (interface{}, error) { return parseSet(response) } - return nil, &RequestError{"Unexpected return type from Valkey"} + return nil, &errors.RequestError{Msg: "Unexpected return type from Valkey"} } func parseString(response *C.struct_CommandResponse) (interface{}, error) { @@ -208,7 +210,7 @@ func handle2DStringArrayResponse(response *C.struct_CommandResponse) ([][]string } res, ok := converted.([][]string) if !ok { - return nil, &RequestError{fmt.Sprintf("unexpected type: %T", converted)} + return nil, &errors.RequestError{Msg: fmt.Sprintf("unexpected type: %T", converted)} } return res, nil } @@ -423,7 +425,7 @@ func handleStringDoubleMapResponse(response *C.struct_CommandResponse) (map[stri } result, ok := converted.(map[string]float64) if !ok { - return nil, &RequestError{fmt.Sprintf("unexpected type of map: %T", converted)} + return nil, &errors.RequestError{Msg: fmt.Sprintf("unexpected type of map: %T", converted)} } return result, nil } @@ -450,7 +452,7 @@ func handleStringToStringMapResponse(response *C.struct_CommandResponse) (map[st } result, ok := converted.(map[string]string) if !ok { - return nil, &RequestError{fmt.Sprintf("unexpected type of map: %T", converted)} + return nil, &errors.RequestError{Msg: fmt.Sprintf("unexpected type of map: %T", converted)} } return result, nil } @@ -487,7 +489,7 @@ func handleStringToStringArrayMapOrNilResponse( return result, nil } - return nil, &RequestError{fmt.Sprintf("unexpected type received: %T", res)} + return nil, &errors.RequestError{Msg: fmt.Sprintf("unexpected type received: %T", res)} } func handleStringSetResponse(response *C.struct_CommandResponse) (map[string]struct{}, error) { @@ -594,7 +596,7 @@ func (node mapConverter[T]) convert(data interface{}) (interface{}, error) { if node.canBeNil { return nil, nil } else { - return nil, &RequestError{fmt.Sprintf("Unexpected type received: nil, expected: map[string]%v", getType[T]())} + return nil, &errors.RequestError{Msg: fmt.Sprintf("Unexpected type received: nil, expected: map[string]%v", getType[T]())} } } result := make(map[string]T) @@ -605,7 +607,9 @@ func (node mapConverter[T]) convert(data interface{}) (interface{}, error) { // try direct conversion to T when there is no next converter valueT, ok := value.(T) if !ok { - return nil, &RequestError{fmt.Sprintf("Unexpected type of map element: %T, expected: %v", value, getType[T]())} + return nil, &errors.RequestError{ + Msg: fmt.Sprintf("Unexpected type of map element: %T, expected: %v", value, getType[T]()), + } } result[key] = valueT } else { @@ -622,7 +626,7 @@ func (node mapConverter[T]) convert(data interface{}) (interface{}, error) { // convert to T valueT, ok := val.(T) if !ok { - return nil, &RequestError{fmt.Sprintf("Unexpected type of map element: %T, expected: %v", val, getType[T]())} + return nil, &errors.RequestError{Msg: fmt.Sprintf("Unexpected type of map element: %T, expected: %v", val, getType[T]())} } result[key] = valueT } @@ -642,7 +646,7 @@ func (node arrayConverter[T]) convert(data interface{}) (interface{}, error) { if node.canBeNil { return nil, nil } else { - return nil, &RequestError{fmt.Sprintf("Unexpected type received: nil, expected: []%v", getType[T]())} + return nil, &errors.RequestError{Msg: fmt.Sprintf("Unexpected type received: nil, expected: []%v", getType[T]())} } } arrData := data.([]interface{}) @@ -651,8 +655,8 @@ func (node arrayConverter[T]) convert(data interface{}) (interface{}, error) { if node.next == nil { valueT, ok := value.(T) if !ok { - return nil, &RequestError{ - fmt.Sprintf("Unexpected type of array element: %T, expected: %v", value, getType[T]()), + return nil, &errors.RequestError{ + Msg: fmt.Sprintf("Unexpected type of array element: %T, expected: %v", value, getType[T]()), } } result = append(result, valueT) @@ -668,7 +672,7 @@ func (node arrayConverter[T]) convert(data interface{}) (interface{}, error) { } valueT, ok := val.(T) if !ok { - return nil, &RequestError{fmt.Sprintf("Unexpected type of array element: %T, expected: %v", val, getType[T]())} + return nil, &errors.RequestError{Msg: fmt.Sprintf("Unexpected type of array element: %T, expected: %v", val, getType[T]())} } result = append(result, valueT) } @@ -705,7 +709,7 @@ func handleMapOfArrayOfStringArrayResponse(response *C.struct_CommandResponse) ( } claimedEntries, ok := converted.(map[string][][]string) if !ok { - return nil, &RequestError{fmt.Sprintf("unexpected type of second element: %T", converted)} + return nil, &errors.RequestError{Msg: fmt.Sprintf("unexpected type of second element: %T", converted)} } return claimedEntries, nil @@ -725,7 +729,7 @@ func handleXAutoClaimResponse(response *C.struct_CommandResponse) (XAutoClaimRes arr := slice.([]interface{}) len := len(arr) if len < 2 || len > 3 { - return null, &RequestError{fmt.Sprintf("Unexpected response array length: %d", len)} + return null, &errors.RequestError{Msg: fmt.Sprintf("Unexpected response array length: %d", len)} } converted, err := mapConverter[[][]string]{ arrayConverter[[]string]{ @@ -742,7 +746,7 @@ func handleXAutoClaimResponse(response *C.struct_CommandResponse) (XAutoClaimRes } claimedEntries, ok := converted.(map[string][][]string) if !ok { - return null, &RequestError{fmt.Sprintf("unexpected type of second element: %T", converted)} + return null, &errors.RequestError{Msg: fmt.Sprintf("unexpected type of second element: %T", converted)} } var deletedMessages []string deletedMessages = nil @@ -756,7 +760,7 @@ func handleXAutoClaimResponse(response *C.struct_CommandResponse) (XAutoClaimRes } deletedMessages, ok = converted.([]string) if !ok { - return null, &RequestError{fmt.Sprintf("unexpected type of third element: %T", converted)} + return null, &errors.RequestError{Msg: fmt.Sprintf("unexpected type of third element: %T", converted)} } } return XAutoClaimResponse{arr[0].(string), claimedEntries, deletedMessages}, nil @@ -776,7 +780,7 @@ func handleXAutoClaimJustIdResponse(response *C.struct_CommandResponse) (XAutoCl arr := slice.([]interface{}) len := len(arr) if len < 2 || len > 3 { - return null, &RequestError{fmt.Sprintf("Unexpected response array length: %d", len)} + return null, &errors.RequestError{Msg: fmt.Sprintf("Unexpected response array length: %d", len)} } converted, err := arrayConverter[string]{ nil, @@ -787,7 +791,7 @@ func handleXAutoClaimJustIdResponse(response *C.struct_CommandResponse) (XAutoCl } claimedEntries, ok := converted.([]string) if !ok { - return null, &RequestError{fmt.Sprintf("unexpected type of second element: %T", converted)} + return null, &errors.RequestError{Msg: fmt.Sprintf("unexpected type of second element: %T", converted)} } var deletedMessages []string deletedMessages = nil @@ -801,7 +805,7 @@ func handleXAutoClaimJustIdResponse(response *C.struct_CommandResponse) (XAutoCl } deletedMessages, ok = converted.([]string) if !ok { - return null, &RequestError{fmt.Sprintf("unexpected type of third element: %T", converted)} + return null, &errors.RequestError{Msg: fmt.Sprintf("unexpected type of third element: %T", converted)} } } return XAutoClaimJustIdResponse{arr[0].(string), claimedEntries, deletedMessages}, nil @@ -838,7 +842,7 @@ func handleXReadResponse(response *C.struct_CommandResponse) (map[string]map[str if result, ok := res.(map[string]map[string][][]string); ok { return result, nil } - return nil, &RequestError{fmt.Sprintf("unexpected type received: %T", res)} + return nil, &errors.RequestError{Msg: fmt.Sprintf("unexpected type received: %T", res)} } func handleXReadGroupResponse(response *C.struct_CommandResponse) (map[string]map[string][][]string, error) { @@ -872,7 +876,7 @@ func handleXReadGroupResponse(response *C.struct_CommandResponse) (map[string]ma if result, ok := res.(map[string]map[string][][]string); ok { return result, nil } - return nil, &RequestError{fmt.Sprintf("unexpected type received: %T", res)} + return nil, &errors.RequestError{Msg: fmt.Sprintf("unexpected type received: %T", res)} } func handleXPendingSummaryResponse(response *C.struct_CommandResponse) (XPendingSummary, error) { @@ -982,7 +986,7 @@ func handleRawStringArrayResponse(response *C.struct_CommandResponse) ([]string, } if v.string_value == nil { - return nil, &RequestError{"Unexpected nil string in array"} + return nil, &errors.RequestError{Msg: "Unexpected nil string in array"} } byteSlice := C.GoBytes(unsafe.Pointer(v.string_value), C.int(int64(v.string_value_len))) @@ -1018,7 +1022,7 @@ func handleRawStringArrayMapResponse(response *C.struct_CommandResponse) (map[st return nil, err } if strVal.string_value == nil { - return nil, &RequestError{"Unexpected nil string in array"} + return nil, &errors.RequestError{Msg: "Unexpected nil string in array"} } byteSlice := C.GoBytes(unsafe.Pointer(strVal.string_value), C.int(int64(strVal.string_value_len))) timeStrings = append(timeStrings, string(byteSlice)) @@ -1029,3 +1033,25 @@ func handleRawStringArrayMapResponse(response *C.struct_CommandResponse) (map[st return result, nil } + +func handleTimeClusterResponse(response *C.struct_CommandResponse) (ClusterValue[[]string], error) { + // Handle multi-node response + if err := checkResponseType(response, C.Map, true); err == nil { + mapData, err := handleRawStringArrayMapResponse(response) + if err != nil { + return CreateEmptyStringArrayClusterValue(), err + } + var times []string + for _, nodeTimes := range mapData { + times = append(times, nodeTimes...) + } + return CreateClusterMultiValue(times), nil + } + + // Handle single node response + data, err := handleRawStringArrayResponse(response) + if err != nil { + return CreateEmptyStringArrayClusterValue(), err + } + return CreateClusterSingleValue(data), nil +} diff --git a/go/api/response_types.go b/go/api/response_types.go index 84de6aed7f..7c47bad9fe 100644 --- a/go/api/response_types.go +++ b/go/api/response_types.go @@ -161,6 +161,13 @@ func CreateEmptyClusterValue() ClusterValue[interface{}] { } } +func CreateEmptyStringArrayClusterValue() ClusterValue[[]string] { + var empty []string + return ClusterValue[[]string]{ + value: Result[[]string]{val: empty, isNil: true}, + } +} + // XPendingSummary represents a summary of pending messages in a stream group. // It includes the total number of pending messages, the ID of the first and last pending messages, // and a list of consumer pending messages. diff --git a/go/api/server_management_cluster_commands.go b/go/api/server_management_cluster_commands.go index c7bfa39214..c4da3d09f4 100644 --- a/go/api/server_management_cluster_commands.go +++ b/go/api/server_management_cluster_commands.go @@ -2,11 +2,13 @@ package api +import "github.com/valkey-io/valkey-glide/go/glide/api/options" + // ServerManagementClusterCommands supports commands for the "Server Management Commands" group for cluster client. // // See [valkey.io] for details. // // [valkey.io]: https://valkey.io/commands/#server type ServerManagementClusterCommands interface { - Time(route route) (ClusterValue[[]string], error) + TimeWithOptions(timeOptions *options.TimeOptions) (ClusterValue[[]string], error) } diff --git a/go/integTest/cluster_commands_test.go b/go/integTest/cluster_commands_test.go index 6241bd79d4..dd9548c0cc 100644 --- a/go/integTest/cluster_commands_test.go +++ b/go/integTest/cluster_commands_test.go @@ -6,7 +6,8 @@ import ( "strings" "github.com/stretchr/testify/assert" - "github.com/valkey-io/valkey-glide/go/glide/api" + "github.com/valkey-io/valkey-glide/go/glide/api/config" + "github.com/valkey-io/valkey-glide/go/glide/api/options" ) func (suite *GlideTestSuite) TestClusterCustomCommandInfo() { @@ -31,9 +32,9 @@ func (suite *GlideTestSuite) TestClusterCustomCommandEcho() { func (suite *GlideTestSuite) TestTime_RandomRoute() { client := suite.defaultClusterClient() - route := api.RandomRoute - - result, err := client.Time(route) + route := config.SimpleNodeRoute(config.RandomRoute) + options := options.NewTimeOptionsBuilder().SetRoute(route) + result, err := client.TimeWithOptions(options) assert.NoError(suite.T(), err) assert.NotNil(suite.T(), result) @@ -44,10 +45,9 @@ func (suite *GlideTestSuite) TestTime_RandomRoute() { func (suite *GlideTestSuite) TestTime_AllNodes_MultipleValues() { client := suite.defaultClusterClient() - route := api.AllNodes - - result, err := client.Time(route) - + route := config.AllNodes + options := options.NewTimeOptionsBuilder().SetRoute(route) + result, err := client.TimeWithOptions(options) assert.NoError(suite.T(), err) assert.NotNil(suite.T(), result) assert.NotEmpty(suite.T(), result.Value()) @@ -61,9 +61,10 @@ func (suite *GlideTestSuite) TestTime_AllNodes_MultipleValues() { func (suite *GlideTestSuite) TestTime_ErrorHandling() { client := suite.defaultClusterClient() - invalidRoute := api.NewByAddressRoute("invalidHost", 9999) + invalidRoute := config.NewByAddressRoute("invalidHost", 9999) - result, err := client.Time(invalidRoute) + options := options.NewTimeOptionsBuilder().SetRoute(invalidRoute) + result, err := client.TimeWithOptions(options) assert.NotNil(suite.T(), err) assert.Empty(suite.T(), result.Value()) diff --git a/go/integTest/connection_test.go b/go/integTest/connection_test.go index 22c6b809e8..da0bde260e 100644 --- a/go/integTest/connection_test.go +++ b/go/integTest/connection_test.go @@ -5,6 +5,7 @@ package integTest import ( "github.com/stretchr/testify/assert" "github.com/valkey-io/valkey-glide/go/glide/api" + "github.com/valkey-io/valkey-glide/go/glide/api/errors" ) func (suite *GlideTestSuite) TestStandaloneConnect() { @@ -51,5 +52,5 @@ func (suite *GlideTestSuite) TestConnectWithInvalidAddress() { assert.Nil(suite.T(), client) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.ConnectionError{}, err) + assert.IsType(suite.T(), &errors.ConnectionError{}, err) } diff --git a/go/api/request_routing_config_test.go b/go/integTest/request_routing_config_test.go similarity index 69% rename from go/api/request_routing_config_test.go rename to go/integTest/request_routing_config_test.go index f16b6cfcb7..a9692d241f 100644 --- a/go/api/request_routing_config_test.go +++ b/go/integTest/request_routing_config_test.go @@ -1,30 +1,31 @@ // Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 -package api +package integTest import ( "testing" "github.com/stretchr/testify/assert" + "github.com/valkey-io/valkey-glide/go/glide/api/config" "github.com/valkey-io/valkey-glide/go/glide/protobuf" ) func TestSimpleNodeRoute(t *testing.T) { - config := AllNodes + routeConfig := config.AllNodes expected := &protobuf.Routes{ Value: &protobuf.Routes_SimpleRoutes{ SimpleRoutes: protobuf.SimpleRoutes_AllNodes, }, } - result, err := config.toRoutesProtobuf() + result, err := routeConfig.ToRoutesProtobuf() assert.Equal(t, expected, result) assert.Nil(t, err) } func TestSlotIdRoute(t *testing.T) { - config := NewSlotIdRoute(SlotTypePrimary, int32(100)) + routeConfig := config.NewSlotIdRoute(config.SlotTypePrimary, int32(100)) expected := &protobuf.Routes{ Value: &protobuf.Routes_SlotIdRoute{ SlotIdRoute: &protobuf.SlotIdRoute{ @@ -34,14 +35,14 @@ func TestSlotIdRoute(t *testing.T) { }, } - result, err := config.toRoutesProtobuf() + result, err := routeConfig.ToRoutesProtobuf() assert.Equal(t, expected, result) assert.Nil(t, err) } func TestSlotKeyRoute(t *testing.T) { - config := NewSlotKeyRoute(SlotTypePrimary, "Slot1") + routeConfig := config.NewSlotKeyRoute(config.SlotTypePrimary, "Slot1") expected := &protobuf.Routes{ Value: &protobuf.Routes_SlotKeyRoute{ SlotKeyRoute: &protobuf.SlotKeyRoute{ @@ -51,46 +52,46 @@ func TestSlotKeyRoute(t *testing.T) { }, } - result, err := config.toRoutesProtobuf() + result, err := routeConfig.ToRoutesProtobuf() assert.Equal(t, expected, result) assert.Nil(t, err) } func TestByAddressRoute(t *testing.T) { - config := NewByAddressRoute("localhost", int32(6739)) + routeConfig := config.NewByAddressRoute("localhost", int32(6739)) expected := &protobuf.Routes{ Value: &protobuf.Routes_ByAddressRoute{ ByAddressRoute: &protobuf.ByAddressRoute{Host: "localhost", Port: 6739}, }, } - result, err := config.toRoutesProtobuf() + result, err := routeConfig.ToRoutesProtobuf() assert.Equal(t, expected, result) assert.Nil(t, err) } func TestByAddressRouteWithHost(t *testing.T) { - config, _ := NewByAddressRouteWithHost("localhost:6739") + routeConfig, _ := config.NewByAddressRouteWithHost("localhost:6739") expected := &protobuf.Routes{ Value: &protobuf.Routes_ByAddressRoute{ ByAddressRoute: &protobuf.ByAddressRoute{Host: "localhost", Port: 6739}, }, } - result, err := config.toRoutesProtobuf() + result, err := routeConfig.ToRoutesProtobuf() assert.Equal(t, expected, result) assert.Nil(t, err) } func TestByAddressRoute_MultiplePorts(t *testing.T) { - _, err := NewByAddressRouteWithHost("localhost:6739:6740") + _, err := config.NewByAddressRouteWithHost("localhost:6739:6740") assert.NotNil(t, err) } func TestByAddressRoute_InvalidHost(t *testing.T) { - _, err := NewByAddressRouteWithHost("localhost") + _, err := config.NewByAddressRouteWithHost("localhost") assert.NotNil(t, err) } diff --git a/go/integTest/shared_commands_test.go b/go/integTest/shared_commands_test.go index 02fc2fc4c2..0361576468 100644 --- a/go/integTest/shared_commands_test.go +++ b/go/integTest/shared_commands_test.go @@ -13,6 +13,7 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/valkey-io/valkey-glide/go/glide/api" + "github.com/valkey-io/valkey-glide/go/glide/api/errors" "github.com/valkey-io/valkey-glide/go/glide/api/options" ) @@ -367,17 +368,17 @@ func (suite *GlideTestSuite) TestIncrCommands_TypeError() { res1, err := client.Incr(key) assert.Equal(suite.T(), int64(0), res1) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) res2, err := client.IncrBy(key, 10) assert.Equal(suite.T(), int64(0), res2) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) res3, err := client.IncrByFloat(key, float64(10.1)) assert.Equal(suite.T(), float64(0), res3) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -455,7 +456,7 @@ func (suite *GlideTestSuite) TestSetRange_existingAndNonExistingKeys() { res, err = client.SetRange(key, math.MaxInt32, "test") assert.Equal(suite.T(), int64(0), res) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -1330,11 +1331,11 @@ func (suite *GlideTestSuite) TestHRandField() { key = uuid.NewString() suite.verifyOK(client.Set(key, "HRandField")) _, err = client.HRandField(key) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) _, err = client.HRandFieldWithCount(key, 42) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) _, err = client.HRandFieldWithCountWithValues(key, 42) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -1379,12 +1380,12 @@ func (suite *GlideTestSuite) TestLPushLPop_typeError() { res1, err := client.LPush(key, []string{"value1"}) assert.Equal(suite.T(), int64(0), res1) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) res2, err := client.LPopCount(key, 2) assert.Nil(suite.T(), res2) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -1435,13 +1436,13 @@ func (suite *GlideTestSuite) TestLPos_withAndWithoutOptions() { res8, err := client.LPosWithOptions(key, "a", api.NewLPosOptionsBuilder().SetRank(0)) assert.Equal(suite.T(), api.CreateNilInt64Result(), res8) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // invalid maxlen value res9, err := client.LPosWithOptions(key, "a", api.NewLPosOptionsBuilder().SetMaxLen(-1)) assert.Equal(suite.T(), api.CreateNilInt64Result(), res9) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // non-existent key res10, err := client.LPos("non_existent_key", "a") @@ -1454,7 +1455,7 @@ func (suite *GlideTestSuite) TestLPos_withAndWithoutOptions() { res11, err := client.LPos(keyString, "a") assert.Equal(suite.T(), api.CreateNilInt64Result(), res11) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -1478,7 +1479,7 @@ func (suite *GlideTestSuite) TestLPosCount() { res4, err := client.LPosCount(key, "a", int64(-1)) assert.Nil(suite.T(), res4) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // non-existent key res5, err := client.LPosCount("non_existent_key", "a", int64(1)) @@ -1491,7 +1492,7 @@ func (suite *GlideTestSuite) TestLPosCount() { res6, err := client.LPosCount(keyString, "a", int64(1)) assert.Nil(suite.T(), res6) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -1533,7 +1534,7 @@ func (suite *GlideTestSuite) TestRPush() { res2, err := client.RPush(key2, []string{"value1"}) assert.Equal(suite.T(), int64(0), res2) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -1704,7 +1705,7 @@ func (suite *GlideTestSuite) TestSUnionStore() { res11, err := client.SUnionStore(key4, []string{}) assert.Equal(suite.T(), int64(0), res11) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // non-set key _, err = client.Set(stringKey, "value") @@ -1713,7 +1714,7 @@ func (suite *GlideTestSuite) TestSUnionStore() { res12, err := client.SUnionStore(key4, []string{stringKey, key1}) assert.Equal(suite.T(), int64(0), res12) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // overwrite destination when destination is not a set res13, err := client.SUnionStore(stringKey, []string{key1, key3}) @@ -1993,7 +1994,7 @@ func (suite *GlideTestSuite) TestSinterStore() { res10, err := client.SInterStore(key3, []string{}) assert.Equal(suite.T(), int64(0), res10) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // non-set key _, err = client.Set(stringKey, "value") @@ -2002,7 +2003,7 @@ func (suite *GlideTestSuite) TestSinterStore() { res11, err := client.SInterStore(key3, []string{stringKey}) assert.Equal(suite.T(), int64(0), res11) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // overwrite the non-set key res12, err := client.SInterStore(stringKey, []string{key2}) @@ -2142,13 +2143,13 @@ func (suite *GlideTestSuite) TestSMIsMember() { // invalid argument - member list must not be empty _, err4 := client.SMIsMember(key1, []string{}) assert.NotNil(suite.T(), err4) - assert.IsType(suite.T(), &api.RequestError{}, err4) + assert.IsType(suite.T(), &errors.RequestError{}, err4) // source key exists, but it is not a set suite.verifyOK(client.Set(stringKey, "value")) _, err5 := client.SMIsMember(stringKey, []string{"two"}) assert.NotNil(suite.T(), err5) - assert.IsType(suite.T(), &api.RequestError{}, err5) + assert.IsType(suite.T(), &errors.RequestError{}, err5) }) } @@ -2196,13 +2197,13 @@ func (suite *GlideTestSuite) TestSUnion() { // Exceptions with empty keys res6, err := client.SUnion([]string{}) assert.Nil(suite.T(), res6) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // Exception with a non-set key suite.verifyOK(client.Set(nonSetKey, "value")) res7, err := client.SUnion([]string{nonSetKey, key1}) assert.Nil(suite.T(), res7) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -2301,7 +2302,7 @@ func (suite *GlideTestSuite) TestSMove() { _, err = client.SMove(stringKey, key1, "_") assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -2338,7 +2339,7 @@ func (suite *GlideTestSuite) TestSScan() { } else { _, _, err = client.SScan(key1, "-1") assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) } // result contains the whole set @@ -2406,7 +2407,7 @@ func (suite *GlideTestSuite) TestSScan() { _, _, err = client.SScan(key2, initialCursor) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -2433,7 +2434,7 @@ func (suite *GlideTestSuite) TestLRange() { res4, err := client.LRange(key2, int64(0), int64(1)) assert.Nil(suite.T(), res4) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -2466,7 +2467,7 @@ func (suite *GlideTestSuite) TestLIndex() { res5, err := client.LIndex(key2, int64(0)) assert.Equal(suite.T(), api.CreateNilStringResult(), res5) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -2497,7 +2498,7 @@ func (suite *GlideTestSuite) TestLTrim() { res4, err := client.LIndex(key2, int64(0)) assert.Equal(suite.T(), api.CreateNilStringResult(), res4) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -2524,7 +2525,7 @@ func (suite *GlideTestSuite) TestLLen() { res4, err := client.LLen(key2) assert.Equal(suite.T(), int64(0), res4) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -2596,12 +2597,12 @@ func (suite *GlideTestSuite) TestRPopAndRPopCount() { res6, err := client.RPop(key2) assert.Equal(suite.T(), api.CreateNilStringResult(), res6) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) res7, err := client.RPopCount(key2, int64(2)) assert.Nil(suite.T(), res7) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -2640,7 +2641,7 @@ func (suite *GlideTestSuite) TestLInsert() { res7, err := client.LInsert(key2, api.Before, "value5", "value6") assert.Equal(suite.T(), int64(0), res7) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -2667,7 +2668,7 @@ func (suite *GlideTestSuite) TestBLPop() { res4, err := client.BLPop([]string{key}, float64(1.0)) assert.Nil(suite.T(), res4) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -2694,7 +2695,7 @@ func (suite *GlideTestSuite) TestBRPop() { res4, err := client.BRPop([]string{key}, float64(1.0)) assert.Nil(suite.T(), res4) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -2729,12 +2730,12 @@ func (suite *GlideTestSuite) TestRPushX() { res6, err := client.RPushX(key3, []string{"value1"}) assert.Equal(suite.T(), int64(0), res6) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) res7, err := client.RPushX(key2, []string{}) assert.Equal(suite.T(), int64(0), res7) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -2769,12 +2770,12 @@ func (suite *GlideTestSuite) TestLPushX() { res6, err := client.LPushX(key3, []string{"value1"}) assert.Equal(suite.T(), int64(0), res6) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) res7, err := client.LPushX(key2, []string{}) assert.Equal(suite.T(), int64(0), res7) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -2825,12 +2826,12 @@ func (suite *GlideTestSuite) TestLMPopAndLMPopCount() { res7, err := client.LMPop([]string{key3}, api.Left) assert.Nil(suite.T(), res7) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) res8, err := client.LMPop([]string{key3}, "Invalid") assert.Nil(suite.T(), res8) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -2881,7 +2882,7 @@ func (suite *GlideTestSuite) TestBLMPopAndBLMPopCount() { res7, err := client.BLMPop([]string{key3}, api.Left, float64(0.1)) assert.Nil(suite.T(), res7) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -2892,7 +2893,7 @@ func (suite *GlideTestSuite) TestLSet() { _, err := client.LSet(nonExistentKey, int64(0), "zero") assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) res2, err := client.LPush(key, []string{"four", "three", "two", "one"}) assert.Nil(suite.T(), err) @@ -2900,7 +2901,7 @@ func (suite *GlideTestSuite) TestLSet() { _, err = client.LSet(key, int64(10), "zero") assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) suite.verifyOK(client.LSet(key, int64(0), "zero")) @@ -2973,7 +2974,7 @@ func (suite *GlideTestSuite) TestLMove() { res11, err := client.LMove(nonListKey, key1, api.Left, api.Left) assert.Equal(suite.T(), api.CreateNilStringResult(), res11) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // destination exists but is not a list type key suite.verifyOK(client.Set(nonListKey, "value")) @@ -2981,7 +2982,7 @@ func (suite *GlideTestSuite) TestLMove() { res12, err := client.LMove(key1, nonListKey, api.Left, api.Left) assert.Equal(suite.T(), api.CreateNilStringResult(), res12) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -3968,7 +3969,7 @@ func (suite *GlideTestSuite) TestBLMove() { res11, err := client.BLMove(nonListKey, key1, api.Left, api.Left, float64(0.1)) assert.Equal(suite.T(), api.CreateNilStringResult(), res11) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // destination exists but is not a list type key suite.verifyOK(client.Set(nonListKey, "value")) @@ -3976,7 +3977,7 @@ func (suite *GlideTestSuite) TestBLMove() { res12, err := client.BLMove(key1, nonListKey, api.Left, api.Left, float64(0.1)) assert.Equal(suite.T(), api.CreateNilStringResult(), res12) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -4080,7 +4081,7 @@ func (suite *GlideTestSuite) TestRename() { res1, err := client.Rename(key1, "invalidKey") assert.Equal(suite.T(), "", res1) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -4293,7 +4294,7 @@ func (suite *GlideTestSuite) TestXAutoClaim() { key2 := uuid.New().String() suite.verifyOK(client.Set(key2, key2)) _, err = client.XAutoClaim(key2, "_", "_", 0, "_") - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -4394,11 +4395,11 @@ func (suite *GlideTestSuite) TestXReadGroup() { // error cases: // key does not exist _, err = client.XReadGroup("_", "_", map[string]string{key3: "0"}) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // key is not a stream suite.verifyOK(client.Set(key3, uuid.New().String())) _, err = client.XReadGroup("_", "_", map[string]string{key3: "0"}) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) del, err := client.Del([]string{key3}) assert.NoError(suite.T(), err) assert.Equal(suite.T(), int64(1), del) @@ -4407,7 +4408,7 @@ func (suite *GlideTestSuite) TestXReadGroup() { assert.NoError(suite.T(), err) assert.NotNil(suite.T(), xadd) _, err = client.XReadGroup("_", "_", map[string]string{key3: "0"}) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // consumer don't exist sendWithCustomCommand( suite, @@ -4464,7 +4465,7 @@ func (suite *GlideTestSuite) TestXRead() { client.Set(key3, "xread") _, err = client.XRead(map[string]string{key1: "0-0", key3: "0-0"}) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // ensure that commands doesn't time out even if timeout > request timeout var testClient api.BaseClient @@ -4568,11 +4569,11 @@ func (suite *GlideTestSuite) TestXGroupSetId() { // An error is raised if XGROUP SETID is called with a non-existing key _, err = client.XGroupSetId(uuid.NewString(), group, "1-1") - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // An error is raised if XGROUP SETID is called with a non-existing group _, err = client.XGroupSetId(key, uuid.NewString(), "1-1") - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // Setting the ID to a non-existing ID is allowed suite.verifyOK(client.XGroupSetId(key, group, "99-99")) @@ -4581,7 +4582,7 @@ func (suite *GlideTestSuite) TestXGroupSetId() { key = uuid.NewString() suite.verifyOK(client.Set(key, "xgroup setid")) _, err = client.XGroupSetId(key, group, "1-1") - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -4613,12 +4614,12 @@ func (suite *GlideTestSuite) TestZAddAndZAddIncr() { _, err = client.ZAdd(key2, membersScoreMap) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // wrong key type for zaddincr _, err = client.ZAddIncr(key2, "one", float64(2)) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // with NX & XX onlyIfExistsOpts := options.NewZAddOptionsBuilder().SetConditionalChange(options.OnlyIfExists) @@ -4703,7 +4704,7 @@ func (suite *GlideTestSuite) TestZincrBy() { _, err = client.ZIncrBy(key2, 0.5, "_") assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -4744,7 +4745,7 @@ func (suite *GlideTestSuite) TestBZPopMin() { // Attempt to pop from key3 which is not a sorted set _, err = client.BZPopMin([]string{key3}, float64(.5)) if assert.Error(suite.T(), err) { - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) } }) } @@ -4777,7 +4778,7 @@ func (suite *GlideTestSuite) TestZPopMin() { _, err = client.ZPopMin(key2) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -4808,7 +4809,7 @@ func (suite *GlideTestSuite) TestZPopMax() { _, err = client.ZPopMax(key2) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -4827,7 +4828,7 @@ func (suite *GlideTestSuite) TestZRem() { // no members to remove _, err = client.ZRem(key, []string{}) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) res, err = client.ZRem(key, []string{"one"}) assert.Nil(suite.T(), err) @@ -4844,7 +4845,7 @@ func (suite *GlideTestSuite) TestZRem() { _, err = client.ZRem(key, []string{"value"}) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -5104,7 +5105,7 @@ func (suite *GlideTestSuite) TestZRank() { _, err = client.ZRank(stringKey, "value") assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -5137,7 +5138,7 @@ func (suite *GlideTestSuite) TestZRevRank() { _, err = client.ZRevRank(stringKey, "value") assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -5230,10 +5231,10 @@ func (suite *GlideTestSuite) Test_XAdd_XLen_XTrim() { suite.verifyOK(client.Set(key2, "xtrimtest")) _, err = client.XTrim(key2, options.NewXTrimOptionsWithMinId("0-1")) assert.NotNil(t, err) - assert.IsType(t, &api.RequestError{}, err) + assert.IsType(t, &errors.RequestError{}, err) _, err = client.XLen(key2) assert.NotNil(t, err) - assert.IsType(t, &api.RequestError{}, err) + assert.IsType(t, &errors.RequestError{}, err) }) } @@ -5272,7 +5273,7 @@ func (suite *GlideTestSuite) Test_ZScore() { _, err = client.ZScore(key2, "one") assert.NotNil(t, err) - assert.IsType(t, &api.RequestError{}, err) + assert.IsType(t, &errors.RequestError{}, err) }) } @@ -5351,7 +5352,7 @@ func (suite *GlideTestSuite) TestZCount() { ) _, err = client.ZCount(key2, zCountRange) assert.NotNil(t, err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -5400,7 +5401,7 @@ func (suite *GlideTestSuite) Test_XDel() { _, err = client.XDel(key2, []string{streamId3}) assert.NotNil(t, err) - assert.IsType(t, &api.RequestError{}, err) + assert.IsType(t, &errors.RequestError{}, err) }) } @@ -5435,7 +5436,7 @@ func (suite *GlideTestSuite) TestZScan() { if suite.serverVersion >= "8.0.0" { _, _, err = client.ZScan(key1, "-1") assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) } else { resCursor, resCollection, err = client.ZScan(key1, "-1") assert.NoError(suite.T(), err) @@ -5547,18 +5548,18 @@ func (suite *GlideTestSuite) TestZScan() { _, _, err = client.ZScan(stringKey, initialCursor) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) opts = options.NewZScanOptionsBuilder().SetMatch("test").SetCount(1) _, _, err = client.ZScanWithOptions(stringKey, initialCursor, opts) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // Negative count opts = options.NewZScanOptionsBuilder().SetCount(-1) _, _, err = client.ZScanWithOptions(key1, "-1", opts) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -5835,7 +5836,7 @@ func (suite *GlideTestSuite) TestXPendingFailures() { options.NewXPendingOptions("invalid-id", "+", 10), ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) _, err = client.XPendingWithOptions( key, @@ -5843,7 +5844,7 @@ func (suite *GlideTestSuite) TestXPendingFailures() { options.NewXPendingOptions("-", "invalid-id", 10), ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // invalid count should return no results detailResult, err = client.XPendingWithOptions( @@ -5860,7 +5861,7 @@ func (suite *GlideTestSuite) TestXPendingFailures() { "invalid-group", ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) assert.True(suite.T(), strings.Contains(err.Error(), "NOGROUP")) // non-existent key throws a RequestError (NOGROUP) @@ -5869,7 +5870,7 @@ func (suite *GlideTestSuite) TestXPendingFailures() { groupName, ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) assert.True(suite.T(), strings.Contains(err.Error(), "NOGROUP")) _, err = client.XPendingWithOptions( @@ -5878,7 +5879,7 @@ func (suite *GlideTestSuite) TestXPendingFailures() { options.NewXPendingOptions("-", "+", 10), ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) assert.True(suite.T(), strings.Contains(err.Error(), "NOGROUP")) // Key exists, but it is not a stream @@ -5888,7 +5889,7 @@ func (suite *GlideTestSuite) TestXPendingFailures() { groupName, ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) assert.True(suite.T(), strings.Contains(err.Error(), "WRONGTYPE")) _, err = client.XPendingWithOptions( @@ -5897,7 +5898,7 @@ func (suite *GlideTestSuite) TestXPendingFailures() { options.NewXPendingOptions("-", "+", 10), ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) assert.True(suite.T(), strings.Contains(err.Error(), "WRONGTYPE")) } @@ -5985,7 +5986,7 @@ func (suite *GlideTestSuite) TestXPendingFailures() { options.NewXPendingOptions("invalid-id", "+", 10), ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) _, err = client.XPendingWithOptions( key, @@ -5993,7 +5994,7 @@ func (suite *GlideTestSuite) TestXPendingFailures() { options.NewXPendingOptions("-", "invalid-id", 10), ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // invalid count should return no results detailResult, err = client.XPendingWithOptions( @@ -6010,7 +6011,7 @@ func (suite *GlideTestSuite) TestXPendingFailures() { "invalid-group", ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) assert.True(suite.T(), strings.Contains(err.Error(), "NOGROUP")) // non-existent key throws a RequestError (NOGROUP) @@ -6019,7 +6020,7 @@ func (suite *GlideTestSuite) TestXPendingFailures() { groupName, ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) assert.True(suite.T(), strings.Contains(err.Error(), "NOGROUP")) _, err = client.XPendingWithOptions( @@ -6028,7 +6029,7 @@ func (suite *GlideTestSuite) TestXPendingFailures() { options.NewXPendingOptions("-", "+", 10), ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) assert.True(suite.T(), strings.Contains(err.Error(), "NOGROUP")) // Key exists, but it is not a stream @@ -6038,7 +6039,7 @@ func (suite *GlideTestSuite) TestXPendingFailures() { groupName, ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) assert.True(suite.T(), strings.Contains(err.Error(), "WRONGTYPE")) _, err = client.XPendingWithOptions( @@ -6047,7 +6048,7 @@ func (suite *GlideTestSuite) TestXPendingFailures() { options.NewXPendingOptions("-", "+", 10), ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) assert.True(suite.T(), strings.Contains(err.Error(), "WRONGTYPE")) } @@ -6074,7 +6075,7 @@ func (suite *GlideTestSuite) TestXGroupCreate_XGroupDestroy() { // Stream not created results in error _, err := client.XGroupCreate(key, group, id) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // Stream with option to create creates stream & Group opts := options.NewXGroupCreateOptions().SetMakeStream() @@ -6083,7 +6084,7 @@ func (suite *GlideTestSuite) TestXGroupCreate_XGroupDestroy() { // ...and again results in BUSYGROUP error, because group names must be unique _, err = client.XGroupCreate(key, group, id) assert.ErrorContains(suite.T(), err, "BUSYGROUP") - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // Stream Group can be destroyed returns: true destroyed, err := client.XGroupDestroy(key, group) @@ -6102,7 +6103,7 @@ func (suite *GlideTestSuite) TestXGroupCreate_XGroupDestroy() { } else { _, err = client.XGroupCreateWithOptions(key, group, id, opts) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) } // key is not a stream @@ -6110,7 +6111,7 @@ func (suite *GlideTestSuite) TestXGroupCreate_XGroupDestroy() { suite.verifyOK(client.Set(key, id)) _, err = client.XGroupCreate(key, group, id) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -6277,7 +6278,7 @@ func (suite *GlideTestSuite) TestZRemRangeByRank() { _, err = client.ZRemRangeByRank(stringKey, 0, 10) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -6332,7 +6333,7 @@ func (suite *GlideTestSuite) TestZRemRangeByLex() { *options.NewRangeByLexQuery(options.NewLexBoundary("a", false), options.NewLexBoundary("c", false)), ) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -6388,7 +6389,7 @@ func (suite *GlideTestSuite) TestZRemRangeByScore() { *options.NewRangeByScoreQuery(options.NewScoreBoundary(1.0, false), options.NewScoreBoundary(10.0, true)), ) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -6619,7 +6620,7 @@ func (suite *GlideTestSuite) TestXGroupStreamCommands() { // create a consumer for a group that doesn't exist should result in a NOGROUP error _, err = client.XGroupCreateConsumer(key, "non-existent-group", consumerName) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) assert.True(suite.T(), strings.Contains(err.Error(), "NOGROUP")) // create consumer that already exists should return false @@ -6703,11 +6704,11 @@ func (suite *GlideTestSuite) TestXGroupStreamCommands() { assert.NoError(suite.T(), err) _, err = client.XGroupCreateConsumer(stringKey, groupName, consumerName) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) _, err = client.XGroupDelConsumer(stringKey, groupName, consumerName) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } @@ -7133,7 +7134,7 @@ func (suite *GlideTestSuite) TestXClaimFailure() { // claim with invalid stream entry IDs _, err = client.XClaimJustId(key, groupName, consumer1, int64(1), []string{"invalid-stream-id"}) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) // claim with empty stream entry IDs returns empty map claimResult, err := client.XClaimJustId(key, groupName, consumer1, int64(1), []string{}) @@ -7144,7 +7145,7 @@ func (suite *GlideTestSuite) TestXClaimFailure() { claimOptions := options.NewStreamClaimOptions().SetIdleTime(1) _, err = client.XClaim(stringKey, groupName, consumer1, int64(1), []string{streamid_1.Value()}) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) assert.Contains(suite.T(), err.Error(), "NOGROUP") _, err = client.XClaimWithOptions( @@ -7156,12 +7157,12 @@ func (suite *GlideTestSuite) TestXClaimFailure() { claimOptions, ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) assert.Contains(suite.T(), err.Error(), "NOGROUP") _, err = client.XClaimJustId(stringKey, groupName, consumer1, int64(1), []string{streamid_1.Value()}) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) assert.Contains(suite.T(), err.Error(), "NOGROUP") _, err = client.XClaimJustIdWithOptions( @@ -7173,7 +7174,7 @@ func (suite *GlideTestSuite) TestXClaimFailure() { claimOptions, ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) assert.Contains(suite.T(), err.Error(), "NOGROUP") // key exists, but is not a stream @@ -7181,7 +7182,7 @@ func (suite *GlideTestSuite) TestXClaimFailure() { assert.NoError(suite.T(), err) _, err = client.XClaim(stringKey, groupName, consumer1, int64(1), []string{streamid_1.Value()}) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) _, err = client.XClaimWithOptions( stringKey, @@ -7192,11 +7193,11 @@ func (suite *GlideTestSuite) TestXClaimFailure() { claimOptions, ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) _, err = client.XClaimJustId(stringKey, groupName, consumer1, int64(1), []string{streamid_1.Value()}) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) _, err = client.XClaimJustIdWithOptions( stringKey, @@ -7207,6 +7208,6 @@ func (suite *GlideTestSuite) TestXClaimFailure() { claimOptions, ) assert.Error(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) }) } diff --git a/go/integTest/standalone_commands_test.go b/go/integTest/standalone_commands_test.go index 7c36e91d96..8d1b3a4528 100644 --- a/go/integTest/standalone_commands_test.go +++ b/go/integTest/standalone_commands_test.go @@ -10,6 +10,7 @@ import ( "github.com/google/uuid" "github.com/valkey-io/valkey-glide/go/glide/api" + "github.com/valkey-io/valkey-glide/go/glide/api/errors" "github.com/valkey-io/valkey-glide/go/glide/api/options" "github.com/stretchr/testify/assert" @@ -152,7 +153,7 @@ func (suite *GlideTestSuite) TestCustomCommand_invalidCommand() { assert.Nil(suite.T(), result) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) } func (suite *GlideTestSuite) TestCustomCommand_invalidArgs() { @@ -161,7 +162,7 @@ func (suite *GlideTestSuite) TestCustomCommand_invalidArgs() { assert.Nil(suite.T(), result) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) } func (suite *GlideTestSuite) TestCustomCommand_closedClient() { @@ -172,7 +173,7 @@ func (suite *GlideTestSuite) TestCustomCommand_closedClient() { assert.Nil(suite.T(), result) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.ClosingError{}, err) + assert.IsType(suite.T(), &errors.ClosingError{}, err) } func (suite *GlideTestSuite) TestConfigSetAndGet_multipleArgs() { @@ -197,12 +198,12 @@ func (suite *GlideTestSuite) TestConfigSetAndGet_noArgs() { _, err := client.ConfigSet(configMap) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) result2, err := client.ConfigGet([]string{}) assert.Nil(suite.T(), result2) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) } func (suite *GlideTestSuite) TestConfigSetAndGet_invalidArgs() { @@ -212,7 +213,7 @@ func (suite *GlideTestSuite) TestConfigSetAndGet_invalidArgs() { _, err := client.ConfigSet(configMap) assert.NotNil(suite.T(), err) - assert.IsType(suite.T(), &api.RequestError{}, err) + assert.IsType(suite.T(), &errors.RequestError{}, err) result2, err := client.ConfigGet([]string{"time"}) assert.Equal(suite.T(), map[string]string{}, result2) @@ -415,5 +416,5 @@ func (suite *GlideTestSuite) TestTime_Error() { assert.NotNil(suite.T(), err) assert.Nil(suite.T(), results) - assert.IsType(suite.T(), &api.ClosingError{}, err) + assert.IsType(suite.T(), &errors.ClosingError{}, err) } From 2ce5dbcae5f6c0694e96a3160a8a9a7042365387 Mon Sep 17 00:00:00 2001 From: Niharika Bhavaraju Date: Mon, 27 Jan 2025 16:18:14 +0000 Subject: [PATCH 5/9] Fixed code review comments for multi cluster value Signed-off-by: Niharika Bhavaraju --- go/api/glide_cluster_client.go | 30 +++++++++++++------ go/api/options/time_options.go | 13 ++------ go/api/server_management_cluster_commands.go | 2 +- go/integTest/cluster_commands_test.go | 31 +++++++++++++------- 4 files changed, 45 insertions(+), 31 deletions(-) diff --git a/go/api/glide_cluster_client.go b/go/api/glide_cluster_client.go index 7d7e808026..88e0d2e1dd 100644 --- a/go/api/glide_cluster_client.go +++ b/go/api/glide_cluster_client.go @@ -5,7 +5,9 @@ package api // #cgo LDFLAGS: -L../target/release -lglide_rs // #include "../lib.h" import "C" -import "github.com/valkey-io/valkey-glide/go/glide/api/options" +import ( + "github.com/valkey-io/valkey-glide/go/glide/api/options" +) // GlideClusterClient interface compliance check. var _ GlideClusterClientCommands = (*GlideClusterClient)(nil) @@ -89,15 +91,25 @@ func (client *GlideClusterClient) CustomCommand(args []string) (ClusterValue[int // Example: // // route := api.SimpleNodeRoute(api.RandomRoute) -// options := options.NewTimeOptionsBuilder().SetRoute(route) -// result, err := client.TimeWithOptions(route) -// fmt.Println(result.Value()) // Output: [1737285074 67888] +// route := config.Route(config.AllNodes) +// opts := options.ClusterTimeOptions{ +// Route: &route, +// } +// fmt.Println(clusterResponse.Value()) // Output: [1737994354 547816 1737994354 547856] // // [valkey.io]: https://valkey.io/commands/time/ -func (client *glideClusterClient) TimeWithOptions(opts *options.TimeOptions) (ClusterValue[[]string], error) { - result, err := client.executeCommandWithRoute(C.Time, []string{}, opts.Route) - if err != nil { - return CreateEmptyStringArrayClusterValue(), err +func (client *GlideClusterClient) TimeWithOptions(opts options.ClusterTimeOptions) (ClusterValue[[]string], error) { + if opts.Route == nil { + response, err := client.executeCommand(C.Time, []string{}) + if err != nil { + return CreateEmptyStringArrayClusterValue(), err + } + return handleTimeClusterResponse(response) + } else { + result, err := client.executeCommandWithRoute(C.Time, []string{}, *opts.Route) + if err != nil { + return CreateEmptyStringArrayClusterValue(), err + } + return handleTimeClusterResponse(result) } - return handleTimeClusterResponse(result) } diff --git a/go/api/options/time_options.go b/go/api/options/time_options.go index fdbd326ccb..65966ee03a 100644 --- a/go/api/options/time_options.go +++ b/go/api/options/time_options.go @@ -2,15 +2,6 @@ package options import "github.com/valkey-io/valkey-glide/go/glide/api/config" -type TimeOptions struct { - Route config.Route -} - -func NewTimeOptionsBuilder() *TimeOptions { - return &TimeOptions{} -} - -func (timeOptions *TimeOptions) SetRoute(route config.Route) *TimeOptions { - timeOptions.Route = route - return timeOptions +type ClusterTimeOptions struct { + Route *config.Route } diff --git a/go/api/server_management_cluster_commands.go b/go/api/server_management_cluster_commands.go index c4da3d09f4..7e144140cc 100644 --- a/go/api/server_management_cluster_commands.go +++ b/go/api/server_management_cluster_commands.go @@ -10,5 +10,5 @@ import "github.com/valkey-io/valkey-glide/go/glide/api/options" // // [valkey.io]: https://valkey.io/commands/#server type ServerManagementClusterCommands interface { - TimeWithOptions(timeOptions *options.TimeOptions) (ClusterValue[[]string], error) + TimeWithOptions(timeOptions options.ClusterTimeOptions) (ClusterValue[[]string], error) } diff --git a/go/integTest/cluster_commands_test.go b/go/integTest/cluster_commands_test.go index dd9548c0cc..61404002f6 100644 --- a/go/integTest/cluster_commands_test.go +++ b/go/integTest/cluster_commands_test.go @@ -30,10 +30,9 @@ func (suite *GlideTestSuite) TestClusterCustomCommandEcho() { assert.Equal(suite.T(), "GO GLIDE GO", result.Value().(string)) } -func (suite *GlideTestSuite) TestTime_RandomRoute() { +func (suite *GlideTestSuite) TestTimeWithoutRoute() { client := suite.defaultClusterClient() - route := config.SimpleNodeRoute(config.RandomRoute) - options := options.NewTimeOptionsBuilder().SetRoute(route) + options := options.ClusterTimeOptions{Route: nil} result, err := client.TimeWithOptions(options) assert.NoError(suite.T(), err) @@ -43,15 +42,15 @@ func (suite *GlideTestSuite) TestTime_RandomRoute() { assert.Equal(suite.T(), 2, len(result.Value())) } -func (suite *GlideTestSuite) TestTime_AllNodes_MultipleValues() { +func (suite *GlideTestSuite) TestTimeWithAllNodesRoute() { client := suite.defaultClusterClient() - route := config.AllNodes - options := options.NewTimeOptionsBuilder().SetRoute(route) + route := config.Route(config.AllNodes) + options := options.ClusterTimeOptions{Route: &route} result, err := client.TimeWithOptions(options) + assert.NoError(suite.T(), err) assert.NotNil(suite.T(), result) assert.NotEmpty(suite.T(), result.Value()) - assert.Greater(suite.T(), len(result.Value()), 1) for _, timeStr := range result.Value() { @@ -59,11 +58,23 @@ func (suite *GlideTestSuite) TestTime_AllNodes_MultipleValues() { } } -func (suite *GlideTestSuite) TestTime_ErrorHandling() { +func (suite *GlideTestSuite) TestTimeWithRandomRoute() { client := suite.defaultClusterClient() - invalidRoute := config.NewByAddressRoute("invalidHost", 9999) + route := config.Route(config.RandomRoute) + options := options.ClusterTimeOptions{Route: &route} + result, err := client.TimeWithOptions(options) - options := options.NewTimeOptionsBuilder().SetRoute(invalidRoute) + assert.NoError(suite.T(), err) + assert.NotNil(suite.T(), result) + assert.NotEmpty(suite.T(), result.Value()) + assert.IsType(suite.T(), "", result.Value()[0]) + assert.Equal(suite.T(), 2, len(result.Value())) +} + +func (suite *GlideTestSuite) TestTimeWithInvalidRoute() { + client := suite.defaultClusterClient() + invalidRoute := config.Route(config.NewByAddressRoute("invalidHost", 9999)) + options := options.ClusterTimeOptions{Route: &invalidRoute} result, err := client.TimeWithOptions(options) assert.NotNil(suite.T(), err) From 5364841cdbf919c1385d99361fd0364b02534b5e Mon Sep 17 00:00:00 2001 From: Niharika Bhavaraju Date: Mon, 27 Jan 2025 16:30:35 +0000 Subject: [PATCH 6/9] Fixed linting error Signed-off-by: Niharika Bhavaraju --- go/api/glide_cluster_client.go | 1 + 1 file changed, 1 insertion(+) diff --git a/go/api/glide_cluster_client.go b/go/api/glide_cluster_client.go index 88e0d2e1dd..1f8bfca13c 100644 --- a/go/api/glide_cluster_client.go +++ b/go/api/glide_cluster_client.go @@ -5,6 +5,7 @@ package api // #cgo LDFLAGS: -L../target/release -lglide_rs // #include "../lib.h" import "C" + import ( "github.com/valkey-io/valkey-glide/go/glide/api/options" ) From ff976a7b36c9fe4c622b9753cdf264e30b245a21 Mon Sep 17 00:00:00 2001 From: Niharika Bhavaraju Date: Tue, 28 Jan 2025 09:31:24 +0000 Subject: [PATCH 7/9] Fixed code review changes Signed-off-by: Niharika Bhavaraju --- go/api/base_client.go | 2 +- go/api/glide_cluster_client.go | 21 ++---- go/api/options/route_options.go | 9 +++ go/api/options/time_options.go | 7 -- go/api/response_handlers.go | 68 +++++--------------- go/api/server_management_cluster_commands.go | 2 +- go/integTest/cluster_commands_test.go | 8 +-- 7 files changed, 37 insertions(+), 80 deletions(-) create mode 100644 go/api/options/route_options.go delete mode 100644 go/api/options/time_options.go diff --git a/go/api/base_client.go b/go/api/base_client.go index e4d2ee9be5..f64112dbd6 100644 --- a/go/api/base_client.go +++ b/go/api/base_client.go @@ -6838,5 +6838,5 @@ func (client *baseClient) Time() ([]string, error) { if err != nil { return nil, err } - return handleRawStringArrayResponse(result) + return handleStringArrayResponse(result) } diff --git a/go/api/glide_cluster_client.go b/go/api/glide_cluster_client.go index 1f8bfca13c..b80112dedc 100644 --- a/go/api/glide_cluster_client.go +++ b/go/api/glide_cluster_client.go @@ -91,26 +91,17 @@ func (client *GlideClusterClient) CustomCommand(args []string) (ClusterValue[int // // Example: // -// route := api.SimpleNodeRoute(api.RandomRoute) // route := config.Route(config.AllNodes) // opts := options.ClusterTimeOptions{ // Route: &route, // } -// fmt.Println(clusterResponse.Value()) // Output: [1737994354 547816 1737994354 547856] +// fmt.Println(clusterResponse.Value()) // Output: [1737994354 547816] // // [valkey.io]: https://valkey.io/commands/time/ -func (client *GlideClusterClient) TimeWithOptions(opts options.ClusterTimeOptions) (ClusterValue[[]string], error) { - if opts.Route == nil { - response, err := client.executeCommand(C.Time, []string{}) - if err != nil { - return CreateEmptyStringArrayClusterValue(), err - } - return handleTimeClusterResponse(response) - } else { - result, err := client.executeCommandWithRoute(C.Time, []string{}, *opts.Route) - if err != nil { - return CreateEmptyStringArrayClusterValue(), err - } - return handleTimeClusterResponse(result) +func (client *GlideClusterClient) TimeWithOptions(opts options.RouteOption) (ClusterValue[[]string], error) { + result, err := client.executeCommandWithRoute(C.Time, []string{}, opts.Route) + if err != nil { + return CreateEmptyStringArrayClusterValue(), err } + return handleTimeClusterResponse(result) } diff --git a/go/api/options/route_options.go b/go/api/options/route_options.go new file mode 100644 index 0000000000..1f1cbd2b20 --- /dev/null +++ b/go/api/options/route_options.go @@ -0,0 +1,9 @@ +// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 + +package options + +import "github.com/valkey-io/valkey-glide/go/glide/api/config" + +type RouteOption struct { + Route config.Route +} diff --git a/go/api/options/time_options.go b/go/api/options/time_options.go deleted file mode 100644 index 65966ee03a..0000000000 --- a/go/api/options/time_options.go +++ /dev/null @@ -1,7 +0,0 @@ -package options - -import "github.com/valkey-io/valkey-glide/go/glide/api/config" - -type ClusterTimeOptions struct { - Route *config.Route -} diff --git a/go/api/response_handlers.go b/go/api/response_handlers.go index 1ab38847f6..46cfae10e9 100644 --- a/go/api/response_handlers.go +++ b/go/api/response_handlers.go @@ -978,32 +978,6 @@ func handleXPendingDetailResponse(response *C.struct_CommandResponse) ([]XPendin return pendingDetails, nil } -func handleRawStringArrayResponse(response *C.struct_CommandResponse) ([]string, error) { - defer C.free_command_response(response) - - typeErr := checkResponseType(response, C.Array, false) - if typeErr != nil { - return nil, typeErr - } - - slice := make([]string, 0, response.array_value_len) - for _, v := range unsafe.Slice(response.array_value, response.array_value_len) { - err := checkResponseType(&v, C.String, false) - if err != nil { - return nil, err - } - - if v.string_value == nil { - return nil, &errors.RequestError{Msg: "Unexpected nil string in array"} - } - - byteSlice := C.GoBytes(unsafe.Pointer(v.string_value), C.int(int64(v.string_value_len))) - slice = append(slice, string(byteSlice)) - } - - return slice, nil -} - func handleRawStringArrayMapResponse(response *C.struct_CommandResponse) (map[string][]string, error) { defer C.free_command_response(response) typeErr := checkResponseType(response, C.Map, false) @@ -1011,35 +985,25 @@ func handleRawStringArrayMapResponse(response *C.struct_CommandResponse) (map[st return nil, typeErr } - result := make(map[string][]string) - for _, v := range unsafe.Slice(response.array_value, response.array_value_len) { - key, err := convertCharArrayToString(v.map_key, true) - if err != nil { - return nil, err - } - - err = checkResponseType(v.map_value, C.Array, false) - if err != nil { - return nil, err - } + data, err := parseMap(response) + if err != nil { + return nil, err + } - timeStrings := make([]string, 0, v.map_value.array_value_len) - for _, strVal := range unsafe.Slice(v.map_value.array_value, v.map_value.array_value_len) { - err := checkResponseType(&strVal, C.String, false) - if err != nil { - return nil, err - } - if strVal.string_value == nil { - return nil, &errors.RequestError{Msg: "Unexpected nil string in array"} - } - byteSlice := C.GoBytes(unsafe.Pointer(strVal.string_value), C.int(int64(strVal.string_value_len))) - timeStrings = append(timeStrings, string(byteSlice)) - } + result, err := mapConverter[[]string]{ + next: arrayConverter[string]{}, + canBeNil: false, + }.convert(data) - result[key.Value()] = timeStrings + if err != nil { + return nil, err + } + mapResult, ok := result.(map[string][]string) + if !ok { + return nil, &errors.RequestError{Msg: "Unexpected conversion result type"} } - return result, nil + return mapResult, nil } func handleTimeClusterResponse(response *C.struct_CommandResponse) (ClusterValue[[]string], error) { @@ -1057,7 +1021,7 @@ func handleTimeClusterResponse(response *C.struct_CommandResponse) (ClusterValue } // Handle single node response - data, err := handleRawStringArrayResponse(response) + data, err := handleStringArrayResponse(response) if err != nil { return CreateEmptyStringArrayClusterValue(), err } diff --git a/go/api/server_management_cluster_commands.go b/go/api/server_management_cluster_commands.go index 7e144140cc..1ae831bcb0 100644 --- a/go/api/server_management_cluster_commands.go +++ b/go/api/server_management_cluster_commands.go @@ -10,5 +10,5 @@ import "github.com/valkey-io/valkey-glide/go/glide/api/options" // // [valkey.io]: https://valkey.io/commands/#server type ServerManagementClusterCommands interface { - TimeWithOptions(timeOptions options.ClusterTimeOptions) (ClusterValue[[]string], error) + TimeWithOptions(routeOption options.RouteOption) (ClusterValue[[]string], error) } diff --git a/go/integTest/cluster_commands_test.go b/go/integTest/cluster_commands_test.go index 61404002f6..7d72010bb3 100644 --- a/go/integTest/cluster_commands_test.go +++ b/go/integTest/cluster_commands_test.go @@ -32,7 +32,7 @@ func (suite *GlideTestSuite) TestClusterCustomCommandEcho() { func (suite *GlideTestSuite) TestTimeWithoutRoute() { client := suite.defaultClusterClient() - options := options.ClusterTimeOptions{Route: nil} + options := options.RouteOption{Route: nil} result, err := client.TimeWithOptions(options) assert.NoError(suite.T(), err) @@ -45,7 +45,7 @@ func (suite *GlideTestSuite) TestTimeWithoutRoute() { func (suite *GlideTestSuite) TestTimeWithAllNodesRoute() { client := suite.defaultClusterClient() route := config.Route(config.AllNodes) - options := options.ClusterTimeOptions{Route: &route} + options := options.RouteOption{Route: route} result, err := client.TimeWithOptions(options) assert.NoError(suite.T(), err) @@ -61,7 +61,7 @@ func (suite *GlideTestSuite) TestTimeWithAllNodesRoute() { func (suite *GlideTestSuite) TestTimeWithRandomRoute() { client := suite.defaultClusterClient() route := config.Route(config.RandomRoute) - options := options.ClusterTimeOptions{Route: &route} + options := options.RouteOption{Route: route} result, err := client.TimeWithOptions(options) assert.NoError(suite.T(), err) @@ -74,7 +74,7 @@ func (suite *GlideTestSuite) TestTimeWithRandomRoute() { func (suite *GlideTestSuite) TestTimeWithInvalidRoute() { client := suite.defaultClusterClient() invalidRoute := config.Route(config.NewByAddressRoute("invalidHost", 9999)) - options := options.ClusterTimeOptions{Route: &invalidRoute} + options := options.RouteOption{Route: invalidRoute} result, err := client.TimeWithOptions(options) assert.NotNil(suite.T(), err) From 355b339090e49970e9eae401854a60cbe545aaa7 Mon Sep 17 00:00:00 2001 From: Niharika Bhavaraju Date: Tue, 28 Jan 2025 09:51:51 +0000 Subject: [PATCH 8/9] Fix linting error Signed-off-by: Niharika Bhavaraju --- go/api/response_handlers.go | 1 - 1 file changed, 1 deletion(-) diff --git a/go/api/response_handlers.go b/go/api/response_handlers.go index 8fb5658c40..e949acc027 100644 --- a/go/api/response_handlers.go +++ b/go/api/response_handlers.go @@ -1020,7 +1020,6 @@ func handleRawStringArrayMapResponse(response *C.struct_CommandResponse) (map[st next: arrayConverter[string]{}, canBeNil: false, }.convert(data) - if err != nil { return nil, err } From 88c3e2d35ae2978737ea20fbdf8fd4b84cf326a8 Mon Sep 17 00:00:00 2001 From: Niharika Bhavaraju Date: Wed, 29 Jan 2025 08:31:07 +0000 Subject: [PATCH 9/9] Fixed code review comments and failing tests Signed-off-by: Niharika Bhavaraju --- go/api/glide_cluster_client.go | 7 ++-- go/api/response_handlers.go | 15 ++++---- go/api/response_types.go | 7 ---- go/api/server_management_cluster_commands.go | 16 ++------- go/integTest/cluster_commands_test.go | 37 ++++++++++++-------- 5 files changed, 37 insertions(+), 45 deletions(-) diff --git a/go/api/glide_cluster_client.go b/go/api/glide_cluster_client.go index 0cfd5d013f..59f9c589f1 100644 --- a/go/api/glide_cluster_client.go +++ b/go/api/glide_cluster_client.go @@ -166,6 +166,7 @@ func (client *GlideClusterClient) InfoWithOptions(options ClusterInfoOptions) (C } // Returns the server time. +// The command will be routed to a random node, unless Route in opts is provided. // // See [valkey.io] for details. // @@ -181,17 +182,17 @@ func (client *GlideClusterClient) InfoWithOptions(options ClusterInfoOptions) (C // // Example: // -// route := config.Route(config.AllNodes) +// route := config.Route(config.RandomRoute) // opts := options.ClusterTimeOptions{ // Route: &route, // } -// fmt.Println(clusterResponse.Value()) // Output: [1737994354 547816] +// fmt.Println(clusterResponse.SingleValue()) // Output: [1737994354 547816] // // [valkey.io]: https://valkey.io/commands/time/ func (client *GlideClusterClient) TimeWithOptions(opts options.RouteOption) (ClusterValue[[]string], error) { result, err := client.executeCommandWithRoute(C.Time, []string{}, opts.Route) if err != nil { - return CreateEmptyStringArrayClusterValue(), err + return createEmptyClusterValue[[]string](), err } return handleTimeClusterResponse(result) } diff --git a/go/api/response_handlers.go b/go/api/response_handlers.go index e949acc027..4136dd09cb 100644 --- a/go/api/response_handlers.go +++ b/go/api/response_handlers.go @@ -1036,19 +1036,20 @@ func handleTimeClusterResponse(response *C.struct_CommandResponse) (ClusterValue if err := checkResponseType(response, C.Map, true); err == nil { mapData, err := handleRawStringArrayMapResponse(response) if err != nil { - return CreateEmptyStringArrayClusterValue(), err + return createEmptyClusterValue[[]string](), err } - var times []string - for _, nodeTimes := range mapData { - times = append(times, nodeTimes...) + multiNodeTimes := make(map[string][]string) + for nodeName, nodeTimes := range mapData { + multiNodeTimes[nodeName] = nodeTimes } - return CreateClusterMultiValue(times), nil + + return createClusterMultiValue(multiNodeTimes), nil } // Handle single node response data, err := handleStringArrayResponse(response) if err != nil { - return CreateEmptyStringArrayClusterValue(), err + return createEmptyClusterValue[[]string](), err } - return CreateClusterSingleValue(data), nil + return createClusterSingleValue(data), nil } diff --git a/go/api/response_types.go b/go/api/response_types.go index b758235a25..1036c55c6b 100644 --- a/go/api/response_types.go +++ b/go/api/response_types.go @@ -169,13 +169,6 @@ func createEmptyClusterValue[T any]() ClusterValue[T] { } } -func CreateEmptyStringArrayClusterValue() ClusterValue[[]string] { - var empty []string - return ClusterValue[[]string]{ - value: Result[[]string]{val: empty, isNil: true}, - } -} - // XPendingSummary represents a summary of pending messages in a stream group. // It includes the total number of pending messages, the ID of the first and last pending messages, // and a list of consumer pending messages. diff --git a/go/api/server_management_cluster_commands.go b/go/api/server_management_cluster_commands.go index 7c6f7d5565..b49737c05b 100644 --- a/go/api/server_management_cluster_commands.go +++ b/go/api/server_management_cluster_commands.go @@ -2,7 +2,9 @@ package api -// ServerManagementCommands supports commands for the "Server Management" group for a cluster client. +import "github.com/valkey-io/valkey-glide/go/glide/api/options" + +// ServerManagementClusterCommands supports commands for the "Server Management Commands" group for cluster client. // // See [valkey.io] for details. // @@ -11,18 +13,6 @@ type ServerManagementClusterCommands interface { Info() (map[string]string, error) InfoWithOptions(options ClusterInfoOptions) (ClusterValue[string], error) -} -// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 - -package api -import "github.com/valkey-io/valkey-glide/go/glide/api/options" - -// ServerManagementClusterCommands supports commands for the "Server Management Commands" group for cluster client. -// -// See [valkey.io] for details. -// -// [valkey.io]: https://valkey.io/commands/#server -type ServerManagementClusterCommands interface { TimeWithOptions(routeOption options.RouteOption) (ClusterValue[[]string], error) } diff --git a/go/integTest/cluster_commands_test.go b/go/integTest/cluster_commands_test.go index 15d96ca1e4..34c683a355 100644 --- a/go/integTest/cluster_commands_test.go +++ b/go/integTest/cluster_commands_test.go @@ -112,12 +112,13 @@ func (suite *GlideTestSuite) TestTimeWithoutRoute() { client := suite.defaultClusterClient() options := options.RouteOption{Route: nil} result, err := client.TimeWithOptions(options) - assert.NoError(suite.T(), err) assert.NotNil(suite.T(), result) - assert.NotEmpty(suite.T(), result.Value()) - assert.IsType(suite.T(), "", result.Value()[0]) - assert.Equal(suite.T(), 2, len(result.Value())) + assert.False(suite.T(), result.IsEmpty()) + assert.True(suite.T(), result.IsSingleValue()) + assert.NotEmpty(suite.T(), result.SingleValue()) + assert.IsType(suite.T(), "", result.SingleValue()[0]) + assert.Equal(suite.T(), 2, len(result.SingleValue())) } func (suite *GlideTestSuite) TestTimeWithAllNodesRoute() { @@ -125,14 +126,19 @@ func (suite *GlideTestSuite) TestTimeWithAllNodesRoute() { route := config.Route(config.AllNodes) options := options.RouteOption{Route: route} result, err := client.TimeWithOptions(options) - assert.NoError(suite.T(), err) assert.NotNil(suite.T(), result) - assert.NotEmpty(suite.T(), result.Value()) - assert.Greater(suite.T(), len(result.Value()), 1) + assert.False(suite.T(), result.IsEmpty()) + assert.True(suite.T(), result.IsMultiValue()) + + multiValue := result.MultiValue() + assert.Greater(suite.T(), len(multiValue), 1) - for _, timeStr := range result.Value() { - assert.IsType(suite.T(), "", timeStr) + for nodeName, timeStrings := range multiValue { + assert.NotEmpty(suite.T(), timeStrings, "Node %s should have time values", nodeName) + for _, timeStr := range timeStrings { + assert.IsType(suite.T(), "", timeStr) + } } } @@ -141,12 +147,13 @@ func (suite *GlideTestSuite) TestTimeWithRandomRoute() { route := config.Route(config.RandomRoute) options := options.RouteOption{Route: route} result, err := client.TimeWithOptions(options) - assert.NoError(suite.T(), err) assert.NotNil(suite.T(), result) - assert.NotEmpty(suite.T(), result.Value()) - assert.IsType(suite.T(), "", result.Value()[0]) - assert.Equal(suite.T(), 2, len(result.Value())) + assert.False(suite.T(), result.IsEmpty()) + assert.True(suite.T(), result.IsSingleValue()) + assert.NotEmpty(suite.T(), result.SingleValue()) + assert.IsType(suite.T(), "", result.SingleValue()[0]) + assert.Equal(suite.T(), 2, len(result.SingleValue())) } func (suite *GlideTestSuite) TestTimeWithInvalidRoute() { @@ -154,7 +161,7 @@ func (suite *GlideTestSuite) TestTimeWithInvalidRoute() { invalidRoute := config.Route(config.NewByAddressRoute("invalidHost", 9999)) options := options.RouteOption{Route: invalidRoute} result, err := client.TimeWithOptions(options) - assert.NotNil(suite.T(), err) - assert.Empty(suite.T(), result.Value()) + assert.True(suite.T(), result.IsEmpty()) + assert.Empty(suite.T(), result.SingleValue()) }