package rules import ( "context" "strings" "testing" ) func testMessage() *Message { return &Message{ ID: "msg-1", From: "Alice ", To: []string{"bob@example.com", "carol@example.com"}, Subject: "Invoice Q1", BodyText: "Please review the attached invoice.", HasAttachments: true, } } func TestMatchCondition_fieldsAndOperators(t *testing.T) { msg := testMessage() tests := []struct { name string cond Condition match bool }{ {"from contains", Condition{Field: "from", Operator: "contains", Value: "alice@"}, true}, {"from equals case insensitive", Condition{Field: "from", Operator: "equals", Value: "alice "}, true}, {"to contains", Condition{Field: "to", Operator: "contains", Value: "carol@"}, true}, {"subject starts_with", Condition{Field: "subject", Operator: "starts_with", Value: "invoice"}, true}, {"body ends_with", Condition{Field: "body", Operator: "ends_with", Value: "invoice."}, true}, {"has_attachment true", Condition{Field: "has_attachment", Operator: "equals", Value: "true"}, true}, {"has_attachment false", Condition{Field: "has_attachment", Operator: "equals", Value: "false"}, false}, {"not_contains", Condition{Field: "subject", Operator: "not_contains", Value: "spam"}, true}, {"unknown field", Condition{Field: "unknown", Operator: "contains", Value: "x"}, false}, {"unknown operator", Condition{Field: "subject", Operator: "matches", Value: "Invoice"}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := matchCondition(tt.cond, msg); got != tt.match { t.Fatalf("matchCondition() = %v, want %v", got, tt.match) } }) } } func TestMatchesAll(t *testing.T) { msg := testMessage() t.Run("all match", func(t *testing.T) { conditions := []Condition{ {Field: "from", Operator: "contains", Value: "alice"}, {Field: "subject", Operator: "contains", Value: "invoice"}, } if !matchesAll(conditions, msg) { t.Fatal("matchesAll() = false, want true") } }) t.Run("one fails", func(t *testing.T) { conditions := []Condition{ {Field: "from", Operator: "contains", Value: "alice"}, {Field: "subject", Operator: "equals", Value: "other"}, } if matchesAll(conditions, msg) { t.Fatal("matchesAll() = true, want false") } }) t.Run("empty conditions", func(t *testing.T) { if !matchesAll(nil, msg) { t.Fatal("matchesAll(nil) = false, want true") } }) } func TestExecuteAction_unknownType(t *testing.T) { e := &Engine{} err := e.executeAction(context.Background(), Action{Type: "forward", Value: "x@example.com"}, &Message{ID: "msg-1"}) if err == nil { t.Fatal("executeAction() error = nil, want unknown action type error") } if !strings.Contains(err.Error(), "unknown action type: forward") { t.Fatalf("executeAction() error = %v, want unknown action type", err) } }