mirror of
https://github.com/hanzokv/go.git
synced 2026-09-24 00:31:51 +00:00
* feat: impl array data structure * add integration tests for array commands Co-Authored-By: Claude Opus 4.7 <[email protected]> * fix: move CmdTypeUint and CmdTypeUintSlice to end of iota sequence Co-Authored-By: Claude Opus 4.7 <[email protected]> * test: add boundary tests for large uint64 index values Covers ARSet, ARGet, ARGetRange, ARMSet, ARMGet, ARDel, ARDelRange, ARScan, ARSeek, ARNext, and AROp. Co-Authored-By: Claude Opus 4.7 <[email protected]> * refactor: replace generic AROp with typed methods and improve test coverage - Split AROp into specific methods: AROpSum, AROpMin, AROpMax, AROpAnd, AROpOr, AROpXor, AROpMatch, AROpUsed with proper return types - Pre-allocate slices in ARScan, ARGrep, ARGrepWithValues, ARLastItems - Add tests: ARInfoFull, negative AND/OR/XOR results, ARGrep nil args Co-Authored-By: Claude Opus 4.7 <[email protected]> * mark ArrayCmdable API as experimental Co-Authored-By: Claude Opus 4.7 <[email protected]> * Use `any` instead of `interface{}` and document index range constraint on ArrayCmdable interface. Co-Authored-By: Claude Opus 4.7 <[email protected]> --------- Co-authored-by: Claude Opus 4.7 <[email protected]>
2182 lines
75 KiB
Go
2182 lines
75 KiB
Go
package redis_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math"
|
|
"strings"
|
|
|
|
. "github.com/bsm/ginkgo/v2"
|
|
. "github.com/bsm/gomega"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
var _ = Describe("Array Commands", Label("array"), func() {
|
|
ctx := context.TODO()
|
|
var client *redis.Client
|
|
|
|
BeforeEach(func() {
|
|
SkipBeforeRedisVersion(8.8, "Redis 8.8.0 introduces support for Array")
|
|
client = redis.NewClient(redisOptions())
|
|
Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
|
|
})
|
|
|
|
AfterEach(func() {
|
|
Expect(client.Close()).NotTo(HaveOccurred())
|
|
})
|
|
|
|
Describe("ARSet and ARGet", func() {
|
|
It("should ARSet and ARGet basics", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARSet(ctx, "myarray", 0, "hello").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(1)))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("hello"))
|
|
|
|
_, err = client.ARGet(ctx, "myarray", 1).Result()
|
|
Expect(err).To(Equal(redis.Nil))
|
|
})
|
|
|
|
It("should ARSet overwrite existing value", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARSet(ctx, "myarray", 0, "hello").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(1)))
|
|
|
|
n, err = client.ARSet(ctx, "myarray", 0, "world").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(0)))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("world"))
|
|
})
|
|
|
|
It("should ARGet non-existing key", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
_, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).To(Equal(redis.Nil))
|
|
})
|
|
|
|
It("should ARGet validate index on non-existing key", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
err := client.ARGet(ctx, "myarray", 0).Err()
|
|
// Non-existing key returns redis.Nil, not an index error
|
|
Expect(err).To(Equal(redis.Nil))
|
|
})
|
|
|
|
It("should ARSet and ARGet with integer values", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 0, "12345").Err()).NotTo(HaveOccurred())
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("12345"))
|
|
})
|
|
|
|
It("should ARSet and ARGet with float values", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 0, "3.14159").Err()).NotTo(HaveOccurred())
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("3.14159"))
|
|
})
|
|
|
|
It("should ARSet and ARGet with small strings", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 0, "abc").Err()).NotTo(HaveOccurred())
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("abc"))
|
|
})
|
|
|
|
It("should ARSet and ARGet with large string", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
longstr := strings.Repeat("x", 100)
|
|
Expect(client.ARSet(ctx, "myarray", 0, longstr).Err()).NotTo(HaveOccurred())
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(longstr))
|
|
})
|
|
|
|
It("should ARSet and ARGet with empty string", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 0, "").Err()).NotTo(HaveOccurred())
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(""))
|
|
})
|
|
})
|
|
|
|
Describe("ARLen and ARCount", func() {
|
|
It("should ARLen and ARCount basics", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
length, err := client.ARLen(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(length).To(Equal(uint64(0)))
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(0)))
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 0, "a").Err()).NotTo(HaveOccurred())
|
|
length, err = client.ARLen(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(length).To(Equal(uint64(1)))
|
|
count, err = client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(1)))
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 5, "b").Err()).NotTo(HaveOccurred())
|
|
length, err = client.ARLen(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(length).To(Equal(uint64(6)))
|
|
count, err = client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(2)))
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 100, "c").Err()).NotTo(HaveOccurred())
|
|
length, err = client.ARLen(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(length).To(Equal(uint64(101)))
|
|
count, err = client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(3)))
|
|
})
|
|
})
|
|
|
|
Describe("ARDel", func() {
|
|
It("should ARDel basics", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 0, "a").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 1, "b").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 2, "c").Err()).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARDel(ctx, "myarray", 1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(1)))
|
|
|
|
_, err = client.ARGet(ctx, "myarray", 1).Result()
|
|
Expect(err).To(Equal(redis.Nil))
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(2)))
|
|
|
|
// Delete non-existing index returns 0
|
|
n, err = client.ARDel(ctx, "myarray", 1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(0)))
|
|
})
|
|
|
|
It("should ARDel multiple indices", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 0, "a").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 1, "b").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 2, "c").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 3, "d").Err()).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARDel(ctx, "myarray", 0, 1, 2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(3)))
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(1)))
|
|
})
|
|
|
|
It("should ARDel last element delete key", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 0, "a").Err()).NotTo(HaveOccurred())
|
|
|
|
_, err := client.ARDel(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
exists, err := client.Exists(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(exists).To(Equal(int64(0)))
|
|
})
|
|
})
|
|
|
|
Describe("ARDelRange", func() {
|
|
It("should ARDelRange basics", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
for i := 0; i < 10; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("%d", i*10)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(10)))
|
|
|
|
n, err := client.ARDelRange(ctx, "myarray", redis.ARRange{Start: 2, End: 6}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(uint64(5)))
|
|
|
|
count, err = client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(5)))
|
|
})
|
|
|
|
It("should ARDelRange reverse order", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
for i := 0; i < 10; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("%d", i*10)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
n, err := client.ARDelRange(ctx, "myarray", redis.ARRange{Start: 6, End: 2}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(uint64(5)))
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(5)))
|
|
})
|
|
|
|
It("should ARDelRange with multiple ranges in single call", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
for i := 0; i < 20; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("val%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
n, err := client.ARDelRange(ctx, "myarray",
|
|
redis.ARRange{Start: 2, End: 4},
|
|
redis.ARRange{Start: 10, End: 14},
|
|
).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(uint64(8)))
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(12)))
|
|
|
|
// Verify correct elements deleted
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("val0"))
|
|
|
|
_, err = client.ARGet(ctx, "myarray", 2).Result()
|
|
Expect(err).To(Equal(redis.Nil))
|
|
|
|
_, err = client.ARGet(ctx, "myarray", 4).Result()
|
|
Expect(err).To(Equal(redis.Nil))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 15).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("val15"))
|
|
})
|
|
})
|
|
|
|
Describe("ARMSet and ARMGet", func() {
|
|
It("should ARMSet basics", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "a"},
|
|
redis.AREntry{Index: 1, Value: "b"},
|
|
redis.AREntry{Index: 2, Value: "c"},
|
|
).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(3)))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("a"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("b"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("c"))
|
|
})
|
|
|
|
It("should ARMSet return only newly filled slots", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 0, "a").Err()).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "aa"},
|
|
redis.AREntry{Index: 1, Value: "b"},
|
|
).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(1)))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("aa"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("b"))
|
|
})
|
|
|
|
It("should ARMGet basics", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 0, "a").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 1, "b").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 5, "c").Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARMGet(ctx, "myarray", 0, 1, 5, 3).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(HaveLen(4))
|
|
Expect(result[0]).To(Equal("a"))
|
|
Expect(result[1]).To(Equal("b"))
|
|
Expect(result[2]).To(Equal("c"))
|
|
Expect(result[3]).To(BeNil())
|
|
})
|
|
})
|
|
|
|
Describe("ARGetRange and contiguous ARSet", func() {
|
|
It("should ARGetRange basics", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "a"},
|
|
redis.AREntry{Index: 1, Value: "b"},
|
|
redis.AREntry{Index: 2, Value: "c"},
|
|
redis.AREntry{Index: 3, Value: "d"},
|
|
redis.AREntry{Index: 4, Value: "e"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARGetRange(ctx, "myarray", 1, 3).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"b", "c", "d"}))
|
|
})
|
|
|
|
It("should ARGetRange reverse", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "a"},
|
|
redis.AREntry{Index: 1, Value: "b"},
|
|
redis.AREntry{Index: 2, Value: "c"},
|
|
redis.AREntry{Index: 3, Value: "d"},
|
|
redis.AREntry{Index: 4, Value: "e"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARGetRange(ctx, "myarray", 3, 1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"d", "c", "b"}))
|
|
})
|
|
|
|
It("should ARGetRange error when range exceeds hard limit", func() {
|
|
err := client.ARGetRange(ctx, "myarray", 0, 1000000).Err()
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(err.Error()).To(ContainSubstring("range exceeds maximum"))
|
|
})
|
|
|
|
It("should ARGetRange reverse error when range exceeds hard limit", func() {
|
|
err := client.ARGetRange(ctx, "myarray", 1000000, 0).Err()
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(err.Error()).To(ContainSubstring("range exceeds maximum"))
|
|
})
|
|
|
|
It("should ARSet contiguous write basics", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARSet(ctx, "myarray", 0, "a", "b", "c").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(3)))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("a"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("b"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("c"))
|
|
})
|
|
})
|
|
|
|
Describe("ARScan", func() {
|
|
It("should ARScan return only existing elements with indices", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 0, "a").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 5, "b").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 9, "c").Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARScan(ctx, "myarray", 0, 10, nil).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(HaveLen(3))
|
|
Expect(result[0]).To(Equal(redis.AREntry{Index: 0, Value: "a"}))
|
|
Expect(result[1]).To(Equal(redis.AREntry{Index: 5, Value: "b"}))
|
|
Expect(result[2]).To(Equal(redis.AREntry{Index: 9, Value: "c"}))
|
|
})
|
|
|
|
It("should ARScan on empty range return empty array", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 500, "x").Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARScan(ctx, "myarray", 0, 100, nil).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(BeEmpty())
|
|
})
|
|
|
|
It("should ARScan reversed range", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 0, "a").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 5, "b").Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARScan(ctx, "myarray", 5, 0, nil).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(HaveLen(2))
|
|
Expect(result[0]).To(Equal(redis.AREntry{Index: 5, Value: "b"}))
|
|
Expect(result[1]).To(Equal(redis.AREntry{Index: 0, Value: "a"}))
|
|
})
|
|
|
|
It("should ARScan on non-existent key return empty array", func() {
|
|
Expect(client.Del(ctx, "nokey").Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARScan(ctx, "nokey", 0, 100, nil).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(BeEmpty())
|
|
})
|
|
|
|
It("should ARScan with mixed value types", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 0, "string").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 1, "12345").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 2, "3.14").Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARScan(ctx, "myarray", 0, 10, nil).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(HaveLen(3))
|
|
Expect(result[0]).To(Equal(redis.AREntry{Index: 0, Value: "string"}))
|
|
Expect(result[1]).To(Equal(redis.AREntry{Index: 1, Value: "12345"}))
|
|
Expect(result[2]).To(Equal(redis.AREntry{Index: 2, Value: "3.14"}))
|
|
})
|
|
|
|
It("should ARScan with LIMIT", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
for i := 0; i < 20; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
// Delete some in the middle
|
|
_, err := client.ARDelRange(ctx, "myarray", redis.ARRange{Start: 5, End: 14}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARScan(ctx, "myarray", 0, 100, &redis.ARScanArgs{Limit: 3}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(HaveLen(3))
|
|
Expect(result[0]).To(Equal(redis.AREntry{Index: 0, Value: "0"}))
|
|
Expect(result[1]).To(Equal(redis.AREntry{Index: 1, Value: "1"}))
|
|
Expect(result[2]).To(Equal(redis.AREntry{Index: 2, Value: "2"}))
|
|
})
|
|
})
|
|
|
|
Describe("ARInsert", func() {
|
|
It("should ARInsert basics", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
idx, err := client.ARInsert(ctx, "myarray", "a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(idx).To(Equal(uint64(0)))
|
|
|
|
idx, err = client.ARInsert(ctx, "myarray", "b").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(idx).To(Equal(uint64(1)))
|
|
|
|
idx, err = client.ARInsert(ctx, "myarray", "c").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(idx).To(Equal(uint64(2)))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("a"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("b"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("c"))
|
|
})
|
|
|
|
It("should ARInsert with multiple values", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
idx, err := client.ARInsert(ctx, "myarray", "x", "y", "z").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(idx).To(Equal(uint64(2)))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("x"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("y"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("z"))
|
|
})
|
|
})
|
|
|
|
Describe("ARGrep", func() {
|
|
It("should ARGREP MATCH return matching indexes", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "alpha"},
|
|
redis.AREntry{Index: 1, Value: "beta"},
|
|
redis.AREntry{Index: 2, Value: "alphabet"},
|
|
redis.AREntry{Index: 5, Value: "gamma"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARGrep(ctx, "myarray", "-", "+", &redis.ARGrepArgs{
|
|
Predicates: []redis.ARGrepPredicate{
|
|
{Type: redis.ARGrepMatch, Value: "alpha"},
|
|
},
|
|
}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]uint64{0, 2}))
|
|
})
|
|
|
|
It("should ARGREP support WITHVALUES and reverse ranges", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "alpha"},
|
|
redis.AREntry{Index: 1, Value: "beta"},
|
|
redis.AREntry{Index: 2, Value: "alphabet"},
|
|
redis.AREntry{Index: 3, Value: "delta"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARGrepWithValues(ctx, "myarray", "3", "0", &redis.ARGrepArgs{
|
|
Predicates: []redis.ARGrepPredicate{
|
|
{Type: redis.ARGrepMatch, Value: "alpha"},
|
|
},
|
|
}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(HaveLen(2))
|
|
Expect(result[0]).To(Equal(redis.AREntry{Index: 2, Value: "alphabet"}))
|
|
Expect(result[1]).To(Equal(redis.AREntry{Index: 0, Value: "alpha"}))
|
|
})
|
|
|
|
It("should ARGREP support AND, GLOB, and NOCASE", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "RedisArray"},
|
|
redis.AREntry{Index: 1, Value: "redis-match"},
|
|
redis.AREntry{Index: 2, Value: "array-only"},
|
|
redis.AREntry{Index: 3, Value: "plain"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARGrep(ctx, "myarray", "-", "+", &redis.ARGrepArgs{
|
|
Predicates: []redis.ARGrepPredicate{
|
|
{Type: redis.ARGrepMatch, Value: "redis"},
|
|
{Type: redis.ARGrepGlob, Value: "*array*"},
|
|
},
|
|
CombineAnd: true,
|
|
NoCase: true,
|
|
}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]uint64{0}))
|
|
})
|
|
|
|
It("should ARGREP support RE predicates", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "foo123"},
|
|
redis.AREntry{Index: 1, Value: "bar"},
|
|
redis.AREntry{Index: 2, Value: "zoo999"},
|
|
redis.AREntry{Index: 3, Value: "Foo777"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARGrep(ctx, "myarray", "-", "+", &redis.ARGrepArgs{
|
|
Predicates: []redis.ARGrepPredicate{
|
|
{Type: redis.ARGrepRegex, Value: `^.*[0-9]{3}$`},
|
|
},
|
|
}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]uint64{0, 2, 3}))
|
|
|
|
result, err = client.ARGrep(ctx, "myarray", "-", "+", &redis.ARGrepArgs{
|
|
Predicates: []redis.ARGrepPredicate{
|
|
{Type: redis.ARGrepRegex, Value: `^foo[0-9]+$`},
|
|
},
|
|
NoCase: true,
|
|
}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]uint64{0, 3}))
|
|
})
|
|
|
|
It("should ARGREP LIMIT stop after enough matches", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "hit-1"},
|
|
redis.AREntry{Index: 1, Value: "hit-2"},
|
|
redis.AREntry{Index: 2, Value: "miss"},
|
|
redis.AREntry{Index: 3, Value: "hit-3"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARGrep(ctx, "myarray", "-", "+", &redis.ARGrepArgs{
|
|
Predicates: []redis.ARGrepPredicate{
|
|
{Type: redis.ARGrepMatch, Value: "hit"},
|
|
},
|
|
Limit: 2,
|
|
}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]uint64{0, 1}))
|
|
})
|
|
|
|
It("should ARGREP handle missing keys", func() {
|
|
Expect(client.Del(ctx, "nokey").Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARGrep(ctx, "nokey", "-", "+", &redis.ARGrepArgs{
|
|
Predicates: []redis.ARGrepPredicate{
|
|
{Type: redis.ARGrepMatch, Value: "foo"},
|
|
},
|
|
}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(BeEmpty())
|
|
})
|
|
|
|
It("should ARGREP not panic with nil args", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "alpha"},
|
|
redis.AREntry{Index: 1, Value: "beta"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
// nil args should not panic, just send bare ARGREP without predicates
|
|
Expect(func() {
|
|
_ = client.ARGrep(ctx, "myarray", "-", "+", nil)
|
|
}).NotTo(Panic())
|
|
|
|
Expect(func() {
|
|
_ = client.ARGrepWithValues(ctx, "myarray", "-", "+", nil)
|
|
}).NotTo(Panic())
|
|
})
|
|
})
|
|
|
|
Describe("ARRing", func() {
|
|
It("should ARRING create ring buffer", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
for i := 0; i < 10; i++ {
|
|
_, err := client.ARRing(ctx, "myarray", 5, fmt.Sprintf("%d", i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
}
|
|
|
|
// After wrap, indices 0-4 should have values 5-9
|
|
for i := 0; i < 5; i++ {
|
|
val, err := client.ARGet(ctx, "myarray", uint64(i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("%d", i+5)))
|
|
}
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(5)))
|
|
})
|
|
|
|
It("should ARRING basic wraparound", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
for i := 0; i < 20; i++ {
|
|
idx, err := client.ARRing(ctx, "myarray", 10, fmt.Sprintf("val%d", i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(idx).To(Equal(uint64(i % 10)))
|
|
}
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(10)))
|
|
|
|
// Values should be the last 10 inserted (val10-val19)
|
|
for i := 0; i < 10; i++ {
|
|
val, err := client.ARGet(ctx, "myarray", uint64(i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("val%d", i+10)))
|
|
}
|
|
})
|
|
|
|
It("should ARRING with size 1", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
for i := 0; i < 100; i++ {
|
|
_, err := client.ARRing(ctx, "myarray", 1, fmt.Sprintf("val%d", i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
}
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(1)))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("val99"))
|
|
})
|
|
})
|
|
|
|
Describe("ARNext and ARSeek", func() {
|
|
It("should ARNext track insert position", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
next, err := client.ARNext(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(next).To(Equal(uint64(0)))
|
|
|
|
_, err = client.ARInsert(ctx, "myarray", "a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
next, err = client.ARNext(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(next).To(Equal(uint64(1)))
|
|
|
|
_, err = client.ARInsert(ctx, "myarray", "b").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
next, err = client.ARNext(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(next).To(Equal(uint64(2)))
|
|
})
|
|
|
|
It("should ARSeek work", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
_, err := client.ARInsert(ctx, "myarray", "a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
_, err = client.ARInsert(ctx, "myarray", "b").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARSeek(ctx, "myarray", 10).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(1)))
|
|
|
|
idx, err := client.ARInsert(ctx, "myarray", "c").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(idx).To(Equal(uint64(10)))
|
|
|
|
next, err := client.ARNext(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(next).To(Equal(uint64(11)))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 10).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("c"))
|
|
})
|
|
|
|
It("should ARNext return nil when insert cursor is exhausted", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
_, err := client.ARInsert(ctx, "myarray", "a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
// Move to terminal cursor state
|
|
_, err = client.ARSeek(ctx, "myarray", 18446744073709551615).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
_, err = client.ARNext(ctx, "myarray").Result()
|
|
Expect(err).To(HaveOccurred())
|
|
})
|
|
|
|
It("should ARSeek on non-existent key not create it", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARSeek(ctx, "myarray", 100).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(0)))
|
|
|
|
exists, err := client.Exists(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(exists).To(Equal(int64(0)))
|
|
|
|
// Now create the array and verify ARSeek works
|
|
_, err = client.ARInsert(ctx, "myarray", "first").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
exists, err = client.Exists(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(exists).To(Equal(int64(1)))
|
|
|
|
n, err = client.ARSeek(ctx, "myarray", 50).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(1)))
|
|
|
|
_, err = client.ARInsert(ctx, "myarray", "second").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
next, err := client.ARNext(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(next).To(Equal(uint64(51)))
|
|
})
|
|
})
|
|
|
|
Describe("ARLastItems", func() {
|
|
It("should ARLASTITEMS basics", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
for i := 0; i < 5; i++ {
|
|
_, err := client.ARInsert(ctx, "myarray", fmt.Sprintf("%d", i*10)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
}
|
|
|
|
result, err := client.ARLastItems(ctx, "myarray", 3, false).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"20", "30", "40"}))
|
|
|
|
result, err = client.ARLastItems(ctx, "myarray", 3, true).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"40", "30", "20"}))
|
|
})
|
|
|
|
It("should ARLASTITEMS handle empty and partial cases", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
// Empty array
|
|
result, err := client.ARLastItems(ctx, "myarray", 5, false).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(BeEmpty())
|
|
|
|
// Fewer items than requested
|
|
_, err = client.ARRing(ctx, "myarray", 10, "a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
_, err = client.ARRing(ctx, "myarray", 10, "b").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
_, err = client.ARRing(ctx, "myarray", 10, "c").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
result, err = client.ARLastItems(ctx, "myarray", 5, false).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"a", "b", "c"}))
|
|
})
|
|
|
|
It("should ARLASTITEMS with various counts and REV", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
for i := 0; i < 20; i++ {
|
|
_, err := client.ARRing(ctx, "myarray", 10, fmt.Sprintf("item%d", i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
}
|
|
|
|
// Get exactly 1 item
|
|
result, err := client.ARLastItems(ctx, "myarray", 1, false).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"item19"}))
|
|
|
|
result, err = client.ARLastItems(ctx, "myarray", 1, true).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"item19"}))
|
|
|
|
// Get 3 items
|
|
result, err = client.ARLastItems(ctx, "myarray", 3, false).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"item17", "item18", "item19"}))
|
|
|
|
result, err = client.ARLastItems(ctx, "myarray", 3, true).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"item19", "item18", "item17"}))
|
|
})
|
|
|
|
It("should ARLASTITEMS edge cases", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
// Empty array
|
|
result, err := client.ARLastItems(ctx, "myarray", 5, false).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(BeEmpty())
|
|
|
|
result, err = client.ARLastItems(ctx, "myarray", 5, true).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(BeEmpty())
|
|
|
|
// Single element
|
|
_, err = client.ARInsert(ctx, "myarray", "only").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
result, err = client.ARLastItems(ctx, "myarray", 1, false).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"only"}))
|
|
|
|
result, err = client.ARLastItems(ctx, "myarray", 10, false).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"only"}))
|
|
|
|
// Two elements
|
|
_, err = client.ARInsert(ctx, "myarray", "second").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
result, err = client.ARLastItems(ctx, "myarray", 5, false).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"only", "second"}))
|
|
|
|
result, err = client.ARLastItems(ctx, "myarray", 5, true).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"second", "only"}))
|
|
})
|
|
})
|
|
|
|
Describe("AROp", func() {
|
|
It("should AROpSum", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "10"},
|
|
redis.AREntry{Index: 1, Value: "20"},
|
|
redis.AREntry{Index: 2, Value: "30"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
val, err := client.AROpSum(ctx, "myarray", 0, 2).Float64()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(float64(60)))
|
|
})
|
|
|
|
It("should AROpMin", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "30"},
|
|
redis.AREntry{Index: 1, Value: "10"},
|
|
redis.AREntry{Index: 2, Value: "20"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
val, err := client.AROpMin(ctx, "myarray", 0, 2).Float64()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(float64(10)))
|
|
})
|
|
|
|
It("should AROpMax", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "30"},
|
|
redis.AREntry{Index: 1, Value: "10"},
|
|
redis.AREntry{Index: 2, Value: "20"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
val, err := client.AROpMax(ctx, "myarray", 0, 2).Float64()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(float64(30)))
|
|
})
|
|
|
|
It("should AROpMatch", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "hello"},
|
|
redis.AREntry{Index: 1, Value: "world"},
|
|
redis.AREntry{Index: 2, Value: "hello"},
|
|
redis.AREntry{Index: 3, Value: "foo"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
val, err := client.AROpMatch(ctx, "myarray", 0, 3, "hello").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(2)))
|
|
|
|
val, err = client.AROpMatch(ctx, "myarray", 0, 3, "world").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(1)))
|
|
|
|
val, err = client.AROpMatch(ctx, "myarray", 0, 3, "bar").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(0)))
|
|
})
|
|
|
|
It("should AROpUsed", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "a"},
|
|
redis.AREntry{Index: 2, Value: "b"},
|
|
redis.AREntry{Index: 5, Value: "c"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
val, err := client.AROpUsed(ctx, "myarray", 0, 10).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(3)))
|
|
})
|
|
|
|
It("should AROpAnd/AROpOr/AROpXor", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "255"},
|
|
redis.AREntry{Index: 1, Value: "15"},
|
|
redis.AREntry{Index: 2, Value: "240"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
// AND: 255 & 15 & 240 = 0
|
|
val, err := client.AROpAnd(ctx, "myarray", 0, 2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(0)))
|
|
|
|
// OR: 255 | 15 | 240 = 255
|
|
val, err = client.AROpOr(ctx, "myarray", 0, 2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(255)))
|
|
|
|
// XOR: 255 ^ 15 ^ 240 = 0
|
|
val, err = client.AROpXor(ctx, "myarray", 0, 2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(0)))
|
|
})
|
|
|
|
It("should AROpAnd/AROpOr/AROpXor truncate floats toward zero", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "7.9"},
|
|
redis.AREntry{Index: 1, Value: "3.2"},
|
|
redis.AREntry{Index: 2, Value: "1.8"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
// Truncated: 7, 3, 1 → AND = 1
|
|
val, err := client.AROpAnd(ctx, "myarray", 0, 2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(1)))
|
|
|
|
// OR = 7
|
|
val, err = client.AROpOr(ctx, "myarray", 0, 2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(7)))
|
|
|
|
// XOR: 7 ^ 3 ^ 1 = 5
|
|
val, err = client.AROpXor(ctx, "myarray", 0, 2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(5)))
|
|
})
|
|
|
|
It("should AROpAnd/AROpOr/AROpXor return negative results", func() {
|
|
// AND: -1 & -5 = -5 (all bits of -5 are subset of -1)
|
|
cmd := client.Del(ctx, "myarray")
|
|
Expect(cmd.Err()).NotTo(HaveOccurred())
|
|
cmd2 := client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "-1"},
|
|
redis.AREntry{Index: 1, Value: "-5"},
|
|
)
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
val, err := client.AROpAnd(ctx, "myarray", 0, 1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(-5)))
|
|
|
|
// OR: -4 | 3 = -1
|
|
cmd = client.Del(ctx, "myarray")
|
|
Expect(cmd.Err()).NotTo(HaveOccurred())
|
|
cmd2 = client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "-4"},
|
|
redis.AREntry{Index: 1, Value: "3"},
|
|
)
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
val, err = client.AROpOr(ctx, "myarray", 0, 1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(-1)))
|
|
|
|
// XOR: 0 ^ -1 = -1
|
|
cmd = client.Del(ctx, "myarray")
|
|
Expect(cmd.Err()).NotTo(HaveOccurred())
|
|
cmd2 = client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "0"},
|
|
redis.AREntry{Index: 1, Value: "-1"},
|
|
)
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
val, err = client.AROpXor(ctx, "myarray", 0, 1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(-1)))
|
|
})
|
|
|
|
It("should AROpMatch with large strings", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
largestr := strings.Repeat("x", 300)
|
|
largestr2 := strings.Repeat("y", 300)
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 0, largestr).Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 1, "small").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 2, largestr).Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 3, largestr2).Err()).NotTo(HaveOccurred())
|
|
|
|
val, err := client.AROpMatch(ctx, "myarray", 0, 3, largestr).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(2)))
|
|
|
|
val, err = client.AROpMatch(ctx, "myarray", 0, 3, largestr2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(1)))
|
|
|
|
val, err = client.AROpMatch(ctx, "myarray", 0, 3, "small").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(1)))
|
|
|
|
val, err = client.AROpMatch(ctx, "myarray", 0, 3, "notfound").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(int64(0)))
|
|
})
|
|
})
|
|
|
|
Describe("ARInfo", func() {
|
|
It("should ARInfo basics", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "a"},
|
|
redis.AREntry{Index: 1, Value: "b"},
|
|
redis.AREntry{Index: 100, Value: "c"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
info, err := client.ARInfo(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(info["count"]).To(Equal(int64(3)))
|
|
Expect(info["len"]).To(Equal(int64(101)))
|
|
})
|
|
})
|
|
|
|
Describe("ARInfoFull", func() {
|
|
It("should ARInfoFull return all basic fields plus slice statistics", func() {
|
|
cmd := client.Del(ctx, "myarray")
|
|
Expect(cmd.Err()).NotTo(HaveOccurred())
|
|
cmd2 := client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: 0, Value: "a"},
|
|
redis.AREntry{Index: 1, Value: "b"},
|
|
redis.AREntry{Index: 100, Value: "c"},
|
|
)
|
|
Expect(cmd2.Err()).NotTo(HaveOccurred())
|
|
|
|
info, err := client.ARInfoFull(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
// Same basic fields as ARInfo
|
|
Expect(info["count"]).To(Equal(int64(3)))
|
|
Expect(info["len"]).To(Equal(int64(101)))
|
|
Expect(info).To(HaveKey("next-insert-index"))
|
|
Expect(info).To(HaveKey("slices"))
|
|
Expect(info).To(HaveKey("directory-size"))
|
|
Expect(info).To(HaveKey("super-dir-entries"))
|
|
Expect(info).To(HaveKey("slice-size"))
|
|
|
|
// Full-only fields: per-encoding slice statistics
|
|
Expect(info).To(HaveKey("dense-slices"))
|
|
Expect(info).To(HaveKey("sparse-slices"))
|
|
Expect(info).To(HaveKey("avg-dense-size"))
|
|
Expect(info).To(HaveKey("avg-dense-fill"))
|
|
Expect(info).To(HaveKey("avg-sparse-size"))
|
|
|
|
// Sum of dense and sparse slices should equal total slices
|
|
denseSlices := info["dense-slices"].(int64)
|
|
sparseSlices := info["sparse-slices"].(int64)
|
|
totalSlices := info["slices"].(int64)
|
|
Expect(denseSlices + sparseSlices).To(Equal(totalSlices))
|
|
})
|
|
})
|
|
|
|
Describe("Type checks", func() {
|
|
It("should array commands return WRONGTYPE on wrong type", func() {
|
|
Expect(client.Del(ctx, "mykey").Err()).NotTo(HaveOccurred())
|
|
Expect(client.Set(ctx, "mykey", "value", 0).Err()).NotTo(HaveOccurred())
|
|
|
|
err := client.ARGet(ctx, "mykey", 0).Err()
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(err.Error()).To(ContainSubstring("WRONGTYPE"))
|
|
|
|
err = client.ARSet(ctx, "mykey", 0, "foo").Err()
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(err.Error()).To(ContainSubstring("WRONGTYPE"))
|
|
|
|
err = client.ARLen(ctx, "mykey").Err()
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(err.Error()).To(ContainSubstring("WRONGTYPE"))
|
|
|
|
err = client.ARCount(ctx, "mykey").Err()
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(err.Error()).To(ContainSubstring("WRONGTYPE"))
|
|
})
|
|
|
|
It("should ARMGET return WRONGTYPE on wrong type", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.Set(ctx, "myarray", "string_value", 0).Err()).NotTo(HaveOccurred())
|
|
|
|
err := client.ARMGet(ctx, "myarray", 0, 1, 2).Err()
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(err.Error()).To(ContainSubstring("WRONGTYPE"))
|
|
})
|
|
|
|
It("should ARGETRANGE return WRONGTYPE on wrong type", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.Set(ctx, "myarray", "string_value", 0).Err()).NotTo(HaveOccurred())
|
|
|
|
err := client.ARGetRange(ctx, "myarray", 0, 10).Err()
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(err.Error()).To(ContainSubstring("WRONGTYPE"))
|
|
})
|
|
})
|
|
|
|
Describe("TYPE and OBJECT ENCODING", func() {
|
|
It("should TYPE return array", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 0, "hello").Err()).NotTo(HaveOccurred())
|
|
|
|
typ, err := client.Type(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(typ).To(Equal("array"))
|
|
})
|
|
|
|
It("should OBJECT ENCODING return sliced-array", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 0, "hello").Err()).NotTo(HaveOccurred())
|
|
|
|
encoding, err := client.ObjectEncoding(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(encoding).To(Equal("sliced-array"))
|
|
})
|
|
})
|
|
|
|
Describe("Sparse arrays", func() {
|
|
It("should sparse array with large gaps work", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 0, "a").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 10000, "b").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 1000000, "c").Err()).NotTo(HaveOccurred())
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("a"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 10000).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("b"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 1000000).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("c"))
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(3)))
|
|
|
|
length, err := client.ARLen(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(length).To(Equal(uint64(1000001)))
|
|
})
|
|
})
|
|
|
|
Describe("Directory resizing", func() {
|
|
It("should handle many slices", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
sliceSize := 4096
|
|
for i := 0; i < 20; i++ {
|
|
idx := uint64(i * sliceSize)
|
|
Expect(client.ARSet(ctx, "myarray", idx, fmt.Sprintf("slice%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
for i := 0; i < 20; i++ {
|
|
idx := uint64(i * sliceSize)
|
|
val, err := client.ARGet(ctx, "myarray", idx).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("slice%d", i)))
|
|
}
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(20)))
|
|
})
|
|
|
|
It("should handle very large index jump", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 0, "start").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 1000000, "middle").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 10000000, "end").Err()).NotTo(HaveOccurred())
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("start"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 1000000).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("middle"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 10000000).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("end"))
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(3)))
|
|
})
|
|
})
|
|
|
|
Describe("Dense window growth", func() {
|
|
It("should handle right expansion", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
for i := 0; i < 100; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("val%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
for i := 0; i < 100; i++ {
|
|
val, err := client.ARGet(ctx, "myarray", uint64(i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("val%d", i)))
|
|
}
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(100)))
|
|
})
|
|
|
|
It("should handle left expansion", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 500, "anchor").Err()).NotTo(HaveOccurred())
|
|
for i := 499; i >= 400; i-- {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("val%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 500).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("anchor"))
|
|
|
|
for i := 400; i < 500; i++ {
|
|
val, err := client.ARGet(ctx, "myarray", uint64(i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("val%d", i)))
|
|
}
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(101)))
|
|
})
|
|
|
|
It("should handle bidirectional expansion", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 500, "center").Err()).NotTo(HaveOccurred())
|
|
for i := 1; i <= 50; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(500-i), fmt.Sprintf("left%d", i)).Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", uint64(500+i), fmt.Sprintf("right%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 500).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("center"))
|
|
|
|
for i := 1; i <= 50; i++ {
|
|
val, err := client.ARGet(ctx, "myarray", uint64(500-i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("left%d", i)))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", uint64(500+i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("right%d", i)))
|
|
}
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(101)))
|
|
})
|
|
})
|
|
|
|
Describe("Sparse to dense promotion", func() {
|
|
It("should promote sparse to dense when exceeding kmax threshold", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
// Add 15 elements at scattered offsets within one slice
|
|
for i := 0; i < 15; i++ {
|
|
idx := uint64(i * 100)
|
|
Expect(client.ARSet(ctx, "myarray", idx, fmt.Sprintf("sparse%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
for i := 0; i < 15; i++ {
|
|
idx := uint64(i * 100)
|
|
val, err := client.ARGet(ctx, "myarray", idx).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("sparse%d", i)))
|
|
}
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(15)))
|
|
})
|
|
|
|
It("should promote then continue adding", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
// Phase 1: sparse
|
|
for i := 0; i < 5; i++ {
|
|
idx := uint64(i * 200)
|
|
Expect(client.ARSet(ctx, "myarray", idx, fmt.Sprintf("phase1_%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
// Phase 2: more to trigger promotion
|
|
for i := 5; i < 20; i++ {
|
|
idx := uint64(i * 200)
|
|
Expect(client.ARSet(ctx, "myarray", idx, fmt.Sprintf("phase2_%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
// Verify all
|
|
for i := 0; i < 5; i++ {
|
|
idx := uint64(i * 200)
|
|
val, err := client.ARGet(ctx, "myarray", idx).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("phase1_%d", i)))
|
|
}
|
|
for i := 5; i < 20; i++ {
|
|
idx := uint64(i * 200)
|
|
val, err := client.ARGet(ctx, "myarray", idx).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("phase2_%d", i)))
|
|
}
|
|
})
|
|
})
|
|
|
|
Describe("Dense to sparse demotion", func() {
|
|
It("should demote dense to sparse when deleting below kmin", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
// Create dense slice with 50 elements
|
|
for i := 0; i < 50; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("val%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
// Delete most elements, leaving only 3
|
|
for i := 3; i < 50; i++ {
|
|
_, err := client.ARDel(ctx, "myarray", uint64(i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
}
|
|
|
|
// Verify remaining elements
|
|
for i := 0; i < 3; i++ {
|
|
val, err := client.ARGet(ctx, "myarray", uint64(i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("val%d", i)))
|
|
}
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(3)))
|
|
})
|
|
|
|
It("should demote then add again", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
// Create dense
|
|
for i := 0; i < 30; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("initial%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
// Delete to demote
|
|
for i := 4; i < 30; i++ {
|
|
_, err := client.ARDel(ctx, "myarray", uint64(i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
}
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(4)))
|
|
|
|
// Add new elements
|
|
for i := 100; i < 105; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("new%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
// Verify old and new
|
|
for i := 0; i < 4; i++ {
|
|
val, err := client.ARGet(ctx, "myarray", uint64(i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("initial%d", i)))
|
|
}
|
|
for i := 100; i < 105; i++ {
|
|
val, err := client.ARGet(ctx, "myarray", uint64(i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("new%d", i)))
|
|
}
|
|
})
|
|
})
|
|
|
|
Describe("ARDelRange detailed", func() {
|
|
It("should ARDELRANGE trigger dense to sparse demotion", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
// Create dense slice with 50 elements
|
|
for i := 0; i < 50; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("val%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
// Delete most elements with ARDELRANGE
|
|
n, err := client.ARDelRange(ctx, "myarray", redis.ARRange{Start: 3, End: 49}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(uint64(47)))
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(3)))
|
|
|
|
// Verify remaining elements
|
|
for i := 0; i < 3; i++ {
|
|
val, err := client.ARGet(ctx, "myarray", uint64(i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("val%d", i)))
|
|
}
|
|
})
|
|
|
|
It("should ARDELRANGE with overlapping ranges", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
for i := 0; i < 20; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("val%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
// Overlapping ranges: [5,12] and [8,15]
|
|
n, err := client.ARDelRange(ctx, "myarray",
|
|
redis.ARRange{Start: 5, End: 12},
|
|
redis.ARRange{Start: 8, End: 15},
|
|
).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(uint64(11)))
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(9)))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 4).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("val4"))
|
|
|
|
_, err = client.ARGet(ctx, "myarray", 5).Result()
|
|
Expect(err).To(Equal(redis.Nil))
|
|
|
|
_, err = client.ARGet(ctx, "myarray", 15).Result()
|
|
Expect(err).To(Equal(redis.Nil))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 16).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("val16"))
|
|
})
|
|
|
|
It("should ARDELRANGE sparse slice middle-span deletion", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 10, "a").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 20, "b").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 30, "c").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 40, "d").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 50, "e").Err()).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARDelRange(ctx, "myarray", redis.ARRange{Start: 20, End: 40}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(uint64(3)))
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(2)))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 10).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("a"))
|
|
|
|
_, err = client.ARGet(ctx, "myarray", 20).Result()
|
|
Expect(err).To(Equal(redis.Nil))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 50).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("e"))
|
|
})
|
|
|
|
It("should ARDELRANGE sparse whole-slice deletion", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 10, "a").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 20, "b").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 30, "c").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 40, "d").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 50, "e").Err()).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARDelRange(ctx, "myarray", redis.ARRange{Start: 0, End: 100}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(uint64(5)))
|
|
|
|
exists, err := client.Exists(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(exists).To(Equal(int64(0)))
|
|
})
|
|
|
|
It("should ARDELRANGE sparse no-hit range", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 10, "a").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 20, "b").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 30, "c").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 40, "d").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 50, "e").Err()).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARDelRange(ctx, "myarray", redis.ARRange{Start: 11, End: 19}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(uint64(0)))
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(5)))
|
|
})
|
|
|
|
It("should ARDELRANGE delete entire slice then verify iteration", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
// Two slices
|
|
for i := 0; i < 10; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("slice0_%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
for i := 4096; i < 4106; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("slice1_%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
// Delete entire first slice
|
|
n, err := client.ARDelRange(ctx, "myarray", redis.ARRange{Start: 0, End: 4095}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(uint64(10)))
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(10)))
|
|
|
|
// ARSCAN should only find second slice elements
|
|
result, err := client.ARScan(ctx, "myarray", 0, 5000, nil).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(HaveLen(10))
|
|
Expect(result[0].Index).To(Equal(uint64(4096)))
|
|
Expect(result[0].Value).To(Equal("slice1_4096"))
|
|
})
|
|
})
|
|
|
|
Describe("Circular buffer", func() {
|
|
It("should ARRING wraparound with ARLASTITEMS", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
// Create ring with 8 items, MOD 5
|
|
for i := 0; i < 8; i++ {
|
|
_, err := client.ARRing(ctx, "myarray", 5, fmt.Sprintf("%d", i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
}
|
|
|
|
// Values: 0->3, 1->4, 2->5, 3->6, 4->7
|
|
result, err := client.ARLastItems(ctx, "myarray", 3, false).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"5", "6", "7"}))
|
|
|
|
result, err = client.ARLastItems(ctx, "myarray", 3, true).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"7", "6", "5"}))
|
|
|
|
// Request more items than exist
|
|
result, err = client.ARLastItems(ctx, "myarray", 10, false).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(HaveLen(5))
|
|
})
|
|
|
|
It("should ARLASTITEMS handle empty and partial cases", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
// Empty array
|
|
result, err := client.ARLastItems(ctx, "myarray", 5, false).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(BeEmpty())
|
|
|
|
// Fewer items than requested
|
|
_, err = client.ARRing(ctx, "myarray", 10, "a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
_, err = client.ARRing(ctx, "myarray", 10, "b").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
_, err = client.ARRing(ctx, "myarray", 10, "c").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
result, err = client.ARLastItems(ctx, "myarray", 5, false).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"a", "b", "c"}))
|
|
})
|
|
|
|
It("should ARNEXT track correctly with ARRING", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
for i := 0; i < 7; i++ {
|
|
expectedIdx := uint64(i % 4)
|
|
idx, err := client.ARRing(ctx, "myarray", 4, fmt.Sprintf("%d", i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(idx).To(Equal(expectedIdx))
|
|
|
|
next, err := client.ARNext(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
if i < 4 {
|
|
Expect(next).To(Equal(uint64(i + 1)))
|
|
} else {
|
|
Expect(next).To(Equal(expectedIdx + 1))
|
|
}
|
|
}
|
|
})
|
|
|
|
It("should ARRING truncation when size decreases", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
// Create ring buffer with MOD 10
|
|
for i := 0; i < 15; i++ {
|
|
_, err := client.ARRing(ctx, "myarray", 10, fmt.Sprintf("v%d", i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
}
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(10)))
|
|
|
|
// Use smaller MOD - truncates and inserts new value
|
|
_, err = client.ARRing(ctx, "myarray", 5, "truncated").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
count, err = client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(5)))
|
|
|
|
// Verify values
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("truncated"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", 1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("v11"))
|
|
|
|
// Positions 5-9 should be empty
|
|
_, err = client.ARGet(ctx, "myarray", 5).Result()
|
|
Expect(err).To(Equal(redis.Nil))
|
|
})
|
|
|
|
It("should ARRING shrink stop at first hole", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
for i := 0; i < 5; i++ {
|
|
_, err := client.ARRing(ctx, "myarray", 5, fmt.Sprintf("v%d", i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
}
|
|
|
|
_, err := client.ARDel(ctx, "myarray", 3).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
_, err = client.ARRing(ctx, "myarray", 3, "new").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(2)))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("v4"))
|
|
|
|
_, err = client.ARGet(ctx, "myarray", 2).Result()
|
|
Expect(err).To(Equal(redis.Nil))
|
|
})
|
|
|
|
It("should ARSEEK followed by ARRING work", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
_, err := client.ARInsert(ctx, "myarray", "a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
_, err = client.ARInsert(ctx, "myarray", "b").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
_, err = client.ARInsert(ctx, "myarray", "c").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
// Seek to position 10
|
|
_, err = client.ARSeek(ctx, "myarray", 10).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
next, err := client.ARNext(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(next).To(Equal(uint64(10)))
|
|
|
|
// Now use MOD - should insert at index 0 (10 % 5 = 0)
|
|
_, err = client.ARRing(ctx, "myarray", 5, "x").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
val, err := client.ARGet(ctx, "myarray", 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("x"))
|
|
})
|
|
|
|
It("should ARLASTITEMS reverse order work", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
// Create ring with wraparound
|
|
for i := 0; i < 12; i++ {
|
|
_, err := client.ARRing(ctx, "myarray", 8, fmt.Sprintf("v%d", i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
}
|
|
|
|
result, err := client.ARLastItems(ctx, "myarray", 4, false).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"v8", "v9", "v10", "v11"}))
|
|
|
|
result, err = client.ARLastItems(ctx, "myarray", 4, true).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"v11", "v10", "v9", "v8"}))
|
|
|
|
// Request all items
|
|
result, err = client.ARLastItems(ctx, "myarray", 100, false).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(HaveLen(8))
|
|
})
|
|
})
|
|
|
|
Describe("Large uint64 index values", func() {
|
|
const int64MaxPlus1 = uint64(math.MaxInt64) + 1
|
|
const uint64Max = uint64(math.MaxUint64)
|
|
|
|
It("should ARSet and ARGet with index exceeding int64 max", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARSet(ctx, "myarray", int64MaxPlus1, "value").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(1)))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", int64MaxPlus1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("value"))
|
|
|
|
length, err := client.ARLen(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(length).To(Equal(uint64(int64MaxPlus1 + 1)))
|
|
})
|
|
|
|
It("should ARSet contiguous write across int64 boundary", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
base := int64MaxPlus1 - 1
|
|
n, err := client.ARSet(ctx, "myarray", base, "a", "b", "c").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(3)))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", base).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("a"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", base+1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("b"))
|
|
|
|
val, err = client.ARGet(ctx, "myarray", base+2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("c"))
|
|
})
|
|
|
|
It("should ARGetRange with large uint64 values", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
base := int64MaxPlus1
|
|
Expect(client.ARSet(ctx, "myarray", base, "x").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", base+1, "y").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", base+2, "z").Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARGetRange(ctx, "myarray", base, base+2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"x", "y", "z"}))
|
|
})
|
|
|
|
It("should ARMSet and ARMGet with large uint64 indexes", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: int64MaxPlus1, Value: "a"},
|
|
redis.AREntry{Index: int64MaxPlus1 + 1, Value: "b"},
|
|
).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(2)))
|
|
|
|
result, err := client.ARMGet(ctx, "myarray", int64MaxPlus1, int64MaxPlus1+1, 0).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(HaveLen(3))
|
|
Expect(result[0]).To(Equal("a"))
|
|
Expect(result[1]).To(Equal("b"))
|
|
Expect(result[2]).To(BeNil())
|
|
})
|
|
|
|
It("should ARDel with large uint64 index", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
Expect(client.ARSet(ctx, "myarray", int64MaxPlus1, "val").Err()).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARDel(ctx, "myarray", int64MaxPlus1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(1)))
|
|
|
|
_, err = client.ARGet(ctx, "myarray", int64MaxPlus1).Result()
|
|
Expect(err).To(Equal(redis.Nil))
|
|
})
|
|
|
|
It("should ARDelRange with large uint64 values", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
base := int64MaxPlus1
|
|
Expect(client.ARSet(ctx, "myarray", base, "a").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", base+1, "b").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", base+2, "c").Err()).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARDelRange(ctx, "myarray", redis.ARRange{Start: base, End: base + 1}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(uint64(2)))
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(1)))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", base+2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("c"))
|
|
})
|
|
|
|
It("should ARScan with large uint64 values", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
base := int64MaxPlus1
|
|
Expect(client.ARSet(ctx, "myarray", base, "a").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", base+5, "b").Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARScan(ctx, "myarray", base, base+10, nil).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(HaveLen(2))
|
|
Expect(result[0]).To(Equal(redis.AREntry{Index: base, Value: "a"}))
|
|
Expect(result[1]).To(Equal(redis.AREntry{Index: base + 5, Value: "b"}))
|
|
})
|
|
|
|
It("should ARSeek and ARNext with large uint64 index", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
_, err := client.ARInsert(ctx, "myarray", "a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARSeek(ctx, "myarray", int64MaxPlus1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(1)))
|
|
|
|
idx, err := client.ARInsert(ctx, "myarray", "b").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(idx).To(Equal(int64MaxPlus1))
|
|
|
|
next, err := client.ARNext(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(next).To(Equal(int64MaxPlus1 + 1))
|
|
|
|
val, err := client.ARGet(ctx, "myarray", int64MaxPlus1).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal("b"))
|
|
})
|
|
|
|
It("should ARSeek to uint64 max then ARNext return error", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
_, err := client.ARInsert(ctx, "myarray", "a").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
n, err := client.ARSeek(ctx, "myarray", uint64Max).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(n).To(Equal(int64(1)))
|
|
|
|
_, err = client.ARNext(ctx, "myarray").Result()
|
|
Expect(err).To(HaveOccurred())
|
|
})
|
|
|
|
It("should AROp with large uint64 range values", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
base := int64MaxPlus1
|
|
Expect(client.ARMSet(ctx, "myarray",
|
|
redis.AREntry{Index: base, Value: "10"},
|
|
redis.AREntry{Index: base + 1, Value: "20"},
|
|
redis.AREntry{Index: base + 2, Value: "30"},
|
|
).Err()).NotTo(HaveOccurred())
|
|
|
|
val, err := client.AROpSum(ctx, "myarray", base, base+2).Float64()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(float64(60)))
|
|
|
|
intVal, err := client.AROpUsed(ctx, "myarray", base, base+2).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(intVal).To(Equal(int64(3)))
|
|
})
|
|
})
|
|
|
|
Describe("Regression tests", func() {
|
|
It("should AROP work on whole-number floats", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 0, "10.0").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 1, "20.0").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 2, "30.0").Err()).NotTo(HaveOccurred())
|
|
|
|
// SUM
|
|
val, err := client.AROpSum(ctx, "myarray", 0, 2).Float64()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(float64(60)))
|
|
|
|
// MIN
|
|
val, err = client.AROpMin(ctx, "myarray", 0, 2).Float64()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(float64(10)))
|
|
|
|
// MAX
|
|
val, err = client.AROpMax(ctx, "myarray", 0, 2).Float64()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(float64(30)))
|
|
|
|
// MATCH
|
|
intVal, err := client.AROpMatch(ctx, "myarray", 0, 2, "10.0").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(intVal).To(Equal(int64(1)))
|
|
})
|
|
|
|
It("should ARDELRANGE with multiple ranges across slices", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
// Create elements across slice boundaries
|
|
for i := 0; i < 10; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
for i := 4096; i < 4106; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("%d", i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(20)))
|
|
|
|
// Delete first slice partially
|
|
_, err = client.ARDelRange(ctx, "myarray", redis.ARRange{Start: 5, End: 9}).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
// AROP SUM should work across slices
|
|
val, err := client.AROpSum(ctx, "myarray", 0, 5000).Float64()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(float64(41015)))
|
|
|
|
// AROP USED
|
|
intVal, err := client.AROpUsed(ctx, "myarray", 0, 5000).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(intVal).To(Equal(int64(15)))
|
|
})
|
|
|
|
It("should ARSCAN work over superdir blocks", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
// Elements in different superdir blocks
|
|
Expect(client.ARSet(ctx, "myarray", 0, "first").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 8388608, "second").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 16777216, "third").Err()).NotTo(HaveOccurred())
|
|
|
|
// Scan entire range
|
|
result, err := client.ARScan(ctx, "myarray", 0, 20000000, nil).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(HaveLen(3))
|
|
Expect(result[0]).To(Equal(redis.AREntry{Index: 0, Value: "first"}))
|
|
Expect(result[1]).To(Equal(redis.AREntry{Index: 8388608, Value: "second"}))
|
|
Expect(result[2]).To(Equal(redis.AREntry{Index: 16777216, Value: "third"}))
|
|
|
|
// Reverse scan
|
|
result, err = client.ARScan(ctx, "myarray", 20000000, 0, nil).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(HaveLen(3))
|
|
Expect(result[0]).To(Equal(redis.AREntry{Index: 16777216, Value: "third"}))
|
|
Expect(result[1]).To(Equal(redis.AREntry{Index: 8388608, Value: "second"}))
|
|
Expect(result[2]).To(Equal(redis.AREntry{Index: 0, Value: "first"}))
|
|
})
|
|
|
|
It("should ARGETRANGE work across superdir slice boundary", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
Expect(client.ARSet(ctx, "myarray", 8388607, "left").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 8388608, "mid").Err()).NotTo(HaveOccurred())
|
|
Expect(client.ARSet(ctx, "myarray", 8388609, "right").Err()).NotTo(HaveOccurred())
|
|
|
|
result, err := client.ARGetRange(ctx, "myarray", 8388607, 8388609).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"left", "mid", "right"}))
|
|
|
|
result, err = client.ARGetRange(ctx, "myarray", 8388609, 8388607).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result).To(Equal([]any{"right", "mid", "left"}))
|
|
})
|
|
|
|
It("should stress test mixed operations across multiple slices", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
sliceSize := 4096
|
|
|
|
// Create elements across 5 slices
|
|
for slice := 0; slice < 5; slice++ {
|
|
base := slice * sliceSize
|
|
for i := 0; i < 20; i++ {
|
|
idx := uint64(base + i*50)
|
|
Expect(client.ARSet(ctx, "myarray", idx, fmt.Sprintf("s%d_e%d", slice, i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
}
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(100)))
|
|
|
|
// Delete half from each slice
|
|
for slice := 0; slice < 5; slice++ {
|
|
base := slice * sliceSize
|
|
for i := 10; i < 20; i++ {
|
|
_, err := client.ARDel(ctx, "myarray", uint64(base+i*50)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
}
|
|
}
|
|
|
|
count, err = client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(50)))
|
|
|
|
// Verify remaining elements
|
|
for slice := 0; slice < 5; slice++ {
|
|
base := slice * sliceSize
|
|
for i := 0; i < 10; i++ {
|
|
idx := uint64(base + i*50)
|
|
val, err := client.ARGet(ctx, "myarray", idx).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("s%d_e%d", slice, i)))
|
|
}
|
|
}
|
|
})
|
|
|
|
It("should stress test rapid insert/delete cycles", func() {
|
|
Expect(client.Del(ctx, "myarray").Err()).NotTo(HaveOccurred())
|
|
|
|
// Multiple cycles of growth and shrinkage
|
|
for cycle := 0; cycle < 3; cycle++ {
|
|
// Grow
|
|
for i := 0; i < 100; i++ {
|
|
Expect(client.ARSet(ctx, "myarray", uint64(i), fmt.Sprintf("cycle%d_%d", cycle, i)).Err()).NotTo(HaveOccurred())
|
|
}
|
|
|
|
count, err := client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(100)))
|
|
|
|
// Shrink (but leave some)
|
|
for i := 10; i < 100; i++ {
|
|
_, err := client.ARDel(ctx, "myarray", uint64(i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
}
|
|
|
|
count, err = client.ARCount(ctx, "myarray").Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(count).To(Equal(uint64(10)))
|
|
}
|
|
|
|
// Verify final state
|
|
for i := 0; i < 10; i++ {
|
|
val, err := client.ARGet(ctx, "myarray", uint64(i)).Result()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(val).To(Equal(fmt.Sprintf("cycle2_%d", i)))
|
|
}
|
|
})
|
|
})
|
|
})
|