Skip to content

feat(tests): validate that ConfigSet and ConfigGet work with Modules #3258

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ testdata/*
.DS_Store
*.tar.gz
*.dic
redis8tests.sh
48 changes: 33 additions & 15 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -3862,30 +3862,48 @@ func (cmd *MapMapStringInterfaceCmd) Val() map[string]interface{} {
return cmd.val
}

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

data := make(map[string]interface{}, n/2)
for i := 0; i < n; i += 2 {
_, err := rd.ReadArrayLen()
if err != nil {
cmd.err = err
}
key, err := rd.ReadString()
if err != nil {
cmd.err = err
switch midResponse := data.(type) {
case map[interface{}]interface{}: // resp3 will return map
for k, v := range midResponse {
stringKey, ok := k.(string)
if !ok {
return fmt.Errorf("redis: invalid map key %#v", k)
}
resultMap[stringKey] = v
}
value, err := rd.ReadString()
if err != nil {
cmd.err = err
case []interface{}: // resp2 will return array of arrays
n := len(midResponse)
for i := 0; i < n; i++ {
finalArr, ok := midResponse[i].([]interface{}) // final array that we need to transform to map
if !ok {
return fmt.Errorf("redis: unexpected response %#v", data)
}
m := len(finalArr)
if m%2 != 0 { // since this should be map, keys should be even number
return fmt.Errorf("redis: unexpected response %#v", data)
}

for j := 0; j < m; j += 2 {
stringKey, ok := finalArr[j].(string) // the first one
if !ok {
return fmt.Errorf("redis: invalid map key %#v", finalArr[i])
}
resultMap[stringKey] = finalArr[j+1] // second one is value
}
}
data[key] = value
default:
return fmt.Errorf("redis: unexpected response %#v", data)
}

cmd.val = data
cmd.val = resultMap
return nil
}

Expand Down
138 changes: 138 additions & 0 deletions commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,23 @@ var _ = Describe("Commands", func() {
Expect(val).NotTo(BeEmpty())
})

It("should ConfigGet Modules", func() {
SkipBeforeRedisMajor(8, "Config doesn't include modules before Redis 8")
expected := map[string]string{
"search-*": "search-timeout",
"ts-*": "ts-retention-policy",
"bf-*": "bf-error-rate",
"cf-*": "cf-initial-size",
}

for prefix, lookup := range expected {
val, err := client.ConfigGet(ctx, prefix).Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).NotTo(BeEmpty())
Expect(val[lookup]).NotTo(BeEmpty())
}
})

It("should ConfigResetStat", Label("NonRedisEnterprise"), func() {
r := client.ConfigResetStat(ctx)
Expect(r.Err()).NotTo(HaveOccurred())
Expand All @@ -362,6 +379,127 @@ var _ = Describe("Commands", func() {
Expect(configSet.Val()).To(Equal("OK"))
})

It("should ConfigGet with Modules", Label("NonRedisEnterprise"), func() {
SkipBeforeRedisMajor(8, "config get won't return modules configs before redis 8")
configGet := client.ConfigGet(ctx, "*")
Expect(configGet.Err()).NotTo(HaveOccurred())
Expect(configGet.Val()).To(HaveKey("maxmemory"))
Expect(configGet.Val()).To(HaveKey("search-timeout"))
Expect(configGet.Val()).To(HaveKey("ts-retention-policy"))
Expect(configGet.Val()).To(HaveKey("bf-error-rate"))
Expect(configGet.Val()).To(HaveKey("cf-initial-size"))
})

It("should ConfigSet FT DIALECT", func() {
SkipBeforeRedisMajor(8, "config doesn't include modules before Redis 8")
defaultState, err := client.ConfigGet(ctx, "search-default-dialect").Result()
Expect(err).NotTo(HaveOccurred())

// set to 3
res, err := client.ConfigSet(ctx, "search-default-dialect", "3").Result()
Expect(err).NotTo(HaveOccurred())
Expect(res).To(BeEquivalentTo("OK"))

defDialect, err := client.FTConfigGet(ctx, "DEFAULT_DIALECT").Result()
Expect(err).NotTo(HaveOccurred())
Expect(defDialect).To(BeEquivalentTo(map[string]interface{}{"DEFAULT_DIALECT": "3"}))

resGet, err := client.ConfigGet(ctx, "search-default-dialect").Result()
Expect(err).NotTo(HaveOccurred())
Expect(resGet).To(BeEquivalentTo(map[string]string{"search-default-dialect": "3"}))

// set to 2
res, err = client.ConfigSet(ctx, "search-default-dialect", "2").Result()
Expect(err).NotTo(HaveOccurred())
Expect(res).To(BeEquivalentTo("OK"))

defDialect, err = client.FTConfigGet(ctx, "DEFAULT_DIALECT").Result()
Expect(err).NotTo(HaveOccurred())
Expect(defDialect).To(BeEquivalentTo(map[string]interface{}{"DEFAULT_DIALECT": "2"}))

// set to 1
res, err = client.ConfigSet(ctx, "search-default-dialect", "1").Result()
Expect(err).NotTo(HaveOccurred())
Expect(res).To(BeEquivalentTo("OK"))

defDialect, err = client.FTConfigGet(ctx, "DEFAULT_DIALECT").Result()
Expect(err).NotTo(HaveOccurred())
Expect(defDialect).To(BeEquivalentTo(map[string]interface{}{"DEFAULT_DIALECT": "1"}))

resGet, err = client.ConfigGet(ctx, "search-default-dialect").Result()
Expect(err).NotTo(HaveOccurred())
Expect(resGet).To(BeEquivalentTo(map[string]string{"search-default-dialect": "1"}))

// set to default
res, err = client.ConfigSet(ctx, "search-default-dialect", defaultState["search-default-dialect"]).Result()
Expect(err).NotTo(HaveOccurred())
Expect(res).To(BeEquivalentTo("OK"))
})

It("should ConfigSet fail for ReadOnly", func() {
SkipBeforeRedisMajor(8, "Config doesn't include modules before Redis 8")
_, err := client.ConfigSet(ctx, "search-max-doctablesize", "100000").Result()
Expect(err).To(HaveOccurred())
})

It("should ConfigSet Modules", func() {
SkipBeforeRedisMajor(8, "Config doesn't include modules before Redis 8")
defaults := map[string]string{}
expected := map[string]string{
"search-timeout": "100",
"ts-retention-policy": "2",
"bf-error-rate": "0.13",
"cf-initial-size": "64",
}

// read the defaults to set them back later
for setting, _ := range expected {
val, err := client.ConfigGet(ctx, setting).Result()
Expect(err).NotTo(HaveOccurred())
defaults[setting] = val[setting]
}

// check if new values can be set
for setting, value := range expected {
val, err := client.ConfigSet(ctx, setting, value).Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).NotTo(BeEmpty())
Expect(val).To(Equal("OK"))
}

for setting, value := range expected {
val, err := client.ConfigGet(ctx, setting).Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).NotTo(BeEmpty())
Expect(val[setting]).To(Equal(value))
}

// set back to the defaults
for setting, value := range defaults {
val, err := client.ConfigSet(ctx, setting, value).Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).NotTo(BeEmpty())
Expect(val).To(Equal("OK"))
}
})

It("should Fail ConfigSet Modules", func() {
SkipBeforeRedisMajor(8, "Config doesn't include modules before Redis 8")
expected := map[string]string{
"search-timeout": "-100",
"ts-retention-policy": "-10",
"bf-error-rate": "1.5",
"cf-initial-size": "-10",
}

for setting, value := range expected {
val, err := client.ConfigSet(ctx, setting, value).Result()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(ContainSubstring(setting)))
Expect(val).To(BeEmpty())
}
})

It("should ConfigRewrite", Label("NonRedisEnterprise"), func() {
configRewrite := client.ConfigRewrite(ctx)
Expect(configRewrite.Err()).NotTo(HaveOccurred())
Expand Down
30 changes: 23 additions & 7 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,19 @@ var RCEDocker = false
// Notes the major version of redis we are executing tests.
// This can be used before we change the bsm fork of ginkgo for one,
// which have support for label sets, so we can filter tests per redis major version.
var REDIS_MAJOR_VERSION = 7
var RedisMajorVersion = 7

func SkipBeforeRedisMajor(version int, msg string) {
if RedisMajorVersion < version {
Skip(fmt.Sprintf("(redis major version < %d) %s", version, msg))
}
}

func SkipAfterRedisMajor(version int, msg string) {
if RedisMajorVersion > version {
Skip(fmt.Sprintf("(redis major version > %d) %s", version, msg))
}
}

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

REDIS_MAJOR_VERSION, _ = strconv.Atoi(os.Getenv("REDIS_MAJOR_VERSION"))
if REDIS_MAJOR_VERSION == 0 {
REDIS_MAJOR_VERSION = 7
RedisMajorVersion, _ = strconv.Atoi(os.Getenv("REDIS_MAJOR_VERSION"))

if RedisMajorVersion == 0 {
RedisMajorVersion = 7
}
Expect(REDIS_MAJOR_VERSION).To(BeNumerically(">=", 6))
Expect(REDIS_MAJOR_VERSION).To(BeNumerically("<=", 8))

fmt.Printf("RECluster: %v\n", RECluster)
fmt.Printf("RCEDocker: %v\n", RCEDocker)
fmt.Printf("REDIS_MAJOR_VERSION: %v\n", REDIS_MAJOR_VERSION)
fmt.Printf("REDIS_MAJOR_VERSION: %v\n", RedisMajorVersion)

if RedisMajorVersion < 6 || RedisMajorVersion > 8 {
panic("incorrect or not supported redis major version")
}

if !RECluster && !RCEDocker {

redisMain, err = startRedis(redisPort)
Expand Down
24 changes: 18 additions & 6 deletions search_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -831,20 +831,32 @@ func (c cmdable) FTAlter(ctx context.Context, index string, skipInitialScan bool
return cmd
}

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

// FTConfigSet - Sets the value of a RediSearch configuration parameter.
// Sets the value of a RediSearch configuration parameter.
// The 'option' parameter specifies the configuration parameter to set, and the 'value' parameter specifies the new value.
// For more information, please refer to the Redis documentation:
// [FT.CONFIG SET]: (https://redis.io/commands/ft.config-set/)
// For more information, please refer to the Redis [FT.CONFIG SET] documentation.
//
// Deprecated: FTConfigSet is deprecated in Redis 8.
// All configuration will be done with the CONFIG SET command.
// For more information check [Client.ConfigSet] and [CONFIG SET Documentation]
//
// [CONFIG SET Documentation]: https://redis.io/commands/config-set/
// [FT.CONFIG SET]: https://redis.io/commands/ft.config-set/
func (c cmdable) FTConfigSet(ctx context.Context, option string, value interface{}) *StatusCmd {
cmd := NewStatusCmd(ctx, "FT.CONFIG", "SET", option, value)
_ = c(ctx, cmd)
Expand Down
Loading
Loading