577 lines
19 KiB
Go
577 lines
19 KiB
Go
package automation
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/gochat/gochat/internal/model"
|
|
)
|
|
|
|
// ===========================
|
|
// Macro Clone tests
|
|
// ===========================
|
|
|
|
func TestMacroService_Clone(t *testing.T) {
|
|
dbProvider := setupAutomationTestDBProvider(t)
|
|
accountID, userID := seedTestAccount(dbProvider.DB(), t)
|
|
macroSvc := NewMacroService(dbProvider)
|
|
|
|
// Create original macro
|
|
original := &Macro{
|
|
AccountID: accountID,
|
|
Name: "Close Ticket",
|
|
Actions: Actions{
|
|
{ActionName: "change_status", ActionParams: map[string]interface{}{"status": "resolved"}},
|
|
{ActionName: "add_label", ActionParams: map[string]interface{}{"label": "closed"}},
|
|
},
|
|
Visibility: MacroVisibilityGlobal,
|
|
Active: true,
|
|
CreatedByID: userID,
|
|
UpdatedByID: userID,
|
|
}
|
|
if err := macroSvc.Create(context.Background(), original); err != nil {
|
|
t.Fatalf("failed to create original macro: %v", err)
|
|
}
|
|
|
|
// Clone the macro
|
|
cloned, err := macroSvc.Clone(context.Background(), original.ID)
|
|
if err != nil {
|
|
t.Fatalf("failed to clone macro: %v", err)
|
|
}
|
|
|
|
// Verify clone properties
|
|
if cloned.ID == original.ID {
|
|
t.Fatalf("cloned macro should have a different ID")
|
|
}
|
|
if cloned.Name != "Close Ticket (copy)" {
|
|
t.Fatalf("expected name 'Close Ticket (copy)', got '%s'", cloned.Name)
|
|
}
|
|
if cloned.AccountID != original.AccountID {
|
|
t.Fatalf("expected account_id %d, got %d", original.AccountID, cloned.AccountID)
|
|
}
|
|
if cloned.Visibility != original.Visibility {
|
|
t.Fatalf("expected visibility %d, got %d", original.Visibility, cloned.Visibility)
|
|
}
|
|
if len(cloned.Actions) != len(original.Actions) {
|
|
t.Fatalf("expected %d actions, got %d", len(original.Actions), len(cloned.Actions))
|
|
}
|
|
if cloned.Actions[0].ActionName != "change_status" {
|
|
t.Fatalf("expected first action 'change_status', got '%s'", cloned.Actions[0].ActionName)
|
|
}
|
|
}
|
|
|
|
func TestMacroService_Clone_NotFound(t *testing.T) {
|
|
dbProvider := setupAutomationTestDBProvider(t)
|
|
macroSvc := NewMacroService(dbProvider)
|
|
|
|
_, err := macroSvc.Clone(context.Background(), 9999)
|
|
if err == nil {
|
|
t.Fatalf("expected error for non-existent macro, got nil")
|
|
}
|
|
}
|
|
|
|
// ===========================
|
|
// Macro ToggleActive tests
|
|
// ===========================
|
|
|
|
func TestMacroService_ToggleActive_Deactivate(t *testing.T) {
|
|
dbProvider := setupAutomationTestDBProvider(t)
|
|
accountID, userID := seedTestAccount(dbProvider.DB(), t)
|
|
macroSvc := NewMacroService(dbProvider)
|
|
|
|
// Create active macro
|
|
macro := &Macro{
|
|
AccountID: accountID,
|
|
Name: "Active Macro",
|
|
Actions: Actions{{ActionName: "change_status", ActionParams: map[string]interface{}{"status": "resolved"}}},
|
|
Visibility: MacroVisibilityGlobal,
|
|
Active: true,
|
|
CreatedByID: userID,
|
|
UpdatedByID: userID,
|
|
}
|
|
if err := macroSvc.Create(context.Background(), macro); err != nil {
|
|
t.Fatalf("failed to create macro: %v", err)
|
|
}
|
|
|
|
// Verify it starts active
|
|
found, err := macroSvc.GetByID(context.Background(), macro.ID)
|
|
if err != nil {
|
|
t.Fatalf("failed to get macro: %v", err)
|
|
}
|
|
if !found.Active {
|
|
t.Fatalf("expected macro to be active initially")
|
|
}
|
|
|
|
// Toggle to inactive
|
|
if err := macroSvc.ToggleActive(context.Background(), macro.ID, false); err != nil {
|
|
t.Fatalf("failed to toggle macro inactive: %v", err)
|
|
}
|
|
|
|
// Verify it's now inactive
|
|
found, err = macroSvc.GetByID(context.Background(), macro.ID)
|
|
if err != nil {
|
|
t.Fatalf("failed to get macro: %v", err)
|
|
}
|
|
if found.Active {
|
|
t.Fatalf("expected macro to be inactive after toggle, got active")
|
|
}
|
|
}
|
|
|
|
func TestMacroService_ToggleActive_Reactivate(t *testing.T) {
|
|
dbProvider := setupAutomationTestDBProvider(t)
|
|
accountID, userID := seedTestAccount(dbProvider.DB(), t)
|
|
macroSvc := NewMacroService(dbProvider)
|
|
|
|
// Create inactive macro
|
|
macro := &Macro{
|
|
AccountID: accountID,
|
|
Name: "Inactive Macro",
|
|
Actions: Actions{{ActionName: "add_label", ActionParams: map[string]interface{}{"label": "test"}}},
|
|
Visibility: MacroVisibilityPersonal,
|
|
Active: false,
|
|
CreatedByID: userID,
|
|
UpdatedByID: userID,
|
|
}
|
|
// Use Select to explicitly set Active=false (avoid GORM default override)
|
|
if err := dbProvider.DB().Select(
|
|
"AccountID", "Name", "Actions", "Visibility", "Active", "CreatedByID", "UpdatedByID",
|
|
).Create(macro).Error; err != nil {
|
|
t.Fatalf("failed to create inactive macro: %v", err)
|
|
}
|
|
|
|
// Verify it starts inactive
|
|
found, err := macroSvc.GetByID(context.Background(), macro.ID)
|
|
if err != nil {
|
|
t.Fatalf("failed to get macro: %v", err)
|
|
}
|
|
if found.Active {
|
|
t.Fatalf("expected macro to be inactive initially")
|
|
}
|
|
|
|
// Toggle to active
|
|
if err := macroSvc.ToggleActive(context.Background(), macro.ID, true); err != nil {
|
|
t.Fatalf("failed to toggle macro active: %v", err)
|
|
}
|
|
|
|
// Verify it's now active
|
|
found, err = macroSvc.GetByID(context.Background(), macro.ID)
|
|
if err != nil {
|
|
t.Fatalf("failed to get macro: %v", err)
|
|
}
|
|
if !found.Active {
|
|
t.Fatalf("expected macro to be active after toggle, got inactive")
|
|
}
|
|
}
|
|
|
|
// ===========================
|
|
// ExecutionLogService tests
|
|
// ===========================
|
|
|
|
func TestExecutionLogService_LogRuleExecution(t *testing.T) {
|
|
dbProvider := setupAutomationTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(dbProvider.DB(), t)
|
|
logSvc := NewExecutionLogService(dbProvider)
|
|
ctx := context.Background()
|
|
|
|
err := logSvc.LogRuleExecution(ctx, accountID, 1, 10, ExecutionStatusSuccess, 3, 0, "")
|
|
if err != nil {
|
|
t.Fatalf("failed to log rule execution: %v", err)
|
|
}
|
|
|
|
// Retrieve the log
|
|
logs, err := logSvc.ListRuleExecutions(ctx, accountID, 1, 10)
|
|
if err != nil {
|
|
t.Fatalf("failed to list rule executions: %v", err)
|
|
}
|
|
if len(logs) != 1 {
|
|
t.Fatalf("expected 1 log, got %d", len(logs))
|
|
}
|
|
if logs[0].Status != ExecutionStatusSuccess {
|
|
t.Fatalf("expected status '%s', got '%s'", ExecutionStatusSuccess, logs[0].Status)
|
|
}
|
|
if logs[0].ActionsExecuted != 3 {
|
|
t.Fatalf("expected 3 actions_executed, got %d", logs[0].ActionsExecuted)
|
|
}
|
|
if logs[0].ActionsFailed != 0 {
|
|
t.Fatalf("expected 0 actions_failed, got %d", logs[0].ActionsFailed)
|
|
}
|
|
}
|
|
|
|
func TestExecutionLogService_LogRuleExecution_Partial(t *testing.T) {
|
|
dbProvider := setupAutomationTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(dbProvider.DB(), t)
|
|
logSvc := NewExecutionLogService(dbProvider)
|
|
ctx := context.Background()
|
|
|
|
err := logSvc.LogRuleExecution(ctx, accountID, 2, 20, ExecutionStatusPartial, 2, 1, "action add_label failed: label not found")
|
|
if err != nil {
|
|
t.Fatalf("failed to log partial execution: %v", err)
|
|
}
|
|
|
|
logs, err := logSvc.ListRuleExecutions(ctx, accountID, 2, 10)
|
|
if err != nil {
|
|
t.Fatalf("failed to list rule executions: %v", err)
|
|
}
|
|
if len(logs) != 1 {
|
|
t.Fatalf("expected 1 log, got %d", len(logs))
|
|
}
|
|
if logs[0].Status != ExecutionStatusPartial {
|
|
t.Fatalf("expected status '%s', got '%s'", ExecutionStatusPartial, logs[0].Status)
|
|
}
|
|
if logs[0].ActionsFailed != 1 {
|
|
t.Fatalf("expected 1 actions_failed, got %d", logs[0].ActionsFailed)
|
|
}
|
|
if logs[0].ErrorMessage == "" {
|
|
t.Fatalf("expected non-empty error message")
|
|
}
|
|
}
|
|
|
|
func TestExecutionLogService_LogRuleExecution_Failed(t *testing.T) {
|
|
dbProvider := setupAutomationTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(dbProvider.DB(), t)
|
|
logSvc := NewExecutionLogService(dbProvider)
|
|
ctx := context.Background()
|
|
|
|
err := logSvc.LogRuleExecution(ctx, accountID, 3, 30, ExecutionStatusFailed, 0, 2, "all actions failed")
|
|
if err != nil {
|
|
t.Fatalf("failed to log failed execution: %v", err)
|
|
}
|
|
|
|
logs, err := logSvc.ListRuleExecutions(ctx, accountID, 3, 10)
|
|
if err != nil {
|
|
t.Fatalf("failed to list rule executions: %v", err)
|
|
}
|
|
if logs[0].Status != ExecutionStatusFailed {
|
|
t.Fatalf("expected status '%s', got '%s'", ExecutionStatusFailed, logs[0].Status)
|
|
}
|
|
}
|
|
|
|
func TestExecutionLogService_ListConversationExecutions(t *testing.T) {
|
|
dbProvider := setupAutomationTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(dbProvider.DB(), t)
|
|
logSvc := NewExecutionLogService(dbProvider)
|
|
ctx := context.Background()
|
|
|
|
// Log multiple executions for the same conversation
|
|
logSvc.LogRuleExecution(ctx, accountID, 1, 100, ExecutionStatusSuccess, 3, 0, "")
|
|
logSvc.LogRuleExecution(ctx, accountID, 2, 100, ExecutionStatusPartial, 2, 1, "some failed")
|
|
logSvc.LogRuleExecution(ctx, accountID, 1, 200, ExecutionStatusSuccess, 1, 0, "")
|
|
|
|
// List executions for conversation 100
|
|
logs, err := logSvc.ListConversationExecutions(ctx, accountID, 100, 10)
|
|
if err != nil {
|
|
t.Fatalf("failed to list conversation executions: %v", err)
|
|
}
|
|
if len(logs) != 2 {
|
|
t.Fatalf("expected 2 logs for conversation 100, got %d", len(logs))
|
|
}
|
|
|
|
// List executions for conversation 200
|
|
logs, err = logSvc.ListConversationExecutions(ctx, accountID, 200, 10)
|
|
if err != nil {
|
|
t.Fatalf("failed to list conversation executions: %v", err)
|
|
}
|
|
if len(logs) != 1 {
|
|
t.Fatalf("expected 1 log for conversation 200, got %d", len(logs))
|
|
}
|
|
}
|
|
|
|
func TestExecutionLogService_LogMacroExecution(t *testing.T) {
|
|
dbProvider := setupAutomationTestDBProvider(t)
|
|
accountID, userID := seedTestAccount(dbProvider.DB(), t)
|
|
logSvc := NewExecutionLogService(dbProvider)
|
|
ctx := context.Background()
|
|
|
|
// Create a macro first so the FK constraint works
|
|
macro := &Macro{
|
|
AccountID: accountID,
|
|
Name: "Test Macro",
|
|
Actions: Actions{{ActionName: "change_status", ActionParams: map[string]interface{}{"status": "resolved"}}},
|
|
Visibility: MacroVisibilityGlobal,
|
|
Active: true,
|
|
CreatedByID: userID,
|
|
UpdatedByID: userID,
|
|
}
|
|
if err := dbProvider.DB().Select(
|
|
"AccountID", "Name", "Actions", "Visibility", "Active", "CreatedByID", "UpdatedByID",
|
|
).Create(macro).Error; err != nil {
|
|
t.Fatalf("failed to create macro: %v", err)
|
|
}
|
|
|
|
err := logSvc.LogMacroExecution(ctx, macro.ID, 42, userID)
|
|
if err != nil {
|
|
t.Fatalf("failed to log macro execution: %v", err)
|
|
}
|
|
|
|
logs, err := logSvc.ListMacroExecutions(ctx, macro.ID, 10)
|
|
if err != nil {
|
|
t.Fatalf("failed to list macro executions: %v", err)
|
|
}
|
|
if len(logs) != 1 {
|
|
t.Fatalf("expected 1 log, got %d", len(logs))
|
|
}
|
|
if logs[0].ExecutedByID != userID {
|
|
t.Fatalf("expected executed_by_id %d, got %d", userID, logs[0].ExecutedByID)
|
|
}
|
|
}
|
|
|
|
func TestExecutionLogService_MultipleRuleExecutions(t *testing.T) {
|
|
dbProvider := setupAutomationTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(dbProvider.DB(), t)
|
|
logSvc := NewExecutionLogService(dbProvider)
|
|
ctx := context.Background()
|
|
|
|
// Log 5 executions for the same rule
|
|
for i := 0; i < 5; i++ {
|
|
err := logSvc.LogRuleExecution(ctx, accountID, 1, uint(i+1), ExecutionStatusSuccess, 2, 0, "")
|
|
if err != nil {
|
|
t.Fatalf("failed to log execution %d: %v", i, err)
|
|
}
|
|
}
|
|
|
|
// List with limit 3 — should return the 3 most recent
|
|
logs, err := logSvc.ListRuleExecutions(ctx, accountID, 1, 3)
|
|
if err != nil {
|
|
t.Fatalf("failed to list rule executions: %v", err)
|
|
}
|
|
if len(logs) != 3 {
|
|
t.Fatalf("expected 3 logs (limited), got %d", len(logs))
|
|
}
|
|
|
|
// List without limit — should return all 5
|
|
logs, err = logSvc.ListRuleExecutions(ctx, accountID, 1, 0)
|
|
if err != nil {
|
|
t.Fatalf("failed to list rule executions: %v", err)
|
|
}
|
|
if len(logs) != 5 {
|
|
t.Fatalf("expected 5 logs (no limit), got %d", len(logs))
|
|
}
|
|
}
|
|
|
|
// ===========================
|
|
// Macro Execution + Template Variable Integration tests
|
|
// ===========================
|
|
|
|
// TestMacroService_Execute_WithTemplateVars verifies that template variables
|
|
// in macro action params are resolved before execution. When a macro contains
|
|
// {{contact.name}} in a send_message action, the resulting message should
|
|
// contain the actual contact name, not the raw template placeholder.
|
|
func TestMacroService_Execute_WithTemplateVars(t *testing.T) {
|
|
dbProvider := setupAutomationTestDBProvider(t)
|
|
accountID, userID := seedTestAccount(dbProvider.DB(), t)
|
|
inboxID := seedTestInbox(dbProvider.DB(), t, accountID)
|
|
contactID := seedTestContact(dbProvider.DB(), t, accountID)
|
|
conversationID := seedTestConversation(dbProvider.DB(), t, accountID, inboxID, contactID)
|
|
|
|
macroSvc := NewMacroService(dbProvider)
|
|
ctx := context.Background()
|
|
|
|
// Create a macro with a send_message action containing {{contact.name}}
|
|
macro := &Macro{
|
|
AccountID: accountID,
|
|
Name: "Greet Contact",
|
|
Actions: Actions{
|
|
{
|
|
ActionName: "send_message",
|
|
ActionParams: map[string]interface{}{
|
|
"content": "Hello {{contact.name}}, your ticket is being processed.",
|
|
},
|
|
},
|
|
},
|
|
Visibility: MacroVisibilityGlobal,
|
|
Active: true,
|
|
CreatedByID: userID,
|
|
UpdatedByID: userID,
|
|
}
|
|
if err := macroSvc.Create(ctx, macro); err != nil {
|
|
t.Fatalf("failed to create macro: %v", err)
|
|
}
|
|
|
|
// Execute the macro on the conversation
|
|
if err := macroSvc.Execute(ctx, accountID, conversationID, macro.ID, userID); err != nil {
|
|
t.Fatalf("failed to execute macro: %v", err)
|
|
}
|
|
|
|
// Verify the message was created with resolved template variables
|
|
var messages []model.Message
|
|
if err := dbProvider.DB().WithContext(ctx).
|
|
Where("conversation_id = ? AND account_id = ? AND message_type = 'outgoing'", conversationID, accountID).
|
|
Find(&messages).Error; err != nil {
|
|
t.Fatalf("failed to query messages: %v", err)
|
|
}
|
|
|
|
if len(messages) != 1 {
|
|
t.Fatalf("expected 1 outgoing message, got %d", len(messages))
|
|
}
|
|
|
|
// The template variable {{contact.name}} should have been resolved to "Test Contact"
|
|
// (seedTestContact creates a contact with Name="Test Contact")
|
|
if messages[0].Content != "Hello Test Contact, your ticket is being processed." {
|
|
t.Fatalf("expected resolved content 'Hello Test Contact, your ticket is being processed.', got '%s'", messages[0].Content)
|
|
}
|
|
|
|
// Verify the sender is set as agent (macro execution uses agent sender type)
|
|
if messages[0].SenderType != "agent" {
|
|
t.Fatalf("expected sender_type 'agent', got '%s'", messages[0].SenderType)
|
|
}
|
|
}
|
|
|
|
// TestMacroService_Execute_SelfAssign verifies that macros with assign_agent
|
|
// action using "self" as the user_id parameter correctly assign the executing user.
|
|
// Reference: Chatwoot macros support "self" assignment — the agent running the macro
|
|
// becomes the assignee.
|
|
func TestMacroService_Execute_SelfAssign(t *testing.T) {
|
|
dbProvider := setupAutomationTestDBProvider(t)
|
|
accountID, userID := seedTestAccount(dbProvider.DB(), t)
|
|
inboxID := seedTestInbox(dbProvider.DB(), t, accountID)
|
|
contactID := seedTestContact(dbProvider.DB(), t, accountID)
|
|
conversationID := seedTestConversation(dbProvider.DB(), t, accountID, inboxID, contactID)
|
|
|
|
macroSvc := NewMacroService(dbProvider)
|
|
ctx := context.Background()
|
|
|
|
// Create a macro that assigns "self" (the executing user)
|
|
macro := &Macro{
|
|
AccountID: accountID,
|
|
Name: "Assign to Self",
|
|
Actions: Actions{
|
|
{
|
|
ActionName: "assign_agent",
|
|
ActionParams: map[string]interface{}{
|
|
"assignee_id": "self",
|
|
},
|
|
},
|
|
},
|
|
Visibility: MacroVisibilityGlobal,
|
|
Active: true,
|
|
CreatedByID: userID,
|
|
UpdatedByID: userID,
|
|
}
|
|
if err := macroSvc.Create(ctx, macro); err != nil {
|
|
t.Fatalf("failed to create macro: %v", err)
|
|
}
|
|
|
|
// Execute the macro
|
|
if err := macroSvc.Execute(ctx, accountID, conversationID, macro.ID, userID); err != nil {
|
|
t.Fatalf("failed to execute macro: %v", err)
|
|
}
|
|
|
|
// Verify the conversation was assigned to the executing user
|
|
var conv model.Conversation
|
|
if err := dbProvider.DB().WithContext(ctx).First(&conv, conversationID).Error; err != nil {
|
|
t.Fatalf("failed to query conversation: %v", err)
|
|
}
|
|
|
|
if *conv.AssigneeID != userID {
|
|
t.Fatalf("expected assignee_id %d (self), got %d", userID, *conv.AssigneeID)
|
|
}
|
|
}
|
|
|
|
// TestMacroService_Execute_MultipleActions verifies that a macro with multiple
|
|
// actions executes all of them in sequence.
|
|
func TestMacroService_Execute_MultipleActions(t *testing.T) {
|
|
dbProvider := setupAutomationTestDBProvider(t)
|
|
accountID, userID := seedTestAccount(dbProvider.DB(), t)
|
|
inboxID := seedTestInbox(dbProvider.DB(), t, accountID)
|
|
contactID := seedTestContact(dbProvider.DB(), t, accountID)
|
|
conversationID := seedTestConversationWithDetails(dbProvider.DB(), t, accountID, inboxID, contactID, "open", "", "web", 0)
|
|
|
|
macroSvc := NewMacroService(dbProvider)
|
|
ctx := context.Background()
|
|
|
|
// Create a macro with multiple actions
|
|
macro := &Macro{
|
|
AccountID: accountID,
|
|
Name: "Resolve and Label",
|
|
Actions: Actions{
|
|
{ActionName: "change_status", ActionParams: map[string]interface{}{"status": "resolved"}},
|
|
{ActionName: "add_label", ActionParams: map[string]interface{}{"label": "macro-resolved"}},
|
|
},
|
|
Visibility: MacroVisibilityGlobal,
|
|
Active: true,
|
|
CreatedByID: userID,
|
|
UpdatedByID: userID,
|
|
}
|
|
if err := macroSvc.Create(ctx, macro); err != nil {
|
|
t.Fatalf("failed to create macro: %v", err)
|
|
}
|
|
|
|
// Execute the macro
|
|
if err := macroSvc.Execute(ctx, accountID, conversationID, macro.ID, userID); err != nil {
|
|
t.Fatalf("failed to execute macro: %v", err)
|
|
}
|
|
|
|
// Verify the conversation status was changed
|
|
var conv model.Conversation
|
|
if err := dbProvider.DB().WithContext(ctx).First(&conv, conversationID).Error; err != nil {
|
|
t.Fatalf("failed to query conversation: %v", err)
|
|
}
|
|
if conv.Status != "resolved" {
|
|
t.Fatalf("expected status 'resolved', got '%s'", conv.Status)
|
|
}
|
|
|
|
// Verify the label was added
|
|
var labels []ConversationLabel
|
|
if err := dbProvider.DB().WithContext(ctx).
|
|
Where("conversation_id = ?", conversationID).
|
|
Find(&labels).Error; err != nil {
|
|
t.Fatalf("failed to query labels: %v", err)
|
|
}
|
|
found := false
|
|
for _, l := range labels {
|
|
if l.Label == "macro-resolved" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("expected label 'macro-resolved' to be added, but it was not found among %d labels", len(labels))
|
|
}
|
|
}
|
|
|
|
// TestMacroService_Execute_RecordsExecution verifies that executing a macro
|
|
// creates a MacroExecution audit record.
|
|
func TestMacroService_Execute_RecordsExecution(t *testing.T) {
|
|
dbProvider := setupAutomationTestDBProvider(t)
|
|
accountID, userID := seedTestAccount(dbProvider.DB(), t)
|
|
inboxID := seedTestInbox(dbProvider.DB(), t, accountID)
|
|
contactID := seedTestContact(dbProvider.DB(), t, accountID)
|
|
conversationID := seedTestConversation(dbProvider.DB(), t, accountID, inboxID, contactID)
|
|
|
|
macroSvc := NewMacroService(dbProvider)
|
|
ctx := context.Background()
|
|
|
|
macro := &Macro{
|
|
AccountID: accountID,
|
|
Name: "Simple Macro",
|
|
Actions: Actions{
|
|
{ActionName: "change_status", ActionParams: map[string]interface{}{"status": "resolved"}},
|
|
},
|
|
Visibility: MacroVisibilityGlobal,
|
|
Active: true,
|
|
CreatedByID: userID,
|
|
UpdatedByID: userID,
|
|
}
|
|
if err := macroSvc.Create(ctx, macro); err != nil {
|
|
t.Fatalf("failed to create macro: %v", err)
|
|
}
|
|
|
|
if err := macroSvc.Execute(ctx, accountID, conversationID, macro.ID, userID); err != nil {
|
|
t.Fatalf("failed to execute macro: %v", err)
|
|
}
|
|
|
|
// Verify execution record was created
|
|
var executions []MacroExecution
|
|
if err := dbProvider.DB().WithContext(ctx).
|
|
Where("macro_id = ? AND conversation_id = ?", macro.ID, conversationID).
|
|
Find(&executions).Error; err != nil {
|
|
t.Fatalf("failed to query macro executions: %v", err)
|
|
}
|
|
|
|
if len(executions) != 1 {
|
|
t.Fatalf("expected 1 execution record, got %d", len(executions))
|
|
}
|
|
if executions[0].ExecutedByID != userID {
|
|
t.Fatalf("expected executed_by_id %d, got %d", userID, executions[0].ExecutedByID)
|
|
}
|
|
} |