Files
gochat/internal/automation/macro_service_test.go
T

259 lines
8.4 KiB
Go

package automation
import (
"context"
"testing"
"time"
"github.com/gochat/gochat/internal/model"
"github.com/gochat/gochat/internal/worker"
)
func TestMacroService_Create(t *testing.T) {
dbProvider := setupAutomationTestDBProvider(t)
accountID, userID := seedTestAccount(dbProvider.DB(), t)
svc := NewMacroService(dbProvider)
macro := &Macro{
AccountID: accountID,
Name: "Close and label",
Actions: Actions{
{ActionName: "resolve", ActionParams: map[string]interface{}{}},
{ActionName: "add_label", ActionParams: map[string]interface{}{"labels": []string{"closed"}}},
},
Visibility: MacroVisibilityGlobal,
CreatedByID: userID,
UpdatedByID: userID,
}
err := svc.Create(context.Background(), macro)
if err != nil {
t.Fatalf("expected no error creating macro, got: %v", err)
}
if macro.ID == 0 {
t.Fatal("expected macro ID to be set after creation")
}
}
func TestMacroService_GetByID(t *testing.T) {
dbProvider := setupAutomationTestDBProvider(t)
accountID, userID := seedTestAccount(dbProvider.DB(), t)
svc := NewMacroService(dbProvider)
macro := &Macro{
AccountID: accountID,
Name: "Test macro",
Actions: Actions{},
Visibility: MacroVisibilityGlobal,
CreatedByID: userID,
UpdatedByID: userID,
}
if err := svc.Create(context.Background(), macro); err != nil {
t.Fatalf("setup: %v", err)
}
found, err := svc.GetByID(context.Background(), macro.ID)
if err != nil {
t.Fatalf("expected no error getting macro, got: %v", err)
}
if found.ID != macro.ID {
t.Fatalf("expected macro ID %d, got %d", macro.ID, found.ID)
}
if found.Name != "Test macro" {
t.Fatalf("expected name 'Test macro', got %q", found.Name)
}
}
func TestMacroService_GetByID_NotFound(t *testing.T) {
dbProvider := setupAutomationTestDBProvider(t)
svc := NewMacroService(dbProvider)
_, err := svc.GetByID(context.Background(), 9999)
if err == nil {
t.Fatal("expected error for non-existent macro, got nil")
}
}
func TestMacroService_ListByAccount(t *testing.T) {
dbProvider := setupAutomationTestDBProvider(t)
accountID, userID := seedTestAccount(dbProvider.DB(), t)
svc := NewMacroService(dbProvider)
// Create global macro
m1 := &Macro{
AccountID: accountID, Name: "Global macro", Actions: Actions{},
Visibility: MacroVisibilityGlobal, CreatedByID: userID, UpdatedByID: userID,
}
if err := svc.Create(context.Background(), m1); err != nil {
t.Fatalf("setup: %v", err)
}
// Create personal macro owned by userID
m2 := &Macro{
AccountID: accountID, Name: "Personal macro (mine)", Actions: Actions{},
Visibility: MacroVisibilityPersonal, CreatedByID: userID, UpdatedByID: userID,
}
if err := svc.Create(context.Background(), m2); err != nil {
t.Fatalf("setup: %v", err)
}
macros, err := svc.ListByAccount(context.Background(), accountID, userID)
if err != nil {
t.Fatalf("expected no error listing macros, got: %v", err)
}
// Should see both global and personal (owned by this user)
if len(macros) != 2 {
t.Fatalf("expected 2 macros (global + personal-own), got %d", len(macros))
}
}
func TestMacroService_ListByAccount_PersonalFiltering(t *testing.T) {
dbProvider := setupAutomationTestDBProvider(t)
accountID, userID := seedTestAccount(dbProvider.DB(), t)
svc := NewMacroService(dbProvider)
// Create a second user in the same account
otherUser := &model.User{Name: "Other User", Email: "other@example.com"}
db := dbProvider.DB()
if err := db.Create(otherUser).Error; err != nil {
t.Fatalf("setup: %v", err)
}
// Create personal macro owned by OTHER user (should NOT be visible to userID)
m := &Macro{
AccountID: accountID, Name: "Someone else's macro", Actions: Actions{},
Visibility: MacroVisibilityPersonal, CreatedByID: otherUser.ID, UpdatedByID: otherUser.ID,
}
if err := svc.Create(context.Background(), m); err != nil {
t.Fatalf("setup: %v", err)
}
// userID should not see other user's personal macro
macros, err := svc.ListByAccount(context.Background(), accountID, userID)
if err != nil {
t.Fatalf("expected no error, got: %v", err)
}
for _, mc := range macros {
if mc.Visibility == MacroVisibilityPersonal && mc.CreatedByID != userID {
t.Fatalf("should not see other user's personal macro: %q (created_by_id=%d)", mc.Name, mc.CreatedByID)
}
}
}
func TestMacroService_Update(t *testing.T) {
dbProvider := setupAutomationTestDBProvider(t)
accountID, userID := seedTestAccount(dbProvider.DB(), t)
svc := NewMacroService(dbProvider)
macro := &Macro{
AccountID: accountID, Name: "Original", Actions: Actions{},
Visibility: MacroVisibilityPersonal, CreatedByID: userID, UpdatedByID: userID,
}
if err := svc.Create(context.Background(), macro); err != nil {
t.Fatalf("setup: %v", err)
}
macro.Name = "Updated"
macro.Visibility = MacroVisibilityGlobal
err := svc.Update(context.Background(), macro)
if err != nil {
t.Fatalf("expected no error updating macro, got: %v", err)
}
found, _ := svc.GetByID(context.Background(), macro.ID)
if found.Name != "Updated" {
t.Fatalf("expected name 'Updated', got %q", found.Name)
}
if found.Visibility != MacroVisibilityGlobal {
t.Fatal("expected visibility to be global after update")
}
}
func TestMacroService_Delete(t *testing.T) {
dbProvider := setupAutomationTestDBProvider(t)
accountID, userID := seedTestAccount(dbProvider.DB(), t)
svc := NewMacroService(dbProvider)
macro := &Macro{
AccountID: accountID, Name: "Delete me", Actions: Actions{},
Visibility: MacroVisibilityGlobal, CreatedByID: userID, UpdatedByID: userID,
}
if err := svc.Create(context.Background(), macro); err != nil {
t.Fatalf("setup: %v", err)
}
err := svc.Delete(context.Background(), macro.ID)
if err != nil {
t.Fatalf("expected no error deleting macro, got: %v", err)
}
_, err = svc.GetByID(context.Background(), macro.ID)
if err == nil {
t.Fatal("expected error getting deleted macro, got nil")
}
}
func TestMacroService_ExecuteForDisplayIDsQueuesDurableMacroJob(t *testing.T) {
dbProvider := setupAutomationTestDBProvider(t)
db := dbProvider.DB()
accountID, userID := seedTestAccount(db, t)
inboxID := seedTestInbox(db, t, accountID)
contactID := seedTestContact(db, t, accountID)
displayID := uint(616)
conversationID := seedTestConversationWithDetails(db, t, accountID, inboxID, contactID, "open", "low", "web", 0)
if err := db.Model(&model.Conversation{}).Where("id = ?", conversationID).Update("display_id", displayID).Error; err != nil {
t.Fatalf("set display id: %v", err)
}
macro := &Macro{
AccountID: accountID,
Name: "durable macro",
Actions: Actions{
{ActionName: "add_label", ActionParams: map[string]interface{}{"labels": []string{"durable"}}},
{ActionName: "change_status", ActionParams: map[string]interface{}{"status": "resolved"}},
},
Visibility: MacroVisibilityGlobal,
CreatedByID: userID,
UpdatedByID: userID,
}
if err := NewMacroService(dbProvider).Create(context.Background(), macro); err != nil {
t.Fatalf("create macro: %v", err)
}
wp := worker.NewWorkerPoolWithOptions(db, worker.WithNow(func() time.Time { return time.Date(2026, 6, 5, 13, 15, 0, 0, time.UTC) }))
svc := NewMacroServiceWithWorker(dbProvider, wp)
if err := svc.ExecuteForDisplayIDs(context.Background(), accountID, macro.ID, []uint{displayID}, userID); err != nil {
t.Fatalf("queue macro execution: %v", err)
}
var before model.Conversation
if err := db.First(&before, conversationID).Error; err != nil {
t.Fatalf("load conversation before worker: %v", err)
}
if before.Status != "open" {
t.Fatalf("macro should not run synchronously, status=%s", before.Status)
}
var count int64
if err := db.Model(&model.BackgroundJob{}).Where("job_type = ? AND queue = ? AND status = ?", TaskTypeMacroExecution, "medium", model.BackgroundJobStatusQueued).Count(&count).Error; err != nil {
t.Fatalf("count macro jobs: %v", err)
}
if count != 1 {
t.Fatalf("expected one queued macro job, got %d", count)
}
processed, err := wp.ProcessOne(context.Background())
if err != nil || !processed {
t.Fatalf("process macro job: processed=%v err=%v", processed, err)
}
var after model.Conversation
if err := db.First(&after, conversationID).Error; err != nil {
t.Fatalf("load conversation after worker: %v", err)
}
if after.Status != "resolved" {
t.Fatalf("expected macro job to resolve conversation, got %s", after.Status)
}
var label ConversationLabel
if err := db.Where("conversation_id = ? AND label = ?", conversationID, "durable").First(&label).Error; err != nil {
t.Fatalf("expected macro job to add label: %v", err)
}
}