Files
gochat/internal/service/csat_template_worker_test.go
T

150 lines
6.1 KiB
Go

package service
import (
"context"
"errors"
"strings"
"testing"
"time"
"github.com/gochat/gochat/internal/model"
"github.com/gochat/gochat/internal/repository"
"github.com/gochat/gochat/internal/worker"
)
type fakeCsatTemplateProvider struct {
calls int
result *CsatTemplateProviderResult
err error
}
func (p *fakeCsatTemplateProvider) CreateTemplate(ctx context.Context, inbox *model.Inbox, template *model.CsatTemplate, req CreateCsatTemplateRequest) (*CsatTemplateProviderResult, error) {
p.calls++
if p.err != nil {
return nil, p.err
}
if p.result != nil {
return p.result, nil
}
return &CsatTemplateProviderResult{TemplateExists: true, Status: "PENDING", TemplateName: "csat_survey_test", TemplateID: "tmpl_1", Language: "en"}, nil
}
func (p *fakeCsatTemplateProvider) GetTemplateStatus(ctx context.Context, inbox *model.Inbox, template *model.CsatTemplate) (*CsatTemplateProviderResult, error) {
return p.result, p.err
}
func TestCsatTemplateService_CreateTemplateQueuesDurableProviderCreation(t *testing.T) {
db := setupServiceTestDB(t)
repo := repository.NewCsatTemplateRepo(db)
svc := NewCsatTemplateService(repo)
provider := &fakeCsatTemplateProvider{result: &CsatTemplateProviderResult{TemplateExists: true, Status: "PENDING", TemplateName: "csat_survey_1", TemplateID: "tmpl_123", Language: "en"}}
svc.SetProvider(provider)
now := time.Date(2026, 6, 5, 22, 0, 0, 0, time.UTC)
wp := worker.NewWorkerPoolWithOptions(db, worker.WithNow(func() time.Time { return now }))
svc.SetWorkerPool(wp)
account := createTestAccount(t, db)
inbox := createTestInbox(t, db, account.ID, string(model.InboxChannelTypeWhatsApp))
template, err := svc.CreateTemplate(context.Background(), inbox.ID, CreateCsatTemplateRequest{Message: "Please rate our support", ButtonText: "Please rate us", Language: "en"})
if err != nil {
t.Fatalf("create template: %v", err)
}
if template.Status != "pending" {
t.Fatalf("expected pending before worker, got %s", template.Status)
}
if provider.calls != 0 {
t.Fatalf("expected provider deferred to worker, got %d calls", provider.calls)
}
var jobCount int64
if err := db.Model(&model.BackgroundJob{}).Where("job_type = ? AND queue = ? AND status = ?", TaskTypeCsatTemplateCreate, "automation", model.BackgroundJobStatusQueued).Count(&jobCount).Error; err != nil {
t.Fatalf("count jobs: %v", err)
}
if jobCount != 1 {
t.Fatalf("expected one csat template job, got %d", jobCount)
}
processed, err := wp.ProcessOne(context.Background())
if err != nil || !processed {
t.Fatalf("process csat template job: processed=%v err=%v", processed, err)
}
var completed model.CsatTemplate
if err := db.First(&completed, template.ID).Error; err != nil {
t.Fatalf("load completed template: %v", err)
}
if completed.Status != "PENDING" {
t.Fatalf("expected provider status PENDING, got %s", completed.Status)
}
var reloadedInbox model.Inbox
if err := db.First(&reloadedInbox, inbox.ID).Error; err != nil {
t.Fatalf("load inbox: %v", err)
}
if !strings.Contains(reloadedInbox.CsatConfig, "csat_survey_1") || !strings.Contains(reloadedInbox.CsatConfig, "tmpl_123") {
t.Fatalf("expected inbox csat_config to include provider template metadata, got %s", reloadedInbox.CsatConfig)
}
if provider.calls != 1 {
t.Fatalf("expected one provider call, got %d", provider.calls)
}
}
func TestCsatTemplateService_TwilioTemplateStoresContentSid(t *testing.T) {
db := setupServiceTestDB(t)
repo := repository.NewCsatTemplateRepo(db)
svc := NewCsatTemplateService(repo)
svc.SetProvider(&fakeCsatTemplateProvider{result: &CsatTemplateProviderResult{TemplateExists: true, Status: "PENDING", FriendlyName: "csat_survey_2", ContentSID: "HX123", ApprovalSID: "HA123", Language: "en"}})
account := createTestAccount(t, db)
inbox := createTestInbox(t, db, account.ID, string(model.InboxChannelTypeTwilioSMS), func(inbox *model.Inbox) {
inbox.ChannelConfig = `{"medium":"whatsapp"}`
})
template, err := svc.CreateTemplate(context.Background(), inbox.ID, CreateCsatTemplateRequest{Message: "Rate us over WhatsApp"})
if err != nil {
t.Fatalf("create twilio template: %v", err)
}
if template.Status != "PENDING" {
t.Fatalf("expected sync provider status PENDING, got %s", template.Status)
}
var reloadedInbox model.Inbox
if err := db.First(&reloadedInbox, inbox.ID).Error; err != nil {
t.Fatalf("load inbox: %v", err)
}
if !strings.Contains(reloadedInbox.CsatConfig, "HX123") || !strings.Contains(reloadedInbox.CsatConfig, "HA123") {
t.Fatalf("expected twilio content/approval SIDs in csat_config, got %s", reloadedInbox.CsatConfig)
}
}
func TestCsatTemplateService_TemplateJobRetriesProviderFailures(t *testing.T) {
db := setupServiceTestDB(t)
repo := repository.NewCsatTemplateRepo(db)
svc := NewCsatTemplateService(repo)
svc.SetProvider(&fakeCsatTemplateProvider{err: errors.New("provider unavailable")})
now := time.Date(2026, 6, 5, 22, 30, 0, 0, time.UTC)
wp := worker.NewWorkerPoolWithOptions(db, worker.WithNow(func() time.Time { return now }), worker.WithBackoff(func(attempt int) time.Duration { return time.Minute }))
svc.SetWorkerPool(wp)
account := createTestAccount(t, db)
inbox := createTestInbox(t, db, account.ID, string(model.InboxChannelTypeWhatsApp))
template, err := svc.CreateTemplate(context.Background(), inbox.ID, CreateCsatTemplateRequest{Message: "Please rate our support"})
if err != nil {
t.Fatalf("enqueue template create: %v", err)
}
processed, err := wp.ProcessOne(context.Background())
if err == nil || !processed {
t.Fatalf("expected provider failure to retry, processed=%v err=%v", processed, err)
}
var job model.BackgroundJob
if err := db.Where("job_type = ?", TaskTypeCsatTemplateCreate).First(&job).Error; err != nil {
t.Fatalf("load template job: %v", err)
}
if job.Status != model.BackgroundJobStatusRetrying || job.LastError == "" {
t.Fatalf("expected retrying template job with error, got status=%s last_error=%q", job.Status, job.LastError)
}
var failed model.CsatTemplate
if err := db.First(&failed, template.ID).Error; err != nil {
t.Fatalf("load failed template: %v", err)
}
if failed.Status != "failed" {
t.Fatalf("expected failed template status, got %s", failed.Status)
}
}