Implemented test for SWAPDB command.

This commit is contained in:
Kelvin Mwinuka
2024-06-28 02:22:06 +08:00
parent ed6a5718f6
commit f867bca8e4
3 changed files with 294 additions and 3 deletions

View File

@@ -308,6 +308,7 @@ github.com/echovault/echovault/internal/aof/log/store.go:193.2,193.47 1 0
github.com/echovault/echovault/internal/aof/log/store.go:193.47,195.3 1 0 github.com/echovault/echovault/internal/aof/log/store.go:193.47,195.3 1 0
github.com/echovault/echovault/internal/aof/log/store.go:196.2,196.12 1 0 github.com/echovault/echovault/internal/aof/log/store.go:196.2,196.12 1 0
github.com/echovault/echovault/internal/aof/log/store.go:199.41,203.2 3 1 github.com/echovault/echovault/internal/aof/log/store.go:199.41,203.2 3 1
github.com/echovault/echovault/internal/clock/clock.go:14.23,16.43 1 1 github.com/echovault/echovault/internal/clock/clock.go:14.23,16.43 1 1
github.com/echovault/echovault/internal/clock/clock.go:16.43,18.3 1 1 github.com/echovault/echovault/internal/clock/clock.go:16.43,18.3 1 1
github.com/echovault/echovault/internal/clock/clock.go:19.2,19.20 1 0 github.com/echovault/echovault/internal/clock/clock.go:19.2,19.20 1 0
@@ -538,6 +539,7 @@ github.com/echovault/echovault/internal/aof/engine.go:196.55,198.3 1 0
github.com/echovault/echovault/internal/aof/engine.go:199.2,199.53 1 1 github.com/echovault/echovault/internal/aof/engine.go:199.2,199.53 1 1
github.com/echovault/echovault/internal/aof/engine.go:199.53,201.3 1 0 github.com/echovault/echovault/internal/aof/engine.go:199.53,201.3 1 0
github.com/echovault/echovault/internal/aof/engine.go:202.2,202.12 1 1 github.com/echovault/echovault/internal/aof/engine.go:202.2,202.12 1 1
github.com/echovault/echovault/internal/clock/clock.go:14.23,16.43 1 1 github.com/echovault/echovault/internal/clock/clock.go:14.23,16.43 1 1
github.com/echovault/echovault/internal/clock/clock.go:16.43,18.3 1 1 github.com/echovault/echovault/internal/clock/clock.go:16.43,18.3 1 1
github.com/echovault/echovault/internal/clock/clock.go:19.2,19.20 1 0 github.com/echovault/echovault/internal/clock/clock.go:19.2,19.20 1 0
@@ -1009,6 +1011,7 @@ github.com/echovault/echovault/internal/config/config.go:254.2,256.45 2 0
github.com/echovault/echovault/internal/config/config.go:256.45,258.3 1 0 github.com/echovault/echovault/internal/config/config.go:256.45,258.3 1 0
github.com/echovault/echovault/internal/config/config.go:260.2,260.18 1 0 github.com/echovault/echovault/internal/config/config.go:260.2,260.18 1 0
github.com/echovault/echovault/internal/config/default.go:9.29,42.2 3 0 github.com/echovault/echovault/internal/config/default.go:9.29,42.2 3 0
github.com/echovault/echovault/internal/eviction/lfu.go:35.29,42.2 3 1 github.com/echovault/echovault/internal/eviction/lfu.go:35.29,42.2 3 1
github.com/echovault/echovault/internal/eviction/lfu.go:44.34,46.2 1 1 github.com/echovault/echovault/internal/eviction/lfu.go:44.34,46.2 1 1
github.com/echovault/echovault/internal/eviction/lfu.go:48.44,50.54 1 1 github.com/echovault/echovault/internal/eviction/lfu.go:48.44,50.54 1 1
@@ -1043,6 +1046,7 @@ github.com/echovault/echovault/internal/eviction/lru.go:92.73,94.3 1 0
github.com/echovault/echovault/internal/eviction/lru.go:95.2,95.19 1 0 github.com/echovault/echovault/internal/eviction/lru.go:95.2,95.19 1 0
github.com/echovault/echovault/internal/eviction/lru.go:95.19,97.3 1 0 github.com/echovault/echovault/internal/eviction/lru.go:95.19,97.3 1 0
github.com/echovault/echovault/internal/eviction/lru.go:100.50,103.2 2 1 github.com/echovault/echovault/internal/eviction/lru.go:100.50,103.2 2 1
github.com/echovault/echovault/internal/utils.go:41.38,45.16 2 0 github.com/echovault/echovault/internal/utils.go:41.38,45.16 2 0
github.com/echovault/echovault/internal/utils.go:45.16,47.3 1 0 github.com/echovault/echovault/internal/utils.go:45.16,47.3 1 0
github.com/echovault/echovault/internal/utils.go:49.2,49.15 1 0 github.com/echovault/echovault/internal/utils.go:49.2,49.15 1 0
@@ -1819,6 +1823,7 @@ github.com/echovault/echovault/internal/modules/list/key_funcs.go:115.2,119.8 1
github.com/echovault/echovault/internal/modules/list/key_funcs.go:122.75,123.19 1 1 github.com/echovault/echovault/internal/modules/list/key_funcs.go:122.75,123.19 1 1
github.com/echovault/echovault/internal/modules/list/key_funcs.go:123.19,125.3 1 1 github.com/echovault/echovault/internal/modules/list/key_funcs.go:123.19,125.3 1 1
github.com/echovault/echovault/internal/modules/list/key_funcs.go:126.2,130.8 1 1 github.com/echovault/echovault/internal/modules/list/key_funcs.go:126.2,130.8 1 1
github.com/echovault/echovault/internal/raft/fsm.go:48.36,52.2 1 0 github.com/echovault/echovault/internal/raft/fsm.go:48.36,52.2 1 0
github.com/echovault/echovault/internal/raft/fsm.go:55.50,56.18 1 0 github.com/echovault/echovault/internal/raft/fsm.go:55.50,56.18 1 0
github.com/echovault/echovault/internal/raft/fsm.go:57.10,57.10 0 0 github.com/echovault/echovault/internal/raft/fsm.go:57.10,57.10 0 0
@@ -4160,6 +4165,7 @@ github.com/echovault/echovault/internal/modules/set/commands.go:159.70,161.16 2
github.com/echovault/echovault/internal/modules/set/commands.go:161.16,163.3 1 0 github.com/echovault/echovault/internal/modules/set/commands.go:161.16,163.3 1 0
github.com/echovault/echovault/internal/modules/set/commands.go:165.2,169.37 3 1 github.com/echovault/echovault/internal/modules/set/commands.go:165.2,169.37 3 1
github.com/echovault/echovault/internal/modules/set/commands.go:169.37,170.14 1 1 github.com/echovault/echovault/internal/modules/set/commands.go:169.37,170.14 1 1
github.com/echovault/echovault/internal/modules/set/commands.go:170.14,172.4 1 1 github.com/echovault/echovault/internal/modules/set/commands.go:170.14,172.4 1 1
github.com/echovault/echovault/internal/modules/set/commands.go:173.3,174.10 2 1 github.com/echovault/echovault/internal/modules/set/commands.go:173.3,174.10 2 1
github.com/echovault/echovault/internal/modules/set/commands.go:174.10,177.4 1 1 github.com/echovault/echovault/internal/modules/set/commands.go:174.10,177.4 1 1
@@ -4405,6 +4411,7 @@ github.com/echovault/echovault/internal/modules/set/set.go:184.31,185.19 1 1
github.com/echovault/echovault/internal/modules/set/set.go:186.9,187.17 1 1 github.com/echovault/echovault/internal/modules/set/set.go:186.9,187.17 1 1
github.com/echovault/echovault/internal/modules/set/set.go:188.9,191.15 3 1 github.com/echovault/echovault/internal/modules/set/set.go:188.9,191.15 3 1
github.com/echovault/echovault/internal/modules/set/set.go:192.10,195.28 3 1 github.com/echovault/echovault/internal/modules/set/set.go:192.10,195.28 3 1
github.com/echovault/echovault/internal/raft/fsm.go:48.36,52.2 1 0 github.com/echovault/echovault/internal/raft/fsm.go:48.36,52.2 1 0
github.com/echovault/echovault/internal/raft/fsm.go:55.50,56.18 1 0 github.com/echovault/echovault/internal/raft/fsm.go:55.50,56.18 1 0
github.com/echovault/echovault/internal/raft/fsm.go:57.10,57.10 0 0 github.com/echovault/echovault/internal/raft/fsm.go:57.10,57.10 0 0
@@ -5545,6 +5552,7 @@ github.com/echovault/echovault/internal/modules/sorted_set/utils.go:162.3,162.13
github.com/echovault/echovault/internal/modules/sorted_set/utils.go:163.12,164.16 1 1 github.com/echovault/echovault/internal/modules/sorted_set/utils.go:163.12,164.16 1 1
github.com/echovault/echovault/internal/modules/sorted_set/utils.go:164.16,166.4 1 1 github.com/echovault/echovault/internal/modules/sorted_set/utils.go:164.16,166.4 1 1
github.com/echovault/echovault/internal/modules/sorted_set/utils.go:167.3,167.13 1 1 github.com/echovault/echovault/internal/modules/sorted_set/utils.go:167.3,167.13 1 1
github.com/echovault/echovault/internal/raft/fsm.go:48.36,52.2 1 0 github.com/echovault/echovault/internal/raft/fsm.go:48.36,52.2 1 0
github.com/echovault/echovault/internal/raft/fsm.go:55.50,56.18 1 0 github.com/echovault/echovault/internal/raft/fsm.go:55.50,56.18 1 0
github.com/echovault/echovault/internal/raft/fsm.go:57.10,57.10 0 0 github.com/echovault/echovault/internal/raft/fsm.go:57.10,57.10 0 0

View File

@@ -154,16 +154,16 @@ func handleSwapDB(params internal.HandlerFuncParams) ([]byte, error) {
database1, err := strconv.Atoi(params.Command[1]) database1, err := strconv.Atoi(params.Command[1])
if err != nil { if err != nil {
return nil, err return nil, errors.New("both database indices must be integers")
} }
database2, err := strconv.Atoi(params.Command[2]) database2, err := strconv.Atoi(params.Command[2])
if err != nil { if err != nil {
return nil, err return nil, errors.New("both database indices must be integers")
} }
if database1 < 0 || database2 < 0 { if database1 < 0 || database2 < 0 {
return nil, errors.New("database must be >= 0") return nil, errors.New("database indices must be >= 0")
} }
params.SwapDBs(database1, database2) params.SwapDBs(database1, database2)

View File

@@ -714,4 +714,287 @@ func Test_Connection(t *testing.T) {
}) })
} }
}) })
t.Run("Test_HandleSwapDBs", func(t *testing.T) {
t.Parallel()
port, err := internal.GetFreePort()
if err != nil {
t.Error(err)
return
}
mockServer, err := setUpServer(port, false, "")
if err != nil {
t.Error(err)
return
}
go func() {
mockServer.Start()
}()
t.Cleanup(func() {
mockServer.ShutDown()
})
tests := []struct {
name string
presetValues map[int]map[string]string
database0 string
database1 string
getCommand []resp.Value
swapCommand []resp.Value
want0 []resp.Value
want1 []resp.Value
wantErr error
}{
{
name: "1. Successfully swap databases",
presetValues: map[int]map[string]string{
0: {"key1": "value-01", "key2": "value-02", "key3": "value-03", "key4": "value-04", "key5": "value-05"},
1: {"key1": "value-11", "key2": "value-12", "key3": "value-13", "key4": "value-14", "key5": "value-15"},
},
database0: "0",
database1: "1",
getCommand: []resp.Value{
resp.StringValue("MGET"),
resp.StringValue("key1"), resp.StringValue("key2"), resp.StringValue("key3"),
resp.StringValue("key4"), resp.StringValue("key5"),
},
swapCommand: []resp.Value{
resp.StringValue("SWAPDB"), resp.StringValue("0"), resp.StringValue("1"),
},
want0: []resp.Value{
resp.StringValue("value-01"), resp.StringValue("value-02"), resp.StringValue("value-03"),
resp.StringValue("value-04"), resp.StringValue("value-05"),
},
want1: []resp.Value{
resp.StringValue("value-11"), resp.StringValue("value-12"), resp.StringValue("value-13"),
resp.StringValue("value-14"), resp.StringValue("value-15"),
},
wantErr: nil,
},
{
name: "2. First database index is not an integer",
presetValues: nil,
database0: "index0",
database1: "1",
getCommand: make([]resp.Value, 0),
swapCommand: []resp.Value{
resp.StringValue("SWAPDB"), resp.StringValue("index0"), resp.StringValue("1"),
},
want0: make([]resp.Value, 0),
want1: make([]resp.Value, 0),
wantErr: errors.New("both database indices must be integers"),
},
{
name: "3. Second database index is not an integer",
presetValues: nil,
database0: "0",
database1: "index1",
getCommand: make([]resp.Value, 0),
swapCommand: []resp.Value{
resp.StringValue("SWAPDB"), resp.StringValue("0"), resp.StringValue("index1"),
},
want0: make([]resp.Value, 0),
want1: make([]resp.Value, 0),
wantErr: errors.New("both database indices must be integers"),
},
{
name: "4. First database index is < 0",
presetValues: nil,
database0: "-1",
database1: "1",
getCommand: make([]resp.Value, 0),
swapCommand: []resp.Value{
resp.StringValue("SWAPDB"), resp.StringValue("-1"), resp.StringValue("1"),
},
want0: make([]resp.Value, 0),
want1: make([]resp.Value, 0),
wantErr: errors.New("database indices must be >= 0"),
},
{
name: "5. Second database index is < 0",
presetValues: nil,
database0: "1",
database1: "-1",
getCommand: make([]resp.Value, 0),
swapCommand: []resp.Value{
resp.StringValue("SWAPDB"), resp.StringValue("0"), resp.StringValue("-1"),
},
want0: make([]resp.Value, 0),
want1: make([]resp.Value, 0),
wantErr: errors.New("database indices must be >= 0"),
},
{
name: "6. Command too short",
presetValues: nil,
database0: "-1",
database1: "1",
getCommand: make([]resp.Value, 0),
swapCommand: []resp.Value{resp.StringValue("SWAPDB"), resp.StringValue("0")},
want0: make([]resp.Value, 0),
want1: make([]resp.Value, 0),
wantErr: errors.New(constants.WrongArgsResponse),
},
{
name: "7. Command too long",
presetValues: nil,
database0: "-1",
database1: "1",
getCommand: make([]resp.Value, 0),
swapCommand: []resp.Value{
resp.StringValue("SWAPDB"), resp.StringValue("0"),
resp.StringValue("1"), resp.StringValue("2"),
},
want0: make([]resp.Value, 0),
want1: make([]resp.Value, 0),
wantErr: errors.New(constants.WrongArgsResponse),
},
}
for _, test := range tests {
// Set values for database 0 and 1.
if test.presetValues != nil {
for db, data := range test.presetValues {
_ = mockServer.SelectDB(db)
if _, err = mockServer.MSet(data); err != nil {
t.Error(err)
return
}
}
}
// Create TPC connection for database 0
conn1, err := internal.GetConnection("localhost", port)
if err != nil {
t.Error(err)
return
}
client1 := resp.NewConn(conn1)
if len(test.getCommand) > 0 {
// Select database 0 for connection 1
if err = client1.WriteArray([]resp.Value{
resp.StringValue("SELECT"),
resp.StringValue(test.database0),
}); err != nil {
t.Error(err)
return
}
res, _, err := client1.ReadValue()
if err != nil {
t.Error(err)
return
}
if !strings.EqualFold(res.String(), "ok") {
t.Errorf("expcted OK response when selecting database, got %s", res.String())
return
}
// Check that the connection reads values from database 0
if err = client1.WriteArray(test.getCommand); err != nil {
t.Error(err)
return
}
res, _, err = client1.ReadValue()
if err != nil {
t.Error(err)
return
}
if !reflect.DeepEqual(test.want0, res.Array()) {
t.Errorf("expected response %+v, got %+v", test.want0, res.Array())
}
}
// Create TCP connection for database 1
conn2, err := internal.GetConnection("localhost", port)
if err != nil {
t.Error(err)
return
}
client2 := resp.NewConn(conn2)
if len(test.getCommand) > 0 {
// Select database 1 for the second connection.
if err = client2.WriteArray([]resp.Value{
resp.StringValue("SELECT"),
resp.StringValue(test.database1),
}); err != nil {
t.Error(err)
return
}
res, _, err := client2.ReadValue()
if err != nil {
t.Error(err)
return
}
if !strings.EqualFold(res.String(), "ok") {
t.Errorf("expcted OK response when selecting database, got %s", res.String())
return
}
// Check that the connection reads values from database 1.
if err = client2.WriteArray(test.getCommand); err != nil {
t.Error(err)
return
}
res, _, err = client2.ReadValue()
if err != nil {
t.Error(err)
return
}
if !reflect.DeepEqual(test.want1, res.Array()) {
t.Errorf("expected response %+v, got %+v", test.want1, res.Array())
}
}
// Run SWAPDB command
if err = client1.WriteArray(test.swapCommand); err != nil {
t.Error(err)
return
}
res, _, err := client1.ReadValue()
if err != nil {
t.Error(err)
return
}
// If we expect an error check the error.
if test.wantErr != nil {
if !strings.Contains(res.Error().Error(), test.wantErr.Error()) {
t.Errorf("expected error response to contain \"%s\", go \"%s\"",
test.wantErr.Error(), res.Error().Error())
}
continue
}
// Check if response is OK.
if !strings.EqualFold(res.String(), "ok") {
t.Errorf("expected OK response from SWAPDB command, got %s", res.String())
return
}
// Check that the first connection now reads values from database 1
if err = client1.WriteArray(test.getCommand); err != nil {
t.Error(err)
return
}
res, _, err = client1.ReadValue()
if err != nil {
t.Error(err)
return
}
if !reflect.DeepEqual(test.want1, res.Array()) {
t.Errorf("expected response %+v, got %+v", test.want1, res.Array())
}
// Check that the second connection now reads values from database 0
if err = client2.WriteArray(test.getCommand); err != nil {
t.Error(err)
return
}
res, _, err = client2.ReadValue()
if err != nil {
t.Error(err)
return
}
if !reflect.DeepEqual(test.want0, res.Array()) {
t.Errorf("expected response %+v, got %+v", test.want0, res.Array())
}
}
})
} }