Files
go/iterator_test.go
zeekay 6994cf05aa kv, not redis: rename the package, the extras and the URL scheme
We do not use Redis. The client was a go-redis fork and still said so
everywhere: `package redis`, `redis.NewClient`, `redis://`, `extra/redisotel`.
Consumers wrote `redis.Client` against a Valkey-backed store called KV.

  package redis            -> package kv          (93 decls)
  redis.X                  -> kv.X                (2,794 call sites)
  "redis: …" errors        -> "kv: …"             (206)
  extra/redis{otel,cmd,prometheus,census} -> extra/kv{otel,cmd,prometheus,census}
  redis:// , rediss://     -> kv:// , kvs://      (ParseURL)
  Redis*/‌*Redis* idents    -> KV*/*KV*
  REDIS_VERSION, REDIS_ENDPOINTS_CONFIG_PATH -> KV_*

I previously argued this rename was unsafe because much of it was "wire
contract". That was wrong, and the categorisation shows why: RESP command names
are GET/SET/HSET — they never contain the word. Of 5,771 occurrences only two
kinds were server-visible at all: the ParseURL scheme (we choose what we pass;
our configs already use kv://) and the CLIENT SETINFO lib-name label, which the
server merely records. Nothing here changes a byte of protocol.

Two things deliberately keep the old spelling:
  RediSearch  — names a specific external module the FT.* commands map to.
  StoreDist   — contains the letters by accident (sto-reDis-t), not the word.

Breaking for consumers by design: every import site must move redis.X -> kv.X.
Minor bump, never a major — this module stays v9 forever.
2026-07-26 08:34:19 -07:00

155 lines
3.9 KiB
Go

package kv_test
import (
"fmt"
. "github.com/bsm/ginkgo/v2"
. "github.com/bsm/gomega"
"github.com/hanzokv/go/v9"
)
var _ = Describe("ScanIterator", func() {
var client *kv.Client
seed := func(n int) error {
pipe := client.Pipeline()
for i := 1; i <= n; i++ {
pipe.Set(ctx, fmt.Sprintf("K%02d", i), "x", 0).Err()
}
_, err := pipe.Exec(ctx)
return err
}
extraSeed := func(n int, m int) error {
pipe := client.Pipeline()
for i := 1; i <= m; i++ {
pipe.Set(ctx, fmt.Sprintf("A%02d", i), "x", 0).Err()
}
for i := 1; i <= n; i++ {
pipe.Set(ctx, fmt.Sprintf("K%02d", i), "x", 0).Err()
}
_, err := pipe.Exec(ctx)
return err
}
hashKey := "K_HASHTEST"
hashSeed := func(n int) error {
pipe := client.Pipeline()
for i := 1; i <= n; i++ {
pipe.HSet(ctx, hashKey, fmt.Sprintf("K%02d", i), "x").Err()
}
_, err := pipe.Exec(ctx)
return err
}
BeforeEach(func() {
client = kv.NewClient(kvOptions())
Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
It("should scan across empty DBs", func() {
iter := client.Scan(ctx, 0, "", 10).Iterator()
Expect(iter.Next(ctx)).To(BeFalse())
Expect(iter.Err()).NotTo(HaveOccurred())
})
It("should scan across one page", func() {
Expect(seed(7)).NotTo(HaveOccurred())
var vals []string
iter := client.Scan(ctx, 0, "", 0).Iterator()
for iter.Next(ctx) {
vals = append(vals, iter.Val())
}
Expect(iter.Err()).NotTo(HaveOccurred())
Expect(vals).To(ConsistOf([]string{"K01", "K02", "K03", "K04", "K05", "K06", "K07"}))
})
It("should scan across multiple pages", func() {
Expect(seed(71)).NotTo(HaveOccurred())
var vals []string
iter := client.Scan(ctx, 0, "", 10).Iterator()
for iter.Next(ctx) {
vals = append(vals, iter.Val())
}
Expect(iter.Err()).NotTo(HaveOccurred())
Expect(vals).To(HaveLen(71))
Expect(vals).To(ContainElement("K01"))
Expect(vals).To(ContainElement("K71"))
})
It("should hscan across multiple pages", func() {
SkipBeforeKVVersion(7.4, "doesn't work with older kv stack images")
Expect(hashSeed(71)).NotTo(HaveOccurred())
var vals []string
iter := client.HScan(ctx, hashKey, 0, "", 10).Iterator()
for iter.Next(ctx) {
vals = append(vals, iter.Val())
}
Expect(iter.Err()).NotTo(HaveOccurred())
Expect(vals).To(HaveLen(71 * 2))
Expect(vals).To(ContainElement("K01"))
Expect(vals).To(ContainElement("K71"))
Expect(vals).To(ContainElement("x"))
})
It("should hscan without values across multiple pages", Label("NonKVEnterprise"), func() {
SkipBeforeKVVersion(7.4, "doesn't work with older kv stack images")
Expect(hashSeed(71)).NotTo(HaveOccurred())
var vals []string
iter := client.HScanNoValues(ctx, hashKey, 0, "", 10).Iterator()
for iter.Next(ctx) {
vals = append(vals, iter.Val())
}
Expect(iter.Err()).NotTo(HaveOccurred())
Expect(vals).To(HaveLen(71))
Expect(vals).To(ContainElement("K01"))
Expect(vals).To(ContainElement("K71"))
Expect(vals).NotTo(ContainElement("x"))
})
It("should scan to page borders", func() {
Expect(seed(20)).NotTo(HaveOccurred())
var vals []string
iter := client.Scan(ctx, 0, "", 10).Iterator()
for iter.Next(ctx) {
vals = append(vals, iter.Val())
}
Expect(iter.Err()).NotTo(HaveOccurred())
Expect(vals).To(HaveLen(20))
})
It("should scan with match", func() {
Expect(seed(33)).NotTo(HaveOccurred())
var vals []string
iter := client.Scan(ctx, 0, "K*2*", 10).Iterator()
for iter.Next(ctx) {
vals = append(vals, iter.Val())
}
Expect(iter.Err()).NotTo(HaveOccurred())
Expect(vals).To(HaveLen(13))
})
It("should scan with match across empty pages", func() {
Expect(extraSeed(2, 10)).NotTo(HaveOccurred())
var vals []string
iter := client.Scan(ctx, 0, "K*", 1).Iterator()
for iter.Next(ctx) {
vals = append(vals, iter.Val())
}
Expect(iter.Err()).NotTo(HaveOccurred())
Expect(vals).To(HaveLen(2))
})
})