aboutsummaryrefslogtreecommitdiffstats
path: root/test/functional
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional')
-rw-r--r--test/functional/cases/001_merged/350_magic.robot1
-rw-r--r--test/functional/cases/110_statistics/300-multiclass-redis.robot42
-rw-r--r--test/functional/cases/110_statistics/320-multiclass-peruser.robot31
-rw-r--r--test/functional/cases/110_statistics/multiclass_lib.robot169
-rw-r--r--test/functional/cases/120_fuzzy/lib.robot23
-rw-r--r--test/functional/cases/120_fuzzy/read-only.robot14
-rw-r--r--test/functional/cases/120_fuzzy/split-servers.robot18
-rw-r--r--test/functional/cases/120_fuzzy/write-only.robot14
-rw-r--r--test/functional/cases/270_regexp_maps.robot48
-rw-r--r--test/functional/cases/400_known_senders.robot22
-rw-r--r--test/functional/cases/410_replies.robot37
-rw-r--r--test/functional/cases/411_logging/000_console/000_systemd_logger.robot (renamed from test/functional/cases/410_logging/000_console/000_systemd_logger.robot)0
-rw-r--r--test/functional/cases/411_logging/000_console/001_timestamps.robot (renamed from test/functional/cases/410_logging/000_console/001_timestamps.robot)0
-rw-r--r--test/functional/cases/411_logging/001_file/000_json.robot (renamed from test/functional/cases/410_logging/001_file/000_json.robot)0
-rw-r--r--test/functional/cases/550_milter_headers.robot11
-rw-r--r--test/functional/configs/fuzzy-split-servers.conf97
-rw-r--r--test/functional/configs/maps/advance_fee_rules.map43
-rw-r--r--test/functional/configs/milter_headers.conf12
-rw-r--r--test/functional/configs/multiclass_bayes.conf129
-rw-r--r--test/functional/configs/regexp_maps.conf51
-rw-r--r--test/functional/lib/rspamd.robot26
-rw-r--r--test/functional/messages/advance_fee_fraud.eml37
-rw-r--r--test/functional/messages/gargantua.eml54
-rw-r--r--test/functional/messages/newsletter.eml50
-rw-r--r--test/functional/messages/transactional.eml18
25 files changed, 919 insertions, 28 deletions
diff --git a/test/functional/cases/001_merged/350_magic.robot b/test/functional/cases/001_merged/350_magic.robot
index 66a18f2af..b2746ce3c 100644
--- a/test/functional/cases/001_merged/350_magic.robot
+++ b/test/functional/cases/001_merged/350_magic.robot
@@ -65,3 +65,4 @@ Magic detections bundle 1
... MAGIC_SYM_ICS_55
... MAGIC_SYM_VCF_56
... MAGIC_SYM_CSV_57
+ ... MAGIC_SYM_HEIC_58
diff --git a/test/functional/cases/110_statistics/300-multiclass-redis.robot b/test/functional/cases/110_statistics/300-multiclass-redis.robot
new file mode 100644
index 000000000..278f7e0a0
--- /dev/null
+++ b/test/functional/cases/110_statistics/300-multiclass-redis.robot
@@ -0,0 +1,42 @@
+*** Settings ***
+Documentation Multiclass Bayes Classification Tests with Redis Backend
+Suite Setup Rspamd Redis Setup
+Suite Teardown Rspamd Redis Teardown
+Test Setup Set Test Hash Documentation
+Resource multiclass_lib.robot
+
+*** Variables ***
+${RSPAMD_REDIS_SERVER} ${RSPAMD_REDIS_ADDR}:${RSPAMD_REDIS_PORT}
+${RSPAMD_STATS_HASH} siphash
+${CONFIG} ${RSPAMD_TESTDIR}/configs/multiclass_bayes.conf
+
+*** Test Cases ***
+Multiclass Basic Learning and Classification
+ [Documentation] Test basic multiclass learning and classification
+ [Tags] multiclass basic learning
+ Multiclass Basic Learn Test
+
+Multiclass Legacy Compatibility
+ [Documentation] Test that old learn_spam/learn_ham commands still work
+ [Tags] multiclass compatibility legacy
+ Multiclass Legacy Compatibility Test
+
+Multiclass Relearn
+ [Documentation] Test reclassifying messages to different classes
+ [Tags] multiclass relearn
+ Multiclass Relearn Test
+
+Multiclass Cross-Class Learning
+ [Documentation] Test learning message as different class than expected
+ [Tags] multiclass cross-learn
+ Multiclass Cross-Learn Test
+
+Multiclass Unlearn
+ [Documentation] Test unlearning (learning message as different class)
+ [Tags] multiclass unlearn
+ Multiclass Unlearn Test
+
+Multiclass Statistics
+ [Documentation] Test that statistics show all class information
+ [Tags] multiclass statistics
+ Multiclass Stats Test \ No newline at end of file
diff --git a/test/functional/cases/110_statistics/320-multiclass-peruser.robot b/test/functional/cases/110_statistics/320-multiclass-peruser.robot
new file mode 100644
index 000000000..e8ca34616
--- /dev/null
+++ b/test/functional/cases/110_statistics/320-multiclass-peruser.robot
@@ -0,0 +1,31 @@
+*** Settings ***
+Suite Setup Rspamd Redis Setup
+Suite Teardown Rspamd Redis Teardown
+Test Setup Set Test Hash Documentation
+Resource multiclass_lib.robot
+
+*** Variables ***
+${CONFIG} ${RSPAMD_TESTDIR}/configs/multiclass_bayes.conf
+${REDIS_SCOPE} Suite
+${RSPAMD_REDIS_SERVER} ${RSPAMD_REDIS_ADDR}:${RSPAMD_REDIS_PORT}
+${RSPAMD_SCOPE} Suite
+${RSPAMD_STATS_BACKEND} redis
+${RSPAMD_STATS_HASH} null
+${RSPAMD_STATS_KEY} null
+${RSPAMD_STATS_PER_USER} true
+
+*** Test Cases ***
+Multiclass Per-User Basic Learn Test
+ Multiclass Basic Learn Test test@example.com
+
+Multiclass Per-User Legacy Compatibility Test
+ Multiclass Legacy Compatibility Test test@example.com
+
+Multiclass Per-User Relearn Test
+ Multiclass Relearn Test test@example.com
+
+Multiclass Per-User Cross-Learn Test
+ Multiclass Cross-Learn Test test@example.com
+
+Multiclass Per-User Unlearn Test
+ Multiclass Unlearn Test test@example.com \ No newline at end of file
diff --git a/test/functional/cases/110_statistics/multiclass_lib.robot b/test/functional/cases/110_statistics/multiclass_lib.robot
new file mode 100644
index 000000000..9f70e05fb
--- /dev/null
+++ b/test/functional/cases/110_statistics/multiclass_lib.robot
@@ -0,0 +1,169 @@
+*** Settings ***
+Library OperatingSystem
+Resource lib.robot
+
+*** Variables ***
+${CONFIG} ${RSPAMD_TESTDIR}/configs/multiclass_bayes.conf
+${MESSAGE_HAM} ${RSPAMD_TESTDIR}/messages/ham.eml
+${MESSAGE_SPAM} ${RSPAMD_TESTDIR}/messages/spam_message.eml
+${MESSAGE_NEWSLETTER} ${RSPAMD_TESTDIR}/messages/newsletter.eml
+${REDIS_SCOPE} Suite
+${RSPAMD_REDIS_SERVER} null
+${RSPAMD_SCOPE} Suite
+${RSPAMD_STATS_BACKEND} redis
+${RSPAMD_STATS_HASH} null
+${RSPAMD_STATS_KEY} null
+${RSPAMD_STATS_PER_USER} ${EMPTY}
+
+*** Keywords ***
+Learn Multiclass
+ [Arguments] ${user} ${class} ${message}
+ # Extract filename from message path for queue-id
+ ${path} ${filename} = Split Path ${message}
+ IF "${user}"
+ ${result} = Run Rspamc -d ${user} -h ${RSPAMD_LOCAL_ADDR}:${RSPAMD_PORT_CONTROLLER} learn_class:${class} ${message}
+ ELSE
+ ${result} = Run Rspamc -h ${RSPAMD_LOCAL_ADDR}:${RSPAMD_PORT_CONTROLLER} learn_class:${class} ${message}
+ END
+ Check Rspamc ${result}
+
+Learn Multiclass Legacy
+ [Arguments] ${user} ${class} ${message}
+ # Test backward compatibility with old learn_spam/learn_ham commands
+ # Extract filename from message path for queue-id
+ ${path} ${filename} = Split Path ${message}
+ IF "${user}"
+ ${result} = Run Rspamc -d ${user} -h ${RSPAMD_LOCAL_ADDR}:${RSPAMD_PORT_CONTROLLER} learn_${class} ${message}
+ ELSE
+ ${result} = Run Rspamc -h ${RSPAMD_LOCAL_ADDR}:${RSPAMD_PORT_CONTROLLER} learn_${class} ${message}
+ END
+ Check Rspamc ${result}
+
+Multiclass Basic Learn Test
+ [Arguments] ${user}=${EMPTY}
+ Set Suite Variable ${RSPAMD_STATS_LEARNTEST} 0
+ Set Test Variable ${kwargs} &{EMPTY}
+ IF "${user}"
+ Set To Dictionary ${kwargs} Deliver-To=${user}
+ END
+
+ # Learn all classes
+ Learn Multiclass ${user} spam ${MESSAGE_SPAM}
+ Learn Multiclass ${user} ham ${MESSAGE_HAM}
+ Learn Multiclass ${user} newsletter ${MESSAGE_NEWSLETTER}
+
+ # Test classification
+ Scan File ${MESSAGE_SPAM} &{kwargs}
+ Expect Symbol BAYES_SPAM
+
+ Scan File ${MESSAGE_HAM} &{kwargs}
+ Expect Symbol BAYES_HAM
+
+ Scan File ${MESSAGE_NEWSLETTER} &{kwargs}
+ Expect Symbol BAYES_NEWSLETTER
+
+ Set Suite Variable ${RSPAMD_STATS_LEARNTEST} 1
+
+Multiclass Legacy Compatibility Test
+ [Arguments] ${user}=${EMPTY}
+ Set Test Variable ${kwargs} &{EMPTY}
+ IF "${user}"
+ Set To Dictionary ${kwargs} Deliver-To=${user}
+ END
+
+ # Test legacy learn_spam and learn_ham commands still work
+ Learn Multiclass Legacy ${user} spam ${MESSAGE_SPAM}
+ Learn Multiclass Legacy ${user} ham ${MESSAGE_HAM}
+
+ # Should still classify correctly
+ Scan File ${MESSAGE_SPAM} &{kwargs}
+ Expect Symbol BAYES_SPAM
+
+ Scan File ${MESSAGE_HAM} &{kwargs}
+ Expect Symbol BAYES_HAM
+
+Multiclass Relearn Test
+ [Arguments] ${user}=${EMPTY}
+ IF ${RSPAMD_STATS_LEARNTEST} == 0
+ Fail "Learn test was not run"
+ END
+
+ Set Test Variable ${kwargs} &{EMPTY}
+ IF "${user}"
+ Set To Dictionary ${kwargs} Deliver-To=${user}
+ END
+
+ # Relearn spam message as ham
+ Learn Multiclass ${user} ham ${MESSAGE_SPAM}
+
+ # Should now classify as ham or at least not spam
+ Scan File ${MESSAGE_SPAM} &{kwargs}
+ ${pass} = Run Keyword And Return Status Expect Symbol BAYES_HAM
+ IF ${pass}
+ Pass Execution Successfully reclassified spam as ham
+ END
+ Do Not Expect Symbol BAYES_SPAM
+
+Multiclass Cross-Learn Test
+ [Arguments] ${user}=${EMPTY}
+ Set Test Variable ${kwargs} &{EMPTY}
+ IF "${user}"
+ Set To Dictionary ${kwargs} Deliver-To=${user}
+ END
+
+ # Learn newsletter message as ham to test cross-class learning
+ Learn Multiclass ${user} ham ${MESSAGE_NEWSLETTER}
+
+ # Should classify as ham, not newsletter (since we trained it as ham)
+ Scan File ${MESSAGE_NEWSLETTER} &{kwargs}
+ Expect Symbol BAYES_HAM
+ Do Not Expect Symbol BAYES_NEWSLETTER
+
+Multiclass Unlearn Test
+ [Arguments] ${user}=${EMPTY}
+ Set Test Variable ${kwargs} &{EMPTY}
+ IF "${user}"
+ Set To Dictionary ${kwargs} Deliver-To=${user}
+ END
+
+ # First learn spam
+ Learn Multiclass ${user} spam ${MESSAGE_SPAM}
+ Scan File ${MESSAGE_SPAM} &{kwargs}
+ Expect Symbol BAYES_SPAM
+
+ # Then unlearn spam (learn as ham)
+ Learn Multiclass ${user} ham ${MESSAGE_SPAM}
+
+ # Should no longer classify as spam
+ Scan File ${MESSAGE_SPAM} &{kwargs}
+ Do Not Expect Symbol BAYES_SPAM
+
+Check Multiclass Results
+ [Arguments] ${result} ${expected_class}
+ # Check that scan result contains expected class information
+ Should Contain ${result.stdout} BAYES_${expected_class.upper()}
+ # Check for multiclass result format [class_name]
+ Should Match Regexp ${result.stdout} BAYES_${expected_class.upper()}.*\\[${expected_class}\\]
+
+Multiclass Stats Test
+ # Check that rspamc stat shows learning counts for all classes
+ ${result} = Run Rspamc -h ${RSPAMD_LOCAL_ADDR}:${RSPAMD_PORT_CONTROLLER} stat
+ # Don't use Check Rspamc for stat command as it expects JSON success format
+ Should Be Equal As Integers ${result.rc} 0
+
+ # Should show statistics for all classes
+ Should Contain ${result.stdout} BAYES_SPAM
+ Should Contain ${result.stdout} BAYES_HAM
+ Should Contain ${result.stdout} BAYES_NEWSLETTER
+
+Multiclass Configuration Migration Test
+ # Test that old binary config can be automatically migrated
+ Set Test Variable ${binary_config} ${RSPAMD_TESTDIR}/configs/stats.conf
+
+ # Start with binary config
+ ${result} = Run Rspamc --config ${binary_config} stat
+ Check Rspamc ${result}
+
+ # Should show deprecation warning but work
+ Should Contain ${result.stderr} deprecated ignore_case=True
+
diff --git a/test/functional/cases/120_fuzzy/lib.robot b/test/functional/cases/120_fuzzy/lib.robot
index fda0af54a..a57ecc742 100644
--- a/test/functional/cases/120_fuzzy/lib.robot
+++ b/test/functional/cases/120_fuzzy/lib.robot
@@ -16,6 +16,7 @@ ${RSPAMD_FUZZY_ENCRYPTED_ONLY} false
${RSPAMD_FUZZY_ENCRYPTION_KEY} null
${RSPAMD_FUZZY_INCLUDE} ${RSPAMD_TESTDIR}/configs/empty.conf
${RSPAMD_FUZZY_KEY} null
+${RSPAMD_FUZZY_SERVER_MODE} servers
${RSPAMD_FUZZY_SHINGLES_KEY} null
${RSPAMD_SCOPE} Suite
${SETTINGS_FUZZY_CHECK} ${EMPTY}
@@ -109,6 +110,7 @@ Fuzzy Setup Encrypted
Set Suite Variable ${RSPAMD_FUZZY_ENCRYPTION_KEY} ${RSPAMD_KEY_PUB1}
Set Suite Variable ${RSPAMD_FUZZY_CLIENT_ENCRYPTION_KEY} ${RSPAMD_KEY_PUB1}
Set Suite Variable ${RSPAMD_FUZZY_INCLUDE} ${RSPAMD_TESTDIR}/configs/fuzzy-encryption-key.conf
+ Set Suite Variable ${RSPAMD_FUZZY_SERVER_MODE} servers
Rspamd Redis Setup
Fuzzy Setup Encrypted Dyn1
@@ -142,6 +144,8 @@ Fuzzy Setup Encrypted Keyed
Fuzzy Setup Plain
[Arguments] ${algorithm}
Set Suite Variable ${RSPAMD_FUZZY_ALGORITHM} ${algorithm}
+ Set Suite Variable ${RSPAMD_FUZZY_SERVER_MODE} servers
+ Set Suite Variable ${SETTINGS_FUZZY_CHECK} servers = "${RSPAMD_LOCAL_ADDR}:${RSPAMD_PORT_FUZZY}";
Rspamd Redis Setup
Fuzzy Setup Keyed
@@ -149,6 +153,7 @@ Fuzzy Setup Keyed
Set Suite Variable ${RSPAMD_FUZZY_ALGORITHM} ${algorithm}
Set Suite Variable ${RSPAMD_FUZZY_KEY} mYN888sydwLTfE32g2hN
Set Suite Variable ${RSPAMD_FUZZY_SHINGLES_KEY} hXUCgul9yYY3Zlk1QIT2
+ Set Suite Variable ${RSPAMD_FUZZY_SERVER_MODE} servers
Rspamd Redis Setup
Fuzzy Setup Plain Fasthash
@@ -218,3 +223,21 @@ Fuzzy Multimessage Overwrite Test
FOR ${i} IN @{MESSAGES}
Fuzzy Overwrite Test ${i}
END
+
+Fuzzy Setup Split Servers
+ Set Suite Variable ${RSPAMD_FUZZY_ALGORITHM} siphash
+ Set Suite Variable ${RSPAMD_FUZZY_SERVER_MODE} split
+ Set Suite Variable ${SETTINGS_FUZZY_CHECK} read_servers = "${RSPAMD_LOCAL_ADDR}:${RSPAMD_PORT_FUZZY}"; write_servers = "${RSPAMD_LOCAL_ADDR}:${RSPAMD_PORT_FUZZY}";
+ Rspamd Redis Setup
+
+Fuzzy Setup Read Only
+ Set Suite Variable ${RSPAMD_FUZZY_ALGORITHM} siphash
+ Set Suite Variable ${RSPAMD_FUZZY_SERVER_MODE} read_only
+ Set Suite Variable ${SETTINGS_FUZZY_CHECK} read_only = true;
+ Rspamd Redis Setup
+
+Fuzzy Setup Write Only
+ Set Suite Variable ${RSPAMD_FUZZY_ALGORITHM} siphash
+ Set Suite Variable ${RSPAMD_FUZZY_SERVER_MODE} write_only
+ Set Suite Variable ${SETTINGS_FUZZY_CHECK} mode = "write_only";
+ Rspamd Redis Setup
diff --git a/test/functional/cases/120_fuzzy/read-only.robot b/test/functional/cases/120_fuzzy/read-only.robot
new file mode 100644
index 000000000..0c3be7fec
--- /dev/null
+++ b/test/functional/cases/120_fuzzy/read-only.robot
@@ -0,0 +1,14 @@
+*** Settings ***
+Suite Setup Fuzzy Setup Read Only
+Suite Teardown Rspamd Redis Teardown
+Resource lib.robot
+
+*** Test Cases ***
+Fuzzy Add
+ Fuzzy Multimessage Add Test
+
+Fuzzy Fuzzy
+ Fuzzy Multimessage Fuzzy Test
+
+Fuzzy Miss
+ Fuzzy Multimessage Miss Test
diff --git a/test/functional/cases/120_fuzzy/split-servers.robot b/test/functional/cases/120_fuzzy/split-servers.robot
new file mode 100644
index 000000000..41acb23ff
--- /dev/null
+++ b/test/functional/cases/120_fuzzy/split-servers.robot
@@ -0,0 +1,18 @@
+*** Settings ***
+Suite Setup Fuzzy Setup Split Servers
+Suite Teardown Rspamd Redis Teardown
+Resource lib.robot
+Variables ${RSPAMD_TESTDIR}/lib/vars.py
+
+*** Variables ***
+${CONFIG} ${RSPAMD_TESTDIR}/configs/fuzzy-split-servers.conf
+
+*** Test Cases ***
+Fuzzy Add
+ Fuzzy Multimessage Add Test
+
+Fuzzy Fuzzy
+ Fuzzy Multimessage Fuzzy Test
+
+Fuzzy Miss
+ Fuzzy Multimessage Miss Test
diff --git a/test/functional/cases/120_fuzzy/write-only.robot b/test/functional/cases/120_fuzzy/write-only.robot
new file mode 100644
index 000000000..ee017a1d3
--- /dev/null
+++ b/test/functional/cases/120_fuzzy/write-only.robot
@@ -0,0 +1,14 @@
+*** Settings ***
+Suite Setup Fuzzy Setup Write Only
+Suite Teardown Rspamd Redis Teardown
+Resource lib.robot
+
+*** Test Cases ***
+Fuzzy Add
+ Fuzzy Multimessage Add Test
+
+Fuzzy Fuzzy
+ Fuzzy Multimessage Fuzzy Test
+
+Fuzzy Miss
+ Fuzzy Multimessage Miss Test
diff --git a/test/functional/cases/270_regexp_maps.robot b/test/functional/cases/270_regexp_maps.robot
new file mode 100644
index 000000000..d89143bc8
--- /dev/null
+++ b/test/functional/cases/270_regexp_maps.robot
@@ -0,0 +1,48 @@
+*** Settings ***
+Test Setup Rspamd Setup
+Test Teardown Rspamd Teardown
+Library ${RSPAMD_TESTDIR}/lib/rspamd.py
+Resource ${RSPAMD_TESTDIR}/lib/rspamd.robot
+Variables ${RSPAMD_TESTDIR}/lib/vars.py
+
+*** Variables ***
+${CONFIG} ${RSPAMD_TESTDIR}/configs/regexp_maps.conf
+${MESSAGE1} ${RSPAMD_TESTDIR}/messages/advance_fee_fraud.eml
+${MESSAGE2} ${RSPAMD_TESTDIR}/messages/spam_message.eml
+${RSPAMD_SCOPE} Test
+${RSPAMD_URL_TLD} ${RSPAMD_TESTDIR}/../lua/unit/test_tld.dat
+
+*** Test Cases ***
+Advance Fee Fraud Detection
+ [Documentation] Test that advance fee fraud rules match correctly
+ Scan File ${MESSAGE1}
+ Expect Symbol ADVANCE_FEE_2
+ Expect Symbol ADVANCE_FEE_3
+ # Verify filtered options (no __ atoms, max 5 options)
+ ${symbols} = Get From Dictionary ${SCAN_RESULT}[symbols] ADVANCE_FEE_2
+ ${options} = Get From Dictionary ${symbols} options
+ ${options_count} = Get Length ${options}
+ Should Be True ${options_count} <= 5 msg=Too many options: ${options_count}
+ FOR ${option} IN @{options}
+ Should Not Match Regexp ${option} ^__ msg=Option should not start with __: ${option}
+ END
+
+Meta Rule Combination
+ [Documentation] Test that meta rules correctly combine atom results
+ Scan File ${MESSAGE1}
+ Expect Symbol With Score ADVANCE_FEE_2 4.0
+ Expect Symbol With Score ADVANCE_FEE_3 5.0
+
+No False Positives on Ham
+ [Documentation] Test that regexp rules don't trigger on legitimate messages
+ Scan File ${MESSAGE2}
+ Do Not Expect Symbol ADVANCE_FEE_2
+ Do Not Expect Symbol ADVANCE_FEE_3
+
+Atom Rules Availability
+ [Documentation] Test that individual atom rules are available for combination
+ Scan File ${MESSAGE1}
+ # These should be available internally but not shown as main results
+ # We test by ensuring the meta rules work correctly
+ Expect Symbol ADVANCE_FEE_2
+ Expect Symbol ADVANCE_FEE_3
diff --git a/test/functional/cases/400_known_senders.robot b/test/functional/cases/400_known_senders.robot
index d827acc0e..a7cde59cb 100644
--- a/test/functional/cases/400_known_senders.robot
+++ b/test/functional/cases/400_known_senders.robot
@@ -43,33 +43,37 @@ INCOMING MAIL SENDER IS UNKNOWN
... Settings={symbols_enabled [${SYMBOL_GLOBAL}, ${SYMBOL_LOCAL}]}
Do Not Expect Symbol ${SYMBOL_GLOBAL}
Do Not Expect Symbol ${SYMBOL_LOCAL}
-
+
INCOMING MAIL SENDER IS KNOWN RECIPIENTS ARE UNKNOWN
Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_1_1.eml
- ... IP=8.8.8.8 User=user@emailbl.com
+ ... IP=8.8.8.8
+ ... User=xxx@abrakadabra.com
+ ... From=xxx@abrakadabra.com
... Settings=${SETTINGS_REPLIES}
Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_1.eml
- ... IP=8.8.8.8 User=user@emailbl.com
+ ... IP=8.8.8.8
+ ... Settings=${SETTINGS_REPLIES}
+ ... Rcpt=xxx@abrakadabra.com
... Settings=${SETTINGS_REPLIES}
+ ... From=user@emailbl.com
Scan File ${RSPAMD_TESTDIR}/messages/inc_mail_known_sender.eml
- ... IP=8.8.8.8 User=user@emailbl.com
+ ... IP=8.8.8.8
... Settings={symbols_enabled [${SYMBOL_GLOBAL}, ${SYMBOL_LOCAL}]}
Expect Symbol ${SYMBOL_GLOBAL}
Do Not Expect Symbol ${SYMBOL_LOCAL}
INCOMING MAIL SENDER IS KNOWN RECIPIENTS ARE KNOWN
Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_1_1.eml
- ... IP=8.8.8.8 User=user@emailbl.com
+ ... IP=8.8.8.8 User=user@emailbl.com From=user@emailbl.com
... Settings=${SETTINGS_REPLIES}
Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_1.eml
- ... IP=8.8.8.8 User=user@emailbl.com
+ ... IP=8.8.8.8 User=user@emailbl.com Rcpt=user@emailbl.com
... Settings=${SETTINGS_REPLIES}
Scan File ${RSPAMD_TESTDIR}/messages/inc_mail_known_sender.eml
- ... IP=8.8.8.8 User=user@emailbl.com
+ ... IP=8.8.8.8 User=user@emailbl.com Rcpt=user@emailbl.com
... Settings=${SETTINGS_REPLIES}
Scan File ${RSPAMD_TESTDIR}/messages/inc_mail_known_sender.eml
- ... IP=8.8.8.8 User=user@emailbl.com
+ ... IP=8.8.8.8 User=user@emailbl.com Rcpt=user@emailbl.com
... Settings={symbols_enabled [${SYMBOL_GLOBAL}, ${SYMBOL_LOCAL}]}
Expect Symbol ${SYMBOL_GLOBAL}
Expect Symbol ${SYMBOL_LOCAL}
-
diff --git a/test/functional/cases/410_replies.robot b/test/functional/cases/410_replies.robot
index 23ad9df35..b6710149c 100644
--- a/test/functional/cases/410_replies.robot
+++ b/test/functional/cases/410_replies.robot
@@ -15,33 +15,36 @@ ${RSPAMD_SCOPE} Suite
*** Test Cases ***
Reply to 1 sender 1 recipients
Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_1_1.eml
- ... IP=8.8.8.8 User=user@emailbl.com
+ ... IP=8.8.8.8
+ ... User=xxx@abrakadabra.com
+ ... From=xxx@abrakadabra.com
... Settings=${SETTINGS_REPLIES}
+ ... Rcpt=user@emailbl.com
Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_1.eml
- ... IP=8.8.8.8 User=user@emailbl.com
+ ... IP=8.8.8.8
+ ... Rcpt=xxx@abrakadabra.com
... Settings=${SETTINGS_REPLIES}
+ ... From=user@emailbl.com
Expect Symbol ${SYMBOL}
-Reply to 1 sender 2 recipients first is set second is not
+Reply to 1 sender 2 recipients but SMTP recipient matches
Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_1_2_first.eml
- ... IP=8.8.8.8 User=user@emailbl.com
+ ... IP=8.8.8.8
+ ... User=xxxx@emailbl.com
... Settings=${SETTINGS_REPLIES}
Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_2.eml
- ... IP=8.8.8.8 User=user@emailbl.com
+ ... IP=8.8.8.8
+ ... Rcpt=xxxx@emailbl.com
... Settings=${SETTINGS_REPLIES}
Expect Symbol ${SYMBOL}
-Reply to 1 sender 2 recipients 1 rcpt is same
- Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_2_s.eml
- ... IP=8.8.8.8 User=user@emailbl.com
+Reply to 1 sender 2 recipients but SMTP recipient NOT matches
+ Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_1_2_first.eml
+ ... IP=8.8.8.8
+ ... User=user@emailbl.com
... Settings=${SETTINGS_REPLIES}
- Expect Symbol ${SYMBOL}
-
-Reply to another sender 2 recipients
- Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_2_2.eml
- ... IP=8.8.8.8 User=another@emailbl.com
+ Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_2.eml
+ ... IP=8.8.8.8 User=user@emailbl.com
+ ... Rcpt=another@emailbl.com
... Settings=${SETTINGS_REPLIES}
- Scan File ${RSPAMD_TESTDIR}/messages/replyto_2_2.eml
- ... IP=8.8.8.8 User=another@emailbl.com
- ... Settings=${SETTINGS_REPLIES}
- Expect Symbol ${SYMBOL}
+ Do Not Expect Symbol ${SYMBOL}
diff --git a/test/functional/cases/410_logging/000_console/000_systemd_logger.robot b/test/functional/cases/411_logging/000_console/000_systemd_logger.robot
index 88178461b..88178461b 100644
--- a/test/functional/cases/410_logging/000_console/000_systemd_logger.robot
+++ b/test/functional/cases/411_logging/000_console/000_systemd_logger.robot
diff --git a/test/functional/cases/410_logging/000_console/001_timestamps.robot b/test/functional/cases/411_logging/000_console/001_timestamps.robot
index bd8e2c349..bd8e2c349 100644
--- a/test/functional/cases/410_logging/000_console/001_timestamps.robot
+++ b/test/functional/cases/411_logging/000_console/001_timestamps.robot
diff --git a/test/functional/cases/410_logging/001_file/000_json.robot b/test/functional/cases/411_logging/001_file/000_json.robot
index a2f04e85c..a2f04e85c 100644
--- a/test/functional/cases/410_logging/001_file/000_json.robot
+++ b/test/functional/cases/411_logging/001_file/000_json.robot
diff --git a/test/functional/cases/550_milter_headers.robot b/test/functional/cases/550_milter_headers.robot
index 80471b83c..c09659714 100644
--- a/test/functional/cases/550_milter_headers.robot
+++ b/test/functional/cases/550_milter_headers.robot
@@ -37,3 +37,14 @@ CHECK HEADERS WITHOUT TEST SYMBOL
# Check X-Spam-Level header
Do Not Expect Added Header X-Spam-Level
Expect Removed Header X-Spam-Level
+
+CHECK HEADERS WITH OVERRIDE SETTINGS
+ # id_milter_headers_override setting enables only authentication-results and x-spam-level routines
+ Scan File ${MESSAGE} Settings-Id=id_milter_headers_override
+ # Test the milter_headers override behavior
+ # Check that Authentication-Results and X-Spam-Level headers are present (exact values are not important)
+ Expect Header Is Present Authentication-Results
+ Expect Header Is Present X-Spam-Level
+ # Verify other headers are not added since only authentication-results and x-spam-level routines run
+ Do Not Expect Added Header X-Virus
+ Do Not Expect Added Header My-Spamd-Bar
diff --git a/test/functional/configs/fuzzy-split-servers.conf b/test/functional/configs/fuzzy-split-servers.conf
new file mode 100644
index 000000000..e7c3bb922
--- /dev/null
+++ b/test/functional/configs/fuzzy-split-servers.conf
@@ -0,0 +1,97 @@
+redis {
+ servers = "{= env.REDIS_ADDR =}:{= env.REDIS_PORT =}";
+}
+lua = "{= env.TESTDIR =}/lua/test_coverage.lua";
+options = {
+ filters = "fuzzy_check";
+ pidfile = "{= env.TMPDIR =}/rspamd.pid";
+ control_socket = "{= env.TMPDIR =}/rspamd.sock mode=0600";
+ url_tld = "{= env.TESTDIR =}/../lua/unit/test_tld.dat";
+ dns {
+ retransmits = 10;
+ timeout = 2s;
+ }
+}
+logging = {
+ type = "file",
+ level = "debug"
+ filename = "{= env.TMPDIR =}/rspamd.log"
+}
+metric = {
+ name = "default",
+ actions = {
+ reject = 100500,
+ }
+ unknown_weight = 1
+ symbol {
+ weight = 10.0;
+ name = "{= env.FLAG1_SYMBOL =}";
+ }
+ symbol {
+ weight = -1.0;
+ name = "{= env.FLAG2_SYMBOL =}";
+ }
+}
+
+worker {
+ type = normal
+ bind_socket = "{= env.LOCAL_ADDR =}:{= env.PORT_NORMAL =}";
+ count = 1
+ task_timeout = 60s;
+}
+
+worker {
+ type = controller
+ bind_socket = "{= env.LOCAL_ADDR =}:{= env.PORT_CONTROLLER =}";
+ count = 1
+ secure_ip = ["{= env.LOCAL_ADDR =}"];
+ stats_path = "{= env.TMPDIR =}/stats.ucl";
+}
+
+worker {
+ count = 1;
+ backend = "{= env.FUZZY_BACKEND =}";
+ bind_socket = "{= env.LOCAL_ADDR =}:{= env.PORT_FUZZY =}";
+ type = "fuzzy";
+ hashfile = "{= env.TMPDIR =}/fuzzy.db";
+ allow_update = ["{= env.LOCAL_ADDR =}"];
+ encrypted_only = {= env.FUZZY_ENCRYPTED_ONLY =};
+ keypair {
+ privkey = "{= env.KEY_PVT1 =}";
+ pubkey = "{= env.KEY_PUB1 =}";
+ }
+ dynamic_keys_map = "{= env.TESTDIR =}/configs/maps/fuzzy_keymap.map";
+}
+
+fuzzy_check {
+ min_bytes = 100;
+ timeout = 1s;
+ retransmits = 10;
+
+ rule {
+ min_bytes = 0;
+ min_length = 0;
+ algorithm = "{= env.FUZZY_ALGORITHM =}";
+ read_servers = "{= env.LOCAL_ADDR =}:{= env.PORT_FUZZY =}";
+ write_servers = "{= env.LOCAL_ADDR =}:{= env.PORT_FUZZY =}";
+ symbol = "R_TEST_FUZZY";
+ max_score = 10.0;
+ mime_types = ["application/*"];
+ read_only = false;
+ skip_unknown = true;
+ skip_hashes = "{= env.TMPDIR =}/skip_hash.map";
+ fuzzy_key = {= env.FUZZY_KEY =};
+ fuzzy_shingles_key = {= env.FUZZY_SHINGLES_KEY =};
+.include "{= env.FUZZY_INCLUDE =}";
+ fuzzy_map = {
+ R_TEST_FUZZY_DENIED {
+ max_score = 10.0;
+ flag = {= env.FLAG1_NUMBER =};
+ }
+ R_TEST_FUZZY_WHITE {
+ max_score = 1.0;
+ flag = {= env.FLAG2_NUMBER =};
+ }
+ }
+ }
+}
diff --git a/test/functional/configs/maps/advance_fee_rules.map b/test/functional/configs/maps/advance_fee_rules.map
new file mode 100644
index 000000000..d07b25c71
--- /dev/null
+++ b/test/functional/configs/maps/advance_fee_rules.map
@@ -0,0 +1,43 @@
+# SpamAssassin-style rules for advance fee fraud detection (simplified for testing)
+
+# Individual atom rules (these detect specific patterns)
+header __FRAUD_VQE Subject =~ /^(?:Re:|\[.{1,10}\])?\s*(?:very )?urgent\s+(?:(?:and|&)\s+)?(?:confidential|assistance|business|attention|reply|response|help)\b/i
+
+body __FRAUD_DBI /(?:\bdollars?\b|\busd(?:ollars)?(?:[0-9]|\b)|\bus\$|\$[0-9,.]{6,}|\$[0-9].{0,8}[mb]illion|\$[0-9.,]{2,10} ?m|\beuros?\b|u[.]?s[.]? [0-9.]+ m)/i
+body __FRAUD_KJV /(?:claim|concerning) (?:the|this) money/i
+body __FRAUD_NEB /(?:government|bank) of nigeria/i
+body __FRAUD_XJR /(?:who was a|as a|an? honest|you being a|to any) foreigner/i
+body __FRAUD_JBU /\bforeign account\b/i
+body __FRAUD_XVW /\bhonest cooperation\b/i
+body __FRAUD_LTX /\bmilli?on (?:.{1,25} thousand\s*)?(?:(?:united states|u\.?s\.?) dollars|(?i:U\.?S\.?D?))\b/i
+body __FRAUD_PVN /as the beneficiary/i
+body __FRAUD_MQO /foreign (?:business partner|customer)/i
+body __FRAUD_TCC /foreign (?:offshore )?(?:bank|account)/i
+body __FRAUD_GBW /god gives .{1,10}second chance/i
+body __FRAUD_NRG /i am contacting you/i
+body __FRAUD_YPO /the total sum/i
+body __FRAUD_UOQ /vital documents/i
+body __FRAUD_BEP /\b(?:bank of nigeria|central bank of|trust bank|apex bank|amalgamated bank)\b/i
+body __FRAUD_DPR /\b(?:(?:respond|reply) (?:urgently|immediately)|(?:urgent|immediate|earliest) (?:reply|response))\b/i
+body __FRAUD_QXX /\b(?:my name is|i am) (?:mrs?|engr|barrister|dr|prince(?:ss)?)[. ]/i
+body __FRAUD_PTS /\b(?:ass?ass?inat(?:ed|ion)|murder(?:e?d)?|kill(?:ed|ing)\b[^.]{0,99}\b(?:war veterans|rebels?))\b/i
+body __FRAUD_TDP /\b(?:business partner(?:s|ship)?|silent partner(?:s|ship)?)\b/i
+body __FRAUD_AON /\b(?:confidential|private|alternate|alternative) (?:(?:e-? *)?mail)\b/i
+body __FRAUD_YWW /\bfurnish you with\b/i
+body __FRAUD_ULK /\baffidavits?\b/i
+body __FRAUD_IOU /\b(?:no risks?|risk-? *free|free of risks?|100% safe)\b/i
+body __FRAUD_IRT /\b(?:compliments? of the|dear friend|dear sir|yours faithfully|season'?s greetings)\b/i
+body __FRAUD_ETX /\byour\b[^.]{0,99}\b(?:contact (?:details|information)|private (?:e?[- ]?mail|telephone|tel|phone|fax))\b/i
+body __FRAUD_WDR /\bprivate lawyer\b/i
+
+# Meta rules that combine multiple atoms
+meta ADVANCE_FEE_2 (__FRAUD_KJV + __FRAUD_NEB + __FRAUD_XJR + __FRAUD_JBU + __FRAUD_XVW + __FRAUD_LTX + __FRAUD_PVN + __FRAUD_MQO + __FRAUD_TCC + __FRAUD_GBW + __FRAUD_NRG + __FRAUD_YPO + __FRAUD_UOQ + __FRAUD_DBI + __FRAUD_BEP + __FRAUD_DPR + __FRAUD_QXX + __FRAUD_PTS + __FRAUD_TDP + __FRAUD_AON + __FRAUD_YWW + __FRAUD_ULK + __FRAUD_IOU + __FRAUD_IRT + __FRAUD_ETX + __FRAUD_WDR > 2)
+
+meta ADVANCE_FEE_3 (__FRAUD_KJV + __FRAUD_NEB + __FRAUD_XJR + __FRAUD_JBU + __FRAUD_XVW + __FRAUD_LTX + __FRAUD_PVN + __FRAUD_MQO + __FRAUD_TCC + __FRAUD_GBW + __FRAUD_NRG + __FRAUD_YPO + __FRAUD_UOQ + __FRAUD_DBI + __FRAUD_BEP + __FRAUD_DPR + __FRAUD_QXX + __FRAUD_PTS + __FRAUD_TDP + __FRAUD_AON + __FRAUD_YWW + __FRAUD_ULK + __FRAUD_IOU + __FRAUD_IRT + __FRAUD_ETX + __FRAUD_WDR > 3)
+
+# Descriptions for the rules
+describe ADVANCE_FEE_2 Appears to be advance fee fraud (Nigerian 419) - moderate confidence
+describe ADVANCE_FEE_3 Appears to be advance fee fraud (Nigerian 419) - high confidence
+
+score ADVANCE_FEE_2 4.0
+score ADVANCE_FEE_3 5.0 \ No newline at end of file
diff --git a/test/functional/configs/milter_headers.conf b/test/functional/configs/milter_headers.conf
index 947bc28dd..502747605 100644
--- a/test/functional/configs/milter_headers.conf
+++ b/test/functional/configs/milter_headers.conf
@@ -22,3 +22,15 @@ milter_headers {
}
}
+
+settings {
+ id_milter_headers_override {
+ apply {
+ plugins {
+ milter_headers {
+ routines = [ authentication-results, x-spam-level ];
+ }
+ }
+ }
+ }
+}
diff --git a/test/functional/configs/multiclass_bayes.conf b/test/functional/configs/multiclass_bayes.conf
new file mode 100644
index 000000000..278aeeee9
--- /dev/null
+++ b/test/functional/configs/multiclass_bayes.conf
@@ -0,0 +1,129 @@
+options = {
+ filters = ["spf", "dkim", "regexp"]
+ url_tld = "{= env.TESTDIR =}/../lua/unit/test_tld.dat"
+ pidfile = "{= env.TMPDIR =}/rspamd.pid"
+ dns {
+ retransmits = 10;
+ timeout = 2s;
+ fake_records = [{
+ name = "example.net";
+ type = txt;
+ replies = ["v=spf1 -all"];
+ }]
+ }
+}
+
+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
+ keypair {
+ pubkey = "{= env.KEY_PUB1 =}";
+ privkey = "{= env.KEY_PVT1 =}";
+ }
+ task_timeout = 60s;
+}
+
+worker {
+ type = controller
+ bind_socket = "{= env.LOCAL_ADDR =}:{= env.PORT_CONTROLLER =}"
+ count = 1
+ keypair {
+ pubkey = "{= env.KEY_PUB1 =}";
+ privkey = "{= env.KEY_PVT1 =}";
+ }
+ secure_ip = ["127.0.0.1", "::1"];
+ stats_path = "{= env.TMPDIR =}/stats.ucl";
+}
+
+# Multi-class Bayes classifier configuration
+classifier {
+ languages_enabled = true;
+ tokenizer {
+ name = "osb";
+ hash = {= env.STATS_HASH =};
+ key = {= env.STATS_KEY =};
+ }
+ backend = "{= env.STATS_BACKEND =}";
+
+ # Multi-class statfiles
+ statfile {
+ class = "spam";
+ symbol = BAYES_SPAM;
+ server = {= env.REDIS_SERVER =}
+ }
+ statfile {
+ class = "ham";
+ symbol = BAYES_HAM;
+ server = {= env.REDIS_SERVER =}
+ }
+ statfile {
+ class = "newsletter";
+ symbol = BAYES_NEWSLETTER;
+ server = {= env.REDIS_SERVER =}
+ }
+
+ # Backend class labels for Redis
+ class_labels = {
+ "spam" = "S";
+ "ham" = "H";
+ "newsletter" = "N";
+ }
+
+ cache {
+ server = {= env.REDIS_SERVER =}
+ }
+
+ # Multi-class autolearn configuration
+ autolearn = {
+ classes = {
+ spam = {
+ threshold = 15.0;
+ verdict_mapping = { spam = true };
+ };
+ ham = {
+ threshold = -5.0;
+ verdict_mapping = { ham = true };
+ };
+ newsletter = {
+ symbols = ["NEWSLETTER_HEADER", "BULK_MAIL", "UNSUBSCRIBE_LINK"];
+ threshold = 8.0;
+ };
+ };
+
+ check_balance = true;
+ max_class_ratio = 0.6;
+ skip_threshold = 0.95;
+ }
+
+ min_learns = 1;
+ min_tokens = 1;
+ min_token_hits = 1;
+ min_prob_strength = 0.05;
+
+ {% if env.STATS_PER_USER ~= '' %}
+ per_user = <<EOD
+return function(task)
+ return task:get_principal_recipient()
+end
+EOD;
+ {% endif %}
+}
+
+lua = "{= env.TESTDIR =}/lua/test_coverage.lua";
+
+settings {}
diff --git a/test/functional/configs/regexp_maps.conf b/test/functional/configs/regexp_maps.conf
new file mode 100644
index 000000000..be4cd047b
--- /dev/null
+++ b/test/functional/configs/regexp_maps.conf
@@ -0,0 +1,51 @@
+.include(duplicate=append,priority=0) "{= env.TESTDIR =}/configs/plugins.conf"
+
+# Configure multimap for regexp rules testing
+multimap {
+ ADVANCE_FEE_SA_RULES {
+ type = "regexp_rules";
+ map = "{= env.TESTDIR =}/configs/maps/advance_fee_rules.map";
+ scope = "advance_fee_scope";
+ description = "Advance fee fraud detection rules";
+ }
+}
+
+# Override symbol scores for testing
+symbols {
+ ADVANCE_FEE_2 {
+ score = 4.0;
+ description = "Advance fee fraud pattern (medium confidence)";
+ group = "scam";
+ }
+ ADVANCE_FEE_3 {
+ score = 5.0;
+ description = "Advance fee fraud pattern (high confidence)";
+ group = "scam";
+ }
+}
+
+# Set required score for testing
+actions {
+ reject = 10;
+ add_header = 6;
+ greylist = 4;
+}
+
+# Enable necessary modules
+dmarc { }
+spf { }
+dkim { }
+
+# Module path for multimap
+modules {
+ path = "{= env.TESTDIR =}/../../src/plugins/lua/"
+}
+
+# Disable some modules that might interfere with testing
+rbl {
+ enabled = false;
+}
+
+fuzzy_check {
+ enabled = false;
+}
diff --git a/test/functional/lib/rspamd.robot b/test/functional/lib/rspamd.robot
index 9c30a97db..f61998f46 100644
--- a/test/functional/lib/rspamd.robot
+++ b/test/functional/lib/rspamd.robot
@@ -120,6 +120,15 @@ Expect Added Header
Should Be Equal ${SCAN_RESULT}[milter][add_headers][${header_name}][value] ${header_value}
Should Be Equal as Numbers ${SCAN_RESULT}[milter][add_headers][${header_name}][order] ${pos}
+Expect Header Is Present
+ [Arguments] ${header_name}
+ Dictionary Should Contain Key ${SCAN_RESULT} milter
+ ... msg=milter block was not present in protocol response
+ Dictionary Should Contain Key ${SCAN_RESULT}[milter] add_headers
+ ... msg=add_headers block was not present in protocol response
+ Dictionary Should Contain Key ${SCAN_RESULT}[milter][add_headers] ${header_name}
+ ... msg=${header_name} was not added
+
Expect Email
[Arguments] ${email}
List Should Contain Value ${SCAN_RESULT}[emails] ${email}
@@ -410,10 +419,23 @@ Run Nginx
${nginx_log} = Get File ${RSPAMD_TMPDIR}/nginx.log
Log ${nginx_log}
+Set Test Hash Documentation
+ ${log_tag} = Evaluate __import__('hashlib').md5('${TEST NAME}'.encode()).hexdigest()[:8]
+ Log TEST CONTEXT: [${log_tag}] ${TEST NAME} console=True
+
Run Rspamc
[Arguments] @{args}
- ${result} = Run Process ${RSPAMC} -t 60 --header Queue-ID\=${TEST NAME}
- ... @{args} env:LD_LIBRARY_PATH=${RSPAMD_TESTDIR}/../../contrib/aho-corasick
+ ${log_tag} = Evaluate __import__('hashlib').md5('${TEST NAME}'.encode()).hexdigest()[:8]
+ # Check if --queue-id is already provided in the arguments
+ ${args_str} = Evaluate ' '.join(@{args})
+ ${has_queue_id} = Evaluate '--queue-id' in '${args_str}'
+ IF ${has_queue_id}
+ ${result} = Run Process ${RSPAMC} -t 60 --log-tag ${log_tag}
+ ... @{args} env:LD_LIBRARY_PATH=${RSPAMD_TESTDIR}/../../contrib/aho-corasick
+ ELSE
+ ${result} = Run Process ${RSPAMC} -t 60 --queue-id ${TEST NAME} --log-tag ${log_tag}
+ ... @{args} env:LD_LIBRARY_PATH=${RSPAMD_TESTDIR}/../../contrib/aho-corasick
+ END
Log ${result.stdout}
[Return] ${result}
diff --git a/test/functional/messages/advance_fee_fraud.eml b/test/functional/messages/advance_fee_fraud.eml
new file mode 100644
index 000000000..d9b468b6a
--- /dev/null
+++ b/test/functional/messages/advance_fee_fraud.eml
@@ -0,0 +1,37 @@
+From: Prince John Doe <prince@nigeria-bank.com>
+To: recipient@example.com
+Subject: URGENT BUSINESS ASSISTANCE NEEDED
+Date: Thu, 26 Oct 2023 10:30:00 +0000
+Message-ID: <advance-fee-test@example.com>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+
+Dear Friend,
+
+Compliments of the season. I am contacting you with utmost confidence
+regarding this money transfer opportunity that will be of mutual benefit
+to both of us.
+
+My name is Prince John Doe, director of the Central Bank of Nigeria.
+I am writing to you concerning the total sum of Twenty-Five Million
+United States Dollars ($25,000,000 USD) that belongs to a foreigner
+who died in a plane crash.
+
+As the beneficiary of this vast fortune, I need your honest cooperation
+to claim this money. The Government of Nigeria has approved this transfer
+to a foreign account, and you have been selected as a business partner
+to assist in this confidential transaction.
+
+This is a risk-free business proposal, and I can furnish you with
+all vital documents to prove the legitimacy of this transaction.
+Your private email and contact details are needed to proceed immediately.
+
+Please respond urgently through my private lawyer, as this requires
+your immediate attention. God gives everyone a second chance, and this
+is yours.
+
+Yours faithfully,
+Prince John Doe
+Director, Bank of Nigeria
+
+Note: This is 100% safe and no risk is involved.
diff --git a/test/functional/messages/gargantua.eml b/test/functional/messages/gargantua.eml
index c6edc50f3..acb3d367f 100644
--- a/test/functional/messages/gargantua.eml
+++ b/test/functional/messages/gargantua.eml
@@ -23502,4 +23502,58 @@ X-Real-Type: csv
ImZvbyIsImJhciIsImJheiIKIjEiLCIyIiwiMyIK
+--XXX
+Content-Type: image/heic
+Content-Transfer-Encoding: base64
+X-Real-Type: heic
+
+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
+0YCJBxFcC867msO9wip2vP786vlLeC/fqKwSng==
+
--XXX--
diff --git a/test/functional/messages/newsletter.eml b/test/functional/messages/newsletter.eml
new file mode 100644
index 000000000..93c996956
--- /dev/null
+++ b/test/functional/messages/newsletter.eml
@@ -0,0 +1,50 @@
+From: "Marketing Team" <newsletter@example.com>
+To: user@example.org
+Subject: 🎉 Monthly Newsletter - Exclusive Deals & Product Updates!
+Date: Thu, 21 Jul 2023 10:00:00 +0000
+Message-ID: <newsletter-123@example.com>
+MIME-Version: 1.0
+Content-Type: text/html; charset=utf-8
+List-Unsubscribe: <https://example.com/unsubscribe?id=123>
+Precedence: bulk
+X-Mailer: MailChimp/Pro 12.345
+
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Monthly Newsletter</title>
+</head>
+<body>
+ <h1>🎉 Exclusive Monthly Offers!</h1>
+
+ <p>Dear Valued Subscriber,</p>
+
+ <p>This month we're excited to bring you our <strong>BIGGEST SALE</strong> of the year!</p>
+
+ <h2>🔥 Hot Deals This Month:</h2>
+ <ul>
+ <li>50% OFF all premium products</li>
+ <li>FREE shipping on orders over $50</li>
+ <li>Buy 2 Get 1 FREE on selected items</li>
+ </ul>
+
+ <p><a href="https://example.com/shop?utm_source=newsletter&utm_campaign=monthly">SHOP NOW</a></p>
+
+ <h2>📱 New Product Launch</h2>
+ <p>Check out our revolutionary new gadget that everyone is talking about!</p>
+
+ <h2>🎁 Refer a Friend</h2>
+ <p>Share this newsletter and both you and your friend get $10 credit!</p>
+
+ <hr>
+
+ <p><small>
+ You're receiving this because you subscribed to our newsletter.<br>
+ <a href="https://example.com/unsubscribe?id=123">Unsubscribe here</a> |
+ <a href="https://example.com/preferences">Update preferences</a><br>
+ Marketing Team, Example Corp<br>
+ 123 Business St, City, State 12345
+ </small></p>
+</body>
+</html> \ No newline at end of file
diff --git a/test/functional/messages/transactional.eml b/test/functional/messages/transactional.eml
new file mode 100644
index 000000000..e227aaa77
--- /dev/null
+++ b/test/functional/messages/transactional.eml
@@ -0,0 +1,18 @@
+From: noreply@example.com
+To: user@example.org
+Subject: Password Reset Request
+Date: Thu, 21 Jul 2023 11:00:00 +0000
+Message-ID: <pwd-reset-456@example.com>
+MIME-Version: 1.0
+Content-Type: text/plain
+
+Hello,
+
+You have requested a password reset for your account.
+
+Click here to reset your password: https://example.com/reset?token=abc123
+
+This link expires in 24 hours.
+
+Best regards,
+Security Team \ No newline at end of file