mirror of
				https://github.com/EchoVault/SugarDB.git
				synced 2025-10-27 01:41:04 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			5748 lines
		
	
	
		
			208 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			5748 lines
		
	
	
		
			208 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2024 Kelvin Clement Mwinuka
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| //
 | |
| //      http://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| package sorted_set_test
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"github.com/echovault/echovault/echovault"
 | |
| 	"github.com/echovault/echovault/internal"
 | |
| 	"github.com/echovault/echovault/internal/config"
 | |
| 	"github.com/echovault/echovault/internal/constants"
 | |
| 	"github.com/echovault/echovault/internal/modules/sorted_set"
 | |
| 	"github.com/tidwall/resp"
 | |
| 	"math"
 | |
| 	"slices"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| )
 | |
| 
 | |
| func Test_SortedSet(t *testing.T) {
 | |
| 	port, err := internal.GetFreePort()
 | |
| 	if err != nil {
 | |
| 		t.Error(err)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	mockServer, err := echovault.NewEchoVault(
 | |
| 		echovault.WithConfig(config.Config{
 | |
| 			BindAddr:       "localhost",
 | |
| 			Port:           uint16(port),
 | |
| 			DataDir:        "",
 | |
| 			EvictionPolicy: constants.NoEviction,
 | |
| 		}),
 | |
| 	)
 | |
| 	if err != nil {
 | |
| 		t.Error(err)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	go func() {
 | |
| 		mockServer.Start()
 | |
| 	}()
 | |
| 
 | |
| 	t.Cleanup(func() {
 | |
| 		mockServer.ShutDown()
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZADD", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValue      *sorted_set.SortedSet
 | |
| 			key              string
 | |
| 			command          []string
 | |
| 			expectedResponse int
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name:             "1. Create new sorted set and return the cardinality of the new sorted set",
 | |
| 				presetValue:      nil,
 | |
| 				key:              "ZaddKey1",
 | |
| 				command:          []string{"ZADD", "ZaddKey1", "5.5", "member1", "67.77", "member2", "10", "member3", "-inf", "member4", "+inf", "member5"},
 | |
| 				expectedResponse: 5,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "2. Only add the elements that do not currently exist in the sorted set when NX flag is provided",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "member1", Score: sorted_set.Score(5.5)},
 | |
| 					{Value: "member2", Score: sorted_set.Score(67.77)},
 | |
| 					{Value: "member3", Score: sorted_set.Score(10)},
 | |
| 				}),
 | |
| 				key:              "ZaddKey2",
 | |
| 				command:          []string{"ZADD", "ZaddKey2", "NX", "5.5", "member1", "67.77", "member4", "10", "member5"},
 | |
| 				expectedResponse: 2,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "3. Do not add any elements when providing existing members with NX flag",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "member1", Score: sorted_set.Score(5.5)},
 | |
| 					{Value: "member2", Score: sorted_set.Score(67.77)},
 | |
| 					{Value: "member3", Score: sorted_set.Score(10)},
 | |
| 				}),
 | |
| 				key:              "ZaddKey3",
 | |
| 				command:          []string{"ZADD", "ZaddKey3", "NX", "5.5", "member1", "67.77", "member2", "10", "member3"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "4. Successfully add elements to an existing set when XX flag is provided with existing elements",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "member1", Score: sorted_set.Score(5.5)},
 | |
| 					{Value: "member2", Score: sorted_set.Score(67.77)},
 | |
| 					{Value: "member3", Score: sorted_set.Score(10)},
 | |
| 				}),
 | |
| 				key:              "ZaddKey4",
 | |
| 				command:          []string{"ZADD", "ZaddKey4", "XX", "CH", "55", "member1", "1005", "member2", "15", "member3", "99.75", "member4"},
 | |
| 				expectedResponse: 3,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "5. Fail to add element when providing XX flag with elements that do not exist in the sorted set.",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "member1", Score: sorted_set.Score(5.5)},
 | |
| 					{Value: "member2", Score: sorted_set.Score(67.77)},
 | |
| 					{Value: "member3", Score: sorted_set.Score(10)},
 | |
| 				}),
 | |
| 				key:              "ZaddKey5",
 | |
| 				command:          []string{"ZADD", "ZaddKey5", "XX", "5.5", "member4", "100.5", "member5", "15", "member6"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 6. Only update the elements where provided score is greater than current score and GT flag is provided
 | |
| 				// Return only the new elements added by default
 | |
| 				name: "6. Only update the elements where provided score is greater than current score and GT flag is provided",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "member1", Score: sorted_set.Score(5.5)},
 | |
| 					{Value: "member2", Score: sorted_set.Score(67.77)},
 | |
| 					{Value: "member3", Score: sorted_set.Score(10)},
 | |
| 				}),
 | |
| 				key:              "ZaddKey6",
 | |
| 				command:          []string{"ZADD", "ZaddKey6", "XX", "CH", "GT", "7.5", "member1", "100.5", "member4", "15", "member5"},
 | |
| 				expectedResponse: 1,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 7. Only update the elements where provided score is less than current score if LT flag is provided
 | |
| 				// Return only the new elements added by default.
 | |
| 				name: "7. Only update the elements where provided score is less than current score if LT flag is provided",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "member1", Score: sorted_set.Score(5.5)},
 | |
| 					{Value: "member2", Score: sorted_set.Score(67.77)},
 | |
| 					{Value: "member3", Score: sorted_set.Score(10)},
 | |
| 				}),
 | |
| 				key:              "ZaddKey7",
 | |
| 				command:          []string{"ZADD", "ZaddKey7", "XX", "LT", "3.5", "member1", "100.5", "member4", "15", "member5"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "8. Return all the elements that were updated AND added when CH flag is provided",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "member1", Score: sorted_set.Score(5.5)},
 | |
| 					{Value: "member2", Score: sorted_set.Score(67.77)},
 | |
| 					{Value: "member3", Score: sorted_set.Score(10)},
 | |
| 				}),
 | |
| 				key:              "ZaddKey8",
 | |
| 				command:          []string{"ZADD", "ZaddKey8", "XX", "LT", "CH", "3.5", "member1", "100.5", "member4", "15", "member5"},
 | |
| 				expectedResponse: 1,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "9. Increment the member by score",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "member1", Score: sorted_set.Score(5.5)},
 | |
| 					{Value: "member2", Score: sorted_set.Score(67.77)},
 | |
| 					{Value: "member3", Score: sorted_set.Score(10)},
 | |
| 				}),
 | |
| 				key:              "ZaddKey9",
 | |
| 				command:          []string{"ZADD", "ZaddKey9", "INCR", "5.5", "member3"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "10. Fail when GT/LT flag is provided alongside NX flag",
 | |
| 				presetValue:      nil,
 | |
| 				key:              "ZaddKey10",
 | |
| 				command:          []string{"ZADD", "ZaddKey10", "NX", "LT", "CH", "3.5", "member1", "100.5", "member4", "15", "member5"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("GT/LT flags not allowed if NX flag is provided"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "11. Command is too short",
 | |
| 				presetValue:      nil,
 | |
| 				key:              "ZaddKey11",
 | |
| 				command:          []string{"ZADD", "ZaddKey11"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "12. Throw error when score/member entries are do not match",
 | |
| 				presetValue:      nil,
 | |
| 				key:              "ZaddKey11",
 | |
| 				command:          []string{"ZADD", "ZaddKey12", "10.5", "member1", "12.5"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("score/member pairs must be float/string"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "13. Throw error when INCR flag is passed with more than one score/member pair",
 | |
| 				presetValue:      nil,
 | |
| 				key:              "ZaddKey13",
 | |
| 				command:          []string{"ZADD", "ZaddKey13", "INCR", "10.5", "member1", "12.5", "member2"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("cannot pass more than one score/member pair when INCR flag is provided"),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValue != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 
 | |
| 					command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(test.key)}
 | |
| 					for _, member := range test.presetValue.GetAll() {
 | |
| 						command = append(command, []resp.Value{
 | |
| 							resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 							resp.StringValue(string(member.Value)),
 | |
| 						}...)
 | |
| 					}
 | |
| 
 | |
| 					if err = client.WriteArray(command); err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 					res, _, err := client.ReadValue()
 | |
| 					if err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					if res.Integer() != test.presetValue.Cardinality() {
 | |
| 						t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if res.Integer() != test.expectedResponse {
 | |
| 					t.Errorf("expected response \"%d\", got \"%d\"", test.expectedResponse, res.Integer())
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZCARD", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValue      interface{}
 | |
| 			key              string
 | |
| 			command          []string
 | |
| 			expectedResponse int
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Get cardinality of valid sorted set.",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "member1", Score: sorted_set.Score(5.5)},
 | |
| 					{Value: "member2", Score: sorted_set.Score(67.77)},
 | |
| 					{Value: "member3", Score: sorted_set.Score(10)},
 | |
| 				}),
 | |
| 				key:              "ZcardKey1",
 | |
| 				command:          []string{"ZCARD", "ZcardKey1"},
 | |
| 				expectedResponse: 3,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "2. Return 0 when trying to get cardinality from non-existent key",
 | |
| 				presetValue:      nil,
 | |
| 				key:              "ZcardKey2",
 | |
| 				command:          []string{"ZCARD", "ZcardKey2"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "3. Command is too short",
 | |
| 				presetValue:      nil,
 | |
| 				key:              "ZcardKey3",
 | |
| 				command:          []string{"ZCARD"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "4. Command too long",
 | |
| 				presetValue:      nil,
 | |
| 				key:              "ZcardKey4",
 | |
| 				command:          []string{"ZCARD", "ZcardKey4", "ZcardKey5"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "5. Return error when not a sorted set",
 | |
| 				presetValue:      "Default value",
 | |
| 				key:              "ZcardKey5",
 | |
| 				command:          []string{"ZCARD", "ZcardKey5"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("value at ZcardKey5 is not a sorted set"),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValue != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 
 | |
| 					switch test.presetValue.(type) {
 | |
| 					case string:
 | |
| 						command = []resp.Value{
 | |
| 							resp.StringValue("SET"),
 | |
| 							resp.StringValue(test.key),
 | |
| 							resp.StringValue(test.presetValue.(string)),
 | |
| 						}
 | |
| 						expected = "ok"
 | |
| 					case *sorted_set.SortedSet:
 | |
| 						command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(test.key)}
 | |
| 						for _, member := range test.presetValue.(*sorted_set.SortedSet).GetAll() {
 | |
| 							command = append(command, []resp.Value{
 | |
| 								resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 								resp.StringValue(string(member.Value)),
 | |
| 							}...)
 | |
| 						}
 | |
| 						expected = strconv.Itoa(test.presetValue.(*sorted_set.SortedSet).Cardinality())
 | |
| 					}
 | |
| 
 | |
| 					if err = client.WriteArray(command); err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 					res, _, err := client.ReadValue()
 | |
| 					if err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					if !strings.EqualFold(res.String(), expected) {
 | |
| 						t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if res.Integer() != test.expectedResponse {
 | |
| 					t.Errorf("expected response \"%d\", got \"%d\"", test.expectedResponse, res.Integer())
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZCOUNT", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValue      interface{}
 | |
| 			key              string
 | |
| 			command          []string
 | |
| 			expectedResponse int
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Get entire count using infinity boundaries",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "member1", Score: sorted_set.Score(5.5)},
 | |
| 					{Value: "member2", Score: sorted_set.Score(67.77)},
 | |
| 					{Value: "member3", Score: sorted_set.Score(10)},
 | |
| 					{Value: "member4", Score: sorted_set.Score(1083.13)},
 | |
| 					{Value: "member5", Score: sorted_set.Score(11)},
 | |
| 					{Value: "member6", Score: sorted_set.Score(math.Inf(-1))},
 | |
| 					{Value: "member7", Score: sorted_set.Score(math.Inf(1))},
 | |
| 				}),
 | |
| 				key:              "ZcountKey1",
 | |
| 				command:          []string{"ZCOUNT", "ZcountKey1", "-inf", "+inf"},
 | |
| 				expectedResponse: 7,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "2. Get count of sub-set from -inf to limit",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "member1", Score: sorted_set.Score(5.5)},
 | |
| 					{Value: "member2", Score: sorted_set.Score(67.77)},
 | |
| 					{Value: "member3", Score: sorted_set.Score(10)},
 | |
| 					{Value: "member4", Score: sorted_set.Score(1083.13)},
 | |
| 					{Value: "member5", Score: sorted_set.Score(11)},
 | |
| 					{Value: "member6", Score: sorted_set.Score(math.Inf(-1))},
 | |
| 					{Value: "member7", Score: sorted_set.Score(math.Inf(1))},
 | |
| 				}),
 | |
| 				key:              "ZcountKey2",
 | |
| 				command:          []string{"ZCOUNT", "ZcountKey2", "-inf", "90"},
 | |
| 				expectedResponse: 5,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "3. Get count of sub-set from bottom boundary to +inf limit",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "member1", Score: sorted_set.Score(5.5)},
 | |
| 					{Value: "member2", Score: sorted_set.Score(67.77)},
 | |
| 					{Value: "member3", Score: sorted_set.Score(10)},
 | |
| 					{Value: "member4", Score: sorted_set.Score(1083.13)},
 | |
| 					{Value: "member5", Score: sorted_set.Score(11)},
 | |
| 					{Value: "member6", Score: sorted_set.Score(math.Inf(-1))},
 | |
| 					{Value: "member7", Score: sorted_set.Score(math.Inf(1))},
 | |
| 				}),
 | |
| 				key:              "ZcountKey3",
 | |
| 				command:          []string{"ZCOUNT", "ZcountKey3", "1000", "+inf"},
 | |
| 				expectedResponse: 2,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "4. Return error when bottom boundary is not a valid double/float",
 | |
| 				presetValue:      nil,
 | |
| 				key:              "ZcountKey4",
 | |
| 				command:          []string{"ZCOUNT", "ZcountKey4", "min", "10"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("min constraint must be a double"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "5. Return error when top boundary is not a valid double/float",
 | |
| 				presetValue:      nil,
 | |
| 				key:              "ZcountKey5",
 | |
| 				command:          []string{"ZCOUNT", "ZcountKey5", "-10", "max"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("max constraint must be a double"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "6. Command is too short",
 | |
| 				presetValue:      nil,
 | |
| 				key:              "ZcountKey6",
 | |
| 				command:          []string{"ZCOUNT"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "7. Command too long",
 | |
| 				presetValue:      nil,
 | |
| 				key:              "ZcountKey7",
 | |
| 				command:          []string{"ZCOUNT", "ZcountKey4", "min", "max", "count"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "8. Throw error when value at the key is not a sorted set",
 | |
| 				presetValue:      "Default value",
 | |
| 				key:              "ZcountKey8",
 | |
| 				command:          []string{"ZCOUNT", "ZcountKey8", "1", "10"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("value at ZcountKey8 is not a sorted set"),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValue != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 
 | |
| 					switch test.presetValue.(type) {
 | |
| 					case string:
 | |
| 						command = []resp.Value{
 | |
| 							resp.StringValue("SET"),
 | |
| 							resp.StringValue(test.key),
 | |
| 							resp.StringValue(test.presetValue.(string)),
 | |
| 						}
 | |
| 						expected = "ok"
 | |
| 					case *sorted_set.SortedSet:
 | |
| 						command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(test.key)}
 | |
| 						for _, member := range test.presetValue.(*sorted_set.SortedSet).GetAll() {
 | |
| 							command = append(command, []resp.Value{
 | |
| 								resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 								resp.StringValue(string(member.Value)),
 | |
| 							}...)
 | |
| 						}
 | |
| 						expected = strconv.Itoa(test.presetValue.(*sorted_set.SortedSet).Cardinality())
 | |
| 					}
 | |
| 
 | |
| 					if err = client.WriteArray(command); err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 					res, _, err := client.ReadValue()
 | |
| 					if err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					if !strings.EqualFold(res.String(), expected) {
 | |
| 						t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if res.Integer() != test.expectedResponse {
 | |
| 					t.Errorf("expected response \"%d\", got \"%d\"", test.expectedResponse, res.Integer())
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZLEXCOUNT", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValue      interface{}
 | |
| 			key              string
 | |
| 			command          []string
 | |
| 			expectedResponse int
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Get entire count using infinity boundaries",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "e", Score: sorted_set.Score(1)},
 | |
| 					{Value: "f", Score: sorted_set.Score(1)},
 | |
| 					{Value: "g", Score: sorted_set.Score(1)},
 | |
| 					{Value: "h", Score: sorted_set.Score(1)},
 | |
| 					{Value: "i", Score: sorted_set.Score(1)},
 | |
| 					{Value: "j", Score: sorted_set.Score(1)},
 | |
| 					{Value: "k", Score: sorted_set.Score(1)},
 | |
| 				}),
 | |
| 				key:              "ZlexCountKey1",
 | |
| 				command:          []string{"ZLEXCOUNT", "ZlexCountKey1", "f", "j"},
 | |
| 				expectedResponse: 5,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "2. Return 0 when the members do not have the same score",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "a", Score: sorted_set.Score(5.5)},
 | |
| 					{Value: "b", Score: sorted_set.Score(67.77)},
 | |
| 					{Value: "c", Score: sorted_set.Score(10)},
 | |
| 					{Value: "d", Score: sorted_set.Score(1083.13)},
 | |
| 					{Value: "e", Score: sorted_set.Score(11)},
 | |
| 					{Value: "f", Score: sorted_set.Score(math.Inf(-1))},
 | |
| 					{Value: "g", Score: sorted_set.Score(math.Inf(1))},
 | |
| 				}),
 | |
| 				key:              "ZlexCountKey2",
 | |
| 				command:          []string{"ZLEXCOUNT", "ZlexCountKey2", "a", "b"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "3. Return 0 when the key does not exist",
 | |
| 				presetValue:      nil,
 | |
| 				key:              "ZlexCountKey3",
 | |
| 				command:          []string{"ZLEXCOUNT", "ZlexCountKey3", "a", "z"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "4. Return error when the value at the key is not a sorted set",
 | |
| 				presetValue:      "Default value",
 | |
| 				key:              "ZlexCountKey4",
 | |
| 				command:          []string{"ZLEXCOUNT", "ZlexCountKey4", "a", "z"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("value at ZlexCountKey4 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "5. Command is too short",
 | |
| 				presetValue:      nil,
 | |
| 				key:              "ZlexCountKey5",
 | |
| 				command:          []string{"ZLEXCOUNT"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "6. Command too long",
 | |
| 				presetValue:      nil,
 | |
| 				key:              "ZlexCountKey6",
 | |
| 				command:          []string{"ZLEXCOUNT", "ZlexCountKey6", "min", "max", "count"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValue != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 
 | |
| 					switch test.presetValue.(type) {
 | |
| 					case string:
 | |
| 						command = []resp.Value{
 | |
| 							resp.StringValue("SET"),
 | |
| 							resp.StringValue(test.key),
 | |
| 							resp.StringValue(test.presetValue.(string)),
 | |
| 						}
 | |
| 						expected = "ok"
 | |
| 					case *sorted_set.SortedSet:
 | |
| 						command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(test.key)}
 | |
| 						for _, member := range test.presetValue.(*sorted_set.SortedSet).GetAll() {
 | |
| 							command = append(command, []resp.Value{
 | |
| 								resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 								resp.StringValue(string(member.Value)),
 | |
| 							}...)
 | |
| 						}
 | |
| 						expected = strconv.Itoa(test.presetValue.(*sorted_set.SortedSet).Cardinality())
 | |
| 					}
 | |
| 
 | |
| 					if err = client.WriteArray(command); err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 					res, _, err := client.ReadValue()
 | |
| 					if err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					if !strings.EqualFold(res.String(), expected) {
 | |
| 						t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if res.Integer() != test.expectedResponse {
 | |
| 					t.Errorf("expected response \"%d\", got \"%d\"", test.expectedResponse, res.Integer())
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZDIFF", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValues     map[string]interface{}
 | |
| 			command          []string
 | |
| 			expectedResponse [][]string
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Get the difference between 2 sorted sets without scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZdiffKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1},
 | |
| 						{Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3},
 | |
| 						{Value: "four", Score: 4},
 | |
| 					}),
 | |
| 					"ZdiffKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "three", Score: 3},
 | |
| 						{Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 						{Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7},
 | |
| 						{Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZDIFF", "ZdiffKey1", "ZdiffKey2"},
 | |
| 				expectedResponse: [][]string{{"one"}, {"two"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "2. Get the difference between 2 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZdiffKey3": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1},
 | |
| 						{Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3},
 | |
| 						{Value: "four", Score: 4},
 | |
| 					}),
 | |
| 					"ZdiffKey4": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "three", Score: 3},
 | |
| 						{Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 						{Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7},
 | |
| 						{Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZDIFF", "ZdiffKey3", "ZdiffKey4", "WITHSCORES"},
 | |
| 				expectedResponse: [][]string{{"one", "1"}, {"two", "2"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "3. Get the difference between 3 sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZdiffKey5": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZdiffKey6": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11},
 | |
| 					}),
 | |
| 					"ZdiffKey7": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZDIFF", "ZdiffKey5", "ZdiffKey6", "ZdiffKey7", "WITHSCORES"},
 | |
| 				expectedResponse: [][]string{{"three", "3"}, {"four", "4"}, {"five", "5"}, {"six", "6"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "4. Return sorted set if only one key exists and is a sorted set",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZdiffKey8": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZDIFF", "ZdiffKey8", "ZdiffKey9", "ZdiffKey10", "WITHSCORES"},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"one", "1"}, {"two", "2"}, {"three", "3"}, {"four", "4"}, {"five", "5"},
 | |
| 					{"six", "6"}, {"seven", "7"}, {"eight", "8"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "5. Throw error when one of the keys is not a sorted set.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZdiffKey11": "Default value",
 | |
| 					"ZdiffKey12": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11},
 | |
| 					}),
 | |
| 					"ZdiffKey13": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZDIFF", "ZdiffKey11", "ZdiffKey12", "ZdiffKey13"},
 | |
| 				expectedResponse: nil,
 | |
| 				expectedError:    errors.New("value at ZdiffKey11 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "6. Command too short",
 | |
| 				command:          []string{"ZDIFF"},
 | |
| 				expectedResponse: [][]string{},
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if len(res.Array()) != len(test.expectedResponse) {
 | |
| 					t.Errorf("expected response array of length %d, got %d", len(test.expectedResponse), len(res.Array()))
 | |
| 				}
 | |
| 
 | |
| 				for _, item := range res.Array() {
 | |
| 					value := item.Array()[0].String()
 | |
| 					score := func() string {
 | |
| 						if len(item.Array()) == 2 {
 | |
| 							return item.Array()[1].String()
 | |
| 						}
 | |
| 						return ""
 | |
| 					}()
 | |
| 					if !slices.ContainsFunc(test.expectedResponse, func(expected []string) bool {
 | |
| 						return expected[0] == value
 | |
| 					}) {
 | |
| 						t.Errorf("unexpected member \"%s\" in response", value)
 | |
| 					}
 | |
| 					if score != "" {
 | |
| 						for _, expected := range test.expectedResponse {
 | |
| 							if expected[0] == value && expected[1] != score {
 | |
| 								t.Errorf("expected score for member \"%s\" to be %s, got %s", value, expected[1], score)
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZDIFFSTORE", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValues     map[string]interface{}
 | |
| 			destination      string
 | |
| 			command          []string
 | |
| 			expectedValue    *sorted_set.SortedSet
 | |
| 			expectedResponse int
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Get the difference between 2 sorted sets.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZdiffStoreKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 					}),
 | |
| 					"ZdiffStoreKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination:      "ZdiffStoreDestinationKey1",
 | |
| 				command:          []string{"ZDIFFSTORE", "ZdiffStoreDestinationKey1", "ZdiffStoreKey1", "ZdiffStoreKey2"},
 | |
| 				expectedValue:    sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}, {Value: "two", Score: 2}}),
 | |
| 				expectedResponse: 2,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "2. Get the difference between 3 sorted sets.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZdiffStoreKey3": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZdiffStoreKey4": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11},
 | |
| 					}),
 | |
| 					"ZdiffStoreKey5": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZdiffStoreDestinationKey2",
 | |
| 				command:     []string{"ZDIFFSTORE", "ZdiffStoreDestinationKey2", "ZdiffStoreKey3", "ZdiffStoreKey4", "ZdiffStoreKey5"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 				}),
 | |
| 				expectedResponse: 4,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "3. Return base sorted set element if base set is the only existing key provided and is a valid sorted set",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZdiffStoreKey6": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZdiffStoreDestinationKey3",
 | |
| 				command:     []string{"ZDIFFSTORE", "ZdiffStoreDestinationKey3", "ZdiffStoreKey6", "ZdiffStoreKey7", "ZdiffStoreKey8"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 					{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 					{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 				}),
 | |
| 				expectedResponse: 8,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "4. Throw error when base sorted set is not a set.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZdiffStoreKey9": "Default value",
 | |
| 					"ZdiffStoreKey10": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11},
 | |
| 					}),
 | |
| 					"ZdiffStoreKey11": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination:      "ZdiffStoreDestinationKey4",
 | |
| 				command:          []string{"ZDIFFSTORE", "ZdiffStoreDestinationKey4", "ZdiffStoreKey9", "ZdiffStoreKey10", "ZdiffStoreKey11"},
 | |
| 				expectedValue:    nil,
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("value at ZdiffStoreKey9 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:        "5. Return 0 when base set is non-existent.",
 | |
| 				destination: "ZdiffStoreDestinationKey5",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZdiffStoreKey12": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11},
 | |
| 					}),
 | |
| 					"ZdiffStoreKey13": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZDIFFSTORE", "ZdiffStoreDestinationKey5", "non-existent", "ZdiffStoreKey12", "ZdiffStoreKey13"},
 | |
| 				expectedValue:    nil,
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "6. Command too short",
 | |
| 				command:          []string{"ZDIFFSTORE", "ZdiffStoreDestinationKey6"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if res.Integer() != test.expectedResponse {
 | |
| 					t.Errorf("expected response %d, got %d", test.expectedResponse, res.Integer())
 | |
| 				}
 | |
| 
 | |
| 				// Check if the resulting sorted set has the expected members/scores
 | |
| 				if test.expectedValue == nil {
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray([]resp.Value{
 | |
| 					resp.StringValue("ZRANGE"),
 | |
| 					resp.StringValue(test.destination),
 | |
| 					resp.StringValue("-inf"),
 | |
| 					resp.StringValue("+inf"),
 | |
| 					resp.StringValue("BYSCORE"),
 | |
| 					resp.StringValue("WITHSCORES"),
 | |
| 				}); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				res, _, err = client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if len(res.Array()) != test.expectedValue.Cardinality() {
 | |
| 					t.Errorf("expected resulting set %s to have cardinality %d, got %d",
 | |
| 						test.destination, test.expectedValue.Cardinality(), len(res.Array()))
 | |
| 				}
 | |
| 
 | |
| 				for _, member := range res.Array() {
 | |
| 					value := sorted_set.Value(member.Array()[0].String())
 | |
| 					score := sorted_set.Score(member.Array()[1].Float())
 | |
| 					if !test.expectedValue.Contains(value) {
 | |
| 						t.Errorf("unexpected value %s in resulting sorted set", value)
 | |
| 					}
 | |
| 					if test.expectedValue.Get(value).Score != score {
 | |
| 						t.Errorf("expected value %s to have score %v, got %v", value, test.expectedValue.Get(value).Score, score)
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZINCRBY", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValue      interface{}
 | |
| 			key              string
 | |
| 			command          []string
 | |
| 			expectedValue    *sorted_set.SortedSet
 | |
| 			expectedResponse string
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Successfully increment by int. Return the new score",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 					{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5},
 | |
| 				}),
 | |
| 				key:     "ZincrbyKey1",
 | |
| 				command: []string{"ZINCRBY", "ZincrbyKey1", "5", "one"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 6}, {Value: "two", Score: 2},
 | |
| 					{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5},
 | |
| 				}),
 | |
| 				expectedResponse: "6",
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "2. Successfully increment by float. Return new score",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 					{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5},
 | |
| 				}),
 | |
| 				key:     "ZincrbyKey2",
 | |
| 				command: []string{"ZINCRBY", "ZincrbyKey2", "346.785", "one"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 347.785}, {Value: "two", Score: 2},
 | |
| 					{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5},
 | |
| 				}),
 | |
| 				expectedResponse: "347.785",
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:        "3. Increment on non-existent sorted set will create the set with the member and increment as its score",
 | |
| 				presetValue: nil,
 | |
| 				key:         "ZincrbyKey3",
 | |
| 				command:     []string{"ZINCRBY", "ZincrbyKey3", "346.785", "one"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 346.785},
 | |
| 				}),
 | |
| 				expectedResponse: "346.785",
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "4. Increment score to +inf",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 					{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5},
 | |
| 				}),
 | |
| 				key:     "ZincrbyKey4",
 | |
| 				command: []string{"ZINCRBY", "ZincrbyKey4", "+inf", "one"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: sorted_set.Score(math.Inf(1))}, {Value: "two", Score: 2},
 | |
| 					{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5},
 | |
| 				}),
 | |
| 				expectedResponse: "+Inf",
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "5. Increment score to -inf",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 					{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5},
 | |
| 				}),
 | |
| 				key:     "ZincrbyKey5",
 | |
| 				command: []string{"ZINCRBY", "ZincrbyKey5", "-inf", "one"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: sorted_set.Score(math.Inf(-1))}, {Value: "two", Score: 2},
 | |
| 					{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5},
 | |
| 				}),
 | |
| 				expectedResponse: "-Inf",
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "6. Incrementing score by negative increment should lower the score",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 					{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5},
 | |
| 				}),
 | |
| 				key:     "ZincrbyKey6",
 | |
| 				command: []string{"ZINCRBY", "ZincrbyKey6", "-2.5", "five"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 					{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 2.5},
 | |
| 				}),
 | |
| 				expectedResponse: "2.5",
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "7. Return error when attempting to increment on a value that is not a valid sorted set",
 | |
| 				presetValue:      "Default value",
 | |
| 				key:              "ZincrbyKey7",
 | |
| 				command:          []string{"ZINCRBY", "ZincrbyKey7", "-2.5", "five"},
 | |
| 				expectedValue:    nil,
 | |
| 				expectedResponse: "",
 | |
| 				expectedError:    errors.New("value at ZincrbyKey7 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "8. Return error when trying to increment a member that already has score -inf",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: sorted_set.Score(math.Inf(-1))},
 | |
| 				}),
 | |
| 				key:     "ZincrbyKey8",
 | |
| 				command: []string{"ZINCRBY", "ZincrbyKey8", "2.5", "one"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: sorted_set.Score(math.Inf(-1))},
 | |
| 				}),
 | |
| 				expectedResponse: "",
 | |
| 				expectedError:    errors.New("cannot increment -inf or +inf"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "9. Return error when trying to increment a member that already has score +inf",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: sorted_set.Score(math.Inf(1))},
 | |
| 				}),
 | |
| 				key:     "ZincrbyKey9",
 | |
| 				command: []string{"ZINCRBY", "ZincrbyKey9", "2.5", "one"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: sorted_set.Score(math.Inf(-1))},
 | |
| 				}),
 | |
| 				expectedResponse: "",
 | |
| 				expectedError:    errors.New("cannot increment -inf or +inf"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "10. Return error when increment is not a valid number",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1},
 | |
| 				}),
 | |
| 				key:     "ZincrbyKey10",
 | |
| 				command: []string{"ZINCRBY", "ZincrbyKey10", "increment", "one"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1},
 | |
| 				}),
 | |
| 				expectedResponse: "",
 | |
| 				expectedError:    errors.New("increment must be a double"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "11. Command too short",
 | |
| 				key:              "ZincrbyKey11",
 | |
| 				command:          []string{"ZINCRBY", "ZincrbyKey11", "one"},
 | |
| 				expectedResponse: "",
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "12. Command too long",
 | |
| 				key:              "ZincrbyKey12",
 | |
| 				command:          []string{"ZINCRBY", "ZincrbyKey12", "one", "1", "2"},
 | |
| 				expectedResponse: "",
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValue != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 
 | |
| 					switch test.presetValue.(type) {
 | |
| 					case string:
 | |
| 						command = []resp.Value{
 | |
| 							resp.StringValue("SET"),
 | |
| 							resp.StringValue(test.key),
 | |
| 							resp.StringValue(test.presetValue.(string)),
 | |
| 						}
 | |
| 						expected = "ok"
 | |
| 					case *sorted_set.SortedSet:
 | |
| 						command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(test.key)}
 | |
| 						for _, member := range test.presetValue.(*sorted_set.SortedSet).GetAll() {
 | |
| 							command = append(command, []resp.Value{
 | |
| 								resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 								resp.StringValue(string(member.Value)),
 | |
| 							}...)
 | |
| 						}
 | |
| 						expected = strconv.Itoa(test.presetValue.(*sorted_set.SortedSet).Cardinality())
 | |
| 					}
 | |
| 
 | |
| 					if err = client.WriteArray(command); err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 					res, _, err := client.ReadValue()
 | |
| 					if err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					if !strings.EqualFold(res.String(), expected) {
 | |
| 						t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if res.String() != test.expectedResponse {
 | |
| 					t.Errorf("expected response \"%s\", got \"%s\"", test.expectedResponse, res.String())
 | |
| 				}
 | |
| 
 | |
| 				// Check if the resulting sorted set has the expected members/scores
 | |
| 				if test.expectedValue == nil {
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray([]resp.Value{
 | |
| 					resp.StringValue("ZRANGE"),
 | |
| 					resp.StringValue(test.key),
 | |
| 					resp.StringValue("-inf"),
 | |
| 					resp.StringValue("+inf"),
 | |
| 					resp.StringValue("BYSCORE"),
 | |
| 					resp.StringValue("WITHSCORES"),
 | |
| 				}); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				res, _, err = client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if len(res.Array()) != test.expectedValue.Cardinality() {
 | |
| 					t.Errorf("expected resulting set %s to have cardinality %d, got %d",
 | |
| 						test.key, test.expectedValue.Cardinality(), len(res.Array()))
 | |
| 				}
 | |
| 
 | |
| 				for _, member := range res.Array() {
 | |
| 					value := sorted_set.Value(member.Array()[0].String())
 | |
| 					score := sorted_set.Score(member.Array()[1].Float())
 | |
| 					if !test.expectedValue.Contains(value) {
 | |
| 						t.Errorf("unexpected value %s in resulting sorted set", value)
 | |
| 					}
 | |
| 					if test.expectedValue.Get(value).Score != score {
 | |
| 						t.Errorf("expected value %s to have score %v, got %v", value, test.expectedValue.Get(value).Score, score)
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZMPOP", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			preset           bool
 | |
| 			presetValues     map[string]interface{}
 | |
| 			command          []string
 | |
| 			expectedValues   map[string]*sorted_set.SortedSet
 | |
| 			expectedResponse [][]string
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name:   "1. Successfully pop one min element by default",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZmpopKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZMPOP", "ZmpopKey1"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZmpopKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"one", "1"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:   "2. Successfully pop one min element by specifying MIN",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZmpopKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZMPOP", "ZmpopKey2", "MIN"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZmpopKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"one", "1"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:   "3. Successfully pop one max element by specifying MAX modifier",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZmpopKey3": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZMPOP", "ZmpopKey3", "MAX"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZmpopKey3": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"five", "5"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:   "4. Successfully pop multiple min elements",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZmpopKey4": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZMPOP", "ZmpopKey4", "MIN", "COUNT", "5"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZmpopKey4": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "six", Score: 6},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"one", "1"}, {"two", "2"}, {"three", "3"},
 | |
| 					{"four", "4"}, {"five", "5"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:   "5. Successfully pop multiple max elements",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZmpopKey5": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZMPOP", "ZmpopKey5", "MAX", "COUNT", "5"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZmpopKey5": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: [][]string{{"two", "2"}, {"three", "3"}, {"four", "4"}, {"five", "5"}, {"six", "6"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:   "6. Successfully pop elements from the first set which is non-empty",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZmpopKey7": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZMPOP", "ZmpopKey6", "ZmpopKey7", "MAX", "COUNT", "5"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZmpopKey6": sorted_set.NewSortedSet([]sorted_set.MemberParam{}),
 | |
| 					"ZmpopKey7": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: [][]string{{"two", "2"}, {"three", "3"}, {"four", "4"}, {"five", "5"}, {"six", "6"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:   "7. Skip the non-set items and pop elements from the first non-empty sorted set found",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZmpopKey8": "Default value",
 | |
| 					"ZmpopKey9": "56",
 | |
| 					"ZmpopKey11": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZMPOP", "ZmpopKey8", "ZmpopKey9", "ZmpopKey10", "ZmpopKey11", "MIN", "COUNT", "5"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZmpopKey10": sorted_set.NewSortedSet([]sorted_set.MemberParam{}),
 | |
| 					"ZmpopKey11": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "six", Score: 6},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: [][]string{{"one", "1"}, {"two", "2"}, {"three", "3"}, {"four", "4"}, {"five", "5"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "9. Return error when count is a negative integer",
 | |
| 				preset:        false,
 | |
| 				command:       []string{"ZMPOP", "ZmpopKey8", "MAX", "COUNT", "-20"},
 | |
| 				expectedError: errors.New("count must be a positive integer"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "9. Command too short",
 | |
| 				preset:        false,
 | |
| 				command:       []string{"ZMPOP"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if len(res.Array()) != len(test.expectedResponse) {
 | |
| 					t.Errorf("expected response array of length %d, got %d", len(test.expectedResponse), len(res.Array()))
 | |
| 				}
 | |
| 
 | |
| 				for _, item := range res.Array() {
 | |
| 					value := item.Array()[0].String()
 | |
| 					score := func() string {
 | |
| 						if len(item.Array()) == 2 {
 | |
| 							return item.Array()[1].String()
 | |
| 						}
 | |
| 						return ""
 | |
| 					}()
 | |
| 					if !slices.ContainsFunc(test.expectedResponse, func(expected []string) bool {
 | |
| 						return expected[0] == value
 | |
| 					}) {
 | |
| 						t.Errorf("unexpected member \"%s\" in response", value)
 | |
| 					}
 | |
| 					if score != "" {
 | |
| 						for _, expected := range test.expectedResponse {
 | |
| 							if expected[0] == value && expected[1] != score {
 | |
| 								t.Errorf("expected score for member \"%s\" to be %s, got %s", value, expected[1], score)
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				// Check if the resulting sorted set has the expected members/scores
 | |
| 				for key, expectedSortedSet := range test.expectedValues {
 | |
| 					if expectedSortedSet == nil {
 | |
| 						continue
 | |
| 					}
 | |
| 
 | |
| 					if err = client.WriteArray([]resp.Value{
 | |
| 						resp.StringValue("ZRANGE"),
 | |
| 						resp.StringValue(key),
 | |
| 						resp.StringValue("-inf"),
 | |
| 						resp.StringValue("+inf"),
 | |
| 						resp.StringValue("BYSCORE"),
 | |
| 						resp.StringValue("WITHSCORES"),
 | |
| 					}); err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					res, _, err = client.ReadValue()
 | |
| 					if err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					if len(res.Array()) != expectedSortedSet.Cardinality() {
 | |
| 						t.Errorf("expected resulting set %s to have cardinality %d, got %d",
 | |
| 							key, expectedSortedSet.Cardinality(), len(res.Array()))
 | |
| 					}
 | |
| 
 | |
| 					for _, member := range res.Array() {
 | |
| 						value := sorted_set.Value(member.Array()[0].String())
 | |
| 						score := sorted_set.Score(member.Array()[1].Float())
 | |
| 						if !expectedSortedSet.Contains(value) {
 | |
| 							t.Errorf("unexpected value %s in resulting sorted set", value)
 | |
| 						}
 | |
| 						if expectedSortedSet.Get(value).Score != score {
 | |
| 							t.Errorf("expected value %s to have score %v, got %v",
 | |
| 								value, expectedSortedSet.Get(value).Score, score)
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZPOP", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			preset           bool
 | |
| 			presetValues     map[string]interface{}
 | |
| 			command          []string
 | |
| 			expectedValues   map[string]*sorted_set.SortedSet
 | |
| 			expectedResponse [][]string
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name:   "1. Successfully pop one min element by default",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZmpopMinKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZPOPMIN", "ZmpopMinKey1"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZmpopMinKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"one", "1"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:   "2. Successfully pop one max element by default",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZmpopMaxKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZPOPMAX", "ZmpopMaxKey2"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZmpopMaxKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"five", "5"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:   "3. Successfully pop multiple min elements",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZmpopMinKey3": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZPOPMIN", "ZmpopMinKey3", "5"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZmpopMinKey3": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "six", Score: 6},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"one", "1"}, {"two", "2"}, {"three", "3"},
 | |
| 					{"four", "4"}, {"five", "5"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:   "4. Successfully pop multiple max elements",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZmpopMaxKey4": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZPOPMAX", "ZmpopMaxKey4", "5"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZmpopMaxKey4": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: [][]string{{"two", "2"}, {"three", "3"}, {"four", "4"}, {"five", "5"}, {"six", "6"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:   "5. Throw an error when trying to pop from an element that's not a sorted set",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZmpopMinKey5": "Default value",
 | |
| 				},
 | |
| 				command:          []string{"ZPOPMIN", "ZmpopMinKey5"},
 | |
| 				expectedValues:   nil,
 | |
| 				expectedResponse: nil,
 | |
| 				expectedError:    errors.New("value at key ZmpopMinKey5 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "6. Command too short",
 | |
| 				preset:        false,
 | |
| 				command:       []string{"ZPOPMAX"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "7. Command too long",
 | |
| 				preset:        false,
 | |
| 				command:       []string{"ZPOPMAX", "ZmpopMaxKey7", "6", "3"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if len(res.Array()) != len(test.expectedResponse) {
 | |
| 					t.Errorf("expected response array of length %d, got %d", len(test.expectedResponse), len(res.Array()))
 | |
| 				}
 | |
| 
 | |
| 				for _, item := range res.Array() {
 | |
| 					value := item.Array()[0].String()
 | |
| 					score := func() string {
 | |
| 						if len(item.Array()) == 2 {
 | |
| 							return item.Array()[1].String()
 | |
| 						}
 | |
| 						return ""
 | |
| 					}()
 | |
| 					if !slices.ContainsFunc(test.expectedResponse, func(expected []string) bool {
 | |
| 						return expected[0] == value
 | |
| 					}) {
 | |
| 						t.Errorf("unexpected member \"%s\" in response", value)
 | |
| 					}
 | |
| 					if score != "" {
 | |
| 						for _, expected := range test.expectedResponse {
 | |
| 							if expected[0] == value && expected[1] != score {
 | |
| 								t.Errorf("expected score for member \"%s\" to be %s, got %s", value, expected[1], score)
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				// Check if the resulting sorted set has the expected members/scores
 | |
| 				for key, expectedSortedSet := range test.expectedValues {
 | |
| 					if expectedSortedSet == nil {
 | |
| 						continue
 | |
| 					}
 | |
| 
 | |
| 					if err = client.WriteArray([]resp.Value{
 | |
| 						resp.StringValue("ZRANGE"),
 | |
| 						resp.StringValue(key),
 | |
| 						resp.StringValue("-inf"),
 | |
| 						resp.StringValue("+inf"),
 | |
| 						resp.StringValue("BYSCORE"),
 | |
| 						resp.StringValue("WITHSCORES"),
 | |
| 					}); err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					res, _, err = client.ReadValue()
 | |
| 					if err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					if len(res.Array()) != expectedSortedSet.Cardinality() {
 | |
| 						t.Errorf("expected resulting set %s to have cardinality %d, got %d",
 | |
| 							key, expectedSortedSet.Cardinality(), len(res.Array()))
 | |
| 					}
 | |
| 
 | |
| 					for _, member := range res.Array() {
 | |
| 						value := sorted_set.Value(member.Array()[0].String())
 | |
| 						score := sorted_set.Score(member.Array()[1].Float())
 | |
| 						if !expectedSortedSet.Contains(value) {
 | |
| 							t.Errorf("unexpected value %s in resulting sorted set", value)
 | |
| 						}
 | |
| 						if expectedSortedSet.Get(value).Score != score {
 | |
| 							t.Errorf("expected value %s to have score %v, got %v",
 | |
| 								value, expectedSortedSet.Get(value).Score, score)
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZMSCORE", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValues     map[string]interface{}
 | |
| 			command          []string
 | |
| 			expectedResponse []string
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				// 1. Return multiple scores from the sorted set.
 | |
| 				// Return nil for elements that do not exist in the sorted set.
 | |
| 				name: "1. Return multiple scores from the sorted set.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZmScoreKey1": sorted_set.NewSortedSet([]sorted_set.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", "ZmScoreKey1", "one", "none", "two", "one", "three", "four", "none", "five"},
 | |
| 				expectedResponse: []string{"1.1", "", "245", "1.1", "3", "4.055", "", "5"},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "2. If key does not exist, return empty array",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZMSCORE", "ZmScoreKey2", "one", "two", "three", "four"},
 | |
| 				expectedResponse: []string{},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "3. Throw error when trying to find scores from elements that are not sorted sets",
 | |
| 				presetValues:  map[string]interface{}{"ZmScoreKey3": "Default value"},
 | |
| 				command:       []string{"ZMSCORE", "ZmScoreKey3", "one", "two", "three"},
 | |
| 				expectedError: errors.New("value at ZmScoreKey3 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "9. Command too short",
 | |
| 				command:       []string{"ZMSCORE"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if len(res.Array()) != len(test.expectedResponse) {
 | |
| 					t.Errorf("expected response array of length %d, got %d", len(test.expectedResponse), len(res.Array()))
 | |
| 				}
 | |
| 
 | |
| 				for i := 0; i < len(res.Array()); i++ {
 | |
| 					if test.expectedResponse[i] != res.Array()[i].String() {
 | |
| 						t.Errorf("expected element at index %d to be \"%s\", got %s",
 | |
| 							i, test.expectedResponse[i], res.Array()[i].String())
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZSCORE", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValues     map[string]interface{}
 | |
| 			command          []string
 | |
| 			expectedResponse string
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Return score from a sorted set.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZscoreKey1": sorted_set.NewSortedSet([]sorted_set.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{"ZSCORE", "ZscoreKey1", "four"},
 | |
| 				expectedResponse: "4.055",
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "2. If key does not exist, return nil value",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZSCORE", "ZscoreKey2", "one"},
 | |
| 				expectedResponse: "",
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "3. If key exists and is a sorted set, but the member does not exist, return nil",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZscoreKey3": sorted_set.NewSortedSet([]sorted_set.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{"ZSCORE", "ZscoreKey3", "non-existent"},
 | |
| 				expectedResponse: "",
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "4. Throw error when trying to find scores from elements that are not sorted sets",
 | |
| 				presetValues:  map[string]interface{}{"ZscoreKey4": "Default value"},
 | |
| 				command:       []string{"ZSCORE", "ZscoreKey4", "one"},
 | |
| 				expectedError: errors.New("value at ZscoreKey4 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "5. Command too short",
 | |
| 				command:       []string{"ZSCORE"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "6. Command too long",
 | |
| 				command:       []string{"ZSCORE", "ZscoreKey5", "one", "two"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if res.String() != test.expectedResponse {
 | |
| 					t.Errorf("expected response \"%s\", got \"%s\"", test.expectedResponse, res.String())
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZRANDMEMBER", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			key              string
 | |
| 			presetValue      interface{}
 | |
| 			command          []string
 | |
| 			expectedValue    int // The final cardinality of the resulting set
 | |
| 			allowRepeat      bool
 | |
| 			expectedResponse [][]string
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				// 1. Return multiple random elements without removing them.
 | |
| 				// Count is positive, do not allow repeated elements
 | |
| 				name: "1. Return multiple random elements without removing them.",
 | |
| 				key:  "ZrandMemberKey1",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1}, {Value: "two", Score: 2}, {Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5}, {Value: "six", Score: 6}, {Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 				}),
 | |
| 				command:       []string{"ZRANDMEMBER", "ZrandMemberKey1", "3"},
 | |
| 				expectedValue: 8,
 | |
| 				allowRepeat:   false,
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"one"}, {"two"}, {"three"}, {"four"},
 | |
| 					{"five"}, {"six"}, {"seven"}, {"eight"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 2. Return multiple random elements and their scores without removing them.
 | |
| 				// Count is negative, so allow repeated numbers.
 | |
| 				name: "2. Return multiple random elements and their scores without removing them.",
 | |
| 				key:  "ZrandMemberKey2",
 | |
| 				presetValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1}, {Value: "two", Score: 2}, {Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5}, {Value: "six", Score: 6}, {Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 				}),
 | |
| 				command:       []string{"ZRANDMEMBER", "ZrandMemberKey2", "-5", "WITHSCORES"},
 | |
| 				expectedValue: 8,
 | |
| 				allowRepeat:   true,
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"one", "1"}, {"two", "2"}, {"three", "3"}, {"four", "4"},
 | |
| 					{"five", "5"}, {"six", "6"}, {"seven", "7"}, {"eight", "8"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "2. Return error when the source key is not a sorted set.",
 | |
| 				key:           "ZrandMemberKey3",
 | |
| 				presetValue:   "Default value",
 | |
| 				command:       []string{"ZRANDMEMBER", "ZrandMemberKey3"},
 | |
| 				expectedValue: 0,
 | |
| 				expectedError: errors.New("value at ZrandMemberKey3 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "5. Command too short",
 | |
| 				command:       []string{"ZRANDMEMBER"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "6. Command too long",
 | |
| 				command:       []string{"ZRANDMEMBER", "source5", "source6", "member1", "member2"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "7. Throw error when count is not an integer",
 | |
| 				command:       []string{"ZRANDMEMBER", "ZrandMemberKey1", "count"},
 | |
| 				expectedError: errors.New("count must be an integer"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "8. Throw error when the fourth argument is not WITHSCORES",
 | |
| 				command:       []string{"ZRANDMEMBER", "ZrandMemberKey1", "8", "ANOTHER"},
 | |
| 				expectedError: errors.New("last option must be WITHSCORES"),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValue != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 
 | |
| 					switch test.presetValue.(type) {
 | |
| 					case string:
 | |
| 						command = []resp.Value{
 | |
| 							resp.StringValue("SET"),
 | |
| 							resp.StringValue(test.key),
 | |
| 							resp.StringValue(test.presetValue.(string)),
 | |
| 						}
 | |
| 						expected = "ok"
 | |
| 					case *sorted_set.SortedSet:
 | |
| 						command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(test.key)}
 | |
| 						for _, member := range test.presetValue.(*sorted_set.SortedSet).GetAll() {
 | |
| 							command = append(command, []resp.Value{
 | |
| 								resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 								resp.StringValue(string(member.Value)),
 | |
| 							}...)
 | |
| 						}
 | |
| 						expected = strconv.Itoa(test.presetValue.(*sorted_set.SortedSet).Cardinality())
 | |
| 					}
 | |
| 
 | |
| 					if err = client.WriteArray(command); err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 					res, _, err := client.ReadValue()
 | |
| 					if err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					if !strings.EqualFold(res.String(), expected) {
 | |
| 						t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				// Check that each of the returned elements is in the expected response.
 | |
| 				for _, item := range res.Array() {
 | |
| 					value := sorted_set.Value(item.Array()[0].String())
 | |
| 					if !slices.ContainsFunc(test.expectedResponse, func(expected []string) bool {
 | |
| 						return expected[0] == string(value)
 | |
| 					}) {
 | |
| 						t.Errorf("unexected element \"%s\" in response", value)
 | |
| 					}
 | |
| 					for _, expected := range test.expectedResponse {
 | |
| 						if len(item.Array()) != len(expected) {
 | |
| 							t.Errorf("expected response for element \"%s\" to have length %d, got %d",
 | |
| 								value, len(expected), len(item.Array()))
 | |
| 						}
 | |
| 						if expected[0] != string(value) {
 | |
| 							continue
 | |
| 						}
 | |
| 						if len(expected) == 2 {
 | |
| 							score := item.Array()[1].String()
 | |
| 							if expected[1] != score {
 | |
| 								t.Errorf("expected score for memebr \"%s\" to be %s, got %s", value, expected[1], score)
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				// Check that allowRepeat determines whether elements are repeated or not.
 | |
| 				if !test.allowRepeat {
 | |
| 					ss := sorted_set.NewSortedSet([]sorted_set.MemberParam{})
 | |
| 					for _, item := range res.Array() {
 | |
| 						member := sorted_set.Value(item.Array()[0].String())
 | |
| 						score := func() sorted_set.Score {
 | |
| 							if len(item.Array()) == 2 {
 | |
| 								return sorted_set.Score(item.Array()[1].Float())
 | |
| 							}
 | |
| 							return sorted_set.Score(0)
 | |
| 						}()
 | |
| 						_, err = ss.AddOrUpdate(
 | |
| 							[]sorted_set.MemberParam{{member, score}},
 | |
| 							nil, nil, nil, nil)
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 					}
 | |
| 					if len(res.Array()) != ss.Cardinality() {
 | |
| 						t.Error("unexpected repeated elements in response")
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZRANK", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValues     map[string]interface{}
 | |
| 			command          []string
 | |
| 			expectedResponse []string
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Return element's rank from a sorted set.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrankKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZRANK", "ZrankKey1", "four"},
 | |
| 				expectedResponse: []string{"3"},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "2. Return element's rank from a sorted set with its score.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrankKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100.1}, {Value: "two", Score: 245},
 | |
| 						{Value: "three", Score: 305.43}, {Value: "four", Score: 411.055},
 | |
| 						{Value: "five", Score: 500},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZRANK", "ZrankKey1", "four", "WITHSCORES"},
 | |
| 				expectedResponse: []string{"3", "411.055"},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "3. If key does not exist, return nil value",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZRANK", "ZrankKey3", "one"},
 | |
| 				expectedResponse: nil,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "4. If key exists and is a sorted set, but the member does not exist, return nil",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrankKey4": sorted_set.NewSortedSet([]sorted_set.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{"ZRANK", "ZrankKey4", "non-existent"},
 | |
| 				expectedResponse: nil,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "5. Throw error when trying to find scores from elements that are not sorted sets",
 | |
| 				presetValues:  map[string]interface{}{"ZrankKey5": "Default value"},
 | |
| 				command:       []string{"ZRANK", "ZrankKey5", "one"},
 | |
| 				expectedError: errors.New("value at ZrankKey5 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "5. Command too short",
 | |
| 				command:       []string{"ZRANK"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "6. Command too long",
 | |
| 				command:       []string{"ZRANK", "ZrankKey5", "one", "WITHSCORES", "two"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if len(res.Array()) != len(test.expectedResponse) {
 | |
| 					t.Errorf("expected response array of length %d, got %d", len(test.expectedResponse), len(res.Array()))
 | |
| 				}
 | |
| 
 | |
| 				for i := 0; i < len(res.Array()); i++ {
 | |
| 					if test.expectedResponse[i] != res.Array()[i].String() {
 | |
| 						t.Errorf("expected element at index %d to be \"%s\", got %s",
 | |
| 							i, test.expectedResponse[i], res.Array()[i].String())
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZREM", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValues     map[string]interface{}
 | |
| 			command          []string
 | |
| 			expectedValues   map[string]*sorted_set.SortedSet
 | |
| 			expectedResponse int
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				// Successfully remove multiple elements from sorted set, skipping non-existent members.
 | |
| 				// Return deleted count.
 | |
| 				name: "1. Successfully remove multiple elements from sorted set, skipping non-existent members.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZremKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZREM", "ZremKey1", "three", "four", "five", "none", "six", "none", "seven"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZremKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: 5,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "2. If key does not exist, return 0",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZREM", "ZremKey2", "member"},
 | |
| 				expectedValues:   nil,
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "3. Return error key is not a sorted set",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZremKey3": "Default value",
 | |
| 				},
 | |
| 				command:       []string{"ZREM", "ZremKey3", "member"},
 | |
| 				expectedError: errors.New("value at ZremKey3 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "9. Command too short",
 | |
| 				command:       []string{"ZREM"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if res.Integer() != test.expectedResponse {
 | |
| 					t.Errorf("expected response array of length %d, got %d", test.expectedResponse, res.Integer())
 | |
| 				}
 | |
| 
 | |
| 				// Check if the resulting sorted set has the expected members/scores
 | |
| 				for key, expectedSortedSet := range test.expectedValues {
 | |
| 					if expectedSortedSet == nil {
 | |
| 						continue
 | |
| 					}
 | |
| 
 | |
| 					if err = client.WriteArray([]resp.Value{
 | |
| 						resp.StringValue("ZRANGE"),
 | |
| 						resp.StringValue(key),
 | |
| 						resp.StringValue("-inf"),
 | |
| 						resp.StringValue("+inf"),
 | |
| 						resp.StringValue("BYSCORE"),
 | |
| 						resp.StringValue("WITHSCORES"),
 | |
| 					}); err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					res, _, err = client.ReadValue()
 | |
| 					if err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					if len(res.Array()) != expectedSortedSet.Cardinality() {
 | |
| 						t.Errorf("expected resulting set %s to have cardinality %d, got %d",
 | |
| 							key, expectedSortedSet.Cardinality(), len(res.Array()))
 | |
| 					}
 | |
| 
 | |
| 					for _, member := range res.Array() {
 | |
| 						value := sorted_set.Value(member.Array()[0].String())
 | |
| 						score := sorted_set.Score(member.Array()[1].Float())
 | |
| 						if !expectedSortedSet.Contains(value) {
 | |
| 							t.Errorf("unexpected value %s in resulting sorted set", value)
 | |
| 						}
 | |
| 						if expectedSortedSet.Get(value).Score != score {
 | |
| 							t.Errorf("expected value %s to have score %v, got %v",
 | |
| 								value, expectedSortedSet.Get(value).Score, score)
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZREMRANGEBYSCORE", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValues     map[string]interface{}
 | |
| 			command          []string
 | |
| 			expectedValues   map[string]*sorted_set.SortedSet
 | |
| 			expectedResponse int
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Successfully remove multiple elements with scores inside the provided range",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZremRangeByScoreKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZREMRANGEBYSCORE", "ZremRangeByScoreKey1", "3", "7"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZremRangeByScoreKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: 5,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "2. If key does not exist, return 0",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZREMRANGEBYSCORE", "ZremRangeByScoreKey2", "2", "4"},
 | |
| 				expectedValues:   nil,
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "3. Return error key is not a sorted set",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZremRangeByScoreKey3": "Default value",
 | |
| 				},
 | |
| 				command:       []string{"ZREMRANGEBYSCORE", "ZremRangeByScoreKey3", "4", "4"},
 | |
| 				expectedError: errors.New("value at ZremRangeByScoreKey3 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "4. Command too short",
 | |
| 				command:       []string{"ZREMRANGEBYSCORE", "ZremRangeByScoreKey4", "3"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "5. Command too long",
 | |
| 				command:       []string{"ZREMRANGEBYSCORE", "ZremRangeByScoreKey5", "4", "5", "8"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if res.Integer() != test.expectedResponse {
 | |
| 					t.Errorf("expected response array of length %d, got %d", test.expectedResponse, res.Integer())
 | |
| 				}
 | |
| 
 | |
| 				// Check if the resulting sorted set has the expected members/scores
 | |
| 				for key, expectedSortedSet := range test.expectedValues {
 | |
| 					if expectedSortedSet == nil {
 | |
| 						continue
 | |
| 					}
 | |
| 
 | |
| 					if err = client.WriteArray([]resp.Value{
 | |
| 						resp.StringValue("ZRANGE"),
 | |
| 						resp.StringValue(key),
 | |
| 						resp.StringValue("-inf"),
 | |
| 						resp.StringValue("+inf"),
 | |
| 						resp.StringValue("BYSCORE"),
 | |
| 						resp.StringValue("WITHSCORES"),
 | |
| 					}); err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					res, _, err = client.ReadValue()
 | |
| 					if err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					if len(res.Array()) != expectedSortedSet.Cardinality() {
 | |
| 						t.Errorf("expected resulting set %s to have cardinality %d, got %d",
 | |
| 							key, expectedSortedSet.Cardinality(), len(res.Array()))
 | |
| 					}
 | |
| 
 | |
| 					for _, member := range res.Array() {
 | |
| 						value := sorted_set.Value(member.Array()[0].String())
 | |
| 						score := sorted_set.Score(member.Array()[1].Float())
 | |
| 						if !expectedSortedSet.Contains(value) {
 | |
| 							t.Errorf("unexpected value %s in resulting sorted set", value)
 | |
| 						}
 | |
| 						if expectedSortedSet.Get(value).Score != score {
 | |
| 							t.Errorf("expected value %s to have score %v, got %v",
 | |
| 								value, expectedSortedSet.Get(value).Score, score)
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZREMRANGEBYRANK", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValues     map[string]interface{}
 | |
| 			command          []string
 | |
| 			expectedValues   map[string]*sorted_set.SortedSet
 | |
| 			expectedResponse int
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Successfully remove multiple elements within range",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZremRangeByRankKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZREMRANGEBYRANK", "ZremRangeByRankKey1", "0", "5"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZremRangeByRankKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: 6,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "2. Establish boundaries from the end of the set when negative boundaries are provided",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZremRangeByRankKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZREMRANGEBYRANK", "ZremRangeByRankKey2", "-6", "-3"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZremRangeByRankKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: 4,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "3. If key does not exist, return 0",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZREMRANGEBYRANK", "ZremRangeByRankKey3", "2", "4"},
 | |
| 				expectedValues:   nil,
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "4. Return error key is not a sorted set",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZremRangeByRankKey3": "Default value",
 | |
| 				},
 | |
| 				command:       []string{"ZREMRANGEBYRANK", "ZremRangeByRankKey3", "4", "4"},
 | |
| 				expectedError: errors.New("value at ZremRangeByRankKey3 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "5. Return error when start index is out of bounds",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZremRangeByRankKey5": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZREMRANGEBYRANK", "ZremRangeByRankKey5", "-12", "5"},
 | |
| 				expectedValues:   nil,
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("indices out of bounds"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "6. Return error when end index is out of bounds",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZremRangeByRankKey6": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZREMRANGEBYRANK", "ZremRangeByRankKey6", "0", "11"},
 | |
| 				expectedValues:   nil,
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("indices out of bounds"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "7. Command too short",
 | |
| 				command:       []string{"ZREMRANGEBYRANK", "ZremRangeByRankKey4", "3"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "8. Command too long",
 | |
| 				command:       []string{"ZREMRANGEBYRANK", "ZremRangeByRankKey7", "4", "5", "8"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if res.Integer() != test.expectedResponse {
 | |
| 					t.Errorf("expected response array of length %d, got %d", test.expectedResponse, res.Integer())
 | |
| 				}
 | |
| 
 | |
| 				// Check if the resulting sorted set has the expected members/scores
 | |
| 				for key, expectedSortedSet := range test.expectedValues {
 | |
| 					if expectedSortedSet == nil {
 | |
| 						continue
 | |
| 					}
 | |
| 
 | |
| 					if err = client.WriteArray([]resp.Value{
 | |
| 						resp.StringValue("ZRANGE"),
 | |
| 						resp.StringValue(key),
 | |
| 						resp.StringValue("-inf"),
 | |
| 						resp.StringValue("+inf"),
 | |
| 						resp.StringValue("BYSCORE"),
 | |
| 						resp.StringValue("WITHSCORES"),
 | |
| 					}); err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					res, _, err = client.ReadValue()
 | |
| 					if err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					if len(res.Array()) != expectedSortedSet.Cardinality() {
 | |
| 						t.Errorf("expected resulting set %s to have cardinality %d, got %d",
 | |
| 							key, expectedSortedSet.Cardinality(), len(res.Array()))
 | |
| 					}
 | |
| 
 | |
| 					for _, member := range res.Array() {
 | |
| 						value := sorted_set.Value(member.Array()[0].String())
 | |
| 						score := sorted_set.Score(member.Array()[1].Float())
 | |
| 						if !expectedSortedSet.Contains(value) {
 | |
| 							t.Errorf("unexpected value %s in resulting sorted set", value)
 | |
| 						}
 | |
| 						if expectedSortedSet.Get(value).Score != score {
 | |
| 							t.Errorf("expected value %s to have score %v, got %v",
 | |
| 								value, expectedSortedSet.Get(value).Score, score)
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZREMRANGEBYLEX", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValues     map[string]interface{}
 | |
| 			command          []string
 | |
| 			expectedValues   map[string]*sorted_set.SortedSet
 | |
| 			expectedResponse int
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Successfully remove multiple elements with scores inside the provided range",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZremRangeByLexKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "a", Score: 1}, {Value: "b", Score: 1},
 | |
| 						{Value: "c", Score: 1}, {Value: "d", Score: 1},
 | |
| 						{Value: "e", Score: 1}, {Value: "f", Score: 1},
 | |
| 						{Value: "g", Score: 1}, {Value: "h", Score: 1},
 | |
| 						{Value: "i", Score: 1}, {Value: "j", Score: 1},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZREMRANGEBYLEX", "ZremRangeByLexKey1", "a", "d"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZremRangeByLexKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "e", Score: 1}, {Value: "f", Score: 1},
 | |
| 						{Value: "g", Score: 1}, {Value: "h", Score: 1},
 | |
| 						{Value: "i", Score: 1}, {Value: "j", Score: 1},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: 4,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "2. Return 0 if the members do not have the same score",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZremRangeByLexKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "a", Score: 1}, {Value: "b", Score: 2},
 | |
| 						{Value: "c", Score: 3}, {Value: "d", Score: 4},
 | |
| 						{Value: "e", Score: 5}, {Value: "f", Score: 6},
 | |
| 						{Value: "g", Score: 7}, {Value: "h", Score: 8},
 | |
| 						{Value: "i", Score: 9}, {Value: "j", Score: 10},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZREMRANGEBYLEX", "ZremRangeByLexKey2", "d", "g"},
 | |
| 				expectedValues: map[string]*sorted_set.SortedSet{
 | |
| 					"ZremRangeByLexKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "a", Score: 1}, {Value: "b", Score: 2},
 | |
| 						{Value: "c", Score: 3}, {Value: "d", Score: 4},
 | |
| 						{Value: "e", Score: 5}, {Value: "f", Score: 6},
 | |
| 						{Value: "g", Score: 7}, {Value: "h", Score: 8},
 | |
| 						{Value: "i", Score: 9}, {Value: "j", Score: 10},
 | |
| 					}),
 | |
| 				},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "3. If key does not exist, return 0",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZREMRANGEBYLEX", "ZremRangeByLexKey3", "2", "4"},
 | |
| 				expectedValues:   nil,
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "4. Return error key is not a sorted set",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZremRangeByLexKey3": "Default value",
 | |
| 				},
 | |
| 				command:       []string{"ZREMRANGEBYLEX", "ZremRangeByLexKey3", "a", "d"},
 | |
| 				expectedError: errors.New("value at ZremRangeByLexKey3 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "5. Command too short",
 | |
| 				command:       []string{"ZREMRANGEBYLEX", "ZremRangeByLexKey4", "a"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "6. Command too long",
 | |
| 				command:       []string{"ZREMRANGEBYLEX", "ZremRangeByLexKey5", "a", "b", "c"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if res.Integer() != test.expectedResponse {
 | |
| 					t.Errorf("expected response array of length %d, got %d", test.expectedResponse, res.Integer())
 | |
| 				}
 | |
| 
 | |
| 				// Check if the resulting sorted set has the expected members/scores
 | |
| 				for key, expectedSortedSet := range test.expectedValues {
 | |
| 					if expectedSortedSet == nil {
 | |
| 						continue
 | |
| 					}
 | |
| 
 | |
| 					if err = client.WriteArray([]resp.Value{
 | |
| 						resp.StringValue("ZRANGE"),
 | |
| 						resp.StringValue(key),
 | |
| 						resp.StringValue("-inf"),
 | |
| 						resp.StringValue("+inf"),
 | |
| 						resp.StringValue("BYSCORE"),
 | |
| 						resp.StringValue("WITHSCORES"),
 | |
| 					}); err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					res, _, err = client.ReadValue()
 | |
| 					if err != nil {
 | |
| 						t.Error(err)
 | |
| 					}
 | |
| 
 | |
| 					if len(res.Array()) != expectedSortedSet.Cardinality() {
 | |
| 						t.Errorf("expected resulting set %s to have cardinality %d, got %d",
 | |
| 							key, expectedSortedSet.Cardinality(), len(res.Array()))
 | |
| 					}
 | |
| 
 | |
| 					for _, member := range res.Array() {
 | |
| 						value := sorted_set.Value(member.Array()[0].String())
 | |
| 						score := sorted_set.Score(member.Array()[1].Float())
 | |
| 						if !expectedSortedSet.Contains(value) {
 | |
| 							t.Errorf("unexpected value %s in resulting sorted set", value)
 | |
| 						}
 | |
| 						if expectedSortedSet.Get(value).Score != score {
 | |
| 							t.Errorf("expected value %s to have score %v, got %v",
 | |
| 								value, expectedSortedSet.Get(value).Score, score)
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZRANGE", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValues     map[string]interface{}
 | |
| 			command          []string
 | |
| 			expectedResponse [][]string
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Get elements withing score range without score.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZRANGE", "ZrangeKey1", "3", "7", "BYSCORE"},
 | |
| 				expectedResponse: [][]string{{"three"}, {"four"}, {"five"}, {"six"}, {"seven"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "2. Get elements within score range with score.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZRANGE", "ZrangeKey2", "3", "7", "BYSCORE", "WITHSCORES"},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"three", "3"}, {"four", "4"}, {"five", "5"},
 | |
| 					{"six", "6"}, {"seven", "7"}},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 3. Get elements within score range with offset and limit.
 | |
| 				// Offset and limit are in where we start and stop counting in the original sorted set (NOT THE RESULT).
 | |
| 				name: "3. Get elements within score range with offset and limit.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeKey3": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZRANGE", "ZrangeKey3", "3", "7", "BYSCORE", "WITHSCORES", "LIMIT", "2", "4"},
 | |
| 				expectedResponse: [][]string{{"three", "3"}, {"four", "4"}, {"five", "5"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 4. Get elements within score range with offset and limit + reverse the results.
 | |
| 				// Offset and limit are in where we start and stop counting in the original sorted set (NOT THE RESULT).
 | |
| 				// REV reverses the original set before getting the range.
 | |
| 				name: "4. Get elements within score range with offset and limit + reverse the results.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeKey4": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZRANGE", "ZrangeKey4", "3", "7", "BYSCORE", "WITHSCORES", "LIMIT", "2", "4", "REV"},
 | |
| 				expectedResponse: [][]string{{"six", "6"}, {"five", "5"}, {"four", "4"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "5. Get elements within lex range without score.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeKey5": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "a", Score: 1}, {Value: "e", Score: 1},
 | |
| 						{Value: "b", Score: 1}, {Value: "f", Score: 1},
 | |
| 						{Value: "c", Score: 1}, {Value: "g", Score: 1},
 | |
| 						{Value: "d", Score: 1}, {Value: "h", Score: 1},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZRANGE", "ZrangeKey5", "c", "g", "BYLEX"},
 | |
| 				expectedResponse: [][]string{{"c"}, {"d"}, {"e"}, {"f"}, {"g"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "6. Get elements within lex range with score.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeKey6": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "a", Score: 1}, {Value: "e", Score: 1},
 | |
| 						{Value: "b", Score: 1}, {Value: "f", Score: 1},
 | |
| 						{Value: "c", Score: 1}, {Value: "g", Score: 1},
 | |
| 						{Value: "d", Score: 1}, {Value: "h", Score: 1},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZRANGE", "ZrangeKey6", "a", "f", "BYLEX", "WITHSCORES"},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"a", "1"}, {"b", "1"}, {"c", "1"},
 | |
| 					{"d", "1"}, {"e", "1"}, {"f", "1"}},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 7. Get elements within lex range with offset and limit.
 | |
| 				// Offset and limit are in where we start and stop counting in the original sorted set (NOT THE RESULT).
 | |
| 				name: "7. Get elements within lex range with offset and limit.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeKey7": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "a", Score: 1}, {Value: "b", Score: 1},
 | |
| 						{Value: "c", Score: 1}, {Value: "d", Score: 1},
 | |
| 						{Value: "e", Score: 1}, {Value: "f", Score: 1},
 | |
| 						{Value: "g", Score: 1}, {Value: "h", Score: 1},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZRANGE", "ZrangeKey7", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "2", "4"},
 | |
| 				expectedResponse: [][]string{{"c", "1"}, {"d", "1"}, {"e", "1"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 8. Get elements within lex range with offset and limit + reverse the results.
 | |
| 				// Offset and limit are in where we start and stop counting in the original sorted set (NOT THE RESULT).
 | |
| 				// REV reverses the original set before getting the range.
 | |
| 				name: "8. Get elements within lex range with offset and limit + reverse the results.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeKey8": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "a", Score: 1}, {Value: "b", Score: 1},
 | |
| 						{Value: "c", Score: 1}, {Value: "d", Score: 1},
 | |
| 						{Value: "e", Score: 1}, {Value: "f", Score: 1},
 | |
| 						{Value: "g", Score: 1}, {Value: "h", Score: 1},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZRANGE", "ZrangeKey8", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "2", "4", "REV"},
 | |
| 				expectedResponse: [][]string{{"f", "1"}, {"e", "1"}, {"d", "1"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "9. Return an empty slice when we use BYLEX while elements have different scores",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeKey9": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "a", Score: 1}, {Value: "b", Score: 5},
 | |
| 						{Value: "c", Score: 2}, {Value: "d", Score: 6},
 | |
| 						{Value: "e", Score: 3}, {Value: "f", Score: 7},
 | |
| 						{Value: "g", Score: 4}, {Value: "h", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZRANGE", "ZrangeKey9", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "2", "4"},
 | |
| 				expectedResponse: [][]string{},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "10. Throw error when limit does not provide both offset and limit",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZRANGE", "ZrangeKey10", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "2"},
 | |
| 				expectedResponse: [][]string{},
 | |
| 				expectedError:    errors.New("limit should contain offset and count as integers"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "11. Throw error when offset is not a valid integer",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZRANGE", "ZrangeKey11", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "offset", "4"},
 | |
| 				expectedResponse: [][]string{},
 | |
| 				expectedError:    errors.New("limit offset must be integer"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "12. Throw error when limit is not a valid integer",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZRANGE", "ZrangeKey12", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "4", "limit"},
 | |
| 				expectedResponse: [][]string{},
 | |
| 				expectedError:    errors.New("limit count must be integer"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "13. Throw error when offset is negative",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZRANGE", "ZrangeKey13", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "-4", "9"},
 | |
| 				expectedResponse: [][]string{},
 | |
| 				expectedError:    errors.New("limit offset must be >= 0"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "14. Throw error when the key does not hold a sorted set",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeKey14": "Default value",
 | |
| 				},
 | |
| 				command:          []string{"ZRANGE", "ZrangeKey14", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "2", "4"},
 | |
| 				expectedResponse: [][]string{},
 | |
| 				expectedError:    errors.New("value at ZrangeKey14 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "15. Command too short",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZRANGE", "ZrangeKey15", "1"},
 | |
| 				expectedResponse: [][]string{},
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "16. Command too long",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZRANGE", "ZrangeKey16", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "-4", "9", "REV", "WITHSCORES"},
 | |
| 				expectedResponse: [][]string{},
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if len(res.Array()) != len(test.expectedResponse) {
 | |
| 					t.Errorf("expected response array of length %d, got %d", len(test.expectedResponse), len(res.Array()))
 | |
| 				}
 | |
| 
 | |
| 				for _, item := range res.Array() {
 | |
| 					value := item.Array()[0].String()
 | |
| 					score := func() string {
 | |
| 						if len(item.Array()) == 2 {
 | |
| 							return item.Array()[1].String()
 | |
| 						}
 | |
| 						return ""
 | |
| 					}()
 | |
| 					if !slices.ContainsFunc(test.expectedResponse, func(expected []string) bool {
 | |
| 						return expected[0] == value
 | |
| 					}) {
 | |
| 						t.Errorf("unexpected member \"%s\" in response", value)
 | |
| 					}
 | |
| 					if score != "" {
 | |
| 						for _, expected := range test.expectedResponse {
 | |
| 							if expected[0] == value && expected[1] != score {
 | |
| 								t.Errorf("expected score for member \"%s\" to be %s, got %s", value, expected[1], score)
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZRANGESTORE", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValues     map[string]interface{}
 | |
| 			destination      string
 | |
| 			command          []string
 | |
| 			expectedValue    *sorted_set.SortedSet
 | |
| 			expectedResponse int
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Get elements withing score range without score.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeStoreKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination:      "ZrangeStoreDestinationKey1",
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreDestinationKey1", "ZrangeStoreKey1", "3", "7", "BYSCORE"},
 | |
| 				expectedResponse: 5,
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "three", Score: 3}, {Value: "four", Score: 4}, {Value: "five", Score: 5},
 | |
| 					{Value: "six", Score: 6}, {Value: "seven", Score: 7},
 | |
| 				}),
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "2. Get elements within score range with score.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeStoreKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination:      "ZrangeStoreDestinationKey2",
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreDestinationKey2", "ZrangeStoreKey2", "3", "7", "BYSCORE", "WITHSCORES"},
 | |
| 				expectedResponse: 5,
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "three", Score: 3}, {Value: "four", Score: 4}, {Value: "five", Score: 5},
 | |
| 					{Value: "six", Score: 6}, {Value: "seven", Score: 7},
 | |
| 				}),
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 3. Get elements within score range with offset and limit.
 | |
| 				// Offset and limit are in where we start and stop counting in the original sorted set (NOT THE RESULT).
 | |
| 				name: "3. Get elements within score range with offset and limit.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeStoreKey3": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination:      "ZrangeStoreDestinationKey3",
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreDestinationKey3", "ZrangeStoreKey3", "3", "7", "BYSCORE", "WITHSCORES", "LIMIT", "2", "4"},
 | |
| 				expectedResponse: 3,
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "three", Score: 3}, {Value: "four", Score: 4}, {Value: "five", Score: 5},
 | |
| 				}),
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 4. Get elements within score range with offset and limit + reverse the results.
 | |
| 				// Offset and limit are in where we start and stop counting in the original sorted set (NOT THE RESULT).
 | |
| 				// REV reverses the original set before getting the range.
 | |
| 				name: "4. Get elements within score range with offset and limit + reverse the results.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeStoreKey4": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination:      "ZrangeStoreDestinationKey4",
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreDestinationKey4", "ZrangeStoreKey4", "3", "7", "BYSCORE", "WITHSCORES", "LIMIT", "2", "4", "REV"},
 | |
| 				expectedResponse: 3,
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "six", Score: 6}, {Value: "five", Score: 5}, {Value: "four", Score: 4},
 | |
| 				}),
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "5. Get elements within lex range without score.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeStoreKey5": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "a", Score: 1}, {Value: "e", Score: 1},
 | |
| 						{Value: "b", Score: 1}, {Value: "f", Score: 1},
 | |
| 						{Value: "c", Score: 1}, {Value: "g", Score: 1},
 | |
| 						{Value: "d", Score: 1}, {Value: "h", Score: 1},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination:      "ZrangeStoreDestinationKey5",
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreDestinationKey5", "ZrangeStoreKey5", "c", "g", "BYLEX"},
 | |
| 				expectedResponse: 5,
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "c", Score: 1}, {Value: "d", Score: 1}, {Value: "e", Score: 1},
 | |
| 					{Value: "f", Score: 1}, {Value: "g", Score: 1},
 | |
| 				}),
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "6. Get elements within lex range with score.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeStoreKey6": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "a", Score: 1}, {Value: "e", Score: 1},
 | |
| 						{Value: "b", Score: 1}, {Value: "f", Score: 1},
 | |
| 						{Value: "c", Score: 1}, {Value: "g", Score: 1},
 | |
| 						{Value: "d", Score: 1}, {Value: "h", Score: 1},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination:      "ZrangeStoreDestinationKey6",
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreDestinationKey6", "ZrangeStoreKey6", "a", "f", "BYLEX", "WITHSCORES"},
 | |
| 				expectedResponse: 6,
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "a", Score: 1}, {Value: "b", Score: 1}, {Value: "c", Score: 1},
 | |
| 					{Value: "d", Score: 1}, {Value: "e", Score: 1}, {Value: "f", Score: 1},
 | |
| 				}),
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 7. Get elements within lex range with offset and limit.
 | |
| 				// Offset and limit are in where we start and stop counting in the original sorted set (NOT THE RESULT).
 | |
| 				name: "7. Get elements within lex range with offset and limit.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeStoreKey7": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "a", Score: 1}, {Value: "b", Score: 1},
 | |
| 						{Value: "c", Score: 1}, {Value: "d", Score: 1},
 | |
| 						{Value: "e", Score: 1}, {Value: "f", Score: 1},
 | |
| 						{Value: "g", Score: 1}, {Value: "h", Score: 1},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination:      "ZrangeStoreDestinationKey7",
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreDestinationKey7", "ZrangeStoreKey7", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "2", "4"},
 | |
| 				expectedResponse: 3,
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "c", Score: 1}, {Value: "d", Score: 1}, {Value: "e", Score: 1},
 | |
| 				}),
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 8. Get elements within lex range with offset and limit + reverse the results.
 | |
| 				// Offset and limit are in where we start and stop counting in the original sorted set (NOT THE RESULT).
 | |
| 				// REV reverses the original set before getting the range.
 | |
| 				name: "8. Get elements within lex range with offset and limit + reverse the results.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeStoreKey8": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "a", Score: 1}, {Value: "b", Score: 1},
 | |
| 						{Value: "c", Score: 1}, {Value: "d", Score: 1},
 | |
| 						{Value: "e", Score: 1}, {Value: "f", Score: 1},
 | |
| 						{Value: "g", Score: 1}, {Value: "h", Score: 1},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination:      "ZrangeStoreDestinationKey8",
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreDestinationKey8", "ZrangeStoreKey8", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "2", "4", "REV"},
 | |
| 				expectedResponse: 3,
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "f", Score: 1}, {Value: "e", Score: 1}, {Value: "d", Score: 1},
 | |
| 				}),
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "9. Return an empty slice when we use BYLEX while elements have different scores",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeStoreKey9": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "a", Score: 1}, {Value: "b", Score: 5},
 | |
| 						{Value: "c", Score: 2}, {Value: "d", Score: 6},
 | |
| 						{Value: "e", Score: 3}, {Value: "f", Score: 7},
 | |
| 						{Value: "g", Score: 4}, {Value: "h", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination:      "ZrangeStoreDestinationKey9",
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreDestinationKey9", "ZrangeStoreKey9", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "2", "4"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedValue:    nil,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "10. Throw error when limit does not provide both offset and limit",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreDestinationKey10", "ZrangeStoreKey10", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "2"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("limit should contain offset and count as integers"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "11. Throw error when offset is not a valid integer",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreDestinationKey11", "ZrangeStoreKey11", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "offset", "4"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("limit offset must be integer"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "12. Throw error when limit is not a valid integer",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreDestinationKey12", "ZrangeStoreKey12", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "4", "limit"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("limit count must be integer"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "13. Throw error when offset is negative",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreDestinationKey13", "ZrangeStoreKey13", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "-4", "9"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("limit offset must be >= 0"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "14. Throw error when the key does not hold a sorted set",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZrangeStoreKey14": "Default value",
 | |
| 				},
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreDestinationKey14", "ZrangeStoreKey14", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "2", "4"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("value at ZrangeStoreKey14 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "15. Command too short",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreKey15", "1"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "16 Command too long",
 | |
| 				presetValues:     nil,
 | |
| 				command:          []string{"ZRANGESTORE", "ZrangeStoreDestinationKey16", "ZrangeStoreKey16", "a", "h", "BYLEX", "WITHSCORES", "LIMIT", "-4", "9", "REV", "WITHSCORES"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if res.Integer() != test.expectedResponse {
 | |
| 					t.Errorf("expected response %d, got %d", test.expectedResponse, res.Integer())
 | |
| 				}
 | |
| 
 | |
| 				// Check if the resulting sorted set has the expected members/scores
 | |
| 				if test.expectedValue == nil {
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray([]resp.Value{
 | |
| 					resp.StringValue("ZRANGE"),
 | |
| 					resp.StringValue(test.destination),
 | |
| 					resp.StringValue("-inf"),
 | |
| 					resp.StringValue("+inf"),
 | |
| 					resp.StringValue("BYSCORE"),
 | |
| 					resp.StringValue("WITHSCORES"),
 | |
| 				}); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				res, _, err = client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if len(res.Array()) != test.expectedValue.Cardinality() {
 | |
| 					t.Errorf("expected resulting set %s to have cardinality %d, got %d",
 | |
| 						test.destination, test.expectedValue.Cardinality(), len(res.Array()))
 | |
| 				}
 | |
| 
 | |
| 				for _, member := range res.Array() {
 | |
| 					value := sorted_set.Value(member.Array()[0].String())
 | |
| 					score := sorted_set.Score(member.Array()[1].Float())
 | |
| 					if !test.expectedValue.Contains(value) {
 | |
| 						t.Errorf("unexpected value %s in resulting sorted set", value)
 | |
| 					}
 | |
| 					if test.expectedValue.Get(value).Score != score {
 | |
| 						t.Errorf("expected value %s to have score %v, got %v", value, test.expectedValue.Get(value).Score, score)
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZINTER", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValues     map[string]interface{}
 | |
| 			command          []string
 | |
| 			expectedResponse [][]string
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Get the intersection between 2 sorted sets.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 					}),
 | |
| 					"ZinterKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTER", "ZinterKey1", "ZinterKey2"},
 | |
| 				expectedResponse: [][]string{{"three"}, {"four"}, {"five"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 2. Get the intersection between 3 sorted sets with scores.
 | |
| 				// By default, the SUM aggregate will be used.
 | |
| 				name: "2. Get the intersection between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterKey3": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterKey4": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterKey5": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTER", "ZinterKey3", "ZinterKey4", "ZinterKey5", "WITHSCORES"},
 | |
| 				expectedResponse: [][]string{{"one", "3"}, {"eight", "24"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 3. Get the intersection between 3 sorted sets with scores.
 | |
| 				// Use MIN aggregate.
 | |
| 				name: "3. Get the intersection between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterKey6": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterKey7": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZinterKey8": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTER", "ZinterKey6", "ZinterKey7", "ZinterKey8", "WITHSCORES", "AGGREGATE", "MIN"},
 | |
| 				expectedResponse: [][]string{{"one", "1"}, {"eight", "8"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 4. Get the intersection between 3 sorted sets with scores.
 | |
| 				// Use MAX aggregate.
 | |
| 				name: "4. Get the intersection between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterKey9": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterKey10": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZinterKey11": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTER", "ZinterKey9", "ZinterKey10", "ZinterKey11", "WITHSCORES", "AGGREGATE", "MAX"},
 | |
| 				expectedResponse: [][]string{{"one", "1000"}, {"eight", "800"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 5. Get the intersection between 3 sorted sets with scores.
 | |
| 				// Use SUM aggregate with weights modifier.
 | |
| 				name: "5. Get the intersection between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterKey12": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterKey13": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZinterKey14": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTER", "ZinterKey12", "ZinterKey13", "ZinterKey14", "WITHSCORES", "AGGREGATE", "SUM", "WEIGHTS", "1", "5", "3"},
 | |
| 				expectedResponse: [][]string{{"one", "3105"}, {"eight", "2808"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 6. Get the intersection between 3 sorted sets with scores.
 | |
| 				// Use MAX aggregate with added weights.
 | |
| 				name: "6. Get the intersection between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterKey15": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterKey16": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZinterKey17": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTER", "ZinterKey15", "ZinterKey16", "ZinterKey17", "WITHSCORES", "AGGREGATE", "MAX", "WEIGHTS", "1", "5", "3"},
 | |
| 				expectedResponse: [][]string{{"one", "3000"}, {"eight", "2400"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 7. Get the intersection between 3 sorted sets with scores.
 | |
| 				// Use MIN aggregate with added weights.
 | |
| 				name: "7. Get the intersection between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterKey18": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterKey19": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZinterKey20": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTER", "ZinterKey18", "ZinterKey19", "ZinterKey20", "WITHSCORES", "AGGREGATE", "MIN", "WEIGHTS", "1", "5", "3"},
 | |
| 				expectedResponse: [][]string{{"one", "5"}, {"eight", "8"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "8. Throw an error if there are more weights than keys",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterKey21": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterKey22": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTER", "ZinterKey21", "ZinterKey22", "WEIGHTS", "1", "2", "3"},
 | |
| 				expectedResponse: nil,
 | |
| 				expectedError:    errors.New("number of weights should match number of keys"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "9. Throw an error if there are fewer weights than keys",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterKey23": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterKey24": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 					}),
 | |
| 					"ZinterKey25": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTER", "ZinterKey23", "ZinterKey24", "ZinterKey25", "WEIGHTS", "5", "4"},
 | |
| 				expectedResponse: nil,
 | |
| 				expectedError:    errors.New("number of weights should match number of keys"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "10. Throw an error if there are no keys provided",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterKey26": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 					"ZinterKey27": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 					"ZinterKey28": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTER", "WEIGHTS", "5", "4"},
 | |
| 				expectedResponse: nil,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "11. Throw an error if any of the provided keys are not sorted sets",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterKey29": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterKey30": "Default value",
 | |
| 					"ZinterKey31": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTER", "ZinterKey29", "ZinterKey30", "ZinterKey31"},
 | |
| 				expectedResponse: nil,
 | |
| 				expectedError:    errors.New("value at ZinterKey30 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "12. If any of the keys does not exist, return an empty array.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterKey32": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11},
 | |
| 					}),
 | |
| 					"ZinterKey33": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTER", "non-existent", "ZinterKey32", "ZinterKey33"},
 | |
| 				expectedResponse: [][]string{},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "13. Command too short",
 | |
| 				command:          []string{"ZINTER"},
 | |
| 				expectedResponse: [][]string{},
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if len(res.Array()) != len(test.expectedResponse) {
 | |
| 					t.Errorf("expected response array of length %d, got %d", len(test.expectedResponse), len(res.Array()))
 | |
| 				}
 | |
| 
 | |
| 				for _, item := range res.Array() {
 | |
| 					value := item.Array()[0].String()
 | |
| 					score := func() string {
 | |
| 						if len(item.Array()) == 2 {
 | |
| 							return item.Array()[1].String()
 | |
| 						}
 | |
| 						return ""
 | |
| 					}()
 | |
| 					if !slices.ContainsFunc(test.expectedResponse, func(expected []string) bool {
 | |
| 						return expected[0] == value
 | |
| 					}) {
 | |
| 						t.Errorf("unexpected member \"%s\" in response", value)
 | |
| 					}
 | |
| 					if score != "" {
 | |
| 						for _, expected := range test.expectedResponse {
 | |
| 							if expected[0] == value && expected[1] != score {
 | |
| 								t.Errorf("expected score for member \"%s\" to be %s, got %s", value, expected[1], score)
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZINTERSTORE", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValues     map[string]interface{}
 | |
| 			destination      string
 | |
| 			command          []string
 | |
| 			expectedValue    *sorted_set.SortedSet
 | |
| 			expectedResponse int
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Get the intersection between 2 sorted sets.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterStoreKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 					}),
 | |
| 					"ZinterStoreKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZinterStoreDestinationKey1",
 | |
| 				command:     []string{"ZINTERSTORE", "ZinterStoreDestinationKey1", "ZinterStoreKey1", "ZinterStoreKey2"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "three", Score: 6}, {Value: "four", Score: 8},
 | |
| 					{Value: "five", Score: 10},
 | |
| 				}),
 | |
| 				expectedResponse: 3,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 2. Get the intersection between 3 sorted sets with scores.
 | |
| 				// By default, the SUM aggregate will be used.
 | |
| 				name: "2. Get the intersection between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterStoreKey3": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterStoreKey4": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterStoreKey5": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZinterStoreDestinationKey2",
 | |
| 				command: []string{
 | |
| 					"ZINTERSTORE", "ZinterStoreDestinationKey2", "ZinterStoreKey3", "ZinterStoreKey4", "ZinterStoreKey5", "WITHSCORES",
 | |
| 				},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 3}, {Value: "eight", Score: 24},
 | |
| 				}),
 | |
| 				expectedResponse: 2,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 3. Get the intersection between 3 sorted sets with scores.
 | |
| 				// Use MIN aggregate.
 | |
| 				name: "3. Get the intersection between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterStoreKey6": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterStoreKey7": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZinterStoreKey8": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZinterStoreDestinationKey3",
 | |
| 				command:     []string{"ZINTERSTORE", "ZinterStoreDestinationKey3", "ZinterStoreKey6", "ZinterStoreKey7", "ZinterStoreKey8", "WITHSCORES", "AGGREGATE", "MIN"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1}, {Value: "eight", Score: 8},
 | |
| 				}),
 | |
| 				expectedResponse: 2,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 4. Get the intersection between 3 sorted sets with scores.
 | |
| 				// Use MAX aggregate.
 | |
| 				name: "4. Get the intersection between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterStoreKey9": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterStoreKey10": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZinterStoreKey11": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZinterStoreDestinationKey4",
 | |
| 				command:     []string{"ZINTERSTORE", "ZinterStoreDestinationKey4", "ZinterStoreKey9", "ZinterStoreKey10", "ZinterStoreKey11", "WITHSCORES", "AGGREGATE", "MAX"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 				}),
 | |
| 				expectedResponse: 2,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 5. Get the intersection between 3 sorted sets with scores.
 | |
| 				// Use SUM aggregate with weights modifier.
 | |
| 				name: "5. Get the intersection between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterStoreKey12": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterStoreKey13": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZinterStoreKey14": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZinterStoreDestinationKey5",
 | |
| 				command:     []string{"ZINTERSTORE", "ZinterStoreDestinationKey5", "ZinterStoreKey12", "ZinterStoreKey13", "ZinterStoreKey14", "WITHSCORES", "AGGREGATE", "SUM", "WEIGHTS", "1", "5", "3"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 3105}, {Value: "eight", Score: 2808},
 | |
| 				}),
 | |
| 				expectedResponse: 2,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 6. Get the intersection between 3 sorted sets with scores.
 | |
| 				// Use MAX aggregate with added weights.
 | |
| 				name: "6. Get the intersection between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterStoreKey15": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterStoreKey16": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZinterStoreKey17": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZinterStoreDestinationKey6",
 | |
| 				command:     []string{"ZINTERSTORE", "ZinterStoreDestinationKey6", "ZinterStoreKey15", "ZinterStoreKey16", "ZinterStoreKey17", "WITHSCORES", "AGGREGATE", "MAX", "WEIGHTS", "1", "5", "3"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 3000}, {Value: "eight", Score: 2400},
 | |
| 				}),
 | |
| 				expectedResponse: 2,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 7. Get the intersection between 3 sorted sets with scores.
 | |
| 				// Use MIN aggregate with added weights.
 | |
| 				name: "7. Get the intersection between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterStoreKey18": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterStoreKey19": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZinterStoreKey20": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZinterStoreDestinationKey7",
 | |
| 				command:     []string{"ZINTERSTORE", "ZinterStoreDestinationKey7", "ZinterStoreKey18", "ZinterStoreKey19", "ZinterStoreKey20", "WITHSCORES", "AGGREGATE", "MIN", "WEIGHTS", "1", "5", "3"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 5}, {Value: "eight", Score: 8},
 | |
| 				}),
 | |
| 				expectedResponse: 2,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "8. Throw an error if there are more weights than keys",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterStoreKey21": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterStoreKey22": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTERSTORE", "ZinterStoreDestinationKey8", "ZinterStoreKey21", "ZinterStoreKey22", "WEIGHTS", "1", "2", "3"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("number of weights should match number of keys"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "9. Throw an error if there are fewer weights than keys",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterStoreKey23": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterStoreKey24": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 					}),
 | |
| 					"ZinterStoreKey25": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTERSTORE", "ZinterStoreDestinationKey9", "ZinterStoreKey23", "ZinterStoreKey24", "ZinterStoreKey25", "WEIGHTS", "5", "4"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("number of weights should match number of keys"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "10. Throw an error if there are no keys provided",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterStoreKey26": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 					"ZinterStoreKey27": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 					"ZinterStoreKey28": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTERSTORE", "WEIGHTS", "5", "4"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "11. Throw an error if any of the provided keys are not sorted sets",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterStoreKey29": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZinterStoreKey30": "Default value",
 | |
| 					"ZinterStoreKey31": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTERSTORE", "ZinterStoreKey29", "ZinterStoreKey30", "ZinterStoreKey31"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("value at ZinterStoreKey30 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "12. If any of the keys does not exist, return an empty array.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZinterStoreKey32": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11},
 | |
| 					}),
 | |
| 					"ZinterStoreKey33": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZINTERSTORE", "ZinterStoreDestinationKey12", "non-existent", "ZinterStoreKey32", "ZinterStoreKey33"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "13. Command too short",
 | |
| 				command:          []string{"ZINTERSTORE"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if res.Integer() != test.expectedResponse {
 | |
| 					t.Errorf("expected response %d, got %d", test.expectedResponse, res.Integer())
 | |
| 				}
 | |
| 
 | |
| 				// Check if the resulting sorted set has the expected members/scores
 | |
| 				if test.expectedValue == nil {
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray([]resp.Value{
 | |
| 					resp.StringValue("ZRANGE"),
 | |
| 					resp.StringValue(test.destination),
 | |
| 					resp.StringValue("-inf"),
 | |
| 					resp.StringValue("+inf"),
 | |
| 					resp.StringValue("BYSCORE"),
 | |
| 					resp.StringValue("WITHSCORES"),
 | |
| 				}); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				res, _, err = client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if len(res.Array()) != test.expectedValue.Cardinality() {
 | |
| 					t.Errorf("expected resulting set %s to have cardinality %d, got %d",
 | |
| 						test.destination, test.expectedValue.Cardinality(), len(res.Array()))
 | |
| 				}
 | |
| 
 | |
| 				for _, member := range res.Array() {
 | |
| 					value := sorted_set.Value(member.Array()[0].String())
 | |
| 					score := sorted_set.Score(member.Array()[1].Float())
 | |
| 					if !test.expectedValue.Contains(value) {
 | |
| 						t.Errorf("unexpected value %s in resulting sorted set", value)
 | |
| 					}
 | |
| 					if test.expectedValue.Get(value).Score != score {
 | |
| 						t.Errorf("expected value %s to have score %v, got %v", value, test.expectedValue.Get(value).Score, score)
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZUNION", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			presetValues     map[string]interface{}
 | |
| 			command          []string
 | |
| 			expectedResponse [][]string
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name: "1. Get the union between 2 sorted sets.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 					}),
 | |
| 					"ZunionKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				command:          []string{"ZUNION", "ZunionKey1", "ZunionKey2"},
 | |
| 				expectedResponse: [][]string{{"one"}, {"two"}, {"three"}, {"four"}, {"five"}, {"six"}, {"seven"}, {"eight"}},
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 2. Get the union between 3 sorted sets with scores.
 | |
| 				// By default, the SUM aggregate will be used.
 | |
| 				name: "2. Get the union between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionKey3": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionKey4": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionKey5": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12}, {Value: "thirty-six", Score: 36},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZUNION", "ZunionKey3", "ZunionKey4", "ZunionKey5", "WITHSCORES"},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"one", "3"}, {"two", "4"}, {"three", "3"}, {"four", "4"}, {"five", "5"}, {"six", "6"},
 | |
| 					{"seven", "7"}, {"eight", "24"}, {"nine", "9"}, {"ten", "10"}, {"eleven", "11"},
 | |
| 					{"twelve", "24"}, {"thirty-six", "72"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 3. Get the union between 3 sorted sets with scores.
 | |
| 				// Use MIN aggregate.
 | |
| 				name: "3. Get the union between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionKey6": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionKey7": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZunionKey8": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12}, {Value: "thirty-six", Score: 72},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZUNION", "ZunionKey6", "ZunionKey7", "ZunionKey8", "WITHSCORES", "AGGREGATE", "MIN"},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"one", "1"}, {"two", "2"}, {"three", "3"}, {"four", "4"}, {"five", "5"}, {"six", "6"},
 | |
| 					{"seven", "7"}, {"eight", "8"}, {"nine", "9"}, {"ten", "10"}, {"eleven", "11"},
 | |
| 					{"twelve", "12"}, {"thirty-six", "36"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 4. Get the union between 3 sorted sets with scores.
 | |
| 				// Use MAX aggregate.
 | |
| 				name: "4. Get the union between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionKey9": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionKey10": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZunionKey11": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12}, {Value: "thirty-six", Score: 72},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZUNION", "ZunionKey9", "ZunionKey10", "ZunionKey11", "WITHSCORES", "AGGREGATE", "MAX"},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"one", "1000"}, {"two", "2"}, {"three", "3"}, {"four", "4"}, {"five", "5"}, {"six", "6"},
 | |
| 					{"seven", "7"}, {"eight", "800"}, {"nine", "9"}, {"ten", "10"}, {"eleven", "11"},
 | |
| 					{"twelve", "12"}, {"thirty-six", "72"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 5. Get the union between 3 sorted sets with scores.
 | |
| 				// Use SUM aggregate with weights modifier.
 | |
| 				name: "5. Get the union between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionKey12": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionKey13": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZunionKey14": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZUNION", "ZunionKey12", "ZunionKey13", "ZunionKey14", "WITHSCORES", "AGGREGATE", "SUM", "WEIGHTS", "1", "2", "3"},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"one", "3102"}, {"two", "6"}, {"three", "3"}, {"four", "4"}, {"five", "5"}, {"six", "6"},
 | |
| 					{"seven", "7"}, {"eight", "2568"}, {"nine", "27"}, {"ten", "30"}, {"eleven", "22"},
 | |
| 					{"twelve", "60"}, {"thirty-six", "72"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 6. Get the union between 3 sorted sets with scores.
 | |
| 				// Use MAX aggregate with added weights.
 | |
| 				name: "6. Get the union between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionKey15": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionKey16": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZunionKey17": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZUNION", "ZunionKey15", "ZunionKey16", "ZunionKey17", "WITHSCORES", "AGGREGATE", "MAX", "WEIGHTS", "1", "2", "3"},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"one", "3000"}, {"two", "4"}, {"three", "3"}, {"four", "4"}, {"five", "5"}, {"six", "6"},
 | |
| 					{"seven", "7"}, {"eight", "2400"}, {"nine", "27"}, {"ten", "30"}, {"eleven", "22"},
 | |
| 					{"twelve", "36"}, {"thirty-six", "72"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 7. Get the union between 3 sorted sets with scores.
 | |
| 				// Use MIN aggregate with added weights.
 | |
| 				name: "7. Get the union between 3 sorted sets with scores.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionKey18": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionKey19": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZunionKey20": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZUNION", "ZunionKey18", "ZunionKey19", "ZunionKey20", "WITHSCORES", "AGGREGATE", "MIN", "WEIGHTS", "1", "2", "3"},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"one", "2"}, {"two", "2"}, {"three", "3"}, {"four", "4"}, {"five", "5"}, {"six", "6"}, {"seven", "7"},
 | |
| 					{"eight", "8"}, {"nine", "27"}, {"ten", "30"}, {"eleven", "22"}, {"twelve", "24"}, {"thirty-six", "72"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name: "8. Throw an error if there are more weights than keys",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionKey21": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionKey22": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				command:          []string{"ZUNION", "ZunionKey21", "ZunionKey22", "WEIGHTS", "1", "2", "3"},
 | |
| 				expectedResponse: nil,
 | |
| 				expectedError:    errors.New("number of weights should match number of keys"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "9. Throw an error if there are fewer weights than keys",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionKey23": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionKey24": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 					}),
 | |
| 					"ZunionKey25": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				command:          []string{"ZUNION", "ZunionKey23", "ZunionKey24", "ZunionKey25", "WEIGHTS", "5", "4"},
 | |
| 				expectedResponse: nil,
 | |
| 				expectedError:    errors.New("number of weights should match number of keys"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "10. Throw an error if there are no keys provided",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionKey26": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 					"ZunionKey27": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 					"ZunionKey28": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				command:          []string{"ZUNION", "WEIGHTS", "5", "4"},
 | |
| 				expectedResponse: nil,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "11. Throw an error if any of the provided keys are not sorted sets",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionKey29": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionKey30": "Default value",
 | |
| 					"ZunionKey31": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				command:          []string{"ZUNION", "ZunionKey29", "ZunionKey30", "ZunionKey31"},
 | |
| 				expectedResponse: nil,
 | |
| 				expectedError:    errors.New("value at ZunionKey30 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name: "12. If any of the keys does not exist, skip it.",
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionKey32": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11},
 | |
| 					}),
 | |
| 					"ZunionKey33": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				command: []string{"ZUNION", "non-existent", "ZunionKey32", "ZunionKey33"},
 | |
| 				expectedResponse: [][]string{
 | |
| 					{"one"}, {"two"}, {"thirty-six"}, {"twelve"}, {"eleven"},
 | |
| 					{"seven"}, {"eight"}, {"nine"}, {"ten"},
 | |
| 				},
 | |
| 				expectedError: nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:          "13. Command too short",
 | |
| 				command:       []string{"ZUNION"},
 | |
| 				expectedError: errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if len(res.Array()) != len(test.expectedResponse) {
 | |
| 					t.Errorf("expected response array of length %d, got %d", len(test.expectedResponse), len(res.Array()))
 | |
| 				}
 | |
| 
 | |
| 				for _, item := range res.Array() {
 | |
| 					value := item.Array()[0].String()
 | |
| 					score := func() string {
 | |
| 						if len(item.Array()) == 2 {
 | |
| 							return item.Array()[1].String()
 | |
| 						}
 | |
| 						return ""
 | |
| 					}()
 | |
| 					if !slices.ContainsFunc(test.expectedResponse, func(expected []string) bool {
 | |
| 						return expected[0] == value
 | |
| 					}) {
 | |
| 						t.Errorf("unexpected member \"%s\" in response", value)
 | |
| 					}
 | |
| 					if score != "" {
 | |
| 						for _, expected := range test.expectedResponse {
 | |
| 							if expected[0] == value && expected[1] != score {
 | |
| 								t.Errorf("expected score for member \"%s\" to be %s, got %s", value, expected[1], score)
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("Test_HandleZUNIONSTORE", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		conn, err := internal.GetConnection("localhost", port)
 | |
| 		if err != nil {
 | |
| 			t.Error()
 | |
| 			return
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			_ = conn.Close()
 | |
| 		}()
 | |
| 		client := resp.NewConn(conn)
 | |
| 
 | |
| 		tests := []struct {
 | |
| 			name             string
 | |
| 			preset           bool
 | |
| 			presetValues     map[string]interface{}
 | |
| 			destination      string
 | |
| 			command          []string
 | |
| 			expectedValue    *sorted_set.SortedSet
 | |
| 			expectedResponse int
 | |
| 			expectedError    error
 | |
| 		}{
 | |
| 			{
 | |
| 				name:   "1. Get the union between 2 sorted sets.",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionStoreKey1": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5},
 | |
| 					}),
 | |
| 					"ZunionStoreKey2": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZunionStoreDestinationKey1",
 | |
| 				command:     []string{"ZUNIONSTORE", "ZunionStoreDestinationKey1", "ZunionStoreKey1", "ZunionStoreKey2"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 					{Value: "three", Score: 6}, {Value: "four", Score: 8},
 | |
| 					{Value: "five", Score: 10}, {Value: "six", Score: 6},
 | |
| 					{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 				}),
 | |
| 				expectedResponse: 8,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 2. Get the union between 3 sorted sets with scores.
 | |
| 				// By default, the SUM aggregate will be used.
 | |
| 				name:   "2. Get the union between 3 sorted sets with scores.",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionStoreKey3": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionStoreKey4": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionStoreKey5": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12}, {Value: "thirty-six", Score: 36},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZunionStoreDestinationKey2",
 | |
| 				command:     []string{"ZUNIONSTORE", "ZunionStoreDestinationKey2", "ZunionStoreKey3", "ZunionStoreKey4", "ZunionStoreKey5", "WITHSCORES"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 3}, {Value: "two", Score: 4}, {Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5}, {Value: "six", Score: 6}, {Value: "seven", Score: 7}, {Value: "eight", Score: 24},
 | |
| 					{Value: "nine", Score: 9}, {Value: "ten", Score: 10}, {Value: "eleven", Score: 11},
 | |
| 					{Value: "twelve", Score: 24}, {Value: "thirty-six", Score: 72},
 | |
| 				}),
 | |
| 				expectedResponse: 13,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 3. Get the union between 3 sorted sets with scores.
 | |
| 				// Use MIN aggregate.
 | |
| 				name:   "3. Get the union between 3 sorted sets with scores.",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionStoreKey6": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionStoreKey7": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZunionStoreKey8": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12}, {Value: "thirty-six", Score: 72},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZunionStoreDestinationKey3",
 | |
| 				command:     []string{"ZUNIONSTORE", "ZunionStoreDestinationKey3", "ZunionStoreKey6", "ZunionStoreKey7", "ZunionStoreKey8", "WITHSCORES", "AGGREGATE", "MIN"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1}, {Value: "two", Score: 2}, {Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5}, {Value: "six", Score: 6}, {Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					{Value: "nine", Score: 9}, {Value: "ten", Score: 10}, {Value: "eleven", Score: 11},
 | |
| 					{Value: "twelve", Score: 12}, {Value: "thirty-six", Score: 36},
 | |
| 				}),
 | |
| 				expectedResponse: 13,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 4. Get the union between 3 sorted sets with scores.
 | |
| 				// Use MAX aggregate.
 | |
| 				name:   "4. Get the union between 3 sorted sets with scores.",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionStoreKey9": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionStoreKey10": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZunionStoreKey11": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12}, {Value: "thirty-six", Score: 72},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZunionStoreDestinationKey4",
 | |
| 				command: []string{
 | |
| 					"ZUNIONSTORE", "ZunionStoreDestinationKey4", "ZunionStoreKey9", "ZunionStoreKey10", "ZunionStoreKey11", "WITHSCORES", "AGGREGATE", "MAX",
 | |
| 				},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1000}, {Value: "two", Score: 2}, {Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5}, {Value: "six", Score: 6}, {Value: "seven", Score: 7}, {Value: "eight", Score: 800},
 | |
| 					{Value: "nine", Score: 9}, {Value: "ten", Score: 10}, {Value: "eleven", Score: 11},
 | |
| 					{Value: "twelve", Score: 12}, {Value: "thirty-six", Score: 72},
 | |
| 				}),
 | |
| 				expectedResponse: 13,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 5. Get the union between 3 sorted sets with scores.
 | |
| 				// Use SUM aggregate with weights modifier.
 | |
| 				name:   "5. Get the union between 3 sorted sets with scores.",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionStoreKey12": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionStoreKey13": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZunionStoreKey14": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZunionStoreDestinationKey5",
 | |
| 				command: []string{
 | |
| 					"ZUNIONSTORE", "ZunionStoreDestinationKey5", "ZunionStoreKey12", "ZunionStoreKey13", "ZunionStoreKey14",
 | |
| 					"WITHSCORES", "AGGREGATE", "SUM", "WEIGHTS", "1", "2", "3",
 | |
| 				},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 3102}, {Value: "two", Score: 6}, {Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5}, {Value: "six", Score: 6}, {Value: "seven", Score: 7}, {Value: "eight", Score: 2568},
 | |
| 					{Value: "nine", Score: 27}, {Value: "ten", Score: 30}, {Value: "eleven", Score: 22},
 | |
| 					{Value: "twelve", Score: 60}, {Value: "thirty-six", Score: 72},
 | |
| 				}),
 | |
| 				expectedResponse: 13,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 6. Get the union between 3 sorted sets with scores.
 | |
| 				// Use MAX aggregate with added weights.
 | |
| 				name:   "6. Get the union between 3 sorted sets with scores.",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionStoreKey15": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionStoreKey16": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZunionStoreKey17": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZunionStoreDestinationKey6",
 | |
| 				command: []string{
 | |
| 					"ZUNIONSTORE", "ZunionStoreDestinationKey6", "ZunionStoreKey15", "ZunionStoreKey16", "ZunionStoreKey17",
 | |
| 					"WITHSCORES", "AGGREGATE", "MAX", "WEIGHTS", "1", "2", "3"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 3000}, {Value: "two", Score: 4}, {Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5}, {Value: "six", Score: 6}, {Value: "seven", Score: 7}, {Value: "eight", Score: 2400},
 | |
| 					{Value: "nine", Score: 27}, {Value: "ten", Score: 30}, {Value: "eleven", Score: 22},
 | |
| 					{Value: "twelve", Score: 36}, {Value: "thirty-six", Score: 72},
 | |
| 				}),
 | |
| 				expectedResponse: 13,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				// 7. Get the union between 3 sorted sets with scores.
 | |
| 				// Use MIN aggregate with added weights.
 | |
| 				name:   "7. Get the union between 3 sorted sets with scores.",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionStoreKey18": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 100}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionStoreKey19": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11}, {Value: "eight", Score: 80},
 | |
| 					}),
 | |
| 					"ZunionStoreKey20": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZunionStoreDestinationKey7",
 | |
| 				command: []string{
 | |
| 					"ZUNIONSTORE", "ZunionStoreDestinationKey7", "ZunionStoreKey18", "ZunionStoreKey19", "ZunionStoreKey20",
 | |
| 					"WITHSCORES", "AGGREGATE", "MIN", "WEIGHTS", "1", "2", "3",
 | |
| 				},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 2}, {Value: "two", Score: 2}, {Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 					{Value: "five", Score: 5}, {Value: "six", Score: 6}, {Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					{Value: "nine", Score: 27}, {Value: "ten", Score: 30}, {Value: "eleven", Score: 22},
 | |
| 					{Value: "twelve", Score: 24}, {Value: "thirty-six", Score: 72},
 | |
| 				}),
 | |
| 				expectedResponse: 13,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:   "8. Throw an error if there are more weights than keys",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionStoreKey21": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionStoreKey22": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				destination:      "ZunionStoreDestinationKey8",
 | |
| 				command:          []string{"ZUNIONSTORE", "ZunionStoreDestinationKey8", "ZunionStoreKey21", "ZunionStoreKey22", "WEIGHTS", "1", "2", "3"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("number of weights should match number of keys"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:   "9. Throw an error if there are fewer weights than keys",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionStoreKey23": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionStoreKey24": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 					}),
 | |
| 					"ZunionStoreKey25": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				destination:      "ZunionStoreDestinationKey9",
 | |
| 				command:          []string{"ZUNIONSTORE", "ZunionStoreDestinationKey9", "ZunionStoreKey23", "ZunionStoreKey24", "ZunionStoreKey25", "WEIGHTS", "5", "4"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("number of weights should match number of keys"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:   "10. Throw an error if there are no keys provided",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionStoreKey26": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 					"ZunionStoreKey27": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 					"ZunionStoreKey28": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				command:          []string{"ZUNIONSTORE", "WEIGHTS", "5", "4"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 			{
 | |
| 				name:   "11. Throw an error if any of the provided keys are not sorted sets",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionStoreKey29": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "three", Score: 3}, {Value: "four", Score: 4},
 | |
| 						{Value: "five", Score: 5}, {Value: "six", Score: 6},
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					}),
 | |
| 					"ZunionStoreKey30": "Default value",
 | |
| 					"ZunionStoreKey31": sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: "one", Score: 1}}),
 | |
| 				},
 | |
| 				destination:      "ZunionStoreDestinationKey11",
 | |
| 				command:          []string{"ZUNIONSTORE", "ZunionStoreDestinationKey11", "ZunionStoreKey29", "ZunionStoreKey30", "ZunionStoreKey31"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New("value at ZunionStoreKey30 is not a sorted set"),
 | |
| 			},
 | |
| 			{
 | |
| 				name:   "12. If any of the keys does not exist, skip it.",
 | |
| 				preset: true,
 | |
| 				presetValues: map[string]interface{}{
 | |
| 					"ZunionStoreKey32": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "one", Score: 1}, {Value: "two", Score: 2},
 | |
| 						{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
 | |
| 						{Value: "eleven", Score: 11},
 | |
| 					}),
 | |
| 					"ZunionStoreKey33": sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 						{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 						{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
 | |
| 						{Value: "twelve", Score: 12},
 | |
| 					}),
 | |
| 				},
 | |
| 				destination: "ZunionStoreDestinationKey12",
 | |
| 				command:     []string{"ZUNIONSTORE", "ZunionStoreDestinationKey12", "non-existent", "ZunionStoreKey32", "ZunionStoreKey33"},
 | |
| 				expectedValue: sorted_set.NewSortedSet([]sorted_set.MemberParam{
 | |
| 					{Value: "one", Score: 1}, {Value: "two", Score: 2}, {Value: "seven", Score: 7}, {Value: "eight", Score: 8},
 | |
| 					{Value: "nine", Score: 9}, {Value: "ten", Score: 10}, {Value: "eleven", Score: 11}, {Value: "twelve", Score: 24},
 | |
| 					{Value: "thirty-six", Score: 36},
 | |
| 				}),
 | |
| 				expectedResponse: 9,
 | |
| 				expectedError:    nil,
 | |
| 			},
 | |
| 			{
 | |
| 				name:             "13. Command too short",
 | |
| 				preset:           false,
 | |
| 				command:          []string{"ZUNIONSTORE"},
 | |
| 				expectedResponse: 0,
 | |
| 				expectedError:    errors.New(constants.WrongArgsResponse),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		for _, test := range tests {
 | |
| 			t.Run(test.name, func(t *testing.T) {
 | |
| 				if test.presetValues != nil {
 | |
| 					var command []resp.Value
 | |
| 					var expected string
 | |
| 					for key, value := range test.presetValues {
 | |
| 						switch value.(type) {
 | |
| 						case string:
 | |
| 							command = []resp.Value{
 | |
| 								resp.StringValue("SET"),
 | |
| 								resp.StringValue(key),
 | |
| 								resp.StringValue(value.(string)),
 | |
| 							}
 | |
| 							expected = "ok"
 | |
| 						case *sorted_set.SortedSet:
 | |
| 							command = []resp.Value{resp.StringValue("ZADD"), resp.StringValue(key)}
 | |
| 							for _, member := range value.(*sorted_set.SortedSet).GetAll() {
 | |
| 								command = append(command, []resp.Value{
 | |
| 									resp.StringValue(strconv.FormatFloat(float64(member.Score), 'f', -1, 64)),
 | |
| 									resp.StringValue(string(member.Value)),
 | |
| 								}...)
 | |
| 							}
 | |
| 							expected = strconv.Itoa(value.(*sorted_set.SortedSet).Cardinality())
 | |
| 						}
 | |
| 
 | |
| 						if err = client.WriteArray(command); err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 						res, _, err := client.ReadValue()
 | |
| 						if err != nil {
 | |
| 							t.Error(err)
 | |
| 						}
 | |
| 
 | |
| 						if !strings.EqualFold(res.String(), expected) {
 | |
| 							t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				command := make([]resp.Value, len(test.command))
 | |
| 				for i, c := range test.command {
 | |
| 					command[i] = resp.StringValue(c)
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray(command); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 				res, _, err := client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if test.expectedError != nil {
 | |
| 					if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
 | |
| 						t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
 | |
| 					}
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if res.Integer() != test.expectedResponse {
 | |
| 					t.Errorf("expected response %d, got %d", test.expectedResponse, res.Integer())
 | |
| 				}
 | |
| 
 | |
| 				// Check if the resulting sorted set has the expected members/scores
 | |
| 				if test.expectedValue == nil {
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				if err = client.WriteArray([]resp.Value{
 | |
| 					resp.StringValue("ZRANGE"),
 | |
| 					resp.StringValue(test.destination),
 | |
| 					resp.StringValue("-inf"),
 | |
| 					resp.StringValue("+inf"),
 | |
| 					resp.StringValue("BYSCORE"),
 | |
| 					resp.StringValue("WITHSCORES"),
 | |
| 				}); err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				res, _, err = client.ReadValue()
 | |
| 				if err != nil {
 | |
| 					t.Error(err)
 | |
| 				}
 | |
| 
 | |
| 				if len(res.Array()) != test.expectedValue.Cardinality() {
 | |
| 					t.Errorf("expected resulting set %s to have cardinality %d, got %d",
 | |
| 						test.destination, test.expectedValue.Cardinality(), len(res.Array()))
 | |
| 				}
 | |
| 
 | |
| 				for _, member := range res.Array() {
 | |
| 					value := sorted_set.Value(member.Array()[0].String())
 | |
| 					score := sorted_set.Score(member.Array()[1].Float())
 | |
| 					if !test.expectedValue.Contains(value) {
 | |
| 						t.Errorf("unexpected value %s in resulting sorted set", value)
 | |
| 					}
 | |
| 					if test.expectedValue.Get(value).Score != score {
 | |
| 						t.Errorf("expected value %s to have score %v, got %v", value, test.expectedValue.Get(value).Score, score)
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	})
 | |
| }
 | 
