Files
SugarDB/echovault/api_sorted_set_test.go

3302 lines
105 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 echovault
import (
"context"
"github.com/echovault/echovault/internal"
ss "github.com/echovault/echovault/internal/modules/sorted_set"
"math"
"reflect"
"strconv"
"testing"
)
func TestEchoVault_ZADD(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValue *ss.SortedSet
key string
entries map[string]float64
options ZAddOptions
want int
wantErr bool
}{
{
name: "Create new sorted set and return the cardinality of the new sorted set",
preset: false,
presetValue: nil,
key: "key1",
entries: map[string]float64{
"member1": 5.5,
"member2": 67.77,
"member3": 10,
"member4": math.Inf(-1),
"member5": math.Inf(1),
},
options: ZAddOptions{},
want: 5,
wantErr: false,
},
{
name: "Only add the elements that do not currently exist in the sorted set when NX flag is provided",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "member1", Score: ss.Score(5.5)},
{Value: "member2", Score: ss.Score(67.77)},
{Value: "member3", Score: ss.Score(10)},
}),
key: "key2",
entries: map[string]float64{
"member1": 5.5,
"member4": 67.77,
"member5": 10,
},
options: ZAddOptions{NX: true},
want: 2,
wantErr: false,
},
{
name: "Do not add any elements when providing existing members with NX flag",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "member1", Score: ss.Score(5.5)},
{Value: "member2", Score: ss.Score(67.77)},
{Value: "member3", Score: ss.Score(10)},
}),
key: "key3",
entries: map[string]float64{
"member1": 5.5,
"member2": 67.77,
"member3": 10,
},
options: ZAddOptions{NX: true},
want: 0,
wantErr: false,
},
{
name: "Successfully add elements to an existing set when XX flag is provided with existing elements",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "member1", Score: ss.Score(5.5)},
{Value: "member2", Score: ss.Score(67.77)},
{Value: "member3", Score: ss.Score(10)},
}),
key: "key4",
entries: map[string]float64{
"member1": 55,
"member2": 1005,
"member3": 15,
"member4": 99.75,
},
options: ZAddOptions{XX: true, CH: true},
want: 3,
wantErr: false,
},
{
name: "Fail to add element when providing XX flag with elements that do not exist in the sorted set",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "member1", Score: ss.Score(5.5)},
{Value: "member2", Score: ss.Score(67.77)},
{Value: "member3", Score: ss.Score(10)},
}),
key: "key5",
entries: map[string]float64{
"member4": 5.5,
"member5": 100.5,
"member6": 15,
},
options: ZAddOptions{XX: true},
want: 0,
wantErr: false,
},
{
// Only update the elements where provided score is greater than current score if GT flag
// Return only the new elements added by default
name: "Only update the elements where provided score is greater than current score if GT flag",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "member1", Score: ss.Score(5.5)},
{Value: "member2", Score: ss.Score(67.77)},
{Value: "member3", Score: ss.Score(10)},
}),
key: "key6",
entries: map[string]float64{
"member1": 7.5,
"member4": 100.5,
"member5": 15,
},
options: ZAddOptions{XX: true, CH: true, GT: true},
want: 1,
wantErr: false,
},
{
// 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: "Only update the elements where provided score is less than current score if LT flag is provided",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "member1", Score: ss.Score(5.5)},
{Value: "member2", Score: ss.Score(67.77)},
{Value: "member3", Score: ss.Score(10)},
}),
key: "key7",
entries: map[string]float64{
"member1": 3.5,
"member4": 100.5,
"member5": 15,
},
options: ZAddOptions{XX: true, LT: true},
want: 0,
wantErr: false,
},
{
name: "Return all the elements that were updated AND added when CH flag is provided",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "member1", Score: ss.Score(5.5)},
{Value: "member2", Score: ss.Score(67.77)},
{Value: "member3", Score: ss.Score(10)},
}),
key: "key8",
entries: map[string]float64{
"member1": 3.5,
"member4": 100.5,
"member5": 15,
},
options: ZAddOptions{XX: true, LT: true, CH: true},
want: 1,
wantErr: false,
},
{
name: "Increment the member by score",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "member1", Score: ss.Score(5.5)},
{Value: "member2", Score: ss.Score(67.77)},
{Value: "member3", Score: ss.Score(10)},
}),
key: "key9",
entries: map[string]float64{
"member3": 5.5,
},
options: ZAddOptions{INCR: true},
want: 0,
wantErr: false,
},
{
name: "Fail when GT/LT flag is provided alongside NX flag",
preset: false,
presetValue: nil,
key: "key10",
entries: map[string]float64{
"member1": 3.5,
"member5": 15,
},
options: ZAddOptions{NX: true, LT: true, CH: true},
want: 0,
wantErr: true,
},
{
name: "Throw error when INCR flag is passed with more than one score/member pair",
preset: false,
presetValue: nil,
key: "key11",
entries: map[string]float64{
"member1": 10.5,
"member2": 12.5,
},
options: ZAddOptions{INCR: true},
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
err := presetValue(server, context.Background(), tt.key, tt.presetValue)
if err != nil {
t.Error(err)
return
}
}
got, err := server.ZAdd(tt.key, tt.entries, tt.options)
if (err != nil) != tt.wantErr {
t.Errorf("ZADD() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ZADD() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZCARD(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValue interface{}
key string
want int
wantErr bool
}{
{
name: "Get cardinality of valid sorted set",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "member1", Score: ss.Score(5.5)},
{Value: "member2", Score: ss.Score(67.77)},
{Value: "member3", Score: ss.Score(10)},
}),
key: "key1",
want: 3,
wantErr: false,
},
{
name: "Return 0 when trying to get cardinality from non-existent key",
preset: false,
presetValue: nil,
key: "key2",
want: 0,
wantErr: false,
},
{
name: "Return error when not a sorted set",
preset: true,
presetValue: "Default value",
key: "key3",
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
err := presetValue(server, context.Background(), tt.key, tt.presetValue)
if err != nil {
t.Error(err)
return
}
}
got, err := server.ZCard(tt.key)
if (err != nil) != tt.wantErr {
t.Errorf("ZCARD() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ZCARD() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZCOUNT(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValue interface{}
key string
min float64
max float64
want int
wantErr bool
}{
{
name: "Get entire count using infinity boundaries",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "member1", Score: ss.Score(5.5)},
{Value: "member2", Score: ss.Score(67.77)},
{Value: "member3", Score: ss.Score(10)},
{Value: "member4", Score: ss.Score(1083.13)},
{Value: "member5", Score: ss.Score(11)},
{Value: "member6", Score: ss.Score(math.Inf(-1))},
{Value: "member7", Score: ss.Score(math.Inf(1))},
}),
key: "key1",
min: math.Inf(-1),
max: math.Inf(1),
want: 7,
wantErr: false,
},
{
name: "Get count of sub-set from -inf to limit",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "member1", Score: ss.Score(5.5)},
{Value: "member2", Score: ss.Score(67.77)},
{Value: "member3", Score: ss.Score(10)},
{Value: "member4", Score: ss.Score(1083.13)},
{Value: "member5", Score: ss.Score(11)},
{Value: "member6", Score: ss.Score(math.Inf(-1))},
{Value: "member7", Score: ss.Score(math.Inf(1))},
}),
key: "key2",
min: math.Inf(-1),
max: 90,
want: 5,
wantErr: false,
},
{
name: "Get count of sub-set from bottom boundary to +inf limit",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "member1", Score: ss.Score(5.5)},
{Value: "member2", Score: ss.Score(67.77)},
{Value: "member3", Score: ss.Score(10)},
{Value: "member4", Score: ss.Score(1083.13)},
{Value: "member5", Score: ss.Score(11)},
{Value: "member6", Score: ss.Score(math.Inf(-1))},
{Value: "member7", Score: ss.Score(math.Inf(1))},
}),
key: "key3",
min: 1000,
max: math.Inf(1),
want: 2,
wantErr: false,
},
{
name: "Throw error when value at the key is not a sorted set",
preset: true,
presetValue: "Default value",
key: "key4",
min: 1,
max: 10,
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
err := presetValue(server, context.Background(), tt.key, tt.presetValue)
if err != nil {
t.Error(err)
return
}
}
got, err := server.ZCount(tt.key, tt.min, tt.max)
if (err != nil) != tt.wantErr {
t.Errorf("ZCOUNT() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ZCOUNT() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZDIFF(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValues map[string]interface{}
withscores bool
keys []string
want map[string]float64
wantErr bool
}{
{
name: "Get the difference between 2 sorted sets without scores",
preset: true,
presetValues: map[string]interface{}{
"key1": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1},
{Value: "two", Score: 2},
{Value: "three", Score: 3},
{Value: "four", Score: 4},
}),
"key2": ss.NewSortedSet([]ss.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},
}),
},
withscores: false,
keys: []string{"key1", "key2"},
want: map[string]float64{"one": 0, "two": 0},
wantErr: false,
},
{
name: "Get the difference between 2 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key3": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1},
{Value: "two", Score: 2},
{Value: "three", Score: 3},
{Value: "four", Score: 4},
}),
"key4": ss.NewSortedSet([]ss.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},
}),
},
withscores: true,
keys: []string{"key3", "key4"},
want: map[string]float64{"one": 1, "two": 2},
wantErr: false,
},
{
name: "Get the difference between 3 sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key5": ss.NewSortedSet([]ss.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},
}),
"key6": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
{Value: "eleven", Score: 11},
}),
"key7": ss.NewSortedSet([]ss.MemberParam{
{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
withscores: true,
keys: []string{"key5", "key6", "key7"},
want: map[string]float64{"three": 3, "four": 4, "five": 5, "six": 6},
wantErr: false,
},
{
name: "Return sorted set if only one key exists and is a sorted set",
preset: true,
presetValues: map[string]interface{}{
"key8": ss.NewSortedSet([]ss.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},
}),
},
withscores: true,
keys: []string{"key8", "non-existent-key-1", "non-existent-key-2", "non-existent-key-3"},
want: map[string]float64{
"one": 1, "two": 2, "three": 3, "four": 4,
"five": 5, "six": 6, "seven": 7, "eight": 8,
},
wantErr: false,
},
{
name: "Throw error when one of the keys is not a sorted set",
preset: true,
presetValues: map[string]interface{}{
"key9": "Default value",
"key10": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
{Value: "eleven", Score: 11},
}),
"key11": ss.NewSortedSet([]ss.MemberParam{
{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
withscores: false,
keys: []string{"key9", "key10", "key11"},
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
for k, v := range tt.presetValues {
err := presetValue(server, context.Background(), k, v)
if err != nil {
t.Error(err)
return
}
}
}
got, err := server.ZDiff(tt.withscores, tt.keys...)
if (err != nil) != tt.wantErr {
t.Errorf("ZDIFF() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ZDIFF() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZDIFFSTORE(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValues map[string]interface{}
destination string
keys []string
want int
wantErr bool
}{
{
name: "Get the difference between 2 sorted sets",
preset: true,
presetValues: map[string]interface{}{
"key1": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
"key2": ss.NewSortedSet([]ss.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: "destination1",
keys: []string{"key1", "key2"},
want: 2,
wantErr: false,
},
{
name: "Get the difference between 3 sorted sets",
preset: true,
presetValues: map[string]interface{}{
"key3": ss.NewSortedSet([]ss.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},
}),
"key4": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
{Value: "eleven", Score: 11},
}),
"key5": ss.NewSortedSet([]ss.MemberParam{
{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
destination: "destination2",
keys: []string{"key3", "key4", "key5"},
want: 4,
wantErr: false,
},
{
name: "Return base sorted set element if base set is the only existing key provided and is a valid sorted set",
preset: true,
presetValues: map[string]interface{}{
"key6": ss.NewSortedSet([]ss.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: "destination3",
keys: []string{"key6", "non-existent-key-1", "non-existent-key-2"},
want: 8,
wantErr: false,
},
{
name: "Throw error when base sorted set is not a set",
preset: true,
presetValues: map[string]interface{}{
"key7": "Default value",
"key8": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
{Value: "eleven", Score: 11},
}),
"key9": ss.NewSortedSet([]ss.MemberParam{
{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
destination: "destination4",
keys: []string{"key7", "key8", "key9"},
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
for k, v := range tt.presetValues {
err := presetValue(server, context.Background(), k, v)
if err != nil {
t.Error(err)
return
}
}
}
got, err := server.ZDiffStore(tt.destination, tt.keys...)
if (err != nil) != tt.wantErr {
t.Errorf("ZDIFFSTORE() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ZDIFFSTORE() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZINCRBY(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValue interface{}
key string
increment float64
member string
want float64
wantErr bool
}{
{
name: "Successfully increment by int. Return the new score",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
key: "key1",
increment: 5,
member: "one",
want: 6,
wantErr: false,
},
{
name: "Successfully increment by float. Return new score",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
key: "key2",
increment: 346.785,
member: "one",
want: 347.785,
},
{
name: "Increment on non-existent sorted set will create the set with the member and increment as its score",
preset: false,
presetValue: nil,
key: "key3",
increment: 346.785,
member: "one",
want: 346.785,
wantErr: false,
},
{ // 4.
name: "Increment score to +inf",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
key: "key4",
increment: math.Inf(1),
member: "one",
want: math.Inf(1),
wantErr: false,
},
{
name: "Increment score to -inf",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
key: "key5",
increment: math.Inf(-1),
member: "one",
want: math.Inf(-1),
wantErr: false,
},
{
name: "Incrementing score by negative increment should lower the score",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
key: "key6",
increment: -2.5,
member: "five",
want: 2.5,
wantErr: false,
},
{
name: "Return error when attempting to increment on a value that is not a valid sorted set",
preset: true,
presetValue: "Default value",
key: "key7",
increment: -2.5,
member: "five",
want: 0,
wantErr: true,
},
{
name: "Return error when trying to increment a member that already has score -inf",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: ss.Score(math.Inf(-1))},
}),
key: "key8",
increment: 2.5,
member: "one",
want: 0,
wantErr: true,
},
{
name: "Return error when trying to increment a member that already has score +inf",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: ss.Score(math.Inf(1))},
}),
key: "key9",
increment: 2.5,
member: "one",
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
err := presetValue(server, context.Background(), tt.key, tt.presetValue)
if err != nil {
t.Error(err)
return
}
}
got, err := server.ZIncrBy(tt.key, tt.increment, tt.member)
if (err != nil) != tt.wantErr {
t.Errorf("ZINCRBY() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ZINCRBY() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZINTER(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValues map[string]interface{}
keys []string
options ZInterOptions
want map[string]float64
wantErr bool
}{
{
name: "Get the intersection between 2 sorted sets",
preset: true,
presetValues: map[string]interface{}{
"key1": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
"key2": ss.NewSortedSet([]ss.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},
}),
},
keys: []string{"key1", "key2"},
options: ZInterOptions{},
want: map[string]float64{"three": 0, "four": 0, "five": 0},
wantErr: false,
},
{
// Get the intersection between 3 sorted sets with scores.
// By default, the SUM aggregate will be used.
name: "Get the intersection between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key3": ss.NewSortedSet([]ss.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},
}),
"key4": ss.NewSortedSet([]ss.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},
}),
"key5": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "eight", Score: 8},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
keys: []string{"key3", "key4", "key5"},
options: ZInterOptions{WithScores: true},
want: map[string]float64{"one": 3, "eight": 24},
wantErr: false,
},
{
// Get the intersection between 3 sorted sets with scores.
// Use MIN aggregate.
name: "Get the intersection between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key6": ss.NewSortedSet([]ss.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},
}),
"key7": ss.NewSortedSet([]ss.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},
}),
"key8": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
keys: []string{"key6", "key7", "key8"},
options: ZInterOptions{Aggregate: "MIN", WithScores: true},
want: map[string]float64{"one": 1, "eight": 8},
wantErr: false,
},
{
// Get the intersection between 3 sorted sets with scores.
// Use MAX aggregate.
preset: true,
presetValues: map[string]interface{}{
"key9": ss.NewSortedSet([]ss.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},
}),
"key10": ss.NewSortedSet([]ss.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},
}),
"key11": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
keys: []string{"key9", "key10", "key11"},
options: ZInterOptions{WithScores: true, Aggregate: "MAX"},
want: map[string]float64{"one": 1000, "eight": 800},
wantErr: false,
},
{
// Get the intersection between 3 sorted sets with scores.
// Use SUM aggregate with weights modifier.
name: "Get the intersection between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key12": ss.NewSortedSet([]ss.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},
}),
"key13": ss.NewSortedSet([]ss.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},
}),
"key14": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
keys: []string{"key12", "key13", "key14"},
options: ZInterOptions{WithScores: true, Aggregate: "SUM", Weights: []float64{1, 5, 3}},
want: map[string]float64{"one": 3105, "eight": 2808},
wantErr: false,
},
{
// Get the intersection between 3 sorted sets with scores.
// Use MAX aggregate with added weights.
name: "Get the intersection between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key15": ss.NewSortedSet([]ss.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},
}),
"key16": ss.NewSortedSet([]ss.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},
}),
"key17": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
keys: []string{"key15", "key16", "key17"},
options: ZInterOptions{WithScores: true, Aggregate: "MAX", Weights: []float64{1, 5, 3}},
want: map[string]float64{"one": 3000, "eight": 2400},
wantErr: false,
},
{
// Get the intersection between 3 sorted sets with scores.
// Use MIN aggregate with added weights.
name: "Get the intersection between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key18": ss.NewSortedSet([]ss.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},
}),
"key19": ss.NewSortedSet([]ss.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},
}),
"key20": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
keys: []string{"key18", "key19", "key20"},
options: ZInterOptions{WithScores: true, Aggregate: "MIN", Weights: []float64{1, 5, 3}},
want: map[string]float64{"one": 5, "eight": 8},
wantErr: false,
},
{
name: "Throw an error if there are more weights than keys",
preset: true,
presetValues: map[string]interface{}{
"key21": ss.NewSortedSet([]ss.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},
}),
"key22": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
},
keys: []string{"key21", "key22"},
options: ZInterOptions{Weights: []float64{1, 2, 3}},
want: nil,
wantErr: true,
},
{
name: "Throw an error if there are fewer weights than keys",
preset: true,
presetValues: map[string]interface{}{
"key23": ss.NewSortedSet([]ss.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},
}),
"key24": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
}),
"key25": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
},
keys: []string{"key23", "key24", "key25"},
options: ZInterOptions{Weights: []float64{5, 4}},
want: nil,
wantErr: true,
},
{
name: "Throw an error if there are no keys provided",
preset: true,
presetValues: map[string]interface{}{
"key26": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
"key27": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
"key28": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
},
keys: []string{},
options: ZInterOptions{},
want: nil,
wantErr: true,
},
{
name: "Throw an error if any of the provided keys are not sorted sets",
preset: true,
presetValues: map[string]interface{}{
"key29": ss.NewSortedSet([]ss.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},
}),
"key30": "Default value",
"key31": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
},
keys: []string{"key29", "key30", "key31"},
options: ZInterOptions{},
want: nil,
wantErr: true,
},
{
name: "If any of the keys does not exist, return an empty array",
preset: true,
presetValues: map[string]interface{}{
"key32": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
{Value: "eleven", Score: 11},
}),
"key33": ss.NewSortedSet([]ss.MemberParam{
{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
keys: []string{"non-existent", "key32", "key33"},
options: ZInterOptions{},
want: map[string]float64{},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
for k, v := range tt.presetValues {
err := presetValue(server, context.Background(), k, v)
if err != nil {
t.Error(err)
return
}
}
}
got, err := server.ZInter(tt.keys, tt.options)
if (err != nil) != tt.wantErr {
t.Errorf("ZINTER() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ZINTER() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZINTERSTORE(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValues map[string]interface{}
destination string
keys []string
options ZInterStoreOptions
want int
wantErr bool
}{
{
name: "Get the intersection between 2 sorted sets",
preset: true,
presetValues: map[string]interface{}{
"key1": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
"key2": ss.NewSortedSet([]ss.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: "destination1",
keys: []string{"key1", "key2"},
options: ZInterStoreOptions{},
want: 3,
wantErr: false,
},
{
// Get the intersection between 3 sorted sets with scores.
// By default, the SUM aggregate will be used.
name: "Get the intersection between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key3": ss.NewSortedSet([]ss.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},
}),
"key4": ss.NewSortedSet([]ss.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},
}),
"key5": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "eight", Score: 8},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
destination: "destination2",
keys: []string{"key3", "key4", "key5"},
options: ZInterStoreOptions{WithScores: true},
want: 2,
wantErr: false,
},
{
// Get the intersection between 3 sorted sets with scores.
// Use MIN aggregate.
name: "Get the intersection between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key6": ss.NewSortedSet([]ss.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},
}),
"key7": ss.NewSortedSet([]ss.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},
}),
"key8": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
destination: "destination3",
keys: []string{"key6", "key7", "key8"},
options: ZInterStoreOptions{WithScores: true, Aggregate: "MIN"},
want: 2,
wantErr: false,
},
{
// Get the intersection between 3 sorted sets with scores.
// Use MAX aggregate.
name: "Get the intersection between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key9": ss.NewSortedSet([]ss.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},
}),
"key10": ss.NewSortedSet([]ss.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},
}),
"key11": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
destination: "destination4",
keys: []string{"key9", "key10", "key11"},
options: ZInterStoreOptions{WithScores: true, Aggregate: "MAX"},
want: 2,
wantErr: false,
},
{
// Get the intersection between 3 sorted sets with scores.
// Use SUM aggregate with weights modifier.
name: "Get the intersection between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key12": ss.NewSortedSet([]ss.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},
}),
"key13": ss.NewSortedSet([]ss.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},
}),
"key14": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
destination: "destination5",
keys: []string{"key12", "key13", "key14"},
options: ZInterStoreOptions{WithScores: true, Aggregate: "SUM", Weights: []float64{1, 5, 3}},
want: 2,
wantErr: false,
},
{
// Get the intersection between 3 sorted sets with scores.
// Use MAX aggregate with added weights.
name: "Get the intersection between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key15": ss.NewSortedSet([]ss.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},
}),
"key16": ss.NewSortedSet([]ss.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},
}),
"key17": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
destination: "destination6",
keys: []string{"key15", "key16", "key17"},
options: ZInterStoreOptions{WithScores: true, Aggregate: "MAX", Weights: []float64{1, 5, 3}},
want: 2,
wantErr: false,
},
{
// Get the intersection between 3 sorted sets with scores.
// Use MIN aggregate with added weights.
name: "Get the intersection between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key18": ss.NewSortedSet([]ss.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},
}),
"key19": ss.NewSortedSet([]ss.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},
}),
"key20": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
destination: "destination7",
keys: []string{"key18", "key19", "key20"},
options: ZInterStoreOptions{WithScores: true, Aggregate: "MIN", Weights: []float64{1, 5, 3}},
want: 2,
wantErr: false,
},
{
name: "Throw an error if there are more weights than keys",
preset: true,
presetValues: map[string]interface{}{
"key21": ss.NewSortedSet([]ss.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},
}),
"key22": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
},
destination: "destination8",
keys: []string{"key21", "key22"},
options: ZInterStoreOptions{Weights: []float64{1, 2, 3}},
want: 0,
wantErr: true,
},
{
name: "Throw an error if there are fewer weights than keys",
preset: true,
presetValues: map[string]interface{}{
"key23": ss.NewSortedSet([]ss.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},
}),
"key24": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
}),
"key25": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
},
destination: "destination9",
keys: []string{"key23", "key24"},
options: ZInterStoreOptions{Weights: []float64{5}},
want: 0,
wantErr: true,
},
{
name: "Throw an error if there are no keys provided",
preset: true,
presetValues: map[string]interface{}{
"key26": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
"key27": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
"key28": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
},
destination: "destination10",
keys: []string{},
options: ZInterStoreOptions{Weights: []float64{5, 4}},
want: 0,
wantErr: true,
},
{
name: "Throw an error if any of the provided keys are not sorted sets",
preset: true,
presetValues: map[string]interface{}{
"key29": ss.NewSortedSet([]ss.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},
}),
"key30": "Default value",
"key31": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
},
destination: "destination11",
keys: []string{"key29", "key30", "key31"},
options: ZInterStoreOptions{},
want: 0,
wantErr: true,
},
{
name: "If any of the keys does not exist, return an empty array",
preset: true,
presetValues: map[string]interface{}{
"key32": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
{Value: "eleven", Score: 11},
}),
"key33": ss.NewSortedSet([]ss.MemberParam{
{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
destination: "destination12",
keys: []string{"non-existent", "key32", "key33"},
options: ZInterStoreOptions{},
want: 0,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
for k, v := range tt.presetValues {
err := presetValue(server, context.Background(), k, v)
if err != nil {
t.Error(err)
return
}
}
}
got, err := server.ZInterStore(tt.destination, tt.keys, tt.options)
if (err != nil) != tt.wantErr {
t.Errorf("ZINTERSTORE() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ZINTERSTORE() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZLEXCOUNT(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValue interface{}
key string
min string
max string
want int
wantErr bool
}{
{
name: "Get entire count using infinity boundaries",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "e", Score: ss.Score(1)},
{Value: "f", Score: ss.Score(1)},
{Value: "g", Score: ss.Score(1)},
{Value: "h", Score: ss.Score(1)},
{Value: "i", Score: ss.Score(1)},
{Value: "j", Score: ss.Score(1)},
{Value: "k", Score: ss.Score(1)},
}),
key: "key1",
min: "f",
max: "j",
want: 5,
wantErr: false,
},
{
name: "Return 0 when the members do not have the same score",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "a", Score: ss.Score(5.5)},
{Value: "b", Score: ss.Score(67.77)},
{Value: "c", Score: ss.Score(10)},
{Value: "d", Score: ss.Score(1083.13)},
{Value: "e", Score: ss.Score(11)},
{Value: "f", Score: ss.Score(math.Inf(-1))},
{Value: "g", Score: ss.Score(math.Inf(1))},
}),
key: "key2",
min: "a",
max: "b",
want: 0,
wantErr: false,
},
{
name: "Return 0 when the key does not exist",
preset: false,
presetValue: nil,
key: "key3",
min: "a",
max: "z",
want: 0,
wantErr: false,
},
{
name: "Return error when the value at the key is not a sorted set",
preset: true,
presetValue: "Default value",
key: "key4",
min: "a",
max: "z",
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
err := presetValue(server, context.Background(), tt.key, tt.presetValue)
if err != nil {
t.Error(err)
return
}
}
got, err := server.ZLexCount(tt.key, tt.min, tt.max)
if (err != nil) != tt.wantErr {
t.Errorf("ZLEXCOUNT() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ZLEXCOUNT() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZMPOP(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValues map[string]interface{}
keys []string
options ZMPopOptions
want [][]string
wantErr bool
}{
{
name: "Successfully pop one min element by default",
preset: true,
presetValues: map[string]interface{}{
"key1": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
},
keys: []string{"key1"},
options: ZMPopOptions{},
want: [][]string{
{"one", "1"},
},
wantErr: false,
},
{
name: "Successfully pop one min element by specifying MIN",
preset: true,
presetValues: map[string]interface{}{
"key2": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
},
keys: []string{"key2"},
options: ZMPopOptions{Min: true},
want: [][]string{
{"one", "1"},
},
wantErr: false,
},
{
name: "Successfully pop one max element by specifying MAX modifier",
preset: true,
presetValues: map[string]interface{}{
"key3": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
},
keys: []string{"key3"},
options: ZMPopOptions{Max: true},
want: [][]string{
{"five", "5"},
},
wantErr: false,
},
{
name: "Successfully pop multiple min elements",
preset: true,
presetValues: map[string]interface{}{
"key4": ss.NewSortedSet([]ss.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},
}),
},
keys: []string{"key4"},
options: ZMPopOptions{Min: true, Count: 5},
want: [][]string{
{"one", "1"}, {"two", "2"}, {"three", "3"},
{"four", "4"}, {"five", "5"},
},
wantErr: false,
},
{
name: "Successfully pop multiple max elements",
preset: true,
presetValues: map[string]interface{}{
"key5": ss.NewSortedSet([]ss.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},
}),
},
keys: []string{"key5"},
options: ZMPopOptions{Max: true, Count: 5},
want: [][]string{{"two", "2"}, {"three", "3"}, {"four", "4"}, {"five", "5"}, {"six", "6"}},
wantErr: false,
},
{
name: "Successfully pop elements from the first set which is non-empty",
preset: true,
presetValues: map[string]interface{}{
"key6": ss.NewSortedSet([]ss.MemberParam{}),
"key7": ss.NewSortedSet([]ss.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},
}),
},
keys: []string{"key6", "key7"},
options: ZMPopOptions{Max: true, Count: 5},
want: [][]string{{"two", "2"}, {"three", "3"}, {"four", "4"}, {"five", "5"}, {"six", "6"}},
wantErr: false,
},
{
name: "Skip the non-set items and pop elements from the first non-empty sorted set found",
preset: true,
presetValues: map[string]interface{}{
"key8": "Default value",
"key9": 56,
"key10": ss.NewSortedSet([]ss.MemberParam{}),
"key11": ss.NewSortedSet([]ss.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},
}),
},
keys: []string{"key8", "key9", "key10", "key11"},
options: ZMPopOptions{Min: true, Count: 5},
want: [][]string{{"one", "1"}, {"two", "2"}, {"three", "3"}, {"four", "4"}, {"five", "5"}},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
for k, v := range tt.presetValues {
err := presetValue(server, context.Background(), k, v)
if err != nil {
t.Error(err)
return
}
}
}
got, err := server.ZMPop(tt.keys, tt.options)
if (err != nil) != tt.wantErr {
t.Errorf("ZMPOP() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !internal.CompareNestedStringArrays(got, tt.want) {
t.Errorf("ZMPOP() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZMSCORE(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValue interface{}
key string
members []string
want []interface{}
wantErr bool
}{
{ // Return multiple scores from the sorted set.
// Return nil for elements that do not exist in the sorted set.
name: "Return multiple scores from the sorted set",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1.1}, {Value: "two", Score: 245},
{Value: "three", Score: 3}, {Value: "four", Score: 4.055},
{Value: "five", Score: 5},
}),
key: "key1",
members: []string{"one", "none", "two", "one", "three", "four", "none", "five"},
want: []interface{}{"1.1", nil, "245", "1.1", "3", "4.055", nil, "5"},
wantErr: false,
},
{
name: "If key does not exist, return empty array",
preset: false,
presetValue: nil,
key: "key2",
members: []string{"one", "two", "three", "four"},
want: []interface{}{},
wantErr: false,
},
{
name: "Throw error when trying to find scores from elements that are not sorted sets",
preset: true,
presetValue: "Default value",
key: "key3",
members: []string{"one", "two", "three"},
want: []interface{}{},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
err := presetValue(server, context.Background(), tt.key, tt.presetValue)
if err != nil {
t.Error(err)
return
}
}
got, err := server.ZMScore(tt.key, tt.members...)
if (err != nil) != tt.wantErr {
t.Errorf("ZMSCORE() error = %v, wantErr %v", err, tt.wantErr)
return
}
if len(got) != len(tt.want) {
t.Errorf("ZMSCORE() got length = %v, want length %v", len(got), len(tt.want))
return
}
for i := 0; i < len(got); i++ {
if got[i] == nil && tt.want[i] == nil {
continue
}
if (got[i] == nil) != (tt.want[i] == nil) {
t.Errorf("ZMSCORE() got[%d] = %v, want[%d] %v", i, got, i, tt.want)
}
wantf, _ := strconv.ParseFloat(tt.want[i].(string), 64)
if got[i] != wantf {
t.Errorf("ZMSCORE() got[%d] = %v, want[%d] %v", i, got[i], i, wantf)
}
}
})
}
}
func TestEchoVault_ZPOP(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValue interface{}
key string
count uint
popFunc func(key string, count uint) ([][]string, error)
want [][]string
wantErr bool
}{
{
name: "Successfully pop one min element",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
key: "key1",
count: 1,
popFunc: server.ZPopMin,
want: [][]string{
{"one", "1"},
},
wantErr: false,
},
{
name: "Successfully pop one max element",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
key: "key2",
count: 1,
popFunc: server.ZPopMax,
want: [][]string{{"five", "5"}},
wantErr: false,
},
{
name: "Successfully pop multiple min elements",
preset: true,
presetValue: ss.NewSortedSet([]ss.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},
}),
popFunc: server.ZPopMin,
key: "key3",
count: 5,
want: [][]string{
{"one", "1"}, {"two", "2"}, {"three", "3"},
{"four", "4"}, {"five", "5"},
},
wantErr: false,
},
{
name: "Successfully pop multiple max elements",
preset: true,
presetValue: ss.NewSortedSet([]ss.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},
}),
popFunc: server.ZPopMax,
key: "key4",
count: 5,
want: [][]string{{"two", "2"}, {"three", "3"}, {"four", "4"}, {"five", "5"}, {"six", "6"}},
wantErr: false,
},
{
name: "Throw an error when trying to pop from an element that's not a sorted set",
preset: true,
presetValue: "Default value",
popFunc: server.ZPopMin,
key: "key5",
count: 1,
want: [][]string{},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
err := presetValue(server, context.Background(), tt.key, tt.presetValue)
if err != nil {
t.Error(err)
return
}
}
got, err := tt.popFunc(tt.key, tt.count)
if (err != nil) != tt.wantErr {
t.Errorf("ZPOPMAX() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !internal.CompareNestedStringArrays(got, tt.want) {
t.Errorf("ZPOPMAX() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZRANDMEMBER(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValue interface{}
key string
count int
withscores bool
want int
wantErr bool
}{
{ // Return multiple random elements without removing them.
// Count is positive, do not allow repeated elements.
name: "Return multiple random elements without removing them",
preset: true,
key: "key1",
presetValue: ss.NewSortedSet([]ss.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},
}),
count: 3,
withscores: false,
want: 3,
wantErr: false,
},
{
// Return multiple random elements and their scores without removing them.
// Count is negative, so allow repeated numbers.
name: "Return multiple random elements and their scores without removing them",
preset: true,
key: "key2",
presetValue: ss.NewSortedSet([]ss.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},
}),
count: -5,
withscores: true,
want: 5,
wantErr: false,
},
{
name: "Return error when the source key is not a sorted set",
preset: true,
key: "key3",
presetValue: "Default value",
count: 1,
withscores: false,
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
err := presetValue(server, context.Background(), tt.key, tt.presetValue)
if err != nil {
t.Error(err)
return
}
}
got, err := server.ZRandMember(tt.key, tt.count, tt.withscores)
if (err != nil) != tt.wantErr {
t.Errorf("ZRANDMEMBER() error = %v, wantErr %v", err, tt.wantErr)
return
}
if len(got) != tt.want {
t.Errorf("ZRANDMEMBER() got = %v, want %v", len(got), tt.want)
}
})
}
}
func TestEchoVault_ZRANGE(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValue interface{}
key string
start string
stop string
options ZRangeOptions
want map[string]float64
wantErr bool
}{
{
name: "Get elements withing score range without score",
preset: true,
presetValue: ss.NewSortedSet([]ss.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},
}),
key: "key1",
start: "3",
stop: "7",
options: ZRangeOptions{ByScore: true},
want: map[string]float64{"three": 0, "four": 0, "five": 0, "six": 0, "seven": 0},
wantErr: false,
},
{
name: "Get elements within score range with score",
preset: true,
presetValue: ss.NewSortedSet([]ss.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},
}),
key: "key2",
start: "3",
stop: "7",
options: ZRangeOptions{ByScore: true, WithScores: true},
want: map[string]float64{"three": 3, "four": 4, "five": 5, "six": 6, "seven": 7},
wantErr: false,
},
{
// 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: "Get elements within score range with offset and limit",
preset: true,
presetValue: ss.NewSortedSet([]ss.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},
}),
key: "key3",
start: "3",
stop: "7",
options: ZRangeOptions{WithScores: true, ByScore: true, Offset: 2, Count: 4},
want: map[string]float64{"three": 3, "four": 4, "five": 5},
wantErr: false,
},
{
name: "Get elements within lex range without score",
preset: true,
presetValue: ss.NewSortedSet([]ss.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},
}),
key: "key4",
start: "c",
stop: "g",
options: ZRangeOptions{ByLex: true},
want: map[string]float64{"c": 0, "d": 0, "e": 0, "f": 0, "g": 0},
wantErr: false,
},
{
name: "Get elements within lex range with score",
preset: true,
presetValue: ss.NewSortedSet([]ss.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},
}),
key: "key5",
start: "a",
stop: "f",
options: ZRangeOptions{ByLex: true, WithScores: true},
want: map[string]float64{"a": 1, "b": 1, "c": 1, "d": 1, "e": 1, "f": 1},
wantErr: false,
},
{
// 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: "Get elements within lex range with offset and limit",
preset: true,
presetValue: ss.NewSortedSet([]ss.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},
}),
key: "key6",
start: "a",
stop: "h",
options: ZRangeOptions{WithScores: true, ByLex: true, Offset: 2, Count: 4},
want: map[string]float64{"c": 1, "d": 1, "e": 1},
wantErr: false,
},
{
name: "Return an empty map when we use BYLEX while elements have different scores",
preset: true,
presetValue: ss.NewSortedSet([]ss.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},
}),
key: "key7",
start: "a",
stop: "h",
options: ZRangeOptions{WithScores: true, ByLex: true, Offset: 2, Count: 4},
want: map[string]float64{},
wantErr: false,
},
{
name: "Throw error when the key does not hold a sorted set",
preset: true,
presetValue: "Default value",
key: "key10",
start: "a",
stop: "h",
options: ZRangeOptions{WithScores: true, ByLex: true, Offset: 2, Count: 4},
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
err := presetValue(server, context.Background(), tt.key, tt.presetValue)
if err != nil {
t.Error(err)
return
}
}
got, err := server.ZRange(tt.key, tt.start, tt.stop, tt.options)
if (err != nil) != tt.wantErr {
t.Errorf("ZRANGE() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ZRANGE() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZRANGESTORE(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValues map[string]interface{}
destination string
source string
start string
stop string
options ZRangeStoreOptions
want int
wantErr bool
}{
{
name: "Get elements within score range without score",
preset: true,
presetValues: map[string]interface{}{
"key1": ss.NewSortedSet([]ss.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: "destination1",
source: "key1",
start: "3",
stop: "7",
options: ZRangeStoreOptions{ByScore: true},
want: 5,
wantErr: false,
},
{
name: "Get elements within score range with score",
preset: true,
presetValues: map[string]interface{}{
"key2": ss.NewSortedSet([]ss.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: "destination2",
source: "key2",
start: "3",
stop: "7",
options: ZRangeStoreOptions{WithScores: true, ByScore: true},
want: 5,
wantErr: false,
},
{
// 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: "Get elements within score range with offset and limit",
preset: true,
presetValues: map[string]interface{}{
"key3": ss.NewSortedSet([]ss.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: "destination3",
source: "key3",
start: "3",
stop: "7",
options: ZRangeStoreOptions{ByScore: true, WithScores: true, Offset: 2, Count: 4},
want: 3,
wantErr: false,
},
{
name: "Get elements within lex range without score",
preset: true,
presetValues: map[string]interface{}{
"key4": ss.NewSortedSet([]ss.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: "destination4",
source: "key4",
start: "c",
stop: "g",
options: ZRangeStoreOptions{ByLex: true},
want: 5,
wantErr: false,
},
{
name: "Get elements within lex range with score",
preset: true,
presetValues: map[string]interface{}{
"key5": ss.NewSortedSet([]ss.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: "destination5",
source: "key5",
start: "a",
stop: "f",
options: ZRangeStoreOptions{ByLex: true, WithScores: true},
want: 6,
wantErr: false,
},
{
// 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: "Get elements within lex range with offset and limit",
preset: true,
presetValues: map[string]interface{}{
"key6": ss.NewSortedSet([]ss.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: "destination6",
source: "key6",
start: "a",
stop: "h",
options: ZRangeStoreOptions{WithScores: true, ByLex: true, Offset: 2, Count: 4},
want: 3,
wantErr: false,
},
{
// 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: "Get elements within lex range with offset and limit + reverse the results",
preset: true,
presetValues: map[string]interface{}{
"key7": ss.NewSortedSet([]ss.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: "destination7",
source: "key7",
start: "a",
stop: "h",
options: ZRangeStoreOptions{WithScores: true, ByLex: true, Offset: 2, Count: 4},
want: 3,
wantErr: false,
},
{
name: "Return an empty slice when we use BYLEX while elements have different scores",
preset: true,
presetValues: map[string]interface{}{
"key8": ss.NewSortedSet([]ss.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: "destination8",
source: "key8",
start: "a",
stop: "h",
options: ZRangeStoreOptions{WithScores: true, ByLex: true, Offset: 2, Count: 4},
want: 0,
wantErr: false,
},
{
name: "Throw error when the key does not hold a sorted set",
preset: true,
presetValues: map[string]interface{}{
"key9": "Default value",
},
destination: "destination9",
source: "key9",
start: "a",
stop: "h",
options: ZRangeStoreOptions{WithScores: true, ByLex: true, Offset: 2, Count: 4},
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
for k, v := range tt.presetValues {
err := presetValue(server, context.Background(), k, v)
if err != nil {
t.Error(err)
return
}
}
}
got, err := server.ZRangeStore(tt.destination, tt.source, tt.start, tt.stop, tt.options)
if (err != nil) != tt.wantErr {
t.Errorf("ZRANGESTORE() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ZRANGESTORE() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZRANK(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValue interface{}
key string
member string
withscores bool
want map[int]float64
wantErr bool
}{
{
name: "Return element's rank from a sorted set",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
key: "key1",
member: "four",
withscores: false,
want: map[int]float64{3: 0},
wantErr: false,
},
{
name: "Return element's rank from a sorted set with its score",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 100.1}, {Value: "two", Score: 245},
{Value: "three", Score: 305.43}, {Value: "four", Score: 411.055},
{Value: "five", Score: 500},
}),
key: "key2",
member: "four",
withscores: true,
want: map[int]float64{3: 411.055},
wantErr: false,
},
{
name: "If key does not exist, return nil value",
preset: false,
presetValue: nil,
key: "key3",
member: "one",
withscores: false,
want: nil,
wantErr: false,
},
{
name: "If key exists and is a sorted set, but the member does not exist, return nil",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1.1}, {Value: "two", Score: 245},
{Value: "three", Score: 3}, {Value: "four", Score: 4.055},
{Value: "five", Score: 5},
}),
key: "key4",
member: "non-existent",
withscores: false,
want: nil,
wantErr: false,
},
{
name: "Throw error when trying to find scores from elements that are not sorted sets",
preset: true,
presetValue: "Default value",
key: "key5",
member: "one",
withscores: false,
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
err := presetValue(server, context.Background(), tt.key, tt.presetValue)
if err != nil {
t.Error(err)
return
}
}
got, err := server.ZRank(tt.key, tt.member, tt.withscores)
if (err != nil) != tt.wantErr {
t.Errorf("ZRANK() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ZRANK() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZREM(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValue interface{}
key string
members []string
want int
wantErr bool
}{
{
// Successfully remove multiple elements from sorted set, skipping non-existent members.
// Return deleted count.
name: "Successfully remove multiple elements from sorted set, skipping non-existent members",
preset: true,
presetValue: ss.NewSortedSet([]ss.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},
}),
key: "key1",
members: []string{"three", "four", "five", "none", "six", "none", "seven"},
want: 5,
wantErr: false,
},
{
name: "If key does not exist, return 0",
preset: false,
presetValue: nil,
key: "key2",
members: []string{"member"},
want: 0,
wantErr: false,
},
{
name: "Return error key is not a sorted set",
preset: true,
presetValue: "Default value",
key: "key3",
members: []string{"member"},
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
err := presetValue(server, context.Background(), tt.key, tt.presetValue)
if err != nil {
t.Error(err)
return
}
}
got, err := server.ZRem(tt.key, tt.members...)
if (err != nil) != tt.wantErr {
t.Errorf("ZREM() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ZREM() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZREMRANGEBYSCORE(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValue interface{}
key string
min float64
max float64
want int
wantErr bool
}{
{
name: "Successfully remove multiple elements with scores inside the provided range",
preset: true,
presetValue: ss.NewSortedSet([]ss.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},
}),
key: "key1",
min: 3,
max: 7,
want: 5,
wantErr: false,
},
{
name: "If key does not exist, return 0",
preset: false,
key: "key2",
min: 2,
max: 4,
want: 0,
wantErr: false,
},
{
name: "Return error key is not a sorted set",
preset: true,
presetValue: "Default value",
key: "key3",
min: 2,
max: 4,
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
err := presetValue(server, context.Background(), tt.key, tt.presetValue)
if err != nil {
t.Error(err)
return
}
}
got, err := server.ZRemRangeByScore(tt.key, tt.min, tt.max)
if (err != nil) != tt.wantErr {
t.Errorf("ZREMRANGEBYSCORE() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ZREMRANGEBYSCORE() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZSCORE(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValue interface{}
key string
member string
want interface{}
wantErr bool
}{
{
name: "Return score from a sorted set",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1.1}, {Value: "two", Score: 245},
{Value: "three", Score: 3}, {Value: "four", Score: 4.055},
{Value: "five", Score: 5},
}),
key: "key1",
member: "four",
want: 4.055,
wantErr: false,
},
{
name: "If key does not exist, return nil value",
preset: false,
presetValue: nil,
key: "key2",
member: "one",
want: nil,
wantErr: false,
},
{
name: "If key exists and is a sorted set, but the member does not exist, return nil",
preset: true,
presetValue: ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1.1}, {Value: "two", Score: 245},
{Value: "three", Score: 3}, {Value: "four", Score: 4.055},
{Value: "five", Score: 5},
}),
key: "key3",
member: "non-existent",
want: nil,
wantErr: false,
},
{
name: "Throw error when trying to find scores from elements that are not sorted sets",
preset: true,
presetValue: "Default value",
key: "key4",
member: "one",
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
err := presetValue(server, context.Background(), tt.key, tt.presetValue)
if err != nil {
t.Error(err)
return
}
}
got, err := server.ZScore(tt.key, tt.member)
if (err != nil) != tt.wantErr {
t.Errorf("ZSCORE() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ZSCORE() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZUNION(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValues map[string]interface{}
keys []string
options ZUnionOptions
want map[string]float64
wantErr bool
}{
{
name: "Get the union between 2 sorted sets",
preset: true,
presetValues: map[string]interface{}{
"key1": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
"key2": ss.NewSortedSet([]ss.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},
}),
},
keys: []string{"key1", "key2"},
options: ZUnionOptions{},
want: map[string]float64{
"one": 0, "two": 0, "three": 0, "four": 0,
"five": 0, "six": 0, "seven": 0, "eight": 0,
},
wantErr: false,
},
{
// Get the union between 3 sorted sets with scores.
// By default, the SUM aggregate will be used.
name: "Get the union between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key3": ss.NewSortedSet([]ss.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},
}),
"key4": ss.NewSortedSet([]ss.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},
}),
"key5": ss.NewSortedSet([]ss.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},
}),
},
keys: []string{"key3", "key4", "key5"},
options: ZUnionOptions{WithScores: true},
want: map[string]float64{
"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,
},
wantErr: false,
},
{
// Get the union between 3 sorted sets with scores.
// Use MIN aggregate.
name: "Get the union between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key6": ss.NewSortedSet([]ss.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},
}),
"key7": ss.NewSortedSet([]ss.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},
}),
"key8": ss.NewSortedSet([]ss.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},
}),
},
keys: []string{"key6", "key7", "key8"},
options: ZUnionOptions{WithScores: true, Aggregate: "MIN"},
want: map[string]float64{
"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,
},
wantErr: false,
},
{
// Get the union between 3 sorted sets with scores.
// Use MAX aggregate.
name: "Get the union between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key9": ss.NewSortedSet([]ss.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},
}),
"key10": ss.NewSortedSet([]ss.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},
}),
"key11": ss.NewSortedSet([]ss.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},
}),
},
keys: []string{"key9", "key10", "key11"},
options: ZUnionOptions{WithScores: true, Aggregate: "MAX"},
want: map[string]float64{
"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,
},
wantErr: false,
},
{
// Get the union between 3 sorted sets with scores.
// Use SUM aggregate with weights modifier.
name: "Get the union between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key12": ss.NewSortedSet([]ss.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},
}),
"key13": ss.NewSortedSet([]ss.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},
}),
"key14": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
keys: []string{"key12", "key13", "key14"},
options: ZUnionOptions{WithScores: true, Aggregate: "SUM", Weights: []float64{1, 2, 3}},
want: map[string]float64{
"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,
},
wantErr: false,
},
{
// Get the union between 3 sorted sets with scores.
// Use MAX aggregate with added weights.
name: "Get the union between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key15": ss.NewSortedSet([]ss.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},
}),
"key16": ss.NewSortedSet([]ss.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},
}),
"key17": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
keys: []string{"key15", "key16", "key17"},
options: ZUnionOptions{WithScores: true, Aggregate: "MAX", Weights: []float64{1, 2, 3}},
want: map[string]float64{
"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,
},
wantErr: false,
},
{
// Get the union between 3 sorted sets with scores.
// Use MIN aggregate with added weights.
name: "Get the union between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key18": ss.NewSortedSet([]ss.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},
}),
"key19": ss.NewSortedSet([]ss.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},
}),
"key20": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
keys: []string{"key18", "key19", "key20"},
options: ZUnionOptions{WithScores: true, Aggregate: "MIN", Weights: []float64{1, 2, 3}},
want: map[string]float64{
"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,
},
wantErr: false,
},
{
name: "Throw an error if there are more weights than keys",
preset: true,
presetValues: map[string]interface{}{
"key21": ss.NewSortedSet([]ss.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},
}),
"key22": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
},
keys: []string{"key21", "key22"},
options: ZUnionOptions{Weights: []float64{1, 2, 3}},
want: nil,
wantErr: true,
},
{
name: "Throw an error if there are fewer weights than keys",
preset: true,
presetValues: map[string]interface{}{
"key23": ss.NewSortedSet([]ss.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},
}),
"key24": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
}),
"key25": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
},
keys: []string{"key23", "key24", "key25"},
options: ZUnionOptions{Weights: []float64{5, 4}},
want: nil,
wantErr: true,
},
{
name: "Throw an error if there are no keys provided",
preset: true,
presetValues: map[string]interface{}{
"key26": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
"key27": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
"key28": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
},
keys: []string{},
options: ZUnionOptions{Weights: []float64{5, 4}},
want: nil,
wantErr: true,
},
{
name: "Throw an error if any of the provided keys are not sorted sets",
preset: true,
presetValues: map[string]interface{}{
"key29": ss.NewSortedSet([]ss.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},
}),
"key30": "Default value",
"key31": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
},
keys: []string{"key29", "key30", "key31"},
options: ZUnionOptions{},
want: nil,
wantErr: true,
},
{
name: "If any of the keys does not exist, skip it",
preset: true,
presetValues: map[string]interface{}{
"key32": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
{Value: "eleven", Score: 11},
}),
"key33": ss.NewSortedSet([]ss.MemberParam{
{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
keys: []string{"non-existent", "key32", "key33"},
options: ZUnionOptions{},
want: map[string]float64{
"one": 0, "two": 0, "thirty-six": 0, "twelve": 0, "eleven": 0,
"seven": 0, "eight": 0, "nine": 0, "ten": 0,
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
for k, v := range tt.presetValues {
err := presetValue(server, context.Background(), k, v)
if err != nil {
t.Error(err)
return
}
}
}
got, err := server.ZUnion(tt.keys, tt.options)
if (err != nil) != tt.wantErr {
t.Errorf("ZUNION() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ZUNION() got = %v, want %v", got, tt.want)
}
})
}
}
func TestEchoVault_ZUNIONSTORE(t *testing.T) {
server := createEchoVault()
tests := []struct {
name string
preset bool
presetValues map[string]interface{}
destination string
keys []string
options ZUnionStoreOptions
want int
wantErr bool
}{
{
name: "Get the union between 2 sorted sets",
preset: true,
presetValues: map[string]interface{}{
"key1": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "three", Score: 3}, {Value: "four", Score: 4},
{Value: "five", Score: 5},
}),
"key2": ss.NewSortedSet([]ss.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: "destination1",
keys: []string{"key1", "key2"},
options: ZUnionStoreOptions{},
want: 8,
wantErr: false,
},
{
// Get the union between 3 sorted sets with scores.
// By default, the SUM aggregate will be used.
name: "Get the union between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key3": ss.NewSortedSet([]ss.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},
}),
"key4": ss.NewSortedSet([]ss.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},
}),
"key5": ss.NewSortedSet([]ss.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: "destination2",
keys: []string{"key3", "key4", "key5"},
options: ZUnionStoreOptions{WithScores: true},
want: 13,
wantErr: false,
},
{
// Get the union between 3 sorted sets with scores.
// Use MIN aggregate.
name: "Get the union between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key6": ss.NewSortedSet([]ss.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},
}),
"key7": ss.NewSortedSet([]ss.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},
}),
"key8": ss.NewSortedSet([]ss.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: "destination3",
keys: []string{"key6", "key7", "key8"},
options: ZUnionStoreOptions{WithScores: true, Aggregate: "MIN"},
want: 13,
wantErr: false,
},
{
// Get the union between 3 sorted sets with scores.
// Use MAX aggregate.
name: "Get the union between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key9": ss.NewSortedSet([]ss.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},
}),
"key10": ss.NewSortedSet([]ss.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},
}),
"key11": ss.NewSortedSet([]ss.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: "destination4",
keys: []string{"key9", "key10", "key11"},
options: ZUnionStoreOptions{WithScores: true, Aggregate: "MAX"},
want: 13,
wantErr: false,
},
{
// Get the union between 3 sorted sets with scores.
// Use SUM aggregate with weights modifier.
name: "Get the union between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key12": ss.NewSortedSet([]ss.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},
}),
"key13": ss.NewSortedSet([]ss.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},
}),
"key14": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
destination: "destination5",
keys: []string{"key12", "key13", "key14"},
options: ZUnionStoreOptions{WithScores: true, Aggregate: "SUM", Weights: []float64{1, 2, 3}},
want: 13,
wantErr: false,
},
{
// Get the union between 3 sorted sets with scores.
// Use MAX aggregate with added weights.
name: "Get the union between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key15": ss.NewSortedSet([]ss.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},
}),
"key16": ss.NewSortedSet([]ss.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},
}),
"key17": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
destination: "destination6",
keys: []string{"key15", "key16", "key17"},
options: ZUnionStoreOptions{WithScores: true, Aggregate: "MAX", Weights: []float64{1, 2, 3}},
want: 13,
wantErr: false,
},
{
// Get the union between 3 sorted sets with scores.
// Use MIN aggregate with added weights.
name: "Get the union between 3 sorted sets with scores",
preset: true,
presetValues: map[string]interface{}{
"key18": ss.NewSortedSet([]ss.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},
}),
"key19": ss.NewSortedSet([]ss.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},
}),
"key20": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1000}, {Value: "eight", Score: 800},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
destination: "destination7",
keys: []string{"destination7", "key18", "key19", "key20"},
options: ZUnionStoreOptions{WithScores: true, Aggregate: "MIN", Weights: []float64{1, 2, 3}},
want: 13,
wantErr: false,
},
{
name: "Throw an error if there are more weights than keys",
preset: true,
presetValues: map[string]interface{}{
"key21": ss.NewSortedSet([]ss.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},
}),
"key22": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
},
destination: "destination8",
keys: []string{"key21", "key22"},
options: ZUnionStoreOptions{Weights: []float64{1, 2, 3}},
want: 0,
wantErr: true,
},
{
name: "Throw an error if there are fewer weights than keys",
preset: true,
presetValues: map[string]interface{}{
"key23": ss.NewSortedSet([]ss.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},
}),
"key24": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
}),
"key25": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
},
destination: "destination9",
keys: []string{"key23", "key24", "key25"},
options: ZUnionStoreOptions{Weights: []float64{5, 4}},
want: 0,
wantErr: true,
},
{
name: "Throw an error if any of the provided keys are not sorted sets",
preset: true,
presetValues: map[string]interface{}{
"key29": ss.NewSortedSet([]ss.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},
}),
"key30": "Default value",
"key31": ss.NewSortedSet([]ss.MemberParam{{Value: "one", Score: 1}}),
},
destination: "destination11",
keys: []string{"key29", "key30", "key31"},
options: ZUnionStoreOptions{},
want: 0,
wantErr: true,
},
{
name: "If any of the keys does not exist, skip it",
preset: true,
presetValues: map[string]interface{}{
"key32": ss.NewSortedSet([]ss.MemberParam{
{Value: "one", Score: 1}, {Value: "two", Score: 2},
{Value: "thirty-six", Score: 36}, {Value: "twelve", Score: 12},
{Value: "eleven", Score: 11},
}),
"key33": ss.NewSortedSet([]ss.MemberParam{
{Value: "seven", Score: 7}, {Value: "eight", Score: 8},
{Value: "nine", Score: 9}, {Value: "ten", Score: 10},
{Value: "twelve", Score: 12},
}),
},
destination: "destination12",
keys: []string{"non-existent", "key32", "key33"},
want: 9,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.preset {
for k, v := range tt.presetValues {
err := presetValue(server, context.Background(), k, v)
if err != nil {
t.Error(err)
return
}
}
}
got, err := server.ZUnionStore(tt.destination, tt.keys, tt.options)
if (err != nil) != tt.wantErr {
t.Errorf("ZUNIONSTORE() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ZUNIONSTORE() got = %v, want %v", got, tt.want)
}
})
}
}