package automation import ( "context" "testing" ) func TestAutomationRuleService_Create(t *testing.T) { dbProvider := setupAutomationTestDBProvider(t) accountID, _ := seedTestAccount(dbProvider.DB(), t) svc := NewAutomationRuleService(dbProvider) rule := &AutomationRule{ AccountID: accountID, EventName: "conversation_created", Name: "Auto-assign on create", Description: "Assigns new conversations to team", Conditions: Conditions{ {Attribute: "status", FilterOperator: "equal", Values: []string{"open"}, QueryOperator: "and"}, }, Actions: Actions{ {ActionName: "assign_team", ActionParams: map[string]interface{}{"team_id": "1"}}, }, Active: true, } err := svc.Create(context.Background(), rule) if err != nil { t.Fatalf("expected no error creating rule, got: %v", err) } if rule.ID == 0 { t.Fatal("expected rule ID to be set after creation") } } func TestAutomationRuleService_GetByID(t *testing.T) { dbProvider := setupAutomationTestDBProvider(t) accountID, _ := seedTestAccount(dbProvider.DB(), t) svc := NewAutomationRuleService(dbProvider) rule := &AutomationRule{ AccountID: accountID, EventName: "message_created", Name: "Auto-label spam", Conditions: Conditions{ {Attribute: "content", FilterOperator: "contains", Values: []string{"spam"}, QueryOperator: "and"}, }, Actions: Actions{ {ActionName: "add_label", ActionParams: map[string]interface{}{"labels": []string{"spam"}}}, }, Active: true, } if err := svc.Create(context.Background(), rule); err != nil { t.Fatalf("setup: failed to create rule: %v", err) } found, err := svc.GetByID(context.Background(), rule.ID) if err != nil { t.Fatalf("expected no error getting rule, got: %v", err) } if found.ID != rule.ID { t.Fatalf("expected rule ID %d, got %d", rule.ID, found.ID) } if found.Name != "Auto-label spam" { t.Fatalf("expected name 'Auto-label spam', got %q", found.Name) } } func TestAutomationRuleService_GetByID_NotFound(t *testing.T) { dbProvider := setupAutomationTestDBProvider(t) svc := NewAutomationRuleService(dbProvider) _, err := svc.GetByID(context.Background(), 9999) if err == nil { t.Fatal("expected error for non-existent rule, got nil") } } func TestAutomationRuleService_Update(t *testing.T) { dbProvider := setupAutomationTestDBProvider(t) accountID, _ := seedTestAccount(dbProvider.DB(), t) svc := NewAutomationRuleService(dbProvider) rule := &AutomationRule{ AccountID: accountID, EventName: "conversation_created", Name: "Original name", Conditions: Conditions{}, Actions: Actions{}, Active: true, } if err := svc.Create(context.Background(), rule); err != nil { t.Fatalf("setup: failed to create rule: %v", err) } rule.Name = "Updated name" rule.Active = false err := svc.Update(context.Background(), rule) if err != nil { t.Fatalf("expected no error updating rule, got: %v", err) } found, _ := svc.GetByID(context.Background(), rule.ID) if found.Name != "Updated name" { t.Fatalf("expected name 'Updated name', got %q", found.Name) } if found.Active != false { t.Fatal("expected active to be false after update") } } func TestAutomationRuleService_Delete(t *testing.T) { dbProvider := setupAutomationTestDBProvider(t) accountID, _ := seedTestAccount(dbProvider.DB(), t) svc := NewAutomationRuleService(dbProvider) rule := &AutomationRule{ AccountID: accountID, EventName: "conversation_created", Name: "Delete me", Conditions: Conditions{}, Actions: Actions{}, Active: true, } if err := svc.Create(context.Background(), rule); err != nil { t.Fatalf("setup: failed to create rule: %v", err) } err := svc.Delete(context.Background(), rule.ID) if err != nil { t.Fatalf("expected no error deleting rule, got: %v", err) } _, err = svc.GetByID(context.Background(), rule.ID) if err == nil { t.Fatal("expected error getting deleted rule, got nil") } } func TestAutomationRuleService_ListByAccount(t *testing.T) { dbProvider := setupAutomationTestDBProvider(t) accountID, _ := seedTestAccount(dbProvider.DB(), t) svc := NewAutomationRuleService(dbProvider) // Create 3 rules for i := 0; i < 3; i++ { rule := &AutomationRule{ AccountID: accountID, EventName: "conversation_created", Name: "Rule " + string(rune('A'+i)), Conditions: Conditions{}, Actions: Actions{}, Active: true, } if err := svc.Create(context.Background(), rule); err != nil { t.Fatalf("setup: failed to create rule %d: %v", i, err) } } rules, err := svc.ListByAccount(context.Background(), accountID) if err != nil { t.Fatalf("expected no error listing rules, got: %v", err) } if len(rules) != 3 { t.Fatalf("expected 3 rules, got %d", len(rules)) } } func TestAutomationRuleService_ListActiveByAccountAndEvent(t *testing.T) { dbProvider := setupAutomationTestDBProvider(t) accountID, _ := seedTestAccount(dbProvider.DB(), t) svc := NewAutomationRuleService(dbProvider) // Create active rule for conversation_created rule1 := &AutomationRule{ AccountID: accountID, EventName: "conversation_created", Name: "Active rule", Conditions: Conditions{}, Actions: Actions{}, Active: true, } if err := svc.Create(context.Background(), rule1); err != nil { t.Fatalf("setup: %v", err) } // Create inactive rule for conversation_created rule2 := &AutomationRule{ AccountID: accountID, EventName: "conversation_created", Name: "Inactive rule", Conditions: Conditions{}, Actions: Actions{}, Active: false, } if err := svc.Create(context.Background(), rule2); err != nil { t.Fatalf("setup: %v", err) } // Create active rule for different event rule3 := &AutomationRule{ AccountID: accountID, EventName: "message_created", Name: "Different event", Conditions: Conditions{}, Actions: Actions{}, Active: true, } if err := svc.Create(context.Background(), rule3); err != nil { t.Fatalf("setup: %v", err) } rules, err := svc.ListActiveByAccountAndEvent(context.Background(), accountID, "conversation_created") if err != nil { t.Fatalf("expected no error, got: %v", err) } if len(rules) != 1 { t.Fatalf("expected 1 active rule for conversation_created, got %d", len(rules)) } if rules[0].Name != "Active rule" { t.Fatalf("expected 'Active rule', got %q", rules[0].Name) } } func TestAutomationRuleService_ToggleActive(t *testing.T) { dbProvider := setupAutomationTestDBProvider(t) accountID, _ := seedTestAccount(dbProvider.DB(), t) svc := NewAutomationRuleService(dbProvider) rule := &AutomationRule{ AccountID: accountID, EventName: "conversation_created", Name: "Toggle test", Conditions: Conditions{}, Actions: Actions{}, Active: true, } if err := svc.Create(context.Background(), rule); err != nil { t.Fatalf("setup: %v", err) } // Toggle to inactive err := svc.ToggleActive(context.Background(), rule.ID, false) if err != nil { t.Fatalf("expected no error toggling active, got: %v", err) } found, _ := svc.GetByID(context.Background(), rule.ID) if found.Active { t.Fatal("expected active=false after toggle") } // Toggle back to active err = svc.ToggleActive(context.Background(), rule.ID, true) if err != nil { t.Fatalf("expected no error toggling active back, got: %v", err) } found, _ = svc.GetByID(context.Background(), rule.ID) if !found.Active { t.Fatal("expected active=true after toggle back") } } func TestAutomationRuleService_Clone(t *testing.T) { dbProvider := setupAutomationTestDBProvider(t) accountID, _ := seedTestAccount(dbProvider.DB(), t) svc := NewAutomationRuleService(dbProvider) rule := &AutomationRule{ AccountID: accountID, EventName: "conversation_created", Name: "Source rule", Conditions: Conditions{ {Attribute: "status", FilterOperator: "equal", Values: []string{"open"}, QueryOperator: "and"}, }, Actions: Actions{ {ActionName: "assign_team", ActionParams: map[string]interface{}{"team_id": "1"}}, }, Active: true, } if err := svc.Create(context.Background(), rule); err != nil { t.Fatalf("setup: %v", err) } cloned, err := svc.Clone(context.Background(), rule.ID) if err != nil { t.Fatalf("expected no error cloning rule, got: %v", err) } if cloned.ID == rule.ID { t.Fatal("expected cloned rule to have different ID") } if cloned.Name != "Source rule (copy)" { t.Fatalf("expected cloned name 'Source rule (copy)', got %q", cloned.Name) } if len(cloned.Conditions) != 1 { t.Fatalf("expected 1 cloned condition, got %d", len(cloned.Conditions)) } if len(cloned.Actions) != 1 { t.Fatalf("expected 1 cloned action, got %d", len(cloned.Actions)) } } func TestAutomationRuleService_Clone_NotFound(t *testing.T) { dbProvider := setupAutomationTestDBProvider(t) svc := NewAutomationRuleService(dbProvider) _, err := svc.Clone(context.Background(), 9999) if err == nil { t.Fatal("expected error cloning non-existent rule, got nil") } }