419 lines
14 KiB
Go
419 lines
14 KiB
Go
package canned
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/gochat/gochat/internal/model"
|
|
)
|
|
|
|
func TestCannedResponseService_Create(t *testing.T) {
|
|
provider := setupCannedTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(provider.DB(), t)
|
|
svc := NewCannedResponseService(provider)
|
|
|
|
cr := &CannedResponse{
|
|
AccountID: accountID,
|
|
ShortCode: "greeting",
|
|
Content: "Hello! How can I help you?",
|
|
}
|
|
|
|
if err := svc.Create(context.Background(), cr); err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
if cr.ID == 0 {
|
|
t.Fatal("expected ID to be set after Create")
|
|
}
|
|
if cr.ShortCode != "greeting" {
|
|
t.Fatalf("expected ShortCode 'greeting', got '%s'", cr.ShortCode)
|
|
}
|
|
if cr.Content != "Hello! How can I help you?" {
|
|
t.Fatalf("expected Content 'Hello! How can I help you?', got '%s'", cr.Content)
|
|
}
|
|
}
|
|
|
|
func TestCannedResponseService_Create_DuplicateShortCode(t *testing.T) {
|
|
provider := setupCannedTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(provider.DB(), t)
|
|
svc := NewCannedResponseService(provider)
|
|
|
|
cr1 := &CannedResponse{
|
|
AccountID: accountID,
|
|
ShortCode: "greeting",
|
|
Content: "Hello!",
|
|
}
|
|
if err := svc.Create(context.Background(), cr1); err != nil {
|
|
t.Fatalf("first Create failed: %v", err)
|
|
}
|
|
|
|
// Same short_code within the same account should fail due to uniqueIndex.
|
|
cr2 := &CannedResponse{
|
|
AccountID: accountID,
|
|
ShortCode: "greeting",
|
|
Content: "Hi there!",
|
|
}
|
|
if err := svc.Create(context.Background(), cr2); err == nil {
|
|
t.Fatal("expected error on duplicate short_code, got nil")
|
|
}
|
|
}
|
|
|
|
func TestCannedResponseService_GetByID(t *testing.T) {
|
|
provider := setupCannedTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(provider.DB(), t)
|
|
svc := NewCannedResponseService(provider)
|
|
|
|
cr := &CannedResponse{
|
|
AccountID: accountID,
|
|
ShortCode: "thanks",
|
|
Content: "Thank you for contacting us!",
|
|
}
|
|
if err := svc.Create(context.Background(), cr); err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
found, err := svc.GetByID(context.Background(), cr.ID)
|
|
if err != nil {
|
|
t.Fatalf("GetByID failed: %v", err)
|
|
}
|
|
if found.ID != cr.ID {
|
|
t.Fatalf("expected ID %d, got %d", cr.ID, found.ID)
|
|
}
|
|
if found.ShortCode != "thanks" {
|
|
t.Fatalf("expected ShortCode 'thanks', got '%s'", found.ShortCode)
|
|
}
|
|
if found.Content != "Thank you for contacting us!" {
|
|
t.Fatalf("expected Content 'Thank you for contacting us!', got '%s'", found.Content)
|
|
}
|
|
}
|
|
|
|
func TestCannedResponseService_GetByID_NotFound(t *testing.T) {
|
|
provider := setupCannedTestDBProvider(t)
|
|
svc := NewCannedResponseService(provider)
|
|
|
|
_, err := svc.GetByID(context.Background(), 9999)
|
|
if err == nil {
|
|
t.Fatal("expected error for non-existent ID, got nil")
|
|
}
|
|
}
|
|
|
|
func TestCannedResponseService_GetByAccountAndID(t *testing.T) {
|
|
provider := setupCannedTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(provider.DB(), t)
|
|
otherAccount := &model.Account{Name: "Other Scoped Account"}
|
|
if err := provider.DB().Create(otherAccount).Error; err != nil {
|
|
t.Fatalf("failed to create other account: %v", err)
|
|
}
|
|
svc := NewCannedResponseService(provider)
|
|
|
|
cr := &CannedResponse{AccountID: accountID, ShortCode: "scoped", Content: "Scoped content"}
|
|
if err := svc.Create(context.Background(), cr); err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
found, err := svc.GetByAccountAndID(context.Background(), accountID, cr.ID)
|
|
if err != nil {
|
|
t.Fatalf("GetByAccountAndID failed: %v", err)
|
|
}
|
|
if found.ID != cr.ID {
|
|
t.Fatalf("expected ID %d, got %d", cr.ID, found.ID)
|
|
}
|
|
|
|
if _, err := svc.GetByAccountAndID(context.Background(), otherAccount.ID, cr.ID); err == nil {
|
|
t.Fatal("expected cross-account lookup to fail")
|
|
}
|
|
}
|
|
|
|
func TestCannedResponseService_Update(t *testing.T) {
|
|
provider := setupCannedTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(provider.DB(), t)
|
|
svc := NewCannedResponseService(provider)
|
|
|
|
cr := &CannedResponse{
|
|
AccountID: accountID,
|
|
ShortCode: "bye",
|
|
Content: "Goodbye!",
|
|
}
|
|
if err := svc.Create(context.Background(), cr); err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
updates := map[string]interface{}{
|
|
"short_code": "farewell",
|
|
"content": "Farewell and thank you!",
|
|
}
|
|
if err := svc.Update(context.Background(), cr.ID, updates); err != nil {
|
|
t.Fatalf("Update failed: %v", err)
|
|
}
|
|
|
|
found, err := svc.GetByID(context.Background(), cr.ID)
|
|
if err != nil {
|
|
t.Fatalf("GetByID after Update failed: %v", err)
|
|
}
|
|
if found.ShortCode != "farewell" {
|
|
t.Fatalf("expected ShortCode 'farewell', got '%s'", found.ShortCode)
|
|
}
|
|
if found.Content != "Farewell and thank you!" {
|
|
t.Fatalf("expected Content 'Farewell and thank you!', got '%s'", found.Content)
|
|
}
|
|
}
|
|
|
|
func TestCannedResponseService_UpdateByAccountAndID(t *testing.T) {
|
|
provider := setupCannedTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(provider.DB(), t)
|
|
otherAccount := &model.Account{Name: "Other Update Account"}
|
|
if err := provider.DB().Create(otherAccount).Error; err != nil {
|
|
t.Fatalf("failed to create other account: %v", err)
|
|
}
|
|
svc := NewCannedResponseService(provider)
|
|
|
|
cr := &CannedResponse{AccountID: accountID, ShortCode: "account_update", Content: "Original"}
|
|
if err := svc.Create(context.Background(), cr); err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
if err := svc.UpdateByAccountAndID(context.Background(), otherAccount.ID, cr.ID, map[string]interface{}{"content": "Wrong"}); err == nil {
|
|
t.Fatal("expected cross-account update to fail")
|
|
}
|
|
|
|
if err := svc.UpdateByAccountAndID(context.Background(), accountID, cr.ID, map[string]interface{}{"content": "Updated"}); err != nil {
|
|
t.Fatalf("UpdateByAccountAndID failed: %v", err)
|
|
}
|
|
found, err := svc.GetByAccountAndID(context.Background(), accountID, cr.ID)
|
|
if err != nil {
|
|
t.Fatalf("GetByAccountAndID after update failed: %v", err)
|
|
}
|
|
if found.Content != "Updated" {
|
|
t.Fatalf("expected updated content, got %q", found.Content)
|
|
}
|
|
}
|
|
|
|
func TestCannedResponseService_Delete(t *testing.T) {
|
|
provider := setupCannedTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(provider.DB(), t)
|
|
svc := NewCannedResponseService(provider)
|
|
|
|
cr := &CannedResponse{
|
|
AccountID: accountID,
|
|
ShortCode: "hello",
|
|
Content: "Hello!",
|
|
}
|
|
if err := svc.Create(context.Background(), cr); err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
if err := svc.Delete(context.Background(), cr.ID); err != nil {
|
|
t.Fatalf("Delete failed: %v", err)
|
|
}
|
|
|
|
// After soft delete, GetByID should return error.
|
|
_, err := svc.GetByID(context.Background(), cr.ID)
|
|
if err == nil {
|
|
t.Fatal("expected error after Delete, got nil (soft-deleted record should not be found)")
|
|
}
|
|
}
|
|
|
|
func TestCannedResponseService_DeleteByAccountAndID(t *testing.T) {
|
|
provider := setupCannedTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(provider.DB(), t)
|
|
otherAccount := &model.Account{Name: "Other Delete Account"}
|
|
if err := provider.DB().Create(otherAccount).Error; err != nil {
|
|
t.Fatalf("failed to create other account: %v", err)
|
|
}
|
|
svc := NewCannedResponseService(provider)
|
|
|
|
cr := &CannedResponse{AccountID: accountID, ShortCode: "account_delete", Content: "Delete"}
|
|
if err := svc.Create(context.Background(), cr); err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
if err := svc.DeleteByAccountAndID(context.Background(), otherAccount.ID, cr.ID); err == nil {
|
|
t.Fatal("expected cross-account delete to fail")
|
|
}
|
|
if _, err := svc.GetByAccountAndID(context.Background(), accountID, cr.ID); err != nil {
|
|
t.Fatalf("record should still exist after failed cross-account delete: %v", err)
|
|
}
|
|
|
|
if err := svc.DeleteByAccountAndID(context.Background(), accountID, cr.ID); err != nil {
|
|
t.Fatalf("DeleteByAccountAndID failed: %v", err)
|
|
}
|
|
if _, err := svc.GetByAccountAndID(context.Background(), accountID, cr.ID); err == nil {
|
|
t.Fatal("expected account-scoped lookup to fail after delete")
|
|
}
|
|
|
|
recreated := &CannedResponse{AccountID: accountID, ShortCode: "account_delete", Content: "Recreated"}
|
|
if err := svc.Create(context.Background(), recreated); err != nil {
|
|
t.Fatalf("expected hard-deleted short_code to be reusable: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCannedResponseService_ListByAccount(t *testing.T) {
|
|
provider := setupCannedTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(provider.DB(), t)
|
|
svc := NewCannedResponseService(provider)
|
|
|
|
// Seed multiple canned responses.
|
|
items := []CannedResponse{
|
|
{AccountID: accountID, ShortCode: "alpha", Content: "Alpha content"},
|
|
{AccountID: accountID, ShortCode: "beta", Content: "Beta content"},
|
|
{AccountID: accountID, ShortCode: "gamma", Content: "Gamma content"},
|
|
}
|
|
for i := range items {
|
|
if err := svc.Create(context.Background(), &items[i]); err != nil {
|
|
t.Fatalf("Create #%d failed: %v", i, err)
|
|
}
|
|
}
|
|
|
|
// Create a second account with its own canned response — should not appear.
|
|
db := provider.DB()
|
|
account2 := &model.Account{Name: "Other Account"}
|
|
if err := db.Create(account2).Error; err != nil {
|
|
t.Fatalf("failed to create second account: %v", err)
|
|
}
|
|
crOther := &CannedResponse{
|
|
AccountID: account2.ID,
|
|
ShortCode: "other",
|
|
Content: "Other account content",
|
|
}
|
|
if err := svc.Create(context.Background(), crOther); err != nil {
|
|
t.Fatalf("Create other-account canned response failed: %v", err)
|
|
}
|
|
|
|
results, err := svc.ListByAccount(context.Background(), accountID)
|
|
if err != nil {
|
|
t.Fatalf("ListByAccount failed: %v", err)
|
|
}
|
|
|
|
if len(results) != 3 {
|
|
t.Fatalf("expected 3 results, got %d", len(results))
|
|
}
|
|
|
|
// Results should be ordered by short_code ASC.
|
|
if results[0].ShortCode != "alpha" {
|
|
t.Fatalf("expected first result ShortCode 'alpha', got '%s'", results[0].ShortCode)
|
|
}
|
|
if results[1].ShortCode != "beta" {
|
|
t.Fatalf("expected second result ShortCode 'beta', got '%s'", results[1].ShortCode)
|
|
}
|
|
if results[2].ShortCode != "gamma" {
|
|
t.Fatalf("expected third result ShortCode 'gamma', got '%s'", results[2].ShortCode)
|
|
}
|
|
}
|
|
|
|
func TestCannedResponseService_Search(t *testing.T) {
|
|
provider := setupCannedTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(provider.DB(), t)
|
|
svc := NewCannedResponseService(provider)
|
|
|
|
items := []CannedResponse{
|
|
{AccountID: accountID, ShortCode: "greet_hi", Content: "Hi there! Welcome."},
|
|
{AccountID: accountID, ShortCode: "greet_hello", Content: "Hello! How can we help?"},
|
|
{AccountID: accountID, ShortCode: "farewell", Content: "Goodbye and have a great day!"},
|
|
}
|
|
for i := range items {
|
|
if err := svc.Create(context.Background(), &items[i]); err != nil {
|
|
t.Fatalf("Create #%d failed: %v", i, err)
|
|
}
|
|
}
|
|
|
|
// Search for "greet" — should match short_code on first two items.
|
|
results, err := svc.Search(context.Background(), accountID, "greet")
|
|
if err != nil {
|
|
t.Fatalf("Search failed: %v", err)
|
|
}
|
|
|
|
// short_code matches should rank before content-only matches.
|
|
// Both "greet_hi" and "greet_hello" match on short_code.
|
|
if len(results) < 2 {
|
|
t.Fatalf("expected at least 2 results for 'greet', got %d", len(results))
|
|
}
|
|
|
|
// Verify that short_code match results come first.
|
|
for i := 0; i < 2; i++ {
|
|
if results[i].ShortCode != "greet_hi" && results[i].ShortCode != "greet_hello" {
|
|
t.Fatalf("expected short_code match in top results, got '%s'", results[i].ShortCode)
|
|
}
|
|
}
|
|
|
|
// Search for "great" — matches content in "farewell".
|
|
results2, err := svc.Search(context.Background(), accountID, "great")
|
|
if err != nil {
|
|
t.Fatalf("Search for 'great' failed: %v", err)
|
|
}
|
|
if len(results2) != 1 {
|
|
t.Fatalf("expected 1 result for 'great', got %d", len(results2))
|
|
}
|
|
if results2[0].ShortCode != "farewell" {
|
|
t.Fatalf("expected ShortCode 'farewell', got '%s'", results2[0].ShortCode)
|
|
}
|
|
|
|
// Empty query should return all (same as ListByAccount).
|
|
results3, err := svc.Search(context.Background(), accountID, "")
|
|
if err != nil {
|
|
t.Fatalf("Search with empty query failed: %v", err)
|
|
}
|
|
if len(results3) != 3 {
|
|
t.Fatalf("expected 3 results for empty query, got %d", len(results3))
|
|
}
|
|
}
|
|
|
|
func TestCannedResponseService_Search_RanksLikeChatwoot(t *testing.T) {
|
|
provider := setupCannedTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(provider.DB(), t)
|
|
svc := NewCannedResponseService(provider)
|
|
|
|
items := []CannedResponse{
|
|
{AccountID: accountID, ShortCode: "body_only", Content: "Please mention help in the body"},
|
|
{AccountID: accountID, ShortCode: "say_help", Content: "Generic"},
|
|
{AccountID: accountID, ShortCode: "help_start", Content: "Generic"},
|
|
}
|
|
for i := range items {
|
|
if err := svc.Create(context.Background(), &items[i]); err != nil {
|
|
t.Fatalf("Create #%d failed: %v", i, err)
|
|
}
|
|
}
|
|
|
|
results, err := svc.Search(context.Background(), accountID, "help")
|
|
if err != nil {
|
|
t.Fatalf("Search failed: %v", err)
|
|
}
|
|
if len(results) != 3 {
|
|
t.Fatalf("expected 3 results, got %d", len(results))
|
|
}
|
|
if results[0].ShortCode != "help_start" || results[1].ShortCode != "say_help" || results[2].ShortCode != "body_only" {
|
|
t.Fatalf("unexpected ranking: %s, %s, %s", results[0].ShortCode, results[1].ShortCode, results[2].ShortCode)
|
|
}
|
|
}
|
|
|
|
func TestCannedResponseService_GetByShortCode(t *testing.T) {
|
|
provider := setupCannedTestDBProvider(t)
|
|
accountID, _ := seedTestAccount(provider.DB(), t)
|
|
svc := NewCannedResponseService(provider)
|
|
|
|
cr := &CannedResponse{
|
|
AccountID: accountID,
|
|
ShortCode: "welcome",
|
|
Content: "Welcome to our service!",
|
|
}
|
|
if err := svc.Create(context.Background(), cr); err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
found, err := svc.GetByShortCode(context.Background(), accountID, "welcome")
|
|
if err != nil {
|
|
t.Fatalf("GetByShortCode failed: %v", err)
|
|
}
|
|
if found.ID != cr.ID {
|
|
t.Fatalf("expected ID %d, got %d", cr.ID, found.ID)
|
|
}
|
|
if found.ShortCode != "welcome" {
|
|
t.Fatalf("expected ShortCode 'welcome', got '%s'", found.ShortCode)
|
|
}
|
|
|
|
// Non-existent short_code should return error.
|
|
_, err = svc.GetByShortCode(context.Background(), accountID, "nonexistent")
|
|
if err == nil {
|
|
t.Fatal("expected error for non-existent short_code, got nil")
|
|
}
|
|
}
|