259 lines
8.4 KiB
Go
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)
|
|
}
|
|
}
|