Skip to content

Commit

Permalink
#737: Added Test Cases for EXPIREAT & EXPIRETIME with HTTP Issue (#865)
Browse files Browse the repository at this point in the history
  • Loading branch information
harsh082ip authored Oct 2, 2024
1 parent 4cc4a21 commit b77ab8c
Show file tree
Hide file tree
Showing 2 changed files with 348 additions and 0 deletions.
241 changes: 241 additions & 0 deletions integration_tests/commands/http/expireat_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
package http

import (
"strconv"
"testing"
"time"

"gotest.tools/v3/assert"
)

func TestExpireAtHttp(t *testing.T) {
exec := NewHTTPCommandExecutor()

testCases := []struct {
name string
setup HTTPCommand
commands []HTTPCommand
expected []interface{}
delay []time.Duration
errorExpected bool
}{
{
name: "Set with EXPIREAT command",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+1, 10)}},
},
expected: []interface{}{float64(1)},
delay: []time.Duration{0},
errorExpected: false,
},
{
name: "Check if key is nil after expiration",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+1, 10)}},
{Command: "GET", Body: map[string]interface{}{"key": "test_key"}},
},
expected: []interface{}{float64(1), "(nil)"},
delay: []time.Duration{0, 1100 * time.Millisecond},
errorExpected: false,
},
{
name: "EXPIREAT non-existent key",
setup: HTTPCommand{Command: "", Body: map[string]interface{}{}},
commands: []HTTPCommand{
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "non_existent_key", "seconds": strconv.FormatInt(time.Now().Unix()+1, 10)}},
},
expected: []interface{}{float64(0)},
delay: []time.Duration{0},
errorExpected: false,
},
{
name: "EXPIREAT with past time",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(-1, 10)}},
{Command: "GET", Body: map[string]interface{}{"key": "test_key"}},
},
expected: []interface{}{"ERR invalid expire time in 'expireat' command", "test_value"},
delay: []time.Duration{0, 0},
errorExpected: true,
},
{
name: "EXPIREAT with invalid syntax",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key"}},
},
expected: []interface{}{"ERR wrong number of arguments for 'expireat' command"},
delay: []time.Duration{0},
errorExpected: true,
},
{
name: "Test(NX): Set the expiration only if the key has no expiration time",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+10, 10), "nx": true}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+1, 10), "nx": true}},
},
expected: []interface{}{float64(1), float64(0)},
delay: []time.Duration{0, 0},
errorExpected: false,
},
{
name: "Test(XX): Set the expiration only if the key already has an expiration time",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+10, 10), "xx": true}},
{Command: "TTL", Body: map[string]interface{}{"key": "test_key"}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+10, 10)}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+10, 10), "xx": true}},
},
expected: []interface{}{float64(0), float64(-1), float64(1), float64(1)},
delay: []time.Duration{0, 0, 0, 0},
errorExpected: false,
},
{
name: "TEST(GT): Set the expiration only if the new expiration time is greater than the current one",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+10, 10), "gt": true}},
{Command: "TTL", Body: map[string]interface{}{"key": "test_key"}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+10, 10)}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+20, 10), "gt": true}},
},
expected: []interface{}{float64(0), float64(-1), float64(1), float64(1)},
delay: []time.Duration{0, 0, 0, 0},
errorExpected: false,
},
{
name: "TEST(LT): Set the expiration only if the new expiration time is less than the current one",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+10, 10), "lt": true}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+20, 10), "lt": true}},
},
expected: []interface{}{float64(1), float64(0)},
delay: []time.Duration{0, 0},
errorExpected: false,
},
{
name: "TEST(NX + LT/GT)",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{

{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+20, 10), "nx": true}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+20, 10), "nx": true, "lt": true}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+20, 10), "nx": true, "gt": true}},
{Command: "GET", Body: map[string]interface{}{"key": "test_key"}},
},
expected: []interface{}{float64(1),
"ERR NX and XX, GT or LT options at the same time are not compatible",
"ERR NX and XX, GT or LT options at the same time are not compatible",
"test_value"},
delay: []time.Duration{0, 0, 0, 0},
errorExpected: true,
},
{
name: "TEST(XX + LT/GT)",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{

{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+20, 10)}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+5, 10), "xx": true, "lt": true}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+10, 10), "xx": true, "gt": true}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+20, 10), "xx": true, "gt": true}},
{Command: "GET", Body: map[string]interface{}{"key": "test_key"}},
},
expected: []interface{}{float64(1), float64(1), float64(1), float64(1), "test_value"},
delay: []time.Duration{0, 0, 0, 0, 0, 0},
errorExpected: false,
},
{
name: "Test if value is nil after expiration (XX + LT)",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{

{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+20, 10)}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+2, 10), "xx": true, "lt": true}},
{Command: "GET", Body: map[string]interface{}{"key": "test_key"}},
},
expected: []interface{}{float64(1), float64(1), "(nil)"},
delay: []time.Duration{0, 0, 2 * time.Second},
errorExpected: false,
},
{
name: "Test if value is nil after expiration (NX)",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{

{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+2, 10), "nx": true}},
{Command: "GET", Body: map[string]interface{}{"key": "test_key"}},
},
expected: []interface{}{float64(1), "(nil)"},
delay: []time.Duration{0, 2 * time.Second},
errorExpected: false,
},
{
name: "Invalid Command Test",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{

{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+1, 10), "xx": true, "rr": true}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+1, 10), "xx": true, "nx": true}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+1, 10), "gt": true, "lt": true}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+1, 10), "gt": true, "lt": true, "xx": true}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+1, 10), "gt": true, "lt": true, "nx": true}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+1, 10), "nx": true, "xx": true, "gt": true}},
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(time.Now().Unix()+1, 10), "nx": true, "xx": true, "lt": true}},
},
expected: []interface{}{"ERR Unsupported option rr",
"ERR NX and XX, GT or LT options at the same time are not compatible",
"ERR GT and LT options at the same time are not compatible",
"ERR GT and LT options at the same time are not compatible",
"ERR NX and XX, GT or LT options at the same time are not compatible",
"ERR NX and XX, GT or LT options at the same time are not compatible",
"ERR NX and XX, GT or LT options at the same time are not compatible"},
delay: []time.Duration{0, 0, 0, 0, 0, 0, 0},
errorExpected: true,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if tc.setup.Command != "" {
_, err := exec.FireCommand(tc.setup)
if err != nil && !tc.errorExpected {
t.Fatalf("Setup failed: %v", err)
}
}

var results []interface{}
for i, cmd := range tc.commands {
if tc.delay[i] > 0 {
time.Sleep(tc.delay[i])
}

result, err := exec.FireCommand(cmd)
if err != nil && !tc.errorExpected {
t.Fatalf("Command failed: %v", err)
}
results = append(results, result)
}

// Validate results
for i, expected := range tc.expected {
if i >= len(results) {
t.Fatalf("Not enough results. Expected %d, got %d", len(tc.expected), len(results))
}

if expected == "(nil)" {
assert.Assert(t, results[i] == "(nil)" || results[i] == "",
"Expected nil or empty result, got %v", results[i])
} else {
assert.DeepEqual(t, expected, results[i])
}
}
})
}
}
107 changes: 107 additions & 0 deletions integration_tests/commands/http/expiretime_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package http

import (
"strconv"
"testing"
"time"

"gotest.tools/v3/assert"
)

func TestExpireTimeHttp(t *testing.T) {
exec := NewHTTPCommandExecutor()

futureUnixTimestamp := time.Now().Unix() + 1

testCases := []struct {
name string
setup HTTPCommand
commands []HTTPCommand
expected []interface{}
delay []time.Duration
errorExpected bool
}{
{
name: "EXPIRETIME command",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": strconv.FormatInt(futureUnixTimestamp, 10)}},
{Command: "EXPIRETIME", Body: map[string]interface{}{"key": "test_key"}},
},
expected: []interface{}{float64(1), float64(futureUnixTimestamp)},
delay: []time.Duration{0, 0},
errorExpected: false,
},
{
name: "EXPIRETIME non-existent key",
setup: HTTPCommand{Command: "", Body: map[string]interface{}{}},
commands: []HTTPCommand{
{Command: "EXPIRETIME", Body: map[string]interface{}{"key": "non_existent_key"}},
},
expected: []interface{}{float64(-2)},
delay: []time.Duration{0},
errorExpected: false,
},
{
name: "EXPIRETIME with past time",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{
{Command: "EXPIREAT", Body: map[string]interface{}{"key": "test_key", "seconds": "1724167183"}},
{Command: "EXPIRETIME", Body: map[string]interface{}{"key": "test_key"}},
},
expected: []interface{}{float64(1), float64(-2)},
delay: []time.Duration{0, 0},
errorExpected: false,
},
{
name: "EXPIRETIME with invalid syntax",
setup: HTTPCommand{Command: "SET", Body: map[string]interface{}{"key": "test_key", "value": "test_value"}},
commands: []HTTPCommand{
{Command: "EXPIRETIME", Body: map[string]interface{}{"": ""}},
{Command: "EXPIRETIME", Body: map[string]interface{}{"keys": []interface{}{"key1", "key2"}}},
},
expected: []interface{}{"ERR wrong number of arguments for 'expiretime' command", "ERR wrong number of arguments for 'expiretime' command"},
delay: []time.Duration{0, 0},
errorExpected: true,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// setup
if tc.setup.Command != "" {
_, err := exec.FireCommand(tc.setup)
if err != nil && !tc.errorExpected {
t.Fatalf("Setup failed: %v", err)
}
}

var results []interface{}
for i, cmd := range tc.commands {
if tc.delay[i] > 0 {
time.Sleep(tc.delay[i])
}

result, err := exec.FireCommand(cmd)
if err != nil && !tc.errorExpected {
t.Fatalf("Command failed: %v", err)
}
results = append(results, result)
}

// Validate results
for i, expected := range tc.expected {
if i >= len(results) {
t.Fatalf("Not enough results. Expected %d, got %d", len(tc.expected), len(results))
}

if expected == "(nil)" {
assert.Assert(t, results[i] == "(nil)" || results[i] == "",
"Expected nil or empty result, got %v", results[i])
} else {
assert.DeepEqual(t, expected, results[i])
}
}
})
}
}

0 comments on commit b77ab8c

Please sign in to comment.