mirror of
https://github.com/EchoVault/SugarDB.git
synced 2026-04-22 23:47:09 +08:00
RENAMENX Command Implementation (#149)
* Implemented of RENAMENX command - @DMcP89 --------- Co-authored-by: Kelvin Clement Mwinuka <kelvinmwinuka@hotmail.co.uk>
This commit is contained in:
+9354
-9610
File diff suppressed because it is too large
Load Diff
@@ -677,6 +677,21 @@ func handleRename(params internal.HandlerFuncParams) ([]byte, error) {
|
||||
return []byte("+OK\r\n"), nil
|
||||
}
|
||||
|
||||
func handleRenamenx(params internal.HandlerFuncParams) ([]byte, error) {
|
||||
if len(params.Command) != 3 {
|
||||
return nil, errors.New(constants.WrongArgsResponse)
|
||||
}
|
||||
|
||||
newKey := params.Command[2]
|
||||
|
||||
keyExistsCheck := params.KeysExist(params.Context, []string{newKey})
|
||||
if keyExistsCheck[newKey] {
|
||||
return nil, errors.New("Key already exists!")
|
||||
}
|
||||
|
||||
return handleRename(params)
|
||||
}
|
||||
|
||||
func handleFlush(params internal.HandlerFuncParams) ([]byte, error) {
|
||||
if len(params.Command) != 1 {
|
||||
return nil, errors.New(constants.WrongArgsResponse)
|
||||
@@ -1325,5 +1340,14 @@ The REPLACE option removes the destination key before copying the value to it.`,
|
||||
KeyExtractionFunc: moveKeyFunc,
|
||||
HandlerFunc: handleMove,
|
||||
},
|
||||
{
|
||||
Command: "renamenx",
|
||||
Module: constants.GenericModule,
|
||||
Categories: []string{constants.KeyspaceCategory, constants.WriteCategory, constants.FastCategory},
|
||||
Description: "(RENAMENX key newkey) Renames the specified key with the new name only if the new name does not already exist.",
|
||||
Sync: true,
|
||||
KeyExtractionFunc: renamenxKeyFunc,
|
||||
HandlerFunc: handleRenamenx,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2626,6 +2626,114 @@ func Test_Generic(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Test_HandlerRENAMENX", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, err := internal.GetConnection("localhost", port)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
client := resp.NewConn(conn)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
oldKey string
|
||||
newKey string
|
||||
presetValue interface{}
|
||||
command []resp.Value
|
||||
expectedResponse string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "1. Rename existing key",
|
||||
oldKey: "renamenxOldKey1",
|
||||
newKey: "renamenxNewKey1",
|
||||
presetValue: "value1",
|
||||
command: []resp.Value{resp.StringValue("RENAMENX"), resp.StringValue("renamenxOldKey1"), resp.StringValue("renamenxNewKey1")},
|
||||
expectedResponse: "OK",
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "2. Rename non-existent key",
|
||||
oldKey: "renamenxOldKey2",
|
||||
newKey: "renamenxNewKey2",
|
||||
presetValue: nil,
|
||||
command: []resp.Value{resp.StringValue("RENAMENX"), resp.StringValue("renamenxOldKey2"), resp.StringValue("renamenxNewKey2")},
|
||||
expectedResponse: "",
|
||||
expectedError: errors.New("no such key"),
|
||||
},
|
||||
{
|
||||
name: "3. Rename to existing key",
|
||||
oldKey: "renamenxOldKey3",
|
||||
newKey: "renamenxNewKey1",
|
||||
presetValue: "value3",
|
||||
command: []resp.Value{resp.StringValue("RENAMENX"), resp.StringValue("renamenxOldKey3"), resp.StringValue("renamenxNewKey1")},
|
||||
expectedResponse: "",
|
||||
expectedError: errors.New("Key already exists!"),
|
||||
},
|
||||
{
|
||||
name: "4. Command too short",
|
||||
command: []resp.Value{resp.StringValue("RENAMENX"), resp.StringValue("key")},
|
||||
expectedError: errors.New(constants.WrongArgsResponse),
|
||||
},
|
||||
{
|
||||
name: "5. Command too long",
|
||||
command: []resp.Value{
|
||||
resp.StringValue("RENAMENX"),
|
||||
resp.StringValue("key"),
|
||||
resp.StringValue("newkey"),
|
||||
resp.StringValue("extra_arg"),
|
||||
},
|
||||
expectedError: errors.New(constants.WrongArgsResponse),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if test.presetValue != nil {
|
||||
command := []resp.Value{resp.StringValue("SET"), resp.StringValue(test.oldKey), resp.StringValue(fmt.Sprintf("%v", test.presetValue))}
|
||||
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(), "OK") {
|
||||
t.Errorf("expected preset response to be OK, got %s", res.String())
|
||||
}
|
||||
}
|
||||
|
||||
if err = client.WriteArray(test.command); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
res, _, err := client.ReadValue()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if test.expectedError != nil {
|
||||
if !strings.Contains(res.Error().Error(), test.expectedError.Error()) {
|
||||
t.Errorf("expected error \"%s\", got \"%s\"", test.expectedError.Error(), res.Error().Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
if res.String() != test.expectedResponse {
|
||||
t.Errorf("expected response \"%s\", got \"%s\"", test.expectedResponse, res.String())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Test_HandleFlush", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -191,6 +191,15 @@ func renameKeyFunc(cmd []string) (internal.KeyExtractionFuncResult, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func renamenxKeyFunc(cmd []string) (internal.KeyExtractionFuncResult, error) {
|
||||
if len(cmd) != 3 {
|
||||
return internal.KeyExtractionFuncResult{}, errors.New(constants.WrongArgsResponse)
|
||||
}
|
||||
return internal.KeyExtractionFuncResult{
|
||||
WriteKeys: cmd[1:3],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func randomKeyFunc(cmd []string) (internal.KeyExtractionFuncResult, error) {
|
||||
if len(cmd) != 1 {
|
||||
return internal.KeyExtractionFuncResult{}, errors.New(constants.WrongArgsResponse)
|
||||
@@ -269,10 +278,10 @@ func objIdleTimeKeyFunc(cmd []string) (internal.KeyExtractionFuncResult, error)
|
||||
}
|
||||
|
||||
func copyKeyFunc(cmd []string) (internal.KeyExtractionFuncResult, error) {
|
||||
if len(cmd) < 3 && len(cmd)>6{
|
||||
if len(cmd) < 3 && len(cmd) > 6 {
|
||||
return internal.KeyExtractionFuncResult{}, errors.New(constants.WrongArgsResponse)
|
||||
}
|
||||
|
||||
|
||||
return internal.KeyExtractionFuncResult{
|
||||
Channels: make([]string, 0),
|
||||
ReadKeys: cmd[1:2],
|
||||
|
||||
+22
-1
@@ -599,6 +599,27 @@ func (server *SugarDB) Rename(oldKey string, newKey string) (string, error) {
|
||||
return internal.ParseStringResponse(b)
|
||||
}
|
||||
|
||||
// RenameNX renames the specified key with the new name only if the new name does not already exist.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// `oldKey` - string - The key to be renamed.
|
||||
//
|
||||
// `newKey` - string - The new name for the key.
|
||||
//
|
||||
// Returns: A string indicating the success of the operation.
|
||||
func (server *SugarDB) RenameNX(oldKey string, newKey string) (string, error) {
|
||||
// Construct the command
|
||||
cmd := []string{"RENAMENX", oldKey, newKey}
|
||||
// Execute the command
|
||||
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Parse the simple string response
|
||||
return internal.ParseStringResponse(b)
|
||||
}
|
||||
|
||||
// RandomKey returns a random key from the current active database.
|
||||
// If no keys present in db returns an empty string.
|
||||
func (server *SugarDB) RandomKey() (string, error) {
|
||||
@@ -633,7 +654,7 @@ func (server *SugarDB) GetDel(key string) (string, error) {
|
||||
//
|
||||
// `option` - GetExOption - one of EX, PX, EXAT, PXAT, PERSIST. Can be nil.
|
||||
//
|
||||
// `unixtime` - int - Number of seconds or miliseconds from now.
|
||||
// `unixtime` - int - Number of seconds or milliseconds from now.
|
||||
//
|
||||
// Returns: A string representing the value at the specified key. If the value does not exist, an empty string is returned.
|
||||
func (server *SugarDB) GetEx(key string, option GetExOption, unixtime int) (string, error) {
|
||||
|
||||
@@ -1322,6 +1322,60 @@ func TestSugarDB_Rename(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSugarDB_Renamenx(t *testing.T) {
|
||||
server := createSugarDB()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
oldKey string
|
||||
newKey string
|
||||
presetValues map[string]internal.KeyData
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "1. Rename existing key",
|
||||
oldKey: "oldKey1",
|
||||
newKey: "newKey1",
|
||||
presetValues: map[string]internal.KeyData{"oldKey1": {Value: "value1"}},
|
||||
want: "OK",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "2. Rename non-existent key",
|
||||
oldKey: "oldKey2",
|
||||
newKey: "newKey2",
|
||||
presetValues: nil,
|
||||
want: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "3. Rename to existing key",
|
||||
oldKey: "oldKey3",
|
||||
newKey: "newKey4",
|
||||
presetValues: map[string]internal.KeyData{"oldKey3": {Value: "value3"}, "newKey4": {Value: "value4"}},
|
||||
want: "",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.presetValues != nil {
|
||||
for k, d := range tt.presetValues {
|
||||
presetKeyData(server, context.Background(), k, d)
|
||||
}
|
||||
}
|
||||
got, err := server.RenameNX(tt.oldKey, tt.newKey)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Rename() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("Rename() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestSugarDB_RANDOMKEY(t *testing.T) {
|
||||
server := createSugarDB()
|
||||
|
||||
@@ -1843,7 +1897,7 @@ func TestSugarDB_COPY(t *testing.T) {
|
||||
destKeyPresetValue interface{}
|
||||
destinationKey string
|
||||
options COPYOptions
|
||||
expectedValue string
|
||||
expectedValue string
|
||||
want int
|
||||
wantErr bool
|
||||
}{
|
||||
@@ -1854,7 +1908,7 @@ func TestSugarDB_COPY(t *testing.T) {
|
||||
destKeyPresetValue: nil,
|
||||
destinationKey: "dkey1",
|
||||
options: CopyOptions("0", false),
|
||||
expectedValue: "value1",
|
||||
expectedValue: "value1",
|
||||
want: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -1865,7 +1919,7 @@ func TestSugarDB_COPY(t *testing.T) {
|
||||
destKeyPresetValue: "dValue2",
|
||||
destinationKey: "dkey2",
|
||||
options: CopyOptions("0", false),
|
||||
expectedValue: "dValue2",
|
||||
expectedValue: "dValue2",
|
||||
want: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -1876,7 +1930,7 @@ func TestSugarDB_COPY(t *testing.T) {
|
||||
destKeyPresetValue: "dValue3",
|
||||
destinationKey: "dkey3",
|
||||
options: CopyOptions("0", true),
|
||||
expectedValue: "value3",
|
||||
expectedValue: "value3",
|
||||
want: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -1887,7 +1941,7 @@ func TestSugarDB_COPY(t *testing.T) {
|
||||
destKeyPresetValue: nil,
|
||||
destinationKey: "dkey4",
|
||||
options: CopyOptions("1", false),
|
||||
expectedValue: "value4",
|
||||
expectedValue: "value4",
|
||||
want: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
@@ -5,6 +5,8 @@ services:
|
||||
context: ..
|
||||
dockerfile: windows_test_env/Dockerfile
|
||||
container_name: sugardb_win_test_env
|
||||
volumes:
|
||||
- ../coverage/coverage.out:/testspace/coverage/coverage.out
|
||||
stdin_open: true
|
||||
tty: true
|
||||
|
||||
|
||||
Reference in New Issue
Block a user