108 lines
3.9 KiB
Go
108 lines
3.9 KiB
Go
package mailstore
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"mailgoserver/internal/db"
|
||
|
|
)
|
||
|
|
|
||
|
|
// TestApplyRulesMultiConditionAnd confirms an "all" (AND) rule only matches when
|
||
|
|
// every condition matches.
|
||
|
|
func TestApplyRulesMultiConditionAnd(t *testing.T) {
|
||
|
|
s, mailboxID := newTestMailbox(t, 1024*1024)
|
||
|
|
conditions := []db.RuleCondition{
|
||
|
|
{Field: "to", Op: "contains", Value: "sales"},
|
||
|
|
{Field: "subject", Op: "contains", Value: "invoice"},
|
||
|
|
}
|
||
|
|
if _, err := s.DB.CreateRuleMulti(mailboxID, 0, conditions, "all", "move_to_folder", "Invoices"); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Matches "to" only — AND rule should not fire.
|
||
|
|
action, err := s.ApplyRules(mailboxID, map[string]string{"to": "sales@example.com", "subject": "hello"})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if action.Folder != "" {
|
||
|
|
t.Fatalf("expected no match with only one AND condition satisfied, got folder=%q", action.Folder)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Matches both — AND rule should fire.
|
||
|
|
action, err = s.ApplyRules(mailboxID, map[string]string{"to": "sales@example.com", "subject": "your invoice"})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if action.Folder != "Invoices" {
|
||
|
|
t.Fatalf("expected move to Invoices when both AND conditions match, got %+v", action)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// TestApplyRulesMultiConditionOr confirms an "any" (OR) rule matches when at least
|
||
|
|
// one condition matches.
|
||
|
|
func TestApplyRulesMultiConditionOr(t *testing.T) {
|
||
|
|
s, mailboxID := newTestMailbox(t, 1024*1024)
|
||
|
|
conditions := []db.RuleCondition{
|
||
|
|
{Field: "from", Op: "contains", Value: "boss@work.example"},
|
||
|
|
{Field: "subject", Op: "contains", Value: "urgent"},
|
||
|
|
}
|
||
|
|
if _, err := s.DB.CreateRuleMulti(mailboxID, 0, conditions, "any", "mark_read", ""); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
action, err := s.ApplyRules(mailboxID, map[string]string{"from": "nobody@example.com", "subject": "urgent: read me"})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if !action.MarkRead {
|
||
|
|
t.Fatalf("expected OR rule to fire on subject match alone, got %+v", action)
|
||
|
|
}
|
||
|
|
|
||
|
|
action, err = s.ApplyRules(mailboxID, map[string]string{"from": "nobody@example.com", "subject": "hello"})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if action.MarkRead {
|
||
|
|
t.Fatalf("expected OR rule not to fire when neither condition matches, got %+v", action)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// TestApplyRulesMarkAsSpam confirms the mark_as_spam action routes into the Spam
|
||
|
|
// folder, same as score-based quarantine.
|
||
|
|
func TestApplyRulesMarkAsSpam(t *testing.T) {
|
||
|
|
s, mailboxID := newTestMailbox(t, 1024*1024)
|
||
|
|
conditions := []db.RuleCondition{{Field: "subject", Op: "contains", Value: "viagra"}}
|
||
|
|
if _, err := s.DB.CreateRuleMulti(mailboxID, 0, conditions, "all", "mark_as_spam", ""); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
action, err := s.ApplyRules(mailboxID, map[string]string{"subject": "cheap viagra now"})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if action.Folder != "Spam" {
|
||
|
|
t.Fatalf("expected mark_as_spam to route into the Spam folder, got %+v", action)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// TestApplyRulesLegacySingleConditionFallback confirms a rule row with an empty
|
||
|
|
// ConditionsJSON (as any rule created before multi-condition support existed would
|
||
|
|
// have) still evaluates correctly via the legacy condition_field/op/value columns.
|
||
|
|
func TestApplyRulesLegacySingleConditionFallback(t *testing.T) {
|
||
|
|
s, mailboxID := newTestMailbox(t, 1024*1024)
|
||
|
|
// CreateRule (not CreateRuleMulti) still writes conditions_json today, so to
|
||
|
|
// simulate genuinely old pre-migration data we insert directly with an empty
|
||
|
|
// conditions_json, exactly as an old row would look on disk.
|
||
|
|
if _, err := s.DB.Exec(`INSERT INTO esrv_mailbox_filter_rules (mailbox_id, priority, condition_field, condition_op, condition_value, action, action_value)
|
||
|
|
VALUES (?, 0, 'subject', 'contains', 'newsletter', 'delete', '')`, mailboxID); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
action, err := s.ApplyRules(mailboxID, map[string]string{"subject": "weekly newsletter"})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if !action.Drop {
|
||
|
|
t.Fatalf("expected the legacy single-condition rule to still match, got %+v", action)
|
||
|
|
}
|
||
|
|
}
|