From f29202534dae4207d0ff505b7f8ec2be5eb41045 Mon Sep 17 00:00:00 2001 From: Kelvin Clement Mwinuka Date: Thu, 22 Feb 2024 03:12:59 +0800 Subject: [PATCH] Implemented test for ZMSCORE command handler --- src/modules/sorted_set/commands.go | 54 +++++++++------- src/modules/sorted_set/commands_test.go | 83 ++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 24 deletions(-) diff --git a/src/modules/sorted_set/commands.go b/src/modules/sorted_set/commands.go index 9d4d6eb..40360b8 100644 --- a/src/modules/sorted_set/commands.go +++ b/src/modules/sorted_set/commands.go @@ -752,18 +752,18 @@ func handleZPOP(ctx context.Context, cmd []string, server utils.Server, conn *ne } func handleZMSCORE(ctx context.Context, cmd []string, server utils.Server, conn *net.Conn) ([]byte, error) { - if len(cmd) < 3 { - return nil, errors.New(utils.WRONG_ARGS_RESPONSE) + keys, err := zmscoreKeyFunc(cmd) + if err != nil { + return nil, err } - key := cmd[1] + key := keys[0] if !server.KeyExists(key) { return []byte("*0\r\n\r\n"), nil } - _, err := server.KeyRLock(ctx, key) - if err != nil { + if _, err = server.KeyRLock(ctx, key); err != nil { return nil, err } defer server.KeyRUnlock(key) @@ -773,20 +773,23 @@ func handleZMSCORE(ctx context.Context, cmd []string, server utils.Server, conn return nil, fmt.Errorf("value at %s is not a sorted set", key) } - res := fmt.Sprintf("*%d", len(cmd[2:])) + members := cmd[2:] + + res := fmt.Sprintf("*%d", len(members)) + var member MemberObject - for i, m := range cmd[2:] { - member = set.Get(Value(m)) + + for i := 0; i < len(members); i++ { + member = set.Get(Value(members[i])) if !member.exists { - res = fmt.Sprintf("%s\r\n_", res) + res = fmt.Sprintf("%s\r\n$-1", res) } else { - res = fmt.Sprintf("%s\r\n+%f", res, member.score) - } - if i == len(cmd[2:])-1 { - res += "\r\n\r\n" + res = fmt.Sprintf("%s\r\n+%s", res, strconv.FormatFloat(float64(member.score), 'f', -1, 64)) } } + res += "\r\n\r\n" + return []byte(res), nil } @@ -929,17 +932,19 @@ func handleZREM(ctx context.Context, cmd []string, server utils.Server, conn *ne } func handleZSCORE(ctx context.Context, cmd []string, server utils.Server, conn *net.Conn) ([]byte, error) { - if len(cmd) != 3 { - return nil, errors.New(utils.WRONG_ARGS_RESPONSE) - } - key := cmd[1] - if !server.KeyExists(key) { - return []byte("+(nil)\r\n\r\n"), nil - } - _, err := server.KeyRLock(ctx, key) + keys, err := zscoreKeyFunc(cmd) if err != nil { return nil, err } + + key := keys[0] + + if !server.KeyExists(key) { + return []byte("$-1\r\n\r\n"), nil + } + if _, err = server.KeyRLock(ctx, key); err != nil { + return nil, err + } defer server.KeyRUnlock(key) set, ok := server.GetValue(key).(*SortedSet) if !ok { @@ -947,9 +952,12 @@ func handleZSCORE(ctx context.Context, cmd []string, server utils.Server, conn * } member := set.Get(Value(cmd[2])) if !member.exists { - return []byte("+(nil)\r\n\r\n"), nil + return []byte("$-1\r\n\r\n"), nil } - return []byte(fmt.Sprintf("+%f\r\n\r\n", member.score)), nil + + score := strconv.FormatFloat(float64(member.score), 'f', -1, 64) + + return []byte(fmt.Sprintf("$%d\r\n%s\r\n\r\n", len(score), score)), nil } func handleZREMRANGEBYSCORE(ctx context.Context, cmd []string, server utils.Server, conn *net.Conn) ([]byte, error) { diff --git a/src/modules/sorted_set/commands_test.go b/src/modules/sorted_set/commands_test.go index f30799a..a0eff42 100644 --- a/src/modules/sorted_set/commands_test.go +++ b/src/modules/sorted_set/commands_test.go @@ -1566,7 +1566,88 @@ func Test_HandleZPOP(t *testing.T) { } } -func Test_HandleZMSCORE(t *testing.T) {} +func Test_HandleZMSCORE(t *testing.T) { + mockServer := server.NewServer(server.Opts{}) + + tests := []struct { + preset bool + presetValues map[string]interface{} + command []string + expectedResponse []interface{} + expectedError error + }{ + { // 1. Return multiple scores from the sorted set. + // Return nil for elements that do not exist in the sorted set. + preset: true, + presetValues: map[string]interface{}{ + "key1": NewSortedSet([]MemberParam{ + {value: "one", score: 1.1}, {value: "two", score: 245}, + {value: "three", score: 3}, {value: "four", score: 4.055}, + {value: "five", score: 5}, + }), + }, + command: []string{"ZMSCORE", "key1", "one", "none", "two", "one", "three", "four", "none", "five"}, + expectedResponse: []interface{}{"1.1", nil, "245", "1.1", "3", "4.055", nil, "5"}, + expectedError: nil, + }, + { // 2. If key does not exist, return empty array + preset: false, + presetValues: nil, + command: []string{"ZMSCORE", "key2", "one", "two", "three", "four"}, + expectedResponse: []interface{}{}, + expectedError: nil, + }, + { // 3. Throw error when trying to find scores from elements that are not sorted sets + preset: true, + presetValues: map[string]interface{}{"key3": "Default value"}, + command: []string{"ZMSCORE", "key3", "one", "two", "three"}, + expectedError: errors.New("value at key3 is not a sorted set"), + }, + { // 9. Command too short + preset: false, + command: []string{"ZMSCORE"}, + expectedError: errors.New(utils.WRONG_ARGS_RESPONSE), + }, + } + + for _, test := range tests { + if test.preset { + for key, value := range test.presetValues { + if _, err := mockServer.CreateKeyAndLock(context.Background(), key); err != nil { + t.Error(err) + } + mockServer.SetValue(context.Background(), key, value) + mockServer.KeyUnlock(key) + } + } + res, err := handleZMSCORE(context.Background(), test.command, mockServer, nil) + if test.expectedError != nil { + if err.Error() != test.expectedError.Error() { + t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error()) + } + continue + } + if err != nil { + t.Error(err) + } + rd := resp.NewReader(bytes.NewBuffer(res)) + rv, _, err := rd.ReadValue() + if err != nil { + t.Error(err) + } + for i := 0; i < len(rv.Array()); i++ { + if rv.Array()[i].IsNull() { + if test.expectedResponse[i] != nil { + t.Errorf("expected element at index %d to be %+v, got %+v", i, test.expectedResponse[i], rv.Array()[i]) + } + continue + } + if rv.Array()[i].String() != test.expectedResponse[i] { + t.Errorf("expected \"%s\" at index %d, got %s", test.expectedResponse[i], i, rv.Array()[i].String()) + } + } + } +} func Test_HandleZRANDMEMBER(t *testing.T) {}