Skip to content

Commit a39be37

Browse files
authored
feat(tests): validate that ConfigSet and ConfigGet work with Modules (#3258)
* Add tests for unified config in Redis 8 * WIP: fix reading FT.CONFIG with RESP3 * add more tests * use search-timeout * move deprecated warnings on the bottom
1 parent d0fb810 commit a39be37

File tree

6 files changed

+274
-34
lines changed

6 files changed

+274
-34
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ testdata/*
44
.DS_Store
55
*.tar.gz
66
*.dic
7+
redis8tests.sh

command.go

+33-15
Original file line numberDiff line numberDiff line change
@@ -3862,30 +3862,48 @@ func (cmd *MapMapStringInterfaceCmd) Val() map[string]interface{} {
38623862
return cmd.val
38633863
}
38643864

3865+
// readReply will try to parse the reply from the proto.Reader for both resp2 and resp3
38653866
func (cmd *MapMapStringInterfaceCmd) readReply(rd *proto.Reader) (err error) {
3866-
n, err := rd.ReadArrayLen()
3867+
data, err := rd.ReadReply()
38673868
if err != nil {
38683869
return err
38693870
}
3871+
resultMap := map[string]interface{}{}
38703872

3871-
data := make(map[string]interface{}, n/2)
3872-
for i := 0; i < n; i += 2 {
3873-
_, err := rd.ReadArrayLen()
3874-
if err != nil {
3875-
cmd.err = err
3876-
}
3877-
key, err := rd.ReadString()
3878-
if err != nil {
3879-
cmd.err = err
3873+
switch midResponse := data.(type) {
3874+
case map[interface{}]interface{}: // resp3 will return map
3875+
for k, v := range midResponse {
3876+
stringKey, ok := k.(string)
3877+
if !ok {
3878+
return fmt.Errorf("redis: invalid map key %#v", k)
3879+
}
3880+
resultMap[stringKey] = v
38803881
}
3881-
value, err := rd.ReadString()
3882-
if err != nil {
3883-
cmd.err = err
3882+
case []interface{}: // resp2 will return array of arrays
3883+
n := len(midResponse)
3884+
for i := 0; i < n; i++ {
3885+
finalArr, ok := midResponse[i].([]interface{}) // final array that we need to transform to map
3886+
if !ok {
3887+
return fmt.Errorf("redis: unexpected response %#v", data)
3888+
}
3889+
m := len(finalArr)
3890+
if m%2 != 0 { // since this should be map, keys should be even number
3891+
return fmt.Errorf("redis: unexpected response %#v", data)
3892+
}
3893+
3894+
for j := 0; j < m; j += 2 {
3895+
stringKey, ok := finalArr[j].(string) // the first one
3896+
if !ok {
3897+
return fmt.Errorf("redis: invalid map key %#v", finalArr[i])
3898+
}
3899+
resultMap[stringKey] = finalArr[j+1] // second one is value
3900+
}
38843901
}
3885-
data[key] = value
3902+
default:
3903+
return fmt.Errorf("redis: unexpected response %#v", data)
38863904
}
38873905

3888-
cmd.val = data
3906+
cmd.val = resultMap
38893907
return nil
38903908
}
38913909

commands_test.go

+138
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,23 @@ var _ = Describe("Commands", func() {
344344
Expect(val).NotTo(BeEmpty())
345345
})
346346

347+
It("should ConfigGet Modules", func() {
348+
SkipBeforeRedisMajor(8, "Config doesn't include modules before Redis 8")
349+
expected := map[string]string{
350+
"search-*": "search-timeout",
351+
"ts-*": "ts-retention-policy",
352+
"bf-*": "bf-error-rate",
353+
"cf-*": "cf-initial-size",
354+
}
355+
356+
for prefix, lookup := range expected {
357+
val, err := client.ConfigGet(ctx, prefix).Result()
358+
Expect(err).NotTo(HaveOccurred())
359+
Expect(val).NotTo(BeEmpty())
360+
Expect(val[lookup]).NotTo(BeEmpty())
361+
}
362+
})
363+
347364
It("should ConfigResetStat", Label("NonRedisEnterprise"), func() {
348365
r := client.ConfigResetStat(ctx)
349366
Expect(r.Err()).NotTo(HaveOccurred())
@@ -362,6 +379,127 @@ var _ = Describe("Commands", func() {
362379
Expect(configSet.Val()).To(Equal("OK"))
363380
})
364381

382+
It("should ConfigGet with Modules", Label("NonRedisEnterprise"), func() {
383+
SkipBeforeRedisMajor(8, "config get won't return modules configs before redis 8")
384+
configGet := client.ConfigGet(ctx, "*")
385+
Expect(configGet.Err()).NotTo(HaveOccurred())
386+
Expect(configGet.Val()).To(HaveKey("maxmemory"))
387+
Expect(configGet.Val()).To(HaveKey("search-timeout"))
388+
Expect(configGet.Val()).To(HaveKey("ts-retention-policy"))
389+
Expect(configGet.Val()).To(HaveKey("bf-error-rate"))
390+
Expect(configGet.Val()).To(HaveKey("cf-initial-size"))
391+
})
392+
393+
It("should ConfigSet FT DIALECT", func() {
394+
SkipBeforeRedisMajor(8, "config doesn't include modules before Redis 8")
395+
defaultState, err := client.ConfigGet(ctx, "search-default-dialect").Result()
396+
Expect(err).NotTo(HaveOccurred())
397+
398+
// set to 3
399+
res, err := client.ConfigSet(ctx, "search-default-dialect", "3").Result()
400+
Expect(err).NotTo(HaveOccurred())
401+
Expect(res).To(BeEquivalentTo("OK"))
402+
403+
defDialect, err := client.FTConfigGet(ctx, "DEFAULT_DIALECT").Result()
404+
Expect(err).NotTo(HaveOccurred())
405+
Expect(defDialect).To(BeEquivalentTo(map[string]interface{}{"DEFAULT_DIALECT": "3"}))
406+
407+
resGet, err := client.ConfigGet(ctx, "search-default-dialect").Result()
408+
Expect(err).NotTo(HaveOccurred())
409+
Expect(resGet).To(BeEquivalentTo(map[string]string{"search-default-dialect": "3"}))
410+
411+
// set to 2
412+
res, err = client.ConfigSet(ctx, "search-default-dialect", "2").Result()
413+
Expect(err).NotTo(HaveOccurred())
414+
Expect(res).To(BeEquivalentTo("OK"))
415+
416+
defDialect, err = client.FTConfigGet(ctx, "DEFAULT_DIALECT").Result()
417+
Expect(err).NotTo(HaveOccurred())
418+
Expect(defDialect).To(BeEquivalentTo(map[string]interface{}{"DEFAULT_DIALECT": "2"}))
419+
420+
// set to 1
421+
res, err = client.ConfigSet(ctx, "search-default-dialect", "1").Result()
422+
Expect(err).NotTo(HaveOccurred())
423+
Expect(res).To(BeEquivalentTo("OK"))
424+
425+
defDialect, err = client.FTConfigGet(ctx, "DEFAULT_DIALECT").Result()
426+
Expect(err).NotTo(HaveOccurred())
427+
Expect(defDialect).To(BeEquivalentTo(map[string]interface{}{"DEFAULT_DIALECT": "1"}))
428+
429+
resGet, err = client.ConfigGet(ctx, "search-default-dialect").Result()
430+
Expect(err).NotTo(HaveOccurred())
431+
Expect(resGet).To(BeEquivalentTo(map[string]string{"search-default-dialect": "1"}))
432+
433+
// set to default
434+
res, err = client.ConfigSet(ctx, "search-default-dialect", defaultState["search-default-dialect"]).Result()
435+
Expect(err).NotTo(HaveOccurred())
436+
Expect(res).To(BeEquivalentTo("OK"))
437+
})
438+
439+
It("should ConfigSet fail for ReadOnly", func() {
440+
SkipBeforeRedisMajor(8, "Config doesn't include modules before Redis 8")
441+
_, err := client.ConfigSet(ctx, "search-max-doctablesize", "100000").Result()
442+
Expect(err).To(HaveOccurred())
443+
})
444+
445+
It("should ConfigSet Modules", func() {
446+
SkipBeforeRedisMajor(8, "Config doesn't include modules before Redis 8")
447+
defaults := map[string]string{}
448+
expected := map[string]string{
449+
"search-timeout": "100",
450+
"ts-retention-policy": "2",
451+
"bf-error-rate": "0.13",
452+
"cf-initial-size": "64",
453+
}
454+
455+
// read the defaults to set them back later
456+
for setting, _ := range expected {
457+
val, err := client.ConfigGet(ctx, setting).Result()
458+
Expect(err).NotTo(HaveOccurred())
459+
defaults[setting] = val[setting]
460+
}
461+
462+
// check if new values can be set
463+
for setting, value := range expected {
464+
val, err := client.ConfigSet(ctx, setting, value).Result()
465+
Expect(err).NotTo(HaveOccurred())
466+
Expect(val).NotTo(BeEmpty())
467+
Expect(val).To(Equal("OK"))
468+
}
469+
470+
for setting, value := range expected {
471+
val, err := client.ConfigGet(ctx, setting).Result()
472+
Expect(err).NotTo(HaveOccurred())
473+
Expect(val).NotTo(BeEmpty())
474+
Expect(val[setting]).To(Equal(value))
475+
}
476+
477+
// set back to the defaults
478+
for setting, value := range defaults {
479+
val, err := client.ConfigSet(ctx, setting, value).Result()
480+
Expect(err).NotTo(HaveOccurred())
481+
Expect(val).NotTo(BeEmpty())
482+
Expect(val).To(Equal("OK"))
483+
}
484+
})
485+
486+
It("should Fail ConfigSet Modules", func() {
487+
SkipBeforeRedisMajor(8, "Config doesn't include modules before Redis 8")
488+
expected := map[string]string{
489+
"search-timeout": "-100",
490+
"ts-retention-policy": "-10",
491+
"bf-error-rate": "1.5",
492+
"cf-initial-size": "-10",
493+
}
494+
495+
for setting, value := range expected {
496+
val, err := client.ConfigSet(ctx, setting, value).Result()
497+
Expect(err).To(HaveOccurred())
498+
Expect(err).To(MatchError(ContainSubstring(setting)))
499+
Expect(val).To(BeEmpty())
500+
}
501+
})
502+
365503
It("should ConfigRewrite", Label("NonRedisEnterprise"), func() {
366504
configRewrite := client.ConfigRewrite(ctx)
367505
Expect(configRewrite.Err()).NotTo(HaveOccurred())

main_test.go

+23-7
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,19 @@ var RCEDocker = false
7373
// Notes the major version of redis we are executing tests.
7474
// This can be used before we change the bsm fork of ginkgo for one,
7575
// which have support for label sets, so we can filter tests per redis major version.
76-
var REDIS_MAJOR_VERSION = 7
76+
var RedisMajorVersion = 7
77+
78+
func SkipBeforeRedisMajor(version int, msg string) {
79+
if RedisMajorVersion < version {
80+
Skip(fmt.Sprintf("(redis major version < %d) %s", version, msg))
81+
}
82+
}
83+
84+
func SkipAfterRedisMajor(version int, msg string) {
85+
if RedisMajorVersion > version {
86+
Skip(fmt.Sprintf("(redis major version > %d) %s", version, msg))
87+
}
88+
}
7789

7890
func registerProcess(port string, p *redisProcess) {
7991
if processes == nil {
@@ -92,16 +104,20 @@ var _ = BeforeSuite(func() {
92104
RECluster, _ = strconv.ParseBool(os.Getenv("RE_CLUSTER"))
93105
RCEDocker, _ = strconv.ParseBool(os.Getenv("RCE_DOCKER"))
94106

95-
REDIS_MAJOR_VERSION, _ = strconv.Atoi(os.Getenv("REDIS_MAJOR_VERSION"))
96-
if REDIS_MAJOR_VERSION == 0 {
97-
REDIS_MAJOR_VERSION = 7
107+
RedisMajorVersion, _ = strconv.Atoi(os.Getenv("REDIS_MAJOR_VERSION"))
108+
109+
if RedisMajorVersion == 0 {
110+
RedisMajorVersion = 7
98111
}
99-
Expect(REDIS_MAJOR_VERSION).To(BeNumerically(">=", 6))
100-
Expect(REDIS_MAJOR_VERSION).To(BeNumerically("<=", 8))
101112

102113
fmt.Printf("RECluster: %v\n", RECluster)
103114
fmt.Printf("RCEDocker: %v\n", RCEDocker)
104-
fmt.Printf("REDIS_MAJOR_VERSION: %v\n", REDIS_MAJOR_VERSION)
115+
fmt.Printf("REDIS_MAJOR_VERSION: %v\n", RedisMajorVersion)
116+
117+
if RedisMajorVersion < 6 || RedisMajorVersion > 8 {
118+
panic("incorrect or not supported redis major version")
119+
}
120+
105121
if !RECluster && !RCEDocker {
106122

107123
redisMain, err = startRedis(redisPort)

search_commands.go

+18-6
Original file line numberDiff line numberDiff line change
@@ -831,20 +831,32 @@ func (c cmdable) FTAlter(ctx context.Context, index string, skipInitialScan bool
831831
return cmd
832832
}
833833

834-
// FTConfigGet - Retrieves the value of a RediSearch configuration parameter.
834+
// Retrieves the value of a RediSearch configuration parameter.
835835
// The 'option' parameter specifies the configuration parameter to retrieve.
836-
// For more information, please refer to the Redis documentation:
837-
// [FT.CONFIG GET]: (https://redis.io/commands/ft.config-get/)
836+
// For more information, please refer to the Redis [FT.CONFIG GET] documentation.
837+
//
838+
// Deprecated: FTConfigGet is deprecated in Redis 8.
839+
// All configuration will be done with the CONFIG GET command.
840+
// For more information check [Client.ConfigGet] and [CONFIG GET Documentation]
841+
//
842+
// [CONFIG GET Documentation]: https://redis.io/commands/config-get/
843+
// [FT.CONFIG GET]: https://redis.io/commands/ft.config-get/
838844
func (c cmdable) FTConfigGet(ctx context.Context, option string) *MapMapStringInterfaceCmd {
839845
cmd := NewMapMapStringInterfaceCmd(ctx, "FT.CONFIG", "GET", option)
840846
_ = c(ctx, cmd)
841847
return cmd
842848
}
843849

844-
// FTConfigSet - Sets the value of a RediSearch configuration parameter.
850+
// Sets the value of a RediSearch configuration parameter.
845851
// The 'option' parameter specifies the configuration parameter to set, and the 'value' parameter specifies the new value.
846-
// For more information, please refer to the Redis documentation:
847-
// [FT.CONFIG SET]: (https://redis.io/commands/ft.config-set/)
852+
// For more information, please refer to the Redis [FT.CONFIG SET] documentation.
853+
//
854+
// Deprecated: FTConfigSet is deprecated in Redis 8.
855+
// All configuration will be done with the CONFIG SET command.
856+
// For more information check [Client.ConfigSet] and [CONFIG SET Documentation]
857+
//
858+
// [CONFIG SET Documentation]: https://redis.io/commands/config-set/
859+
// [FT.CONFIG SET]: https://redis.io/commands/ft.config-set/
848860
func (c cmdable) FTConfigSet(ctx context.Context, option string, value interface{}) *StatusCmd {
849861
cmd := NewStatusCmd(ctx, "FT.CONFIG", "SET", option, value)
850862
_ = c(ctx, cmd)

0 commit comments

Comments
 (0)