From cb0488c233117d498af49634a0852fd1d0f732fb Mon Sep 17 00:00:00 2001 From: Sahil Date: Wed, 3 Jul 2024 01:17:06 +0530 Subject: [PATCH] added tests and fixed misplaced function --- echovault/api_generic.go | 17 +-- echovault/api_generic_test.go | 69 +++++++++++ internal/modules/generic/commands_test.go | 138 +++++++++++++++++++++- 3 files changed, 212 insertions(+), 12 deletions(-) diff --git a/echovault/api_generic.go b/echovault/api_generic.go index 3915752a..d6d83e2b 100644 --- a/echovault/api_generic.go +++ b/echovault/api_generic.go @@ -15,12 +15,10 @@ package echovault import ( - "errors" "strconv" "strings" "github.com/echovault/echovault/internal" - "github.com/echovault/echovault/internal/constants" ) // SetOptions modifies the behaviour for the Set command @@ -487,13 +485,16 @@ func (server *EchoVault) IncrBy(key string, value string) (int, error) { return internal.ParseIntegerResponse(b) } -func incrByFloatKeyFunc(cmd []string) (internal.KeyExtractionFuncResult, error) { - if len(cmd) != 3 { - return internal.KeyExtractionFuncResult{}, errors.New(constants.WrongArgsResponse) +func (server *EchoVault) IncrByFloat(key string, value string) (float64, error) { + // Construct the command + cmd := []string{"INCRBYFLOAT", key, value} + // Execute the command + b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true) + if err != nil { + return 0, err } - return internal.KeyExtractionFuncResult{ - WriteKeys: []string{cmd[1]}, - }, nil + // Parse the float response + return internal.ParseFloatResponse(b) } // DecrBy decrements the integer value of the specified key by the given increment. diff --git a/echovault/api_generic_test.go b/echovault/api_generic_test.go index c0ffd3b2..c72625bb 100644 --- a/echovault/api_generic_test.go +++ b/echovault/api_generic_test.go @@ -1117,6 +1117,75 @@ func TestEchoVault_INCRBY(t *testing.T) { } } +func TestEchoVault_INCRBYFLOAT(t *testing.T) { + server := createEchoVault() + + tests := []struct { + name string + key string + increment string + presetValues map[string]internal.KeyData + want float64 + wantErr bool + }{ + { + name: "1. Increment non-existent key by 2.5", + key: "IncrByFloatKey1", + increment: "2.5", + presetValues: nil, + want: 2.5, + wantErr: false, + }, + { + name: "2. Increment existing key with integer value by 1.2", + key: "IncrByFloatKey2", + increment: "1.2", + presetValues: map[string]internal.KeyData{ + "IncrByFloatKey2": {Value: "5"}, + }, + want: 6.2, + wantErr: false, + }, + { + name: "3. Increment existing key with non-integer value by 3.3", + key: "IncrByFloatKey3", + increment: "3.3", + presetValues: map[string]internal.KeyData{ + "IncrByFloatKey3": {Value: "not_a_float"}, + }, + want: 0, + wantErr: true, + }, + { + name: "4. Increment existing key with int64 value by 0.7", + key: "IncrByFloatKey4", + increment: "0.7", + presetValues: map[string]internal.KeyData{ + "IncrByFloatKey4": {Value: int64(10)}, + }, + want: 10.7, + wantErr: false, + }, + } + 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.IncrByFloat(tt.key, tt.increment) + if (err != nil) != tt.wantErr { + t.Errorf("IncrByFloat() error = %v, wantErr %v", err, tt.wantErr) + return + } + if err == nil && got != tt.want { + t.Errorf("IncrByFloat() got = %v, want %v", got, tt.want) + } + }) + } +} + func TestEchoVault_DECRBY(t *testing.T) { server := createEchoVault() diff --git a/internal/modules/generic/commands_test.go b/internal/modules/generic/commands_test.go index e28fb0c6..4468e480 100644 --- a/internal/modules/generic/commands_test.go +++ b/internal/modules/generic/commands_test.go @@ -17,16 +17,17 @@ package generic_test import ( "errors" "fmt" + "strconv" + "strings" + "testing" + "time" + "github.com/echovault/echovault/echovault" "github.com/echovault/echovault/internal" "github.com/echovault/echovault/internal/clock" "github.com/echovault/echovault/internal/config" "github.com/echovault/echovault/internal/constants" "github.com/tidwall/resp" - "strconv" - "strings" - "testing" - "time" ) type KeyData struct { @@ -2268,6 +2269,135 @@ func Test_Generic(t *testing.T) { } }) + t.Run("Test_HandlerINCRBYFLOAT", 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 + key string + increment string + presetValue interface{} + command []resp.Value + expectedResponse float64 + expectedError error + }{ + { + name: "1. Increment non-existent key by 2.5", + key: "IncrByFloatKey1", + increment: "2.5", + presetValue: nil, + command: []resp.Value{resp.StringValue("INCRBYFLOAT"), resp.StringValue("IncrByFloatKey1"), resp.StringValue("2.5")}, + expectedResponse: 2.5, + expectedError: nil, + }, + { + name: "2. Increment existing key with integer value by 1.2", + key: "IncrByFloatKey2", + increment: "1.2", + presetValue: "5", + command: []resp.Value{resp.StringValue("INCRBYFLOAT"), resp.StringValue("IncrByFloatKey2"), resp.StringValue("1.2")}, + expectedResponse: 6.2, + expectedError: nil, + }, + { + name: "3. Increment existing key with non-integer value by 3.3", + key: "IncrByFloatKey3", + increment: "3.3", + presetValue: "not_a_float", + command: []resp.Value{resp.StringValue("INCRBYFLOAT"), resp.StringValue("IncrByFloatKey3"), resp.StringValue("3.3")}, + expectedResponse: 0, + expectedError: errors.New("value is not a float or out of range"), + }, + { + name: "4. Increment existing key with int64 value by 0.7", + key: "IncrByFloatKey4", + increment: "0.7", + presetValue: int64(10), + command: []resp.Value{resp.StringValue("INCRBYFLOAT"), resp.StringValue("IncrByFloatKey4"), resp.StringValue("0.7")}, + expectedResponse: 10.7, + expectedError: nil, + }, + { + name: "5. Command too short", + key: "IncrByFloatKey5", + increment: "5", + presetValue: nil, + command: []resp.Value{resp.StringValue("INCRBYFLOAT"), resp.StringValue("IncrByFloatKey5")}, + expectedResponse: 0, + expectedError: errors.New(constants.WrongArgsResponse), + }, + { + name: "6. Command too long", + key: "IncrByFloatKey6", + increment: "5", + presetValue: nil, + command: []resp.Value{ + resp.StringValue("INCRBYFLOAT"), + resp.StringValue("IncrByFloatKey6"), + resp.StringValue("5"), + resp.StringValue("extra_arg"), + }, + expectedResponse: 0, + 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.key), 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(), err.Error()) + } + return + } + + if err != nil { + t.Error(err) + } else { + responseFloat, err := strconv.ParseFloat(res.String(), 64) + if err != nil { + t.Errorf("error parsing response to float64: %s", err) + } + if responseFloat != test.expectedResponse { + t.Errorf("expected response %f, got %f", test.expectedResponse, responseFloat) + } + } + }) + } + }) + t.Run("Test_HandlerDECRBY", func(t *testing.T) { t.Parallel() conn, err := internal.GetConnection("localhost", port)