mirror of
https://github.com/EchoVault/SugarDB.git
synced 2025-12-18 16:08:14 +08:00
Return "empty command" error from handleCommand method if an empty command is passed to the server. Wait until connection is no longer nil in acl package tests.
2393 lines
76 KiB
Go
2393 lines
76 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 set_test
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"github.com/echovault/echovault/echovault"
|
|
"github.com/echovault/echovault/internal"
|
|
"github.com/echovault/echovault/internal/config"
|
|
"github.com/echovault/echovault/internal/constants"
|
|
"github.com/echovault/echovault/internal/modules/set"
|
|
"github.com/tidwall/resp"
|
|
"net"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
var mockServer *echovault.EchoVault
|
|
var addr = "localhost"
|
|
var port int
|
|
|
|
func init() {
|
|
port, _ = internal.GetFreePort()
|
|
mockServer, _ = echovault.NewEchoVault(
|
|
echovault.WithConfig(config.Config{
|
|
BindAddr: addr,
|
|
Port: uint16(port),
|
|
DataDir: "",
|
|
EvictionPolicy: constants.NoEviction,
|
|
}),
|
|
)
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(1)
|
|
go func() {
|
|
wg.Done()
|
|
mockServer.Start()
|
|
}()
|
|
wg.Wait()
|
|
}
|
|
|
|
func Test_HandleSADD(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
preset bool
|
|
presetValue interface{}
|
|
key string
|
|
command []string
|
|
expectedValue *set.Set
|
|
expectedResponse int
|
|
expectedError error
|
|
}{
|
|
{
|
|
name: "1. Create new set on a non-existent key, return count of added elements",
|
|
preset: false,
|
|
presetValue: nil,
|
|
key: "SaddKey1",
|
|
command: []string{"SADD", "SaddKey1", "one", "two", "three", "four"},
|
|
expectedValue: set.NewSet([]string{"one", "two", "three", "four"}),
|
|
expectedResponse: 4,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "2. Add members to an exiting set, skip members that already exist in the set, return added count.",
|
|
preset: true,
|
|
presetValue: set.NewSet([]string{"one", "two", "three", "four"}),
|
|
key: "SaddKey2",
|
|
command: []string{"SADD", "SaddKey2", "three", "four", "five", "six", "seven"},
|
|
expectedValue: set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven"}),
|
|
expectedResponse: 3,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "3. Throw error when trying to add to a key that does not hold a set",
|
|
preset: true,
|
|
presetValue: "Default value",
|
|
key: "SaddKey3",
|
|
command: []string{"SADD", "SaddKey3", "member"},
|
|
expectedResponse: 0,
|
|
expectedError: errors.New("value at key SaddKey3 is not a set"),
|
|
},
|
|
{
|
|
name: "4. Command too short",
|
|
preset: false,
|
|
key: "SaddKey4",
|
|
command: []string{"SADD", "SaddKey4"},
|
|
expectedValue: nil,
|
|
expectedResponse: 0,
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValue != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
|
|
switch test.presetValue.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(test.key),
|
|
resp.StringValue(test.presetValue.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(test.key)}
|
|
for _, element := range test.presetValue.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(test.presetValue.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
if res.Integer() != test.expectedResponse {
|
|
t.Errorf("expected response \"%d\", got \"%d\"", test.expectedResponse, res.Integer())
|
|
}
|
|
|
|
// Check if the resulting set(s) contain the expected members.
|
|
if test.expectedValue == nil {
|
|
return
|
|
}
|
|
|
|
if err := client.WriteArray([]resp.Value{resp.StringValue("SMEMBERS"), resp.StringValue(test.key)}); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err = client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if len(res.Array()) != test.expectedValue.Cardinality() {
|
|
t.Errorf("expected set at key \"%s\" to have cardinality %d, got %d",
|
|
test.key, test.expectedValue.Cardinality(), len(res.Array()))
|
|
}
|
|
|
|
for _, item := range res.Array() {
|
|
if !test.expectedValue.Contains(item.String()) {
|
|
t.Errorf("unexpected memeber \"%s\", in response", item.String())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_HandleSCARD(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
presetValue interface{}
|
|
key string
|
|
command []string
|
|
expectedValue *set.Set
|
|
expectedResponse int
|
|
expectedError error
|
|
}{
|
|
{
|
|
name: "1. Get cardinality of valid set.",
|
|
presetValue: set.NewSet([]string{"one", "two", "three", "four"}),
|
|
key: "ScardKey1",
|
|
command: []string{"SCARD", "ScardKey1"},
|
|
expectedValue: nil,
|
|
expectedResponse: 4,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "2. Return 0 when trying to get cardinality on non-existent key",
|
|
presetValue: nil,
|
|
key: "ScardKey2",
|
|
command: []string{"SCARD", "ScardKey2"},
|
|
expectedValue: nil,
|
|
expectedResponse: 0,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "3. Throw error when trying to get cardinality of a value that is not a set",
|
|
presetValue: "Default value",
|
|
key: "ScardKey3",
|
|
command: []string{"SCARD", "ScardKey3"},
|
|
expectedResponse: 0,
|
|
expectedError: errors.New("value at key ScardKey3 is not a set"),
|
|
},
|
|
{
|
|
name: "4. Command too short",
|
|
key: "ScardKey4",
|
|
command: []string{"SCARD"},
|
|
expectedValue: nil,
|
|
expectedResponse: 0,
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
{
|
|
name: "5. Command too long",
|
|
key: "ScardKey5",
|
|
command: []string{"SCARD", "ScardKey5", "ScardKey5"},
|
|
expectedValue: nil,
|
|
expectedResponse: 0,
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValue != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
|
|
switch test.presetValue.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(test.key),
|
|
resp.StringValue(test.presetValue.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(test.key)}
|
|
for _, element := range test.presetValue.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(test.presetValue.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
if res.Integer() != test.expectedResponse {
|
|
t.Errorf("expected response \"%d\", got \"%d\"", test.expectedResponse, res.Integer())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_HandleSDIFF(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
presetValues map[string]interface{}
|
|
command []string
|
|
expectedResponse []string
|
|
expectedError error
|
|
}{
|
|
{
|
|
name: "1. Get the difference between 2 sets.",
|
|
presetValues: map[string]interface{}{
|
|
"SdiffKey1": set.NewSet([]string{"one", "two", "three", "four", "five"}),
|
|
"SdiffKey2": set.NewSet([]string{"three", "four", "five", "six", "seven", "eight"}),
|
|
},
|
|
command: []string{"SDIFF", "SdiffKey1", "SdiffKey2"},
|
|
expectedResponse: []string{"one", "two"},
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "2. Get the difference between 3 sets.",
|
|
presetValues: map[string]interface{}{
|
|
"SdiffKey3": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
"SdiffKey4": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven"}),
|
|
"SdiffKey5": set.NewSet([]string{"seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
command: []string{"SDIFF", "SdiffKey3", "SdiffKey4", "SdiffKey5"},
|
|
expectedResponse: []string{"three", "four", "five", "six"},
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "3. Return base set element if base set is the only valid set",
|
|
presetValues: map[string]interface{}{
|
|
"SdiffKey6": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
"SdiffKey7": "Default value",
|
|
"SdiffKey8": "123456789",
|
|
},
|
|
command: []string{"SDIFF", "SdiffKey6", "SdiffKey7", "SdiffKey8"},
|
|
expectedResponse: []string{"one", "two", "three", "four", "five", "six", "seven", "eight"},
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "4. Throw error when base set is not a set.",
|
|
presetValues: map[string]interface{}{
|
|
"SdiffKey9": "Default value",
|
|
"SdiffKey10": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven"}),
|
|
"SdiffKey11": set.NewSet([]string{"seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
command: []string{"SDIFF", "SdiffKey9", "SdiffKey10", "SdiffKey11"},
|
|
expectedResponse: nil,
|
|
expectedError: errors.New("value at key SdiffKey9 is not a set"),
|
|
},
|
|
{
|
|
name: "5. Throw error when base set is non-existent.",
|
|
presetValues: map[string]interface{}{
|
|
"SdiffKey12": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven"}),
|
|
"SdiffKey13": set.NewSet([]string{"seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
command: []string{"SDIFF", "non-existent", "SdiffKey7", "SdiffKey8"},
|
|
expectedResponse: nil,
|
|
expectedError: errors.New("key for base set \"non-existent\" does not exist"),
|
|
},
|
|
{
|
|
name: "6. Command too short",
|
|
command: []string{"SDIFF"},
|
|
expectedResponse: []string{},
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValues != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
for key, value := range test.presetValues {
|
|
switch value.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(key),
|
|
resp.StringValue(value.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(key)}
|
|
for _, element := range value.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(value.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
if len(res.Array()) != len(test.expectedResponse) {
|
|
t.Errorf("expected response array of length \"%d\", got \"%d\"",
|
|
len(test.expectedResponse), len(res.Array()))
|
|
}
|
|
|
|
for _, item := range res.Array() {
|
|
if !slices.Contains(test.expectedResponse, item.String()) {
|
|
t.Errorf("unexpected element \"%s\" in response", item.String())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_HandleSDIFFSTORE(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
presetValues map[string]interface{}
|
|
destination string
|
|
command []string
|
|
expectedValue *set.Set
|
|
expectedResponse int
|
|
expectedError error
|
|
}{
|
|
{
|
|
name: "1. Get the difference between 2 sets.",
|
|
presetValues: map[string]interface{}{
|
|
"SdiffStoreKey1": set.NewSet([]string{"one", "two", "three", "four", "five"}),
|
|
"SdiffStoreKey2": set.NewSet([]string{"three", "four", "five", "six", "seven", "eight"}),
|
|
},
|
|
destination: "SdiffStoreDestination1",
|
|
command: []string{"SDIFFSTORE", "SdiffStoreDestination1", "SdiffStoreKey1", "SdiffStoreKey2"},
|
|
expectedValue: set.NewSet([]string{"one", "two"}),
|
|
expectedResponse: 2,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "2. Get the difference between 3 sets.",
|
|
presetValues: map[string]interface{}{
|
|
"SdiffStoreKey3": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
"SdiffStoreKey4": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven"}),
|
|
"SdiffStoreKey5": set.NewSet([]string{"seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
destination: "SdiffStoreDestination2",
|
|
command: []string{"SDIFFSTORE", "SdiffStoreDestination2", "SdiffStoreKey3", "SdiffStoreKey4", "SdiffStoreKey5"},
|
|
expectedValue: set.NewSet([]string{"three", "four", "five", "six"}),
|
|
expectedResponse: 4,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "3. Return base set element if base set is the only valid set",
|
|
presetValues: map[string]interface{}{
|
|
"SdiffStoreKey6": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
"SdiffStoreKey7": "Default value",
|
|
"SdiffStoreKey8": "123456789",
|
|
},
|
|
destination: "SdiffStoreDestination3",
|
|
command: []string{"SDIFFSTORE", "SdiffStoreDestination3", "SdiffStoreKey6", "SdiffStoreKey7", "SdiffStoreKey8"},
|
|
expectedValue: set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
expectedResponse: 8,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "4. Throw error when base set is not a set.",
|
|
presetValues: map[string]interface{}{
|
|
"SdiffStoreKey9": "Default value",
|
|
"SdiffStoreKey10": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven"}),
|
|
"SdiffStoreKey11": set.NewSet([]string{"seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
destination: "SdiffStoreDestination4",
|
|
command: []string{"SDIFFSTORE", "SdiffStoreDestination4", "SdiffStoreKey9", "SdiffStoreKey10", "SdiffStoreKey11"},
|
|
expectedValue: nil,
|
|
expectedResponse: 0,
|
|
expectedError: errors.New("value at key SdiffStoreKey9 is not a set"),
|
|
},
|
|
{
|
|
name: "5. Throw error when base set is non-existent.",
|
|
destination: "SdiffStoreDestination5",
|
|
presetValues: map[string]interface{}{
|
|
"SdiffStoreKey12": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven"}),
|
|
"SdiffStoreKey13": set.NewSet([]string{"seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
command: []string{"SDIFFSTORE", "SdiffStoreDestination5", "non-existent", "SdiffStoreKey7", "SdiffStoreKey8"},
|
|
expectedValue: nil,
|
|
expectedResponse: 0,
|
|
expectedError: errors.New("key for base set \"non-existent\" does not exist"),
|
|
},
|
|
{
|
|
name: "6. Command too short",
|
|
command: []string{"SDIFFSTORE", "SdiffStoreDestination6"},
|
|
expectedResponse: 0,
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValues != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
for key, value := range test.presetValues {
|
|
switch value.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(key),
|
|
resp.StringValue(value.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(key)}
|
|
for _, element := range value.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(value.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
if res.Integer() != test.expectedResponse {
|
|
t.Errorf("expected response \"%d\", got \"%d\"", test.expectedResponse, res.Integer())
|
|
}
|
|
|
|
// Check if the resulting set(s) contain the expected members.
|
|
if test.expectedValue == nil {
|
|
return
|
|
}
|
|
|
|
if err := client.WriteArray([]resp.Value{
|
|
resp.StringValue("SMEMBERS"),
|
|
resp.StringValue(test.destination),
|
|
}); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err = client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if len(res.Array()) != test.expectedValue.Cardinality() {
|
|
t.Errorf("expected set at key \"%s\" to have cardinality %d, got %d",
|
|
test.destination, test.expectedValue.Cardinality(), len(res.Array()))
|
|
}
|
|
|
|
for _, item := range res.Array() {
|
|
if !test.expectedValue.Contains(item.String()) {
|
|
t.Errorf("unexpected memeber \"%s\", in response", item.String())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_HandleSINTER(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
presetValues map[string]interface{}
|
|
command []string
|
|
expectedResponse []string
|
|
expectedError error
|
|
}{
|
|
{
|
|
name: "1. Get the intersection between 2 sets.",
|
|
presetValues: map[string]interface{}{
|
|
"SinterKey1": set.NewSet([]string{"one", "two", "three", "four", "five"}),
|
|
"SinterKey2": set.NewSet([]string{"three", "four", "five", "six", "seven", "eight"}),
|
|
},
|
|
command: []string{"SINTER", "SinterKey1", "SinterKey2"},
|
|
expectedResponse: []string{"three", "four", "five"},
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "2. Get the intersection between 3 sets.",
|
|
presetValues: map[string]interface{}{
|
|
"SinterKey3": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
"SinterKey4": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven", "eight"}),
|
|
"SinterKey5": set.NewSet([]string{"one", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
command: []string{"SINTER", "SinterKey3", "SinterKey4", "SinterKey5"},
|
|
expectedResponse: []string{"one", "eight"},
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "3. Throw an error if any of the provided keys are not sets",
|
|
presetValues: map[string]interface{}{
|
|
"SinterKey6": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
"SinterKey7": "Default value",
|
|
"SinterKey8": set.NewSet([]string{"one"}),
|
|
},
|
|
command: []string{"SINTER", "SinterKey6", "SinterKey7", "SinterKey8"},
|
|
expectedResponse: nil,
|
|
expectedError: errors.New("value at key SinterKey7 is not a set"),
|
|
},
|
|
{
|
|
name: "4. Throw error when base set is not a set.",
|
|
presetValues: map[string]interface{}{
|
|
"SinterKey9": "Default value",
|
|
"SinterKey10": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven"}),
|
|
"SinterKey11": set.NewSet([]string{"seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
command: []string{"SINTER", "SinterKey9", "SinterKey10", "SinterKey11"},
|
|
expectedResponse: nil,
|
|
expectedError: errors.New("value at key SinterKey9 is not a set"),
|
|
},
|
|
{
|
|
name: "5. If any of the keys does not exist, return an empty array.",
|
|
presetValues: map[string]interface{}{
|
|
"SinterKey12": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven"}),
|
|
"SinterKey13": set.NewSet([]string{"seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
command: []string{"SINTER", "non-existent", "SinterKey7", "SinterKey8"},
|
|
expectedResponse: []string{},
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "6. Command too short",
|
|
command: []string{"SINTER"},
|
|
expectedResponse: []string{},
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValues != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
for key, value := range test.presetValues {
|
|
switch value.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(key),
|
|
resp.StringValue(value.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(key)}
|
|
for _, element := range value.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(value.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
if len(res.Array()) != len(test.expectedResponse) {
|
|
t.Errorf("expected response array of length \"%d\", got \"%d\"",
|
|
len(test.expectedResponse), len(res.Array()))
|
|
}
|
|
|
|
for _, item := range res.Array() {
|
|
if !slices.Contains(test.expectedResponse, item.String()) {
|
|
t.Errorf("unexpected element \"%s\" in response", item.String())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_HandleSINTERCARD(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
presetValues map[string]interface{}
|
|
command []string
|
|
expectedResponse int
|
|
expectedError error
|
|
}{
|
|
{
|
|
name: "1. Get the full intersect cardinality between 2 sets.",
|
|
presetValues: map[string]interface{}{
|
|
"SinterCardKey1": set.NewSet([]string{"one", "two", "three", "four", "five"}),
|
|
"SinterCardKey2": set.NewSet([]string{"three", "four", "five", "six", "seven", "eight"}),
|
|
},
|
|
command: []string{"SINTERCARD", "SinterCardKey1", "SinterCardKey2"},
|
|
expectedResponse: 3,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "2. Get an intersect cardinality between 2 sets with a limit",
|
|
presetValues: map[string]interface{}{
|
|
"SinterCardKey3": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"}),
|
|
"SinterCardKey4": set.NewSet([]string{"three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve"}),
|
|
},
|
|
command: []string{"SINTERCARD", "SinterCardKey3", "SinterCardKey4", "LIMIT", "3"},
|
|
expectedResponse: 3,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "3. Get the full intersect cardinality between 3 sets.",
|
|
presetValues: map[string]interface{}{
|
|
"SinterCardKey5": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
"SinterCardKey6": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven", "eight"}),
|
|
"SinterCardKey7": set.NewSet([]string{"one", "seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
command: []string{"SINTERCARD", "SinterCardKey5", "SinterCardKey6", "SinterCardKey7"},
|
|
expectedResponse: 2,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "4. Get the intersection of 3 sets with a limit",
|
|
presetValues: map[string]interface{}{
|
|
"SinterCardKey8": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
"SinterCardKey9": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven", "eight"}),
|
|
"SinterCardKey10": set.NewSet([]string{"one", "two", "seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
command: []string{"SINTERCARD", "SinterCardKey8", "SinterCardKey9", "SinterCardKey10", "LIMIT", "2"},
|
|
expectedResponse: 2,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "5. Return 0 if any of the keys does not exist",
|
|
presetValues: map[string]interface{}{
|
|
"SinterCardKey11": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
"SinterCardKey12": "Default value",
|
|
"SinterCardKey13": set.NewSet([]string{"one"}),
|
|
},
|
|
command: []string{"SINTERCARD", "SinterCardKey11", "SinterCardKey12", "SinterCardKey13", "non-existent"},
|
|
expectedResponse: 0,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "6. Throw error when one of the keys is not a valid set.",
|
|
presetValues: map[string]interface{}{
|
|
"SinterCardKey14": "Default value",
|
|
"SinterCardKey15": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven"}),
|
|
"SinterCardKey16": set.NewSet([]string{"seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
command: []string{"SINTERCARD", "SinterCardKey14", "SinterCardKey15", "SinterCardKey16"},
|
|
expectedResponse: 0,
|
|
expectedError: errors.New("value at key SinterCardKey14 is not a set"),
|
|
},
|
|
{
|
|
name: "7. Command too short",
|
|
command: []string{"SINTERCARD"},
|
|
expectedResponse: 0,
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValues != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
for key, value := range test.presetValues {
|
|
switch value.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(key),
|
|
resp.StringValue(value.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(key)}
|
|
for _, element := range value.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(value.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
if res.Integer() != test.expectedResponse {
|
|
t.Errorf("expected response array of length \"%d\", got \"%d\"", test.expectedResponse, res.Integer())
|
|
}
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func Test_HandleSINTERSTORE(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
presetValues map[string]interface{}
|
|
destination string
|
|
command []string
|
|
expectedValue *set.Set
|
|
expectedResponse int
|
|
expectedError error
|
|
}{
|
|
{
|
|
name: "1. Get the intersection between 2 sets and store it at the destination.",
|
|
presetValues: map[string]interface{}{
|
|
"SinterStoreKey1": set.NewSet([]string{"one", "two", "three", "four", "five"}),
|
|
"SinterStoreKey2": set.NewSet([]string{"three", "four", "five", "six", "seven", "eight"}),
|
|
},
|
|
destination: "SinterStoreDestination1",
|
|
command: []string{"SINTERSTORE", "SinterStoreDestination1", "SinterStoreKey1", "SinterStoreKey2"},
|
|
expectedValue: set.NewSet([]string{"three", "four", "five"}),
|
|
expectedResponse: 3,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "2. Get the intersection between 3 sets and store it at the destination key.",
|
|
presetValues: map[string]interface{}{
|
|
"SinterStoreKey3": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
"SinterStoreKey4": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven", "eight"}),
|
|
"SinterStoreKey5": set.NewSet([]string{"one", "seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
destination: "SinterStoreDestination2",
|
|
command: []string{"SINTERSTORE", "SinterStoreDestination2", "SinterStoreKey3", "SinterStoreKey4", "SinterStoreKey5"},
|
|
expectedValue: set.NewSet([]string{"one", "eight"}),
|
|
expectedResponse: 2,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "3. Throw error when any of the keys is not a set",
|
|
presetValues: map[string]interface{}{
|
|
"SinterStoreKey6": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
"SinterStoreKey7": "Default value",
|
|
"SinterStoreKey8": set.NewSet([]string{"one"}),
|
|
},
|
|
destination: "SinterStoreDestination3",
|
|
command: []string{"SINTERSTORE", "SinterStoreDestination3", "SinterStoreKey6", "SinterStoreKey7", "SinterStoreKey8"},
|
|
expectedValue: nil,
|
|
expectedResponse: 0,
|
|
expectedError: errors.New("value at key SinterStoreKey7 is not a set"),
|
|
},
|
|
{
|
|
name: "4. Throw error when base set is not a set.",
|
|
presetValues: map[string]interface{}{
|
|
"SinterStoreKey9": "Default value",
|
|
"SinterStoreKey10": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven"}),
|
|
"SinterStoreKey11": set.NewSet([]string{"seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
destination: "SinterStoreDestination4",
|
|
command: []string{"SINTERSTORE", "SinterStoreDestination4", "SinterStoreKey9", "SinterStoreKey10", "SinterStoreKey11"},
|
|
expectedValue: nil,
|
|
expectedResponse: 0,
|
|
expectedError: errors.New("value at key SinterStoreKey9 is not a set"),
|
|
},
|
|
{
|
|
name: "5. Return an empty intersection if one of the keys does not exist.",
|
|
destination: "SinterStoreDestination5",
|
|
presetValues: map[string]interface{}{
|
|
"SinterStoreKey12": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven"}),
|
|
"SinterStoreKey13": set.NewSet([]string{"seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
command: []string{"SINTERSTORE", "SinterStoreDestination5", "non-existent", "SinterStoreKey7", "SinterStoreKey8"},
|
|
expectedValue: nil,
|
|
expectedResponse: 0,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "6. Command too short",
|
|
command: []string{"SINTERSTORE", "SinterStoreDestination6"},
|
|
expectedResponse: 0,
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValues != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
for key, value := range test.presetValues {
|
|
switch value.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(key),
|
|
resp.StringValue(value.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(key)}
|
|
for _, element := range value.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(value.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
if res.Integer() != test.expectedResponse {
|
|
t.Errorf("expected response \"%d\", got \"%d\"", test.expectedResponse, res.Integer())
|
|
}
|
|
|
|
// Check if the resulting set(s) contain the expected members.
|
|
if test.expectedValue == nil {
|
|
return
|
|
}
|
|
|
|
if err := client.WriteArray([]resp.Value{
|
|
resp.StringValue("SMEMBERS"),
|
|
resp.StringValue(test.destination),
|
|
}); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err = client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if len(res.Array()) != test.expectedValue.Cardinality() {
|
|
t.Errorf("expected set at key \"%s\" to have cardinality %d, got %d",
|
|
test.destination, test.expectedValue.Cardinality(), len(res.Array()))
|
|
}
|
|
|
|
for _, item := range res.Array() {
|
|
if !test.expectedValue.Contains(item.String()) {
|
|
t.Errorf("unexpected memeber \"%s\", in response", item.String())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_HandleSISMEMBER(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
presetValue interface{}
|
|
key string
|
|
command []string
|
|
expectedResponse int
|
|
expectedError error
|
|
}{
|
|
{
|
|
name: "1. Return 1 when element is a member of the set",
|
|
presetValue: set.NewSet([]string{"one", "two", "three", "four"}),
|
|
key: "SIsMemberKey1",
|
|
command: []string{"SISMEMBER", "SIsMemberKey1", "three"},
|
|
expectedResponse: 1,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "2. Return 0 when element is not a member of the set",
|
|
presetValue: set.NewSet([]string{"one", "two", "three", "four"}),
|
|
key: "SIsMemberKey2",
|
|
command: []string{"SISMEMBER", "SIsMemberKey2", "five"},
|
|
expectedResponse: 0,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "3. Throw error when trying to assert membership when the key does not hold a valid set",
|
|
presetValue: "Default value",
|
|
key: "SIsMemberKey3",
|
|
command: []string{"SISMEMBER", "SIsMemberKey3", "one"},
|
|
expectedResponse: 0,
|
|
expectedError: errors.New("value at key SIsMemberKey3 is not a set"),
|
|
},
|
|
{
|
|
name: "4. Command too short",
|
|
key: "SIsMemberKey4",
|
|
command: []string{"SISMEMBER", "SIsMemberKey4"},
|
|
expectedResponse: 0,
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
{
|
|
name: "5. Command too long",
|
|
key: "SIsMemberKey5",
|
|
command: []string{"SISMEMBER", "SIsMemberKey5", "one", "two", "three"},
|
|
expectedResponse: 0,
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValue != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
|
|
switch test.presetValue.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(test.key),
|
|
resp.StringValue(test.presetValue.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(test.key)}
|
|
for _, element := range test.presetValue.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(test.presetValue.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
if res.Integer() != test.expectedResponse {
|
|
t.Errorf("expected response \"%d\", got \"%d\"", test.expectedResponse, res.Integer())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_HandleSMEMBERS(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
key string
|
|
presetValue interface{}
|
|
command []string
|
|
expectedResponse []string
|
|
expectedError error
|
|
}{
|
|
{
|
|
name: "1. Return all the members of the set.",
|
|
key: "SmembersKey1",
|
|
presetValue: set.NewSet([]string{"one", "two", "three", "four", "five"}),
|
|
command: []string{"SMEMBERS", "SmembersKey1"},
|
|
expectedResponse: []string{"one", "two", "three", "four", "five"},
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "2. If the key does not exist, return an empty array.",
|
|
key: "SmembersKey2",
|
|
presetValue: nil,
|
|
command: []string{"SMEMBERS", "SmembersKey2"},
|
|
expectedResponse: nil,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "3. Throw error when the provided key is not a set.",
|
|
key: "SmembersKey3",
|
|
presetValue: "Default value",
|
|
command: []string{"SMEMBERS", "SmembersKey3"},
|
|
expectedResponse: nil,
|
|
expectedError: errors.New("value at key SmembersKey3 is not a set"),
|
|
},
|
|
{
|
|
name: "4. Command too short",
|
|
command: []string{"SMEMBERS"},
|
|
expectedResponse: []string{},
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
{
|
|
name: "5. Command too long",
|
|
command: []string{"SMEMBERS", "SmembersKey5", "SmembersKey6"},
|
|
expectedResponse: []string{},
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValue != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
|
|
switch test.presetValue.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(test.key),
|
|
resp.StringValue(test.presetValue.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(test.key)}
|
|
for _, element := range test.presetValue.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(test.presetValue.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
if len(res.Array()) != len(test.expectedResponse) {
|
|
t.Errorf("expected response array of length \"%d\", got \"%d\"",
|
|
len(test.expectedResponse), len(res.Array()))
|
|
}
|
|
|
|
for _, item := range res.Array() {
|
|
if !slices.Contains(test.expectedResponse, item.String()) {
|
|
t.Errorf("unexpected element \"%s\" in response", item.String())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_HandleSMISMEMBER(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
presetValue interface{}
|
|
key string
|
|
command []string
|
|
expectedResponse []int
|
|
expectedError error
|
|
}{
|
|
{
|
|
// 1. Return set membership status for multiple elements
|
|
// Return 1 for present and 0 for absent
|
|
// The placement of the membership status flag should me consistent with the order the elements
|
|
// are in within the original command
|
|
name: "1. Return set membership status for multiple elements",
|
|
presetValue: set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven"}),
|
|
key: "SmismemberKey1",
|
|
command: []string{"SMISMEMBER", "SmismemberKey1", "three", "four", "five", "six", "eight", "nine", "seven"},
|
|
expectedResponse: []int{1, 1, 1, 1, 0, 0, 1},
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "2. If the set key does not exist, return an array of zeroes as long as the list of members",
|
|
presetValue: nil,
|
|
key: "SmismemberKey2",
|
|
command: []string{"SMISMEMBER", "SmismemberKey2", "one", "two", "three", "four"},
|
|
expectedResponse: []int{0, 0, 0, 0},
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "3. Throw error when trying to assert membership when the key does not hold a valid set",
|
|
presetValue: "Default value",
|
|
key: "SmismemberKey3",
|
|
command: []string{"SMISMEMBER", "SmismemberKey3", "one"},
|
|
expectedResponse: nil,
|
|
expectedError: errors.New("value at key SmismemberKey3 is not a set"),
|
|
},
|
|
{
|
|
name: "4. Command too short",
|
|
presetValue: nil,
|
|
key: "SmismemberKey4",
|
|
command: []string{"SMISMEMBER", "SmismemberKey4"},
|
|
expectedResponse: nil,
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValue != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
|
|
switch test.presetValue.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(test.key),
|
|
resp.StringValue(test.presetValue.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(test.key)}
|
|
for _, element := range test.presetValue.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(test.presetValue.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
if len(res.Array()) != len(test.expectedResponse) {
|
|
t.Errorf("expected response array of length \"%d\", got \"%d\"",
|
|
len(test.expectedResponse), len(res.Array()))
|
|
}
|
|
|
|
for _, item := range res.Array() {
|
|
if !slices.Contains(test.expectedResponse, item.Integer()) {
|
|
t.Errorf("unexpected element \"%d\" in response", item.Integer())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_HandleSMOVE(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
presetValues map[string]interface{}
|
|
command []string
|
|
expectedValues map[string]interface{}
|
|
expectedResponse int
|
|
expectedError error
|
|
}{
|
|
{
|
|
name: "1. Return 1 after a successful move of a member from source set to destination set",
|
|
presetValues: map[string]interface{}{
|
|
"SmoveSource1": set.NewSet([]string{"one", "two", "three", "four"}),
|
|
"SmoveDestination1": set.NewSet([]string{"five", "six", "seven", "eight"}),
|
|
},
|
|
command: []string{"SMOVE", "SmoveSource1", "SmoveDestination1", "four"},
|
|
expectedValues: map[string]interface{}{
|
|
"SmoveSource1": set.NewSet([]string{"one", "two", "three"}),
|
|
"SmoveDestination1": set.NewSet([]string{"four", "five", "six", "seven", "eight"}),
|
|
},
|
|
expectedResponse: 1,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "2. Return 0 when trying to move a member from source set to destination set when it doesn't exist in source",
|
|
presetValues: map[string]interface{}{
|
|
"SmoveSource2": set.NewSet([]string{"one", "two", "three", "four", "five"}),
|
|
"SmoveDestination2": set.NewSet([]string{"five", "six", "seven", "eight"}),
|
|
},
|
|
command: []string{"SMOVE", "SmoveSource2", "SmoveDestination2", "six"},
|
|
expectedValues: map[string]interface{}{
|
|
"SmoveSource2": set.NewSet([]string{"one", "two", "three", "four", "five"}),
|
|
"SmoveDestination2": set.NewSet([]string{"five", "six", "seven", "eight"}),
|
|
},
|
|
expectedResponse: 0,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "3. Return error when the source key is not a set",
|
|
presetValues: map[string]interface{}{
|
|
"SmoveSource3": "Default value",
|
|
"SmoveDestination3": set.NewSet([]string{"five", "six", "seven", "eight"}),
|
|
},
|
|
command: []string{"SMOVE", "SmoveSource3", "SmoveDestination3", "five"},
|
|
expectedValues: map[string]interface{}{
|
|
"SmoveSource3": "Default value",
|
|
"SmoveDestination3": set.NewSet([]string{"five", "six", "seven", "eight"}),
|
|
},
|
|
expectedResponse: 0,
|
|
expectedError: errors.New("source is not a set"),
|
|
},
|
|
{
|
|
name: "4. Return error when the destination key is not a set",
|
|
presetValues: map[string]interface{}{
|
|
"SmoveSource4": set.NewSet([]string{"one", "two", "three", "four", "five"}),
|
|
"SmoveDestination4": "Default value",
|
|
},
|
|
command: []string{"SMOVE", "SmoveSource4", "SmoveDestination4", "five"},
|
|
expectedValues: map[string]interface{}{
|
|
"SmoveSource4": set.NewSet([]string{"one", "two", "three", "four", "five"}),
|
|
"SmoveDestination4": "Default value",
|
|
},
|
|
expectedResponse: 0,
|
|
expectedError: errors.New("destination is not a set"),
|
|
},
|
|
{
|
|
name: "5. Command too short",
|
|
presetValues: nil,
|
|
command: []string{"SMOVE", "SmoveSource5", "SmoveSource6"},
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
{
|
|
name: "6. Command too long",
|
|
presetValues: nil,
|
|
command: []string{"SMOVE", "SmoveSource5", "SmoveSource6", "member1", "member2"},
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValues != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
for key, value := range test.presetValues {
|
|
switch value.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(key),
|
|
resp.StringValue(value.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(key)}
|
|
for _, element := range value.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(value.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
if res.Integer() != test.expectedResponse {
|
|
t.Errorf("expected response \"%d\", got \"%d\"", test.expectedResponse, res.Integer())
|
|
}
|
|
|
|
// Check if the resulting set(s) contain the expected members.
|
|
if test.expectedValues == nil {
|
|
return
|
|
}
|
|
|
|
for key, value := range test.expectedValues {
|
|
switch value.(type) {
|
|
case string:
|
|
if err := client.WriteArray([]resp.Value{resp.StringValue("GET"), resp.StringValue(key)}); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err = client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if res.String() != value.(string) {
|
|
t.Errorf("expected value at key \"%s\" to be \"%s\", got \"%s\"", key, value.(string), res.String())
|
|
}
|
|
case *set.Set:
|
|
if err := client.WriteArray([]resp.Value{
|
|
resp.StringValue("SMEMBERS"),
|
|
resp.StringValue(key),
|
|
}); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err = client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if len(res.Array()) != value.(*set.Set).Cardinality() {
|
|
t.Errorf("expected set at key \"%s\" to have cardinality %d, got %d",
|
|
key, value.(*set.Set).Cardinality(), len(res.Array()))
|
|
}
|
|
|
|
for _, item := range res.Array() {
|
|
if !value.(*set.Set).Contains(item.String()) {
|
|
t.Errorf("unexpected memeber \"%s\", in response", item.String())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_HandleSPOP(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
key string
|
|
presetValue interface{}
|
|
command []string
|
|
expectedValue int // The final cardinality of the resulting set
|
|
expectedResponse []string
|
|
expectedError error
|
|
}{
|
|
{
|
|
name: "1. Return multiple popped elements and modify the set",
|
|
key: "SpopKey1",
|
|
presetValue: set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
command: []string{"SPOP", "SpopKey1", "3"},
|
|
expectedValue: 5,
|
|
expectedResponse: []string{"one", "two", "three", "four", "five", "six", "seven", "eight"},
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "2. Return error when the source key is not a set",
|
|
key: "SpopKey2",
|
|
presetValue: "Default value",
|
|
command: []string{"SPOP", "SpopKey2"},
|
|
expectedValue: 0,
|
|
expectedResponse: nil,
|
|
expectedError: errors.New("value at SpopKey2 is not a set"),
|
|
},
|
|
{
|
|
name: "3. Command too short",
|
|
presetValue: nil,
|
|
command: []string{"SPOP"},
|
|
expectedValue: 0,
|
|
expectedResponse: nil,
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
{
|
|
name: "4. Command too long",
|
|
presetValue: nil,
|
|
command: []string{"SPOP", "SpopSource5", "SpopSource6", "member1", "member2"},
|
|
expectedValue: 0,
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
{
|
|
name: "5. Throw error when count is not an integer",
|
|
presetValue: nil,
|
|
command: []string{"SPOP", "SpopKey1", "count"},
|
|
expectedValue: 0,
|
|
expectedError: errors.New("count must be an integer"),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValue != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
|
|
switch test.presetValue.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(test.key),
|
|
resp.StringValue(test.presetValue.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(test.key)}
|
|
for _, element := range test.presetValue.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(test.presetValue.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
// Check that each returned element is in the list of expected elements.
|
|
for _, item := range res.Array() {
|
|
if !slices.Contains(test.expectedResponse, item.String()) {
|
|
t.Errorf("unexpected element \"%s\" in response", item.String())
|
|
}
|
|
}
|
|
|
|
// Check if the resulting set's cardinality is as expected.
|
|
if err := client.WriteArray([]resp.Value{resp.StringValue("SCARD"), resp.StringValue(test.key)}); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err = client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if res.Integer() != test.expectedValue {
|
|
t.Errorf("expected set at key \"%s\" to have cardinality %d, got %d",
|
|
test.key, test.expectedValue, res.Integer())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_HandleSRANDMEMBER(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
key string
|
|
presetValue interface{}
|
|
command []string
|
|
expectedValue int // The final cardinality of the resulting set
|
|
allowRepeat bool
|
|
expectedResponse []string
|
|
expectedError error
|
|
}{
|
|
{
|
|
// 1. Return multiple random elements without removing them
|
|
// Count is positive, do not allow repeated elements
|
|
name: "1. Return multiple random elements without removing them",
|
|
key: "SRandMemberKey1",
|
|
presetValue: set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
command: []string{"SRANDMEMBER", "SRandMemberKey1", "3"},
|
|
expectedValue: 8,
|
|
allowRepeat: false,
|
|
expectedResponse: []string{"one", "two", "three", "four", "five", "six", "seven", "eight"},
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
// 2. Return multiple random elements without removing them
|
|
// Count is negative, so allow repeated numbers
|
|
name: "2. Return multiple random elements without removing them",
|
|
key: "SRandMemberKey2",
|
|
presetValue: set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
command: []string{"SRANDMEMBER", "SRandMemberKey2", "-5"},
|
|
expectedValue: 8,
|
|
allowRepeat: true,
|
|
expectedResponse: []string{"one", "two", "three", "four", "five", "six", "seven", "eight"},
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "3. Return error when the source key is not a set",
|
|
key: "SRandMemberKey3",
|
|
presetValue: "Default value",
|
|
command: []string{"SRANDMEMBER", "SRandMemberKey3"},
|
|
expectedValue: 0,
|
|
expectedResponse: []string{},
|
|
expectedError: errors.New("value at SRandMemberKey3 is not a set"),
|
|
},
|
|
{
|
|
name: "4. Command too short",
|
|
command: []string{"SRANDMEMBER"},
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
{
|
|
name: "5. Command too long",
|
|
command: []string{"SRANDMEMBER", "SRandMemberSource5", "SRandMemberSource6", "member1", "member2"},
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
{
|
|
name: "6. Throw error when count is not an integer",
|
|
command: []string{"SRANDMEMBER", "SRandMemberKey1", "count"},
|
|
expectedError: errors.New("count must be an integer"),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValue != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
|
|
switch test.presetValue.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(test.key),
|
|
resp.StringValue(test.presetValue.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(test.key)}
|
|
for _, element := range test.presetValue.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(test.presetValue.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
// Check that each returned element is in the list of expected elements.
|
|
for _, item := range res.Array() {
|
|
if !slices.Contains(test.expectedResponse, item.String()) {
|
|
t.Errorf("unexpected element \"%s\" in response", item.String())
|
|
}
|
|
}
|
|
|
|
// If no repeats are allowed, check if the response contains any repeated elements
|
|
if !test.allowRepeat {
|
|
s := set.NewSet(func() []string {
|
|
elements := make([]string, len(res.Array()))
|
|
for i, item := range res.Array() {
|
|
elements[i] = item.String()
|
|
}
|
|
return elements
|
|
}())
|
|
if s.Cardinality() != len(res.Array()) {
|
|
t.Error("response has repeated elements, expected only unique elements.")
|
|
}
|
|
}
|
|
|
|
// Check if the resulting set's cardinality is as expected.
|
|
if err := client.WriteArray([]resp.Value{resp.StringValue("SCARD"), resp.StringValue(test.key)}); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err = client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if res.Integer() != test.expectedValue {
|
|
t.Errorf("expected set at key \"%s\" to have cardinality %d, got %d",
|
|
test.key, test.expectedValue, res.Integer())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_HandleSREM(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
key string
|
|
presetValue interface{}
|
|
command []string
|
|
expectedValue *set.Set // The final cardinality of the resulting set
|
|
expectedResponse int
|
|
expectedError error
|
|
}{
|
|
{
|
|
name: "1. Remove multiple elements and return the number of elements removed",
|
|
key: "SremKey1",
|
|
presetValue: set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
command: []string{"SREM", "SremKey1", "one", "two", "three", "nine"},
|
|
expectedValue: set.NewSet([]string{"four", "five", "six", "seven", "eight"}),
|
|
expectedResponse: 3,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "2. If key does not exist, return 0",
|
|
key: "SremKey2",
|
|
presetValue: nil,
|
|
command: []string{"SREM", "SremKey1", "one", "two", "three", "nine"},
|
|
expectedValue: nil,
|
|
expectedResponse: 0,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "3. Return error when the source key is not a set",
|
|
key: "SremKey3",
|
|
presetValue: "Default value",
|
|
command: []string{"SREM", "SremKey3", "one"},
|
|
expectedValue: nil,
|
|
expectedResponse: 0,
|
|
expectedError: errors.New("value at key SremKey3 is not a set"),
|
|
},
|
|
{
|
|
name: "4. Command too short",
|
|
command: []string{"SREM", "SremKey"},
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValue != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
|
|
switch test.presetValue.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(test.key),
|
|
resp.StringValue(test.presetValue.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(test.key)}
|
|
for _, element := range test.presetValue.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(test.presetValue.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
if res.Integer() != test.expectedResponse {
|
|
t.Errorf("expected response \"%d\", got \"%d\"", test.expectedResponse, res.Integer())
|
|
}
|
|
|
|
// Check if the resulting set(s) contain the expected members.
|
|
if test.expectedValue == nil {
|
|
return
|
|
}
|
|
|
|
if err := client.WriteArray([]resp.Value{resp.StringValue("SMEMBERS"), resp.StringValue(test.key)}); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err = client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if len(res.Array()) != test.expectedValue.Cardinality() {
|
|
t.Errorf("expected set at key \"%s\" to have cardinality %d, got %d",
|
|
test.key, test.expectedValue.Cardinality(), len(res.Array()))
|
|
}
|
|
|
|
for _, item := range res.Array() {
|
|
if !test.expectedValue.Contains(item.String()) {
|
|
t.Errorf("unexpected memeber \"%s\", in response", item.String())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_HandleSUNION(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
presetValues map[string]interface{}
|
|
command []string
|
|
expectedResponse []string
|
|
expectedError error
|
|
}{
|
|
{
|
|
name: "1. Get the union between 2 sets.",
|
|
presetValues: map[string]interface{}{
|
|
"SunionKey1": set.NewSet([]string{"one", "two", "three", "four", "five"}),
|
|
"SunionKey2": set.NewSet([]string{"three", "four", "five", "six", "seven", "eight"}),
|
|
},
|
|
command: []string{"SUNION", "SunionKey1", "SunionKey2"},
|
|
expectedResponse: []string{"one", "two", "three", "four", "five", "six", "seven", "eight"},
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "2. Get the union between 3 sets.",
|
|
presetValues: map[string]interface{}{
|
|
"SunionKey3": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
"SunionKey4": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven", "eight"}),
|
|
"SunionKey5": set.NewSet([]string{"one", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
command: []string{"SUNION", "SunionKey3", "SunionKey4", "SunionKey5"},
|
|
expectedResponse: []string{
|
|
"one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
|
"ten", "eleven", "twelve", "thirty-six",
|
|
},
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "3. Throw an error if any of the provided keys are not sets",
|
|
presetValues: map[string]interface{}{
|
|
"SunionKey6": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
"SunionKey7": "Default value",
|
|
"SunionKey8": set.NewSet([]string{"one"}),
|
|
},
|
|
command: []string{"SUNION", "SunionKey6", "SunionKey7", "SunionKey8"},
|
|
expectedResponse: nil,
|
|
expectedError: errors.New("value at key SunionKey7 is not a set"),
|
|
},
|
|
{
|
|
name: "4. Throw error any of the keys does not hold a set.",
|
|
presetValues: map[string]interface{}{
|
|
"SunionKey9": "Default value",
|
|
"SunionKey10": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven"}),
|
|
"SunionKey11": set.NewSet([]string{"seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
command: []string{"SUNION", "SunionKey9", "SunionKey10", "SunionKey11"},
|
|
expectedResponse: nil,
|
|
expectedError: errors.New("value at key SunionKey9 is not a set"),
|
|
},
|
|
{
|
|
name: "6. Command too short",
|
|
command: []string{"SUNION"},
|
|
expectedResponse: []string{},
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValues != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
for key, value := range test.presetValues {
|
|
switch value.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(key),
|
|
resp.StringValue(value.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(key)}
|
|
for _, element := range value.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(value.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
if len(res.Array()) != len(test.expectedResponse) {
|
|
t.Errorf("expected response array of length \"%d\", got \"%d\"",
|
|
len(test.expectedResponse), len(res.Array()))
|
|
}
|
|
|
|
for _, item := range res.Array() {
|
|
if !slices.Contains(test.expectedResponse, item.String()) {
|
|
t.Errorf("unexpected element \"%s\" in response", item.String())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_HandleSUNIONSTORE(t *testing.T) {
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
|
if err != nil {
|
|
t.Error()
|
|
}
|
|
client := resp.NewConn(conn)
|
|
|
|
tests := []struct {
|
|
name string
|
|
presetValues map[string]interface{}
|
|
destination string
|
|
command []string
|
|
expectedValue *set.Set
|
|
expectedResponse int
|
|
expectedError error
|
|
}{
|
|
{
|
|
name: "1. Get the intersection between 2 sets and store it at the destination.",
|
|
presetValues: map[string]interface{}{
|
|
"SunionStoreKey1": set.NewSet([]string{"one", "two", "three", "four", "five"}),
|
|
"SunionStoreKey2": set.NewSet([]string{"three", "four", "five", "six", "seven", "eight"}),
|
|
},
|
|
destination: "SunionStoreDestination1",
|
|
command: []string{"SUNIONSTORE", "SunionStoreDestination1", "SunionStoreKey1", "SunionStoreKey2"},
|
|
expectedValue: set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
expectedResponse: 8,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "2. Get the intersection between 3 sets and store it at the destination key.",
|
|
presetValues: map[string]interface{}{
|
|
"SunionStoreKey3": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
"SunionStoreKey4": set.NewSet([]string{"one", "two", "thirty-six", "twelve", "eleven", "eight"}),
|
|
"SunionStoreKey5": set.NewSet([]string{"one", "seven", "eight", "nine", "ten", "twelve"}),
|
|
},
|
|
destination: "SunionStoreDestination2",
|
|
command: []string{"SUNIONSTORE", "SunionStoreDestination2", "SunionStoreKey3", "SunionStoreKey4", "SunionStoreKey5"},
|
|
expectedValue: set.NewSet([]string{
|
|
"one", "two", "three", "four", "five", "six", "seven", "eight",
|
|
"nine", "ten", "eleven", "twelve", "thirty-six",
|
|
}),
|
|
expectedResponse: 13,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "3. Throw error when any of the keys is not a set",
|
|
presetValues: map[string]interface{}{
|
|
"SunionStoreKey6": set.NewSet([]string{"one", "two", "three", "four", "five", "six", "seven", "eight"}),
|
|
"SunionStoreKey7": "Default value",
|
|
"SunionStoreKey8": set.NewSet([]string{"one"}),
|
|
},
|
|
destination: "SunionStoreDestination3",
|
|
command: []string{"SUNIONSTORE", "SunionStoreDestination3", "SunionStoreKey6", "SunionStoreKey7", "SunionStoreKey8"},
|
|
expectedValue: nil,
|
|
expectedResponse: 0,
|
|
expectedError: errors.New("value at key SunionStoreKey7 is not a set"),
|
|
},
|
|
{
|
|
name: "5. Command too short",
|
|
command: []string{"SUNIONSTORE", "SunionStoreDestination6"},
|
|
expectedResponse: 0,
|
|
expectedError: errors.New(constants.WrongArgsResponse),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.presetValues != nil {
|
|
var command []resp.Value
|
|
var expected string
|
|
for key, value := range test.presetValues {
|
|
switch value.(type) {
|
|
case string:
|
|
command = []resp.Value{
|
|
resp.StringValue("SET"),
|
|
resp.StringValue(key),
|
|
resp.StringValue(value.(string)),
|
|
}
|
|
expected = "ok"
|
|
case *set.Set:
|
|
command = []resp.Value{resp.StringValue("SADD"), resp.StringValue(key)}
|
|
for _, element := range value.(*set.Set).GetAll() {
|
|
command = append(command, []resp.Value{resp.StringValue(element)}...)
|
|
}
|
|
expected = strconv.Itoa(value.(*set.Set).Cardinality())
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if !strings.EqualFold(res.String(), expected) {
|
|
t.Errorf("expected preset response to be \"%s\", got %s", expected, res.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
command := make([]resp.Value, len(test.command))
|
|
for i, c := range test.command {
|
|
command[i] = resp.StringValue(c)
|
|
}
|
|
|
|
if err = client.WriteArray(command); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err := client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if test.expectedError != nil {
|
|
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
|
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
if res.Integer() != test.expectedResponse {
|
|
t.Errorf("expected response \"%d\", got \"%d\"", test.expectedResponse, res.Integer())
|
|
}
|
|
|
|
// Check if the resulting set(s) contain the expected members.
|
|
if test.expectedValue == nil {
|
|
return
|
|
}
|
|
|
|
if err := client.WriteArray([]resp.Value{
|
|
resp.StringValue("SMEMBERS"),
|
|
resp.StringValue(test.destination),
|
|
}); err != nil {
|
|
t.Error(err)
|
|
}
|
|
res, _, err = client.ReadValue()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if len(res.Array()) != test.expectedValue.Cardinality() {
|
|
t.Errorf("expected set at key \"%s\" to have cardinality %d, got %d",
|
|
test.destination, test.expectedValue.Cardinality(), len(res.Array()))
|
|
}
|
|
|
|
for _, item := range res.Array() {
|
|
if !test.expectedValue.Contains(item.String()) {
|
|
t.Errorf("unexpected memeber \"%s\", in response", item.String())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|