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.UniversalClient var rawClient *redis.Client var closeSubject func() error BeforeEach(func() { SkipBeforeRedisVersion("8.8", "Redis 8.8.0 introduces support for Array") rawClient = redis.NewClient(redisOptions()) client, closeSubject = newUniversalSubject(rawClient) Expect(rawClient.FlushDB(ctx).Err()).NotTo(HaveOccurred()) }) AfterEach(func() { Expect(closeSubject()).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))) } }) }) })