diff --git a/internal/server/client.go b/internal/server/client.go index a60d66e5..87221feb 100644 --- a/internal/server/client.go +++ b/internal/server/client.go @@ -92,17 +92,26 @@ func (c *Server) cmdClient(msg *Message, client *Client) (resp.Value, error) { switch msg.OutputType { case JSON: // Create a map of all key/value info fields - m := make(map[string]interface{}) - res := strings.TrimSpace(string(buf)) - for _, kv := range strings.Split(res, " ") { - kv = strings.TrimSpace(kv) - if split := strings.SplitN(kv, "=", 2); len(split) == 2 { - m[split[0]] = tryParseType(split[1]) + var cmap []map[string]interface{} + clients := strings.Split(string(buf), "\n") + for _, client := range clients { + client = strings.TrimSpace(client) + m := make(map[string]interface{}) + var hasFields bool + for _, kv := range strings.Split(client, " ") { + kv = strings.TrimSpace(kv) + if split := strings.SplitN(kv, "=", 2); len(split) == 2 { + hasFields = true + m[split[0]] = tryParseType(split[1]) + } + } + if hasFields { + cmap = append(cmap, m) } } // Marshal the map and use the output in the JSON response - data, err := json.Marshal(m) + data, err := json.Marshal(cmap) if err != nil { return NOMessage, err } diff --git a/tests/client_test.go b/tests/client_test.go new file mode 100644 index 00000000..99fd03ce --- /dev/null +++ b/tests/client_test.go @@ -0,0 +1,69 @@ +package tests + +import ( + "errors" + "fmt" + "testing" + + "github.com/gomodule/redigo/redis" + "github.com/tidwall/gjson" +) + +func subTestClient(t *testing.T, mc *mockServer) { + runStep(t, mc, "valid json", client_valid_json_test) + runStep(t, mc, "valid client count", info_valid_client_count_test) +} + +func client_valid_json_test(mc *mockServer) error { + if _, err := mc.Do("OUTPUT", "JSON"); err != nil { + return err + } + res, err := mc.Do("CLIENT", "list") + if err != nil { + return err + } + bres, ok := res.([]byte) + if !ok { + return errors.New("Failed to type assert CLIENT response") + } + sres := string(bres) + if !gjson.Valid(sres) { + return errors.New("CLIENT response was invalid") + } + info := gjson.Get(sres, "list").String() + if !gjson.Valid(info) { + return errors.New("CLIENT.list response was invalid") + } + return nil +} + +func info_valid_client_count_test(mc *mockServer) error { + numConns := 20 + var conns []redis.Conn + for i := 0; i <= numConns; i++ { + conn, err := redis.Dial("tcp", fmt.Sprintf(":%d", mc.port)) + if err != nil { + return err + } + conns = append(conns, conn) + } + for i := range conns { + defer conns[i].Close() + } + if _, err := mc.Do("OUTPUT", "JSON"); err != nil { + return err + } + res, err := mc.Do("CLIENT", "list") + if err != nil { + return err + } + bres, ok := res.([]byte) + if !ok { + return errors.New("Failed to type assert CLIENT response") + } + sres := string(bres) + if len(gjson.Get(sres, "list").Array()) < numConns { + return errors.New("Invalid number of connections") + } + return nil +} diff --git a/tests/stats_test.go b/tests/stats_test.go new file mode 100644 index 00000000..7cc466ae --- /dev/null +++ b/tests/stats_test.go @@ -0,0 +1,35 @@ +package tests + +import ( + "errors" + "testing" + + "github.com/tidwall/gjson" +) + +func subTestInfo(t *testing.T, mc *mockServer) { + runStep(t, mc, "valid json", info_valid_json_test) +} + +func info_valid_json_test(mc *mockServer) error { + if _, err := mc.Do("OUTPUT", "JSON"); err != nil { + return err + } + res, err := mc.Do("INFO") + if err != nil { + return err + } + bres, ok := res.([]byte) + if !ok { + return errors.New("Failed to type assert INFO response") + } + sres := string(bres) + if !gjson.Valid(sres) { + return errors.New("INFO response was invalid") + } + info := gjson.Get(sres, "info").String() + if !gjson.Valid(info) { + return errors.New("INFO.info response was invalid") + } + return nil +} diff --git a/tests/tests_test.go b/tests/tests_test.go index cfe198f3..4de3d18a 100644 --- a/tests/tests_test.go +++ b/tests/tests_test.go @@ -44,6 +44,8 @@ func TestAll(t *testing.T) { runSubTest(t, "search", mc, subTestSearch) runSubTest(t, "fence", mc, subTestFence) runSubTest(t, "scripts", mc, subTestScripts) + runSubTest(t, "info", mc, subTestInfo) + runSubTest(t, "client", mc, subTestClient) } func runSubTest(t *testing.T, name string, mc *mockServer, test func(t *testing.T, mc *mockServer)) {