diff --git a/go/api/command_options.go b/go/api/command_options.go index 8332afbd32..23bc686e1c 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/config" "github.com/valkey-io/valkey-glide/go/glide/api/errors" "github.com/valkey-io/valkey-glide/go/glide/utils" ) @@ -357,6 +358,73 @@ func (opts *RestoreOptions) toArgs() ([]string, error) { return args, err } +type Section string + +const ( + // SERVER: General information about the server + Server Section = "server" + // CLIENTS: Client connections section + Clients Section = "clients" + // MEMORY: Memory consumption related information + Memory Section = "memory" + // PERSISTENCE: RDB and AOF related information + Persistence Section = "persistence" + // STATS: General statistics + Stats Section = "stats" + // REPLICATION: Master/replica replication information + Replication Section = "replication" + // CPU: CPU consumption statistics + Cpu Section = "cpu" + // COMMANDSTATS: Valkey command statistics + Commandstats Section = "commandstats" + // LATENCYSTATS: Valkey command latency percentile distribution statistics + Latencystats Section = "latencystats" + // SENTINEL: Valkey Sentinel section (only applicable to Sentinel instances) + Sentinel Section = "sentinel" + // CLUSTER: Valkey Cluster section + Cluster Section = "cluster" + // MODULES: Modules section + Modules Section = "modules" + // KEYSPACE: Database related statistics + Keyspace Section = "keyspace" + // ERRORSTATS: Valkey error statistics + Errorstats Section = "errorstats" + // ALL: Return all sections (excluding module generated ones) + All Section = "all" + // DEFAULT: Return only the default set of sections + Default Section = "default" + // EVERYTHING: Includes all and modules + Everything Section = "everything" +) + +// Optional arguments for `Info` for standalone client +type InfoOptions struct { + // A list of [Section] values specifying which sections of information to retrieve. + // When no parameter is provided, [Section.Default] is assumed. + // Starting with server version 7.0.0 `INFO` command supports multiple sections. + Sections []Section +} + +// Optional arguments for `Info` for cluster client +type ClusterInfoOptions struct { + *InfoOptions + // Specifies the routing configuration for the command. + // The client will route the command to the nodes defined by `Route`. + // The command will be routed to all primary nodes, unless `Route` is provided. + Route *config.Route +} + +func (opts *InfoOptions) toArgs() []string { + if opts == nil { + return []string{} + } + args := make([]string, 0, len(opts.Sections)) + for _, section := range opts.Sections { + args = append(args, string(section)) + } + return args +} + // Optional arguments to Copy(source string, destination string, option *CopyOptions) // // [valkey.io]: https://valkey.io/commands/Copy/ diff --git a/go/api/config/request_routing_config.go b/go/api/config/request_routing_config.go index 1d0acc27d3..8bcb0221ab 100644 --- a/go/api/config/request_routing_config.go +++ b/go/api/config/request_routing_config.go @@ -18,8 +18,13 @@ import ( // - [config.ByAddressRoute] type Route interface { ToRoutesProtobuf() (*protobuf.Routes, error) + IsMultiNode() bool } +type notMultiNode struct{} + +func (*notMultiNode) IsMultiNode() bool { return false } + type SimpleNodeRoute int const ( @@ -47,6 +52,15 @@ func (simpleNodeRoute SimpleNodeRoute) ToRoutesProtobuf() (*protobuf.Routes, err return request, nil } +func (route SimpleNodeRoute) IsMultiNode() bool { + return route != RandomRoute +} + +func (snr SimpleNodeRoute) ToPtr() *Route { + a := Route(snr) + return &a +} + func mapSimpleNodeRoute(simpleNodeRoute SimpleNodeRoute) (protobuf.SimpleRoutes, error) { switch simpleNodeRoute { case AllNodes: @@ -86,6 +100,7 @@ func mapSlotType(slotType SlotType) (protobuf.SlotTypes, error) { type SlotIdRoute struct { slotType SlotType slotID int32 + notMultiNode } // - slotType: Defines type of the node being addressed. @@ -117,6 +132,7 @@ func (slotIdRoute *SlotIdRoute) ToRoutesProtobuf() (*protobuf.Routes, error) { type SlotKeyRoute struct { slotType SlotType slotKey string + notMultiNode } // - slotType: Defines type of the node being addressed. @@ -146,6 +162,7 @@ func (slotKeyRoute *SlotKeyRoute) ToRoutesProtobuf() (*protobuf.Routes, error) { type ByAddressRoute struct { host string port int32 + notMultiNode } // Create a route using hostname/address and port. diff --git a/go/api/glide_client.go b/go/api/glide_client.go index 6da53a5609..cefda9b8d2 100644 --- a/go/api/glide_client.go +++ b/go/api/glide_client.go @@ -92,7 +92,7 @@ func (client *GlideClient) CustomCommand(args []string) (interface{}, error) { func (client *GlideClient) ConfigSet(parameters map[string]string) (string, error) { result, err := client.executeCommand(C.ConfigSet, utils.MapToString(parameters)) if err != nil { - return "", err + return defaultStringResponse, err } return handleStringResponse(result) } @@ -145,7 +145,59 @@ func (client *GlideClient) ConfigGet(args []string) (map[string]string, error) { func (client *GlideClient) Select(index int64) (string, error) { result, err := client.executeCommand(C.Select, []string{utils.IntToString(index)}) if err != nil { - return "", err + return defaultStringResponse, err + } + + return handleStringResponse(result) +} + +// Gets information and statistics about the server. +// +// See [valkey.io] for details. +// +// Return value: +// +// A string with the information for the default sections. +// +// Example: +// +// response, err := standaloneClient.Info(opts) +// if err != nil { +// // handle error +// } +// fmt.Println(response) +// +// [valkey.io]: https://valkey.io/commands/info/ +func (client *GlideClient) Info() (string, error) { + return client.InfoWithOptions(InfoOptions{[]Section{}}) +} + +// Gets information and statistics about the server. +// +// See [valkey.io] for details. +// +// Parameters: +// +// options - Additional command parameters, see [InfoOptions] for more details. +// +// Return value: +// +// A string containing the information for the sections requested. +// +// Example: +// +// opts := api.InfoOptions{Sections: []api.Section{api.Server}} +// response, err := standaloneClient.InfoWithOptions(opts) +// if err != nil { +// // handle error +// } +// fmt.Println(response) +// +// [valkey.io]: https://valkey.io/commands/info/ +func (client *GlideClient) InfoWithOptions(options InfoOptions) (string, error) { + result, err := client.executeCommand(C.Info, options.toArgs()) + if err != nil { + return defaultStringResponse, err } return handleStringResponse(result) diff --git a/go/api/glide_cluster_client.go b/go/api/glide_cluster_client.go index 2fba684d8c..5a907286fe 100644 --- a/go/api/glide_cluster_client.go +++ b/go/api/glide_cluster_client.go @@ -13,6 +13,7 @@ var _ GlideClusterClientCommands = (*GlideClusterClient)(nil) type GlideClusterClientCommands interface { BaseClient GenericClusterCommands + ServerManagementClusterCommands } // GlideClusterClient implements cluster mode operations by extending baseClient functionality. @@ -61,11 +62,101 @@ func NewGlideClusterClient(config *GlideClusterClientConfiguration) (GlideCluste func (client *GlideClusterClient) CustomCommand(args []string) (ClusterValue[interface{}], error) { res, err := client.executeCommand(C.CustomCommand, args) if err != nil { - return CreateEmptyClusterValue(), err + return createEmptyClusterValue[interface{}](), err } data, err := handleInterfaceResponse(res) if err != nil { - return CreateEmptyClusterValue(), err + return createEmptyClusterValue[interface{}](), err } - return CreateClusterValue(data), nil + return createClusterValue[interface{}](data), nil +} + +// Gets information and statistics about the server. +// +// The command will be routed to all primary nodes. +// +// See [valkey.io] for details. +// +// Return value: +// +// A map where each address is the key and its corresponding node response is the information for the default sections. +// +// Example: +// +// response, err := clusterClient.Info(opts) +// if err != nil { +// // handle error +// } +// for node, data := range response { +// fmt.Printf("%s node returned %s\n", node, data) +// } +// +// [valkey.io]: https://valkey.io/commands/info/ +func (client *GlideClusterClient) Info() (map[string]string, error) { + result, err := client.executeCommand(C.Info, []string{}) + if err != nil { + return nil, err + } + + return handleStringToStringMapResponse(result) +} + +// Gets information and statistics about the server. +// +// The command will be routed to all primary nodes, unless `route` in [ClusterInfoOptions] is provided. +// +// See [valkey.io] for details. +// +// Parameters: +// +// options - Additional command parameters, see [ClusterInfoOptions] for more details. +// +// Return value: +// +// When specifying a route other than a single node or when route is not given, +// it returns a map where each address is the key and its corresponding node response is the value. +// When a single node route is given, command returns a string containing the information for the sections requested. +// +// Example: +// +// opts := api.ClusterInfoOptions{ +// InfoOptions: &api.InfoOptions{Sections: []api.Section{api.Server}}, +// Route: api.RandomRoute.ToPtr(), +// } +// response, err := clusterClient.InfoWithOptions(opts) +// if err != nil { +// // handle error +// } +// // Command sent to a single random node via RANDOM route, expecting SingleValue result as a `string`. +// fmt.Println(response.SingleValue()) +// +// [valkey.io]: https://valkey.io/commands/info/ +func (client *GlideClusterClient) InfoWithOptions(options ClusterInfoOptions) (ClusterValue[string], error) { + if options.Route == nil { + response, err := client.executeCommand(C.Info, options.toArgs()) + if err != nil { + return createEmptyClusterValue[string](), err + } + data, err := handleStringToStringMapResponse(response) + if err != nil { + return createEmptyClusterValue[string](), err + } + return createClusterMultiValue[string](data), nil + } + response, err := client.executeCommandWithRoute(C.Info, options.toArgs(), *options.Route) + if err != nil { + return createEmptyClusterValue[string](), err + } + if (*options.Route).IsMultiNode() { + data, err := handleStringToStringMapResponse(response) + if err != nil { + return createEmptyClusterValue[string](), err + } + return createClusterMultiValue[string](data), nil + } + data, err := handleStringResponse(response) + if err != nil { + return createEmptyClusterValue[string](), err + } + return createClusterSingleValue[string](data), nil } diff --git a/go/api/response_types.go b/go/api/response_types.go index 84de6aed7f..1036c55c6b 100644 --- a/go/api/response_types.go +++ b/go/api/response_types.go @@ -83,12 +83,13 @@ type ValueType int const ( SingleValue ValueType = 1 MultiValue ValueType = 2 + NoValue ValueType = 3 ) // Enum-like structure which stores either a single-node response or multi-node response. // Multi-node response stored in a map, where keys are hostnames or ":" strings. // -// For example: +// Example: // // // Command failed: // value, err := clusterClient.CustomCommand(args) @@ -96,25 +97,33 @@ const ( // err != nil: true // // // Command returns response from multiple nodes: -// value, _ := clusterClient.info() -// node, nodeResponse := range value.Value().(map[string]interface{}) { -// response := nodeResponse.(string) +// value, _ := clusterClient.Info() +// for node, nodeResponse := range value.MultiValue() { +// response := nodeResponse // // `node` stores cluster node IP/hostname, `response` stores the command output from that node // } // // // Command returns a response from single node: -// value, _ := clusterClient.infoWithRoute(Random{}) -// response := value.Value().(string) +// value, _ := clusterClient.InfoWithOptions(api.ClusterInfoOptions{InfoOptions: nil, Route: api.RandomRoute.ToPtr()}) +// response := value.SingleValue() // // `response` stores the command output from a cluster node type ClusterValue[T any] struct { - valueType ValueType - value Result[T] + valueType ValueType + singleValue T + mutiValue map[string]T } -func (value ClusterValue[T]) Value() T { - return value.value.Value() +// Get the single value stored (value returned by a single cluster node). +func (value ClusterValue[T]) SingleValue() T { + return value.singleValue } +// Get the multi value stored (value returned by multiple cluster nodes). +func (value ClusterValue[T]) MultiValue() map[string]T { + return value.mutiValue +} + +// Get the value type func (value ClusterValue[T]) ValueType() ValueType { return value.valueType } @@ -128,36 +137,35 @@ func (value ClusterValue[T]) IsMultiValue() bool { } func (value ClusterValue[T]) IsEmpty() bool { - return value.value.IsNil() + return value.valueType == NoValue } -func CreateClusterValue[T any](data T) ClusterValue[T] { +func createClusterValue[T any](data any) ClusterValue[T] { switch any(data).(type) { case map[string]interface{}: - return CreateClusterMultiValue(data) + return createClusterMultiValue(data.(map[string]T)) default: - return CreateClusterSingleValue(data) + return createClusterSingleValue(data.(T)) } } -func CreateClusterSingleValue[T any](data T) ClusterValue[T] { +func createClusterSingleValue[T any](data T) ClusterValue[T] { return ClusterValue[T]{ - valueType: SingleValue, - value: Result[T]{val: data, isNil: false}, + valueType: SingleValue, + singleValue: data, } } -func CreateClusterMultiValue[T any](data T) ClusterValue[T] { +func createClusterMultiValue[T any](data map[string]T) ClusterValue[T] { return ClusterValue[T]{ valueType: MultiValue, - value: Result[T]{val: data, isNil: false}, + mutiValue: data, } } -func CreateEmptyClusterValue() ClusterValue[interface{}] { - var empty interface{} - return ClusterValue[interface{}]{ - value: Result[interface{}]{val: empty, isNil: true}, +func createEmptyClusterValue[T any]() ClusterValue[T] { + return ClusterValue[T]{ + valueType: NoValue, } } diff --git a/go/api/server_management_cluster_commands.go b/go/api/server_management_cluster_commands.go new file mode 100644 index 0000000000..c5922c9498 --- /dev/null +++ b/go/api/server_management_cluster_commands.go @@ -0,0 +1,14 @@ +// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 + +package api + +// ServerManagementCommands supports commands for the "Server Management" group for a cluster client. +// +// See [valkey.io] for details. +// +// [valkey.io]: https://valkey.io/commands/#server +type ServerManagementClusterCommands interface { + Info() (map[string]string, error) + + InfoWithOptions(options ClusterInfoOptions) (ClusterValue[string], error) +} diff --git a/go/api/server_management_commands.go b/go/api/server_management_commands.go index af0402bf68..d6909c72a9 100644 --- a/go/api/server_management_commands.go +++ b/go/api/server_management_commands.go @@ -2,7 +2,7 @@ package api -// ServerManagementCommands supports commands and transactions for the "Server Management" group for a standalone client. +// ServerManagementCommands supports commands for the "Server Management" group for a standalone client. // // See [valkey.io] for details. // @@ -14,5 +14,9 @@ type ServerManagementCommands interface { ConfigSet(parameters map[string]string) (string, error) + Info() (string, error) + + InfoWithOptions(options InfoOptions) (string, error) + DBSize() (int64, error) } diff --git a/go/integTest/cluster_commands_test.go b/go/integTest/cluster_commands_test.go index 142f0cf273..50693b5b17 100644 --- a/go/integTest/cluster_commands_test.go +++ b/go/integTest/cluster_commands_test.go @@ -6,6 +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" ) func (suite *GlideTestSuite) TestClusterCustomCommandInfo() { @@ -14,7 +16,7 @@ func (suite *GlideTestSuite) TestClusterCustomCommandInfo() { assert.Nil(suite.T(), err) // INFO is routed to all primary nodes by default - for _, value := range result.Value().(map[string]interface{}) { + for _, value := range result.MultiValue() { assert.True(suite.T(), strings.Contains(value.(string), "# Stats")) } } @@ -25,5 +27,82 @@ func (suite *GlideTestSuite) TestClusterCustomCommandEcho() { assert.Nil(suite.T(), err) // ECHO is routed to a single random node - assert.Equal(suite.T(), "GO GLIDE GO", result.Value().(string)) + assert.Equal(suite.T(), "GO GLIDE GO", result.SingleValue().(string)) +} + +func (suite *GlideTestSuite) TestInfoCluster() { + DEFAULT_INFO_SECTIONS := []string{ + "Server", + "Clients", + "Memory", + "Persistence", + "Stats", + "Replication", + "CPU", + "Modules", + "Errorstats", + "Cluster", + "Keyspace", + } + + client := suite.defaultClusterClient() + t := suite.T() + + // info without options + data, err := client.Info() + assert.NoError(t, err) + for _, info := range data { + for _, section := range DEFAULT_INFO_SECTIONS { + assert.Contains(t, info, "# "+section, "Section "+section+" is missing") + } + } + + // info with option or with multiple options without route + sections := []api.Section{api.Cpu} + if suite.serverVersion >= "7.0.0" { + sections = append(sections, api.Memory) + } + opts := api.ClusterInfoOptions{ + InfoOptions: &api.InfoOptions{Sections: sections}, + Route: nil, + } + response, err := client.InfoWithOptions(opts) + assert.NoError(t, err) + assert.True(t, response.IsMultiValue()) + for _, info := range response.MultiValue() { + for _, section := range sections { + assert.Contains(t, strings.ToLower(info), strings.ToLower("# "+string(section)), "Section "+section+" is missing") + } + } + + // same sections with random route + opts = api.ClusterInfoOptions{ + InfoOptions: &api.InfoOptions{Sections: sections}, + Route: config.RandomRoute.ToPtr(), + } + response, err = client.InfoWithOptions(opts) + assert.NoError(t, err) + assert.True(t, response.IsSingleValue()) + for _, section := range sections { + assert.Contains( + t, + strings.ToLower(response.SingleValue()), + strings.ToLower("# "+string(section)), + "Section "+section+" is missing", + ) + } + + // default sections, multi node route + opts = api.ClusterInfoOptions{ + InfoOptions: nil, + Route: config.AllPrimaries.ToPtr(), + } + response, err = client.InfoWithOptions(opts) + assert.NoError(t, err) + assert.True(t, response.IsMultiValue()) + for _, info := range response.MultiValue() { + for _, section := range DEFAULT_INFO_SECTIONS { + assert.Contains(t, info, "# "+section, "Section "+section+" is missing") + } + } } diff --git a/go/integTest/glide_test_suite_test.go b/go/integTest/glide_test_suite_test.go index 10532b0520..b6dc71032f 100644 --- a/go/integTest/glide_test_suite_test.go +++ b/go/integTest/glide_test_suite_test.go @@ -16,6 +16,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/valkey-io/valkey-glide/go/glide/api" + "github.com/valkey-io/valkey-glide/go/glide/api/config" ) type GlideTestSuite struct { @@ -141,17 +142,16 @@ func runClusterManager(suite *GlideTestSuite, args []string, ignoreExitCode bool func getServerVersion(suite *GlideTestSuite) string { var err error = nil if len(suite.standaloneHosts) > 0 { - config := api.NewGlideClientConfiguration(). + clientConfig := api.NewGlideClientConfiguration(). WithAddress(&suite.standaloneHosts[0]). WithUseTLS(suite.tls). WithRequestTimeout(5000) - client, err := api.NewGlideClient(config) + client, err := api.NewGlideClient(clientConfig) if err == nil && client != nil { defer client.Close() - // TODO use info command - info, _ := client.CustomCommand([]string{"info", "server"}) - return extractServerVersion(suite, info.(string)) + info, _ := client.InfoWithOptions(api.InfoOptions{Sections: []api.Section{api.Server}}) + return extractServerVersion(suite, info) } } if len(suite.clusterHosts) == 0 { @@ -161,19 +161,22 @@ func getServerVersion(suite *GlideTestSuite) string { suite.T().Fatal("No server hosts configured") } - config := api.NewGlideClusterClientConfiguration(). + clientConfig := api.NewGlideClusterClientConfiguration(). WithAddress(&suite.clusterHosts[0]). WithUseTLS(suite.tls). WithRequestTimeout(5000) - client, err := api.NewGlideClusterClient(config) + client, err := api.NewGlideClusterClient(clientConfig) if err == nil && client != nil { defer client.Close() - // TODO use info command with route - info, _ := client.CustomCommand([]string{"info", "server"}) - for _, value := range info.Value().(map[string]interface{}) { - return extractServerVersion(suite, value.(string)) - } + + info, _ := client.InfoWithOptions( + api.ClusterInfoOptions{ + InfoOptions: &api.InfoOptions{Sections: []api.Section{api.Server}}, + Route: config.RandomRoute.ToPtr(), + }, + ) + return extractServerVersion(suite, info.SingleValue()) } suite.T().Fatalf("Can't connect to any server to get version: %s", err.Error()) return "" diff --git a/go/integTest/json_module_test.go b/go/integTest/json_module_test.go index 8f0f33efc4..89868777c1 100644 --- a/go/integTest/json_module_test.go +++ b/go/integTest/json_module_test.go @@ -6,15 +6,17 @@ import ( "strings" "github.com/stretchr/testify/assert" + "github.com/valkey-io/valkey-glide/go/glide/api" ) func (suite *GlideTestSuite) TestModuleVerifyJsonLoaded() { client := suite.defaultClusterClient() - // TODO use INFO command - result, err := client.CustomCommand([]string{"INFO", "MODULES"}) + result, err := client.InfoWithOptions( + api.ClusterInfoOptions{InfoOptions: &api.InfoOptions{Sections: []api.Section{api.Server}}, Route: nil}, + ) assert.Nil(suite.T(), err) - for _, value := range result.Value().(map[string]interface{}) { - assert.True(suite.T(), strings.Contains(value.(string), "# json_core_metrics")) + for _, value := range result.MultiValue() { + assert.True(suite.T(), strings.Contains(value, "# json_core_metrics")) } } diff --git a/go/integTest/shared_commands_test.go b/go/integTest/shared_commands_test.go index 2de1e61fd2..b2fd7eacd4 100644 --- a/go/integTest/shared_commands_test.go +++ b/go/integTest/shared_commands_test.go @@ -5659,17 +5659,17 @@ func (suite *GlideTestSuite) TestXPending() { resp, err := client.CustomCommand(command) assert.NoError(suite.T(), err) - assert.Equal(suite.T(), "OK", resp.Value().(string)) + assert.Equal(suite.T(), "OK", resp.SingleValue().(string)) command = []string{"XGroup", "CreateConsumer", key, groupName, consumer1} resp, err = client.CustomCommand(command) assert.NoError(suite.T(), err) - assert.True(suite.T(), resp.Value().(bool)) + assert.True(suite.T(), resp.SingleValue().(bool)) command = []string{"XGroup", "CreateConsumer", key, groupName, consumer2} resp, err = client.CustomCommand(command) assert.NoError(suite.T(), err) - assert.True(suite.T(), resp.Value().(bool)) + assert.True(suite.T(), resp.SingleValue().(bool)) streamid_1, err := client.XAdd(key, [][]string{{"field1", "value1"}}) assert.NoError(suite.T(), err) @@ -5919,7 +5919,7 @@ func (suite *GlideTestSuite) TestXPendingFailures() { command := []string{"XGroup", "CreateConsumer", key, groupName, consumer1} resp, err := client.CustomCommand(command) assert.NoError(suite.T(), err) - assert.True(suite.T(), resp.Value().(bool)) + assert.True(suite.T(), resp.SingleValue().(bool)) _, err = client.XAdd(key, [][]string{{"field1", "value1"}}) assert.NoError(suite.T(), err) diff --git a/go/integTest/standalone_commands_test.go b/go/integTest/standalone_commands_test.go index 6b7b0908c0..63b07ac3b1 100644 --- a/go/integTest/standalone_commands_test.go +++ b/go/integTest/standalone_commands_test.go @@ -386,6 +386,43 @@ func (suite *GlideTestSuite) TestSortReadOnlyWithOptions_SuccessfulSortByWeightA assert.Equal(suite.T(), "item3", sortResult[5].Value()) } +func (suite *GlideTestSuite) TestInfoStandalone() { + DEFAULT_INFO_SECTIONS := []string{ + "Server", + "Clients", + "Memory", + "Persistence", + "Stats", + "Replication", + "CPU", + "Modules", + "Errorstats", + "Cluster", + "Keyspace", + } + + client := suite.defaultClient() + t := suite.T() + + // info without options + info, err := client.Info() + assert.NoError(t, err) + for _, section := range DEFAULT_INFO_SECTIONS { + assert.Contains(t, info, "# "+section, "Section "+section+" is missing") + } + + // info with option or with multiple options + sections := []api.Section{api.Cpu} + if suite.serverVersion >= "7.0.0" { + sections = append(sections, api.Memory) + } + info, err = client.InfoWithOptions(api.InfoOptions{Sections: sections}) + assert.NoError(t, err) + for _, section := range sections { + assert.Contains(t, strings.ToLower(info), strings.ToLower("# "+string(section)), "Section "+section+" is missing") + } +} + func (suite *GlideTestSuite) TestDBSize() { client := suite.defaultClient() result, err := client.DBSize() diff --git a/go/integTest/vss_module_test.go b/go/integTest/vss_module_test.go index 1ebad87e13..94e42833d8 100644 --- a/go/integTest/vss_module_test.go +++ b/go/integTest/vss_module_test.go @@ -6,15 +6,17 @@ import ( "strings" "github.com/stretchr/testify/assert" + "github.com/valkey-io/valkey-glide/go/glide/api" ) func (suite *GlideTestSuite) TestModuleVerifyVssLoaded() { client := suite.defaultClusterClient() - // TODO use INFO command - result, err := client.CustomCommand([]string{"INFO", "MODULES"}) + result, err := client.InfoWithOptions( + api.ClusterInfoOptions{InfoOptions: &api.InfoOptions{Sections: []api.Section{api.Server}}, Route: nil}, + ) assert.Nil(suite.T(), err) - for _, value := range result.Value().(map[string]interface{}) { - assert.True(suite.T(), strings.Contains(value.(string), "# search_index_stats")) + for _, value := range result.MultiValue() { + assert.True(suite.T(), strings.Contains(value, "# search_index_stats")) } }