summaryrefslogtreecommitdiff
path: root/examples/redis-unstable/tests/unit/moduleapi/moduleconfigs.tcl
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2026-01-21 22:40:55 +0100
committerMitja Felicijan <mitja.felicijan@gmail.com>2026-01-21 22:40:55 +0100
commit5d8dfe892a2ea89f706ee140c3bdcfd89fe03fda (patch)
tree1acdfa5220cd13b7be43a2a01368e80d306473ca /examples/redis-unstable/tests/unit/moduleapi/moduleconfigs.tcl
parentc7ab12bba64d9c20ccd79b132dac475f7bc3923e (diff)
downloadcrep-5d8dfe892a2ea89f706ee140c3bdcfd89fe03fda.tar.gz
Add Redis source code for testing
Diffstat (limited to 'examples/redis-unstable/tests/unit/moduleapi/moduleconfigs.tcl')
-rw-r--r--examples/redis-unstable/tests/unit/moduleapi/moduleconfigs.tcl386
1 files changed, 386 insertions, 0 deletions
diff --git a/examples/redis-unstable/tests/unit/moduleapi/moduleconfigs.tcl b/examples/redis-unstable/tests/unit/moduleapi/moduleconfigs.tcl
new file mode 100644
index 0000000..25ed33a
--- /dev/null
+++ b/examples/redis-unstable/tests/unit/moduleapi/moduleconfigs.tcl
@@ -0,0 +1,386 @@
+set testmodule [file normalize tests/modules/moduleconfigs.so]
+set testmoduletwo [file normalize tests/modules/moduleconfigstwo.so]
+
+start_server {tags {"modules external:skip"}} {
+ r module load $testmodule
+ test {Config get commands work} {
+ # Make sure config get module config works
+ assert_not_equal [lsearch [lmap x [r module list] {dict get $x name}] moduleconfigs] -1
+ assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool yes"
+ assert_equal [r config get moduleconfigs.immutable_bool] "moduleconfigs.immutable_bool no"
+ assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 1024"
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {secret password}"
+ assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum one"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {one two}"
+ assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1"
+
+ # Check un-prefixed and aliased configuration
+ assert_equal [r config get unprefix-bool] "unprefix-bool yes"
+ assert_equal [r config get unprefix-noalias-bool] "unprefix-noalias-bool yes"
+ assert_equal [r config get unprefix-bool-alias] "unprefix-bool-alias yes"
+ assert_equal [r config get unprefix.numeric] "unprefix.numeric -1"
+ assert_equal [r config get unprefix.numeric-alias] "unprefix.numeric-alias -1"
+ assert_equal [r config get unprefix-string] "unprefix-string {secret unprefix}"
+ assert_equal [r config get unprefix.string-alias] "unprefix.string-alias {secret unprefix}"
+ assert_equal [r config get unprefix-enum] "unprefix-enum one"
+ assert_equal [r config get unprefix-enum-alias] "unprefix-enum-alias one"
+ }
+
+ test {Config set commands work} {
+ # Make sure that config sets work during runtime
+ r config set moduleconfigs.mutable_bool no
+ assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool no"
+ r config set moduleconfigs.memory_numeric 1mb
+ assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 1048576"
+ r config set moduleconfigs.string wafflewednesdays
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string wafflewednesdays"
+ set not_embstr [string repeat A 50]
+ r config set moduleconfigs.string $not_embstr
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string $not_embstr"
+ r config set moduleconfigs.string \x73\x75\x70\x65\x72\x20\x00\x73\x65\x63\x72\x65\x74\x20\x70\x61\x73\x73\x77\x6f\x72\x64
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {super \0secret password}"
+ r config set moduleconfigs.enum two
+ assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two"
+ r config set moduleconfigs.flags two
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags two"
+ r config set moduleconfigs.numeric -2
+ assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -2"
+
+ # Check un-prefixed and aliased configuration
+ r config set unprefix-bool no
+ assert_equal [r config get unprefix-bool] "unprefix-bool no"
+ assert_equal [r config get unprefix-bool-alias] "unprefix-bool-alias no"
+ r config set unprefix-bool-alias yes
+ assert_equal [r config get unprefix-bool] "unprefix-bool yes"
+ assert_equal [r config get unprefix-bool-alias] "unprefix-bool-alias yes"
+ r config set unprefix.numeric 5
+ assert_equal [r config get unprefix.numeric] "unprefix.numeric 5"
+ assert_equal [r config get unprefix.numeric-alias] "unprefix.numeric-alias 5"
+ r config set unprefix.numeric-alias 6
+ assert_equal [r config get unprefix.numeric] "unprefix.numeric 6"
+ r config set unprefix.string-alias "blabla"
+ assert_equal [r config get unprefix-string] "unprefix-string blabla"
+ assert_equal [r config get unprefix.string-alias] "unprefix.string-alias blabla"
+ r config set unprefix-enum two
+ assert_equal [r config get unprefix-enum] "unprefix-enum two"
+ assert_equal [r config get unprefix-enum-alias] "unprefix-enum-alias two"
+ }
+
+ test {Config set commands enum flags} {
+ r config set moduleconfigs.flags "none"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags none"
+
+ r config set moduleconfigs.flags "two four"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {two four}"
+
+ r config set moduleconfigs.flags "five"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags five"
+
+ r config set moduleconfigs.flags "one four"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags five"
+
+ r config set moduleconfigs.flags "one two four"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {five two}"
+ }
+
+ test {Immutable flag works properly and rejected strings dont leak} {
+ # Configs flagged immutable should not allow sets
+ catch {[r config set moduleconfigs.immutable_bool yes]} e
+ assert_match {*can't set immutable config*} $e
+ catch {[r config set moduleconfigs.string rejectisfreed]} e
+ assert_match {*Cannot set string to 'rejectisfreed'*} $e
+ }
+
+ test {Numeric limits work properly} {
+ # Configs over/under the limit shouldn't be allowed, and memory configs should only take memory values
+ catch {[r config set moduleconfigs.memory_numeric 200gb]} e
+ assert_match {*argument must be between*} $e
+ catch {[r config set moduleconfigs.memory_numeric -5]} e
+ assert_match {*argument must be a memory value*} $e
+ catch {[r config set moduleconfigs.numeric -10]} e
+ assert_match {*argument must be between*} $e
+ }
+
+ test {Enums only able to be set to passed in values} {
+ # Module authors specify what values are valid for enums, check that only those values are ok on a set
+ catch {[r config set moduleconfigs.enum asdf]} e
+ assert_match {*must be one of the following*} $e
+ }
+
+ test {test blocking of config registration and load outside of OnLoad} {
+ assert_equal [r block.register.configs.outside.onload] OK
+ }
+
+ test {Unload removes module configs} {
+ r module unload moduleconfigs
+ assert_equal [r config get moduleconfigs.*] ""
+ r module load $testmodule
+ # these should have reverted back to their module specified values
+ assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool yes"
+ assert_equal [r config get moduleconfigs.immutable_bool] "moduleconfigs.immutable_bool no"
+ assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 1024"
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {secret password}"
+ assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum one"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {one two}"
+ assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1"
+
+ # Check un-prefixed and aliased configuration
+ assert_equal [r config get unprefix-bool] "unprefix-bool yes"
+ assert_equal [r config get unprefix-bool-alias] "unprefix-bool-alias yes"
+ assert_equal [r config get unprefix.numeric] "unprefix.numeric -1"
+ assert_equal [r config get unprefix.numeric-alias] "unprefix.numeric-alias -1"
+ assert_equal [r config get unprefix-string] "unprefix-string {secret unprefix}"
+ assert_equal [r config get unprefix.string-alias] "unprefix.string-alias {secret unprefix}"
+ assert_equal [r config get unprefix-enum] "unprefix-enum one"
+ assert_equal [r config get unprefix-enum-alias] "unprefix-enum-alias one"
+
+
+ r module unload moduleconfigs
+ }
+
+ test {test loadex functionality} {
+ r module loadex $testmodule CONFIG moduleconfigs.mutable_bool no \
+ CONFIG moduleconfigs.immutable_bool yes \
+ CONFIG moduleconfigs.memory_numeric 2mb \
+ CONFIG moduleconfigs.string tclortickle \
+ CONFIG unprefix-bool no \
+ CONFIG unprefix.numeric-alias 123 \
+ CONFIG unprefix-string abc_def \
+
+ assert_not_equal [lsearch [lmap x [r module list] {dict get $x name}] moduleconfigs] -1
+ assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool no"
+ assert_equal [r config get moduleconfigs.immutable_bool] "moduleconfigs.immutable_bool yes"
+ assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 2097152"
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string tclortickle"
+ # Configs that were not changed should still be their module specified value
+ assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum one"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {one two}"
+ assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1"
+
+ # Check un-prefixed and aliased configuration
+ assert_equal [r config get unprefix-bool] "unprefix-bool no"
+ assert_equal [r config get unprefix-bool-alias] "unprefix-bool-alias no"
+ assert_equal [r config get unprefix.numeric] "unprefix.numeric 123"
+ assert_equal [r config get unprefix.numeric-alias] "unprefix.numeric-alias 123"
+ assert_equal [r config get unprefix-string] "unprefix-string abc_def"
+ assert_equal [r config get unprefix.string-alias] "unprefix.string-alias abc_def"
+ assert_equal [r config get unprefix-enum] "unprefix-enum one"
+ assert_equal [r config get unprefix-enum-alias] "unprefix-enum-alias one"
+
+
+ }
+
+ test {apply function works} {
+ catch {[r config set moduleconfigs.mutable_bool yes]} e
+ assert_match {*Bool configs*} $e
+ assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool no"
+ catch {[r config set moduleconfigs.memory_numeric 1000 moduleconfigs.numeric 1000]} e
+ assert_match {*cannot equal*} $e
+ assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 2097152"
+ assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1"
+ r module unload moduleconfigs
+ }
+
+ test {test double config argument to loadex} {
+ r module loadex $testmodule CONFIG moduleconfigs.mutable_bool yes \
+ CONFIG moduleconfigs.mutable_bool no \
+ CONFIG unprefix.numeric-alias 1 \
+ CONFIG unprefix.numeric-alias 2 \
+ CONFIG unprefix-string blabla
+
+ assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool no"
+ # Check un-prefixed and aliased configuration
+ assert_equal [r config get unprefix.numeric-alias] "unprefix.numeric-alias 2"
+ assert_equal [r config get unprefix.numeric] "unprefix.numeric 2"
+ assert_equal [r config get unprefix-string] "unprefix-string blabla"
+ assert_equal [r config get unprefix.string-alias] "unprefix.string-alias blabla"
+ r module unload moduleconfigs
+ }
+
+ test {missing loadconfigs call} {
+ catch {[r module loadex $testmodule CONFIG moduleconfigs.string "cool" ARGS noload]} e
+ assert_match {*ERR*} $e
+ }
+
+ test {test loadex rejects bad configs} {
+ # Bad config 200gb is over the limit
+ catch {[r module loadex $testmodule CONFIG moduleconfigs.memory_numeric 200gb ARGS]} e
+ assert_match {*ERR*} $e
+ # We should completely remove all configs on a failed load
+ assert_equal [r config get moduleconfigs.*] ""
+ # No value for config, should error out
+ catch {[r module loadex $testmodule CONFIG moduleconfigs.mutable_bool CONFIG moduleconfigs.enum two ARGS]} e
+ assert_match {*ERR*} $e
+ assert_equal [r config get moduleconfigs.*] ""
+ # Asan will catch this if this string is not freed
+ catch {[r module loadex $testmodule CONFIG moduleconfigs.string rejectisfreed]}
+ assert_match {*ERR*} $e
+ assert_equal [r config get moduleconfigs.*] ""
+ # test we can't set random configs
+ catch {[r module loadex $testmodule CONFIG maxclients 333]}
+ assert_match {*ERR*} $e
+ assert_equal [r config get moduleconfigs.*] ""
+ assert_not_equal [r config get maxclients] "maxclients 333"
+ # test we can't set other module's configs
+ r module load $testmoduletwo
+ catch {[r module loadex $testmodule CONFIG configs.test no]}
+ assert_match {*ERR*} $e
+ assert_equal [r config get configs.test] "configs.test yes"
+ r module unload configs
+ # Verify config name and its alias being used together gets failed
+ catch {[r module loadex $testmodule CONFIG unprefix.numeric 1 CONFIG unprefix.numeric-alias 1]}
+ assert_match {*ERR*} $e
+ }
+
+ test {test config rewrite with dynamic load} {
+ #translates to: super \0secret password
+ r module loadex $testmodule CONFIG moduleconfigs.string \x73\x75\x70\x65\x72\x20\x00\x73\x65\x63\x72\x65\x74\x20\x70\x61\x73\x73\x77\x6f\x72\x64 ARGS
+ assert_not_equal [lsearch [lmap x [r module list] {dict get $x name}] moduleconfigs] -1
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {super \0secret password}"
+ r config set moduleconfigs.mutable_bool yes
+ r config set moduleconfigs.memory_numeric 750
+ r config set moduleconfigs.enum two
+ r config set moduleconfigs.flags "four two"
+ r config set unprefix-bool-alias no
+ r config set unprefix.numeric 456
+ r config set unprefix.string-alias "unprefix"
+ r config set unprefix-enum two
+ r config rewrite
+ restart_server 0 true false
+ # Ensure configs we rewrote are present and that the conf file is readable
+ assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool yes"
+ assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 750"
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {super \0secret password}"
+ assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {two four}"
+ assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1"
+
+ # Check unprefixed configuration and alias
+ assert_equal [r config get unprefix-bool] "unprefix-bool no"
+ assert_equal [r config get unprefix-bool-alias] "unprefix-bool-alias no"
+ assert_equal [r config get unprefix.numeric] "unprefix.numeric 456"
+ assert_equal [r config get unprefix.numeric-alias] "unprefix.numeric-alias 456"
+ assert_equal [r config get unprefix-string] "unprefix-string unprefix"
+ assert_equal [r config get unprefix.string-alias] "unprefix.string-alias unprefix"
+ assert_equal [r config get unprefix-enum] "unprefix-enum two"
+ assert_equal [r config get unprefix-enum-alias] "unprefix-enum-alias two"
+
+ r module unload moduleconfigs
+ }
+
+ test {test multiple modules with configs} {
+ r module load $testmodule
+ r module loadex $testmoduletwo CONFIG configs.test yes
+ assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool yes"
+ assert_equal [r config get moduleconfigs.immutable_bool] "moduleconfigs.immutable_bool no"
+ assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 1024"
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {secret password}"
+ assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum one"
+ assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1"
+ assert_equal [r config get configs.test] "configs.test yes"
+ r config set moduleconfigs.mutable_bool no
+ r config set moduleconfigs.string nice
+ r config set moduleconfigs.enum two
+ r config set configs.test no
+ assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool no"
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string nice"
+ assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two"
+ assert_equal [r config get configs.test] "configs.test no"
+ r config rewrite
+ # test we can load from conf file with multiple different modules.
+ restart_server 0 true false
+ assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool no"
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string nice"
+ assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two"
+ assert_equal [r config get configs.test] "configs.test no"
+ r module unload moduleconfigs
+ r module unload configs
+ }
+
+ test {test 1.module load 2.config rewrite 3.module unload 4.config rewrite works} {
+ # Configs need to be removed from the old config file in this case.
+ r module loadex $testmodule CONFIG moduleconfigs.memory_numeric 500 ARGS
+ assert_not_equal [lsearch [lmap x [r module list] {dict get $x name}] moduleconfigs] -1
+ r config rewrite
+ r module unload moduleconfigs
+ r config rewrite
+ restart_server 0 true false
+ # Ensure configs we rewrote are no longer present
+ assert_equal [r config get moduleconfigs.*] ""
+ }
+ test {startup moduleconfigs} {
+ # No loadmodule directive
+ catch {exec src/redis-server --moduleconfigs.string "hello"} err
+ assert_match {*Module Configuration detected without loadmodule directive or no ApplyConfig call: aborting*} $err
+
+ # Bad config value
+ catch {exec src/redis-server --loadmodule "$testmodule" --moduleconfigs.string "rejectisfreed"} err
+ assert_match {*Issue during loading of configuration moduleconfigs.string : Cannot set string to 'rejectisfreed'*} $err
+
+ # missing LoadConfigs call
+ catch {exec src/redis-server --loadmodule "$testmodule" noload --moduleconfigs.string "hello"} err
+ assert_match {*Module Configurations were not set, missing LoadConfigs call. Unloading the module.*} $err
+
+ # successful
+ start_server [list overrides [list loadmodule "$testmodule" moduleconfigs.string "bootedup" moduleconfigs.enum two moduleconfigs.flags "two four"] tags {"external:skip"}] {
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string bootedup"
+ assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool yes"
+ assert_equal [r config get moduleconfigs.immutable_bool] "moduleconfigs.immutable_bool no"
+ assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two"
+ assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {two four}"
+ assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1"
+ assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 1024"
+
+ # Check un-prefixed and aliased configuration
+ assert_equal [r config get unprefix-bool] "unprefix-bool yes"
+ assert_equal [r config get unprefix-bool-alias] "unprefix-bool-alias yes"
+ assert_equal [r config get unprefix.numeric] "unprefix.numeric -1"
+ assert_equal [r config get unprefix.numeric-alias] "unprefix.numeric-alias -1"
+ assert_equal [r config get unprefix-string] "unprefix-string {secret unprefix}"
+ assert_equal [r config get unprefix.string-alias] "unprefix.string-alias {secret unprefix}"
+ assert_equal [r config get unprefix-enum] "unprefix-enum one"
+ assert_equal [r config get unprefix-enum-alias] "unprefix-enum-alias one"
+ }
+ }
+
+ test {loadmodule CONFIG values take precedence over module loadex ARGS values} {
+ # Load module with conflicting CONFIG and ARGS values
+ r module loadex $testmodule \
+ CONFIG moduleconfigs.string goo \
+ CONFIG moduleconfigs.memory_numeric 2mb \
+ ARGS override-default
+
+ # Verify CONFIG values took precedence over the values that override-default would have caused the module to set
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string goo"
+ assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 2097152"
+
+ r module unload moduleconfigs
+ }
+
+ # Test: Ensure that modified configuration values from ARGS are correctly written to the config file
+ test {Modified ARGS values are persisted after config rewrite when set through CONFIG commands} {
+ # Load module with non-default ARGS values
+ r module loadex $testmodule ARGS override-default
+
+ # Verify the initial values were overwritten
+ assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 123"
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string foo"
+
+ # Set new values to simulate user configuration changes
+ r config set moduleconfigs.memory_numeric 1mb
+ r config set moduleconfigs.string "modified_value"
+
+ # Verify that the changes took effect
+ assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 1048576"
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string modified_value"
+
+ # Perform a config rewrite
+ r config rewrite
+
+ restart_server 0 true false
+ assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 1048576"
+ assert_equal [r config get moduleconfigs.string] "moduleconfigs.string modified_value"
+ r module unload moduleconfigs
+ }
+}
+