From: Vsevolod Stakhov Date: Tue, 26 May 2026 20:57:47 +0000 (+0100) Subject: [Test] composites: functional test for dynamic UCL composites map X-Git-Tag: 4.1.0~17^2 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=refs%2Fpull%2F6064%2Fhead;p=thirdparty%2Frspamd.git [Test] composites: functional test for dynamic UCL composites map Exercises load -> reload-with-update -> reload-with-stub: 1. INITIAL MAP - DYN_ONE FIRES: load composites from map.1, scan a message, confirm DYN_ONE and DYN_TWO fire with their declared scores. Static composite STATIC_COMP also fires alongside. 2. RELOAD - UPDATED SCORES AND NEW NAME: swap to map.2 (DYN_ONE score updated, DYN_TWO removed, DYN_THREE introduced), wait for the map watcher, scan, confirm new scores + new composite + DYN_TWO gone (stubbed). 3. RELOAD - REMOVED COMPOSITE BECOMES STUB: swap back to map.1. DYN_ONE/DYN_TWO are back with original scores, DYN_THREE was in the previous generation but is now absent -> verifies the stub path keeps the name out of scan results. Lua plugin registers DYN_BASE_A/B/C as always-firing atomic symbols so the composite expressions resolve deterministically. Config sets map_watch_interval = 0.5s for tight reload turnaround. --- diff --git a/test/functional/cases/119_dynamic_composites.robot b/test/functional/cases/119_dynamic_composites.robot new file mode 100644 index 0000000000..26e2eab5cc --- /dev/null +++ b/test/functional/cases/119_dynamic_composites.robot @@ -0,0 +1,54 @@ +*** Settings *** +Suite Setup Dynamic Composites Setup +Suite Teardown Dynamic Composites Teardown +Library ${RSPAMD_TESTDIR}/lib/rspamd.py +Resource ${RSPAMD_TESTDIR}/lib/rspamd.robot +Variables ${RSPAMD_TESTDIR}/lib/vars.py + +*** Variables *** +${CONFIG} ${RSPAMD_TESTDIR}/configs/dynamic_composites.conf +${RSPAMD_MAP_WATCH_INTERVAL} 0.5s +${MESSAGE} ${RSPAMD_TESTDIR}/messages/spam_message.eml +${RSPAMD_LUA_SCRIPT} ${RSPAMD_TESTDIR}/lua/dynamic_composites.lua +${RSPAMD_SCOPE} Suite +${RSPAMD_URL_TLD} ${RSPAMD_TESTDIR}/../lua/unit/test_tld.dat + +*** Test Cases *** +INITIAL MAP - DYN_ONE FIRES + Scan File ${MESSAGE} + Expect Symbol With Score DYN_ONE 2.5 + Expect Symbol With Score DYN_TWO 3.5 + Expect Symbol With Score STATIC_COMP 1.0 + Do Not Expect Symbol DYN_THREE + +RELOAD - UPDATED SCORES AND NEW NAME + ${TMP_FILE} = Make Temporary File + Copy File ${RSPAMD_TESTDIR}/configs/maps/dynamic_composites.map.2 ${TMP_FILE} + Move File ${TMP_FILE} ${RSPAMD_DYN_COMP_MAP} + Sleep 2s Wait for map reload + Scan File ${MESSAGE} + Expect Symbol With Score DYN_ONE 7.0 + Expect Symbol With Score DYN_THREE 4.0 + Expect Symbol With Score STATIC_COMP 1.0 + Do Not Expect Symbol DYN_TWO + +RELOAD - REMOVED COMPOSITE BECOMES STUB + ${TMP_FILE} = Make Temporary File + Copy File ${RSPAMD_TESTDIR}/configs/maps/dynamic_composites.map.1 ${TMP_FILE} + Move File ${TMP_FILE} ${RSPAMD_DYN_COMP_MAP} + Sleep 2s Wait for map reload + Scan File ${MESSAGE} + Expect Symbol With Score DYN_ONE 2.5 + Expect Symbol With Score DYN_TWO 3.5 + Do Not Expect Symbol DYN_THREE + +*** Keywords *** +Dynamic Composites Setup + ${RSPAMD_DYN_COMP_MAP} = Make Temporary File + Set Suite Variable ${RSPAMD_DYN_COMP_MAP} + Copy File ${RSPAMD_TESTDIR}/configs/maps/dynamic_composites.map.1 ${RSPAMD_DYN_COMP_MAP} + Rspamd Setup + +Dynamic Composites Teardown + Remove File ${RSPAMD_DYN_COMP_MAP} + Rspamd Teardown diff --git a/test/functional/configs/dynamic_composites.conf b/test/functional/configs/dynamic_composites.conf new file mode 100644 index 0000000000..32356a3212 --- /dev/null +++ b/test/functional/configs/dynamic_composites.conf @@ -0,0 +1,42 @@ +options = { + filters = [] + url_tld = "{= env.URL_TLD =}" + pidfile = "{= env.TMPDIR =}/rspamd.pid" + map_watch_interval = {= env.MAP_WATCH_INTERVAL =}; +} +logging = { + type = "file", + level = "debug" + filename = "{= env.TMPDIR =}/rspamd.log" +} +metric = { + name = "default", + actions = { + reject = 100500, + } + unknown_weight = 1 +} + +worker { + type = normal + bind_socket = "{= env.LOCAL_ADDR =}:{= env.PORT_NORMAL =}" + count = 1 + task_timeout = 10s; +} +worker { + type = controller + bind_socket = "{= env.LOCAL_ADDR =}:{= env.PORT_CONTROLLER =}" + count = 1 + secure_ip = ["127.0.0.1", "::1"]; + stats_path = "{= env.TMPDIR =}/stats.ucl" +} +lua = "{= env.TESTDIR =}/lua/test_coverage.lua"; +lua = "{= env.LUA_SCRIPT =}"; + +composites { + STATIC_COMP { + expression = "DYN_BASE_A"; + score = 1.0; + } + dynamic = "{= env.DYN_COMP_MAP =}"; +} diff --git a/test/functional/configs/maps/dynamic_composites.map.1 b/test/functional/configs/maps/dynamic_composites.map.1 new file mode 100644 index 0000000000..7dacf829e5 --- /dev/null +++ b/test/functional/configs/maps/dynamic_composites.map.1 @@ -0,0 +1,8 @@ +DYN_ONE { + expression = "DYN_BASE_A"; + score = 2.5; +} +DYN_TWO { + expression = "DYN_BASE_B"; + score = 3.5; +} diff --git a/test/functional/configs/maps/dynamic_composites.map.2 b/test/functional/configs/maps/dynamic_composites.map.2 new file mode 100644 index 0000000000..22542698f2 --- /dev/null +++ b/test/functional/configs/maps/dynamic_composites.map.2 @@ -0,0 +1,8 @@ +DYN_ONE { + expression = "DYN_BASE_A"; + score = 7.0; +} +DYN_THREE { + expression = "DYN_BASE_C"; + score = 4.0; +} diff --git a/test/functional/lua/dynamic_composites.lua b/test/functional/lua/dynamic_composites.lua new file mode 100644 index 0000000000..ccfe62b241 --- /dev/null +++ b/test/functional/lua/dynamic_composites.lua @@ -0,0 +1,11 @@ +-- Always-firing atomic symbols used as inputs for dynamic composites. +local atoms = { 'DYN_BASE_A', 'DYN_BASE_B', 'DYN_BASE_C' } +for _, name in ipairs(atoms) do + rspamd_config:register_symbol({ + name = name, + score = 0.1, + callback = function() + return true, 'fires always' + end + }) +end