Files
gochat/internal/service/custom_attribute_definition_service_test.go
T
2026-06-04 15:44:48 +08:00

350 lines
11 KiB
Go

package service
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"github.com/gochat/gochat/internal/model"
"github.com/gochat/gochat/internal/repository"
)
// ========== Test Setup ==========
func setupCustomAttrDefServiceTestDB(t *testing.T) *gorm.DB {
t.Helper()
db, err := gorm.Open(sqlite.Open("file::memory:"), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if err != nil {
t.Fatalf("failed to open SQLite test database: %v", err)
}
if err := db.AutoMigrate(
&model.Account{},
&model.User{},
&model.CustomAttributeDefinition{},
); err != nil {
t.Fatalf("failed to auto-migrate models: %v", err)
}
t.Cleanup(func() {
sqlDB, _ := db.DB()
sqlDB.Close()
})
return db
}
func setupCustomAttrDefService(t *testing.T) (*CustomAttributeDefinitionService, uint) {
t.Helper()
db := setupCustomAttrDefServiceTestDB(t)
repo := repository.NewCustomAttributeDefinitionRepo(db)
svc := NewCustomAttributeDefinitionService(repo)
account := createTestAccount(t, db)
return svc, account.ID
}
// ========== Create ==========
func TestCustomAttributeDefinitionService_Create(t *testing.T) {
svc, accountID := setupCustomAttrDefService(t)
req := &CreateCustomAttributeDefinitionRequest{
AttributeKey: "custom_priority_score",
AttributeDisplayName: "Custom Priority Score",
AttributeDisplayType: "text",
AttributeModel: "conversation",
AttributeDescription: "Conversation priority level",
}
def, err := svc.Create(context.Background(), accountID, req)
require.NoError(t, err)
assert.NotZero(t, def.ID)
assert.Equal(t, accountID, def.AccountID)
assert.Equal(t, "custom_priority_score", def.AttributeName)
assert.Equal(t, "Custom Priority Score", def.AttributeDisplayName)
assert.Equal(t, "text", def.AttributeType)
assert.Equal(t, "conversation", def.AttributeModel)
assert.Equal(t, "Conversation priority level", def.Description)
}
func TestCustomAttributeDefinitionService_Create_InvalidType(t *testing.T) {
svc, accountID := setupCustomAttrDefService(t)
req := &CreateCustomAttributeDefinitionRequest{
AttributeKey: "bad_type",
AttributeDisplayName: "Bad Type",
AttributeDisplayType: "invalid_type",
AttributeModel: "conversation",
}
_, err := svc.Create(context.Background(), accountID, req)
assert.Error(t, err, "invalid attribute_type should fail validation")
}
func TestCustomAttributeDefinitionService_Create_InvalidModel(t *testing.T) {
svc, accountID := setupCustomAttrDefService(t)
req := &CreateCustomAttributeDefinitionRequest{
AttributeKey: "bad_model",
AttributeDisplayName: "Bad Model",
AttributeDisplayType: "text",
AttributeModel: "invalid_model",
}
_, err := svc.Create(context.Background(), accountID, req)
assert.Error(t, err, "invalid attribute_model should fail validation")
}
func TestCustomAttributeDefinitionService_Create_EmptyName(t *testing.T) {
svc, accountID := setupCustomAttrDefService(t)
req := &CreateCustomAttributeDefinitionRequest{
AttributeKey: "",
AttributeDisplayName: "Empty Name",
AttributeDisplayType: "text",
AttributeModel: "conversation",
}
_, err := svc.Create(context.Background(), accountID, req)
assert.Error(t, err, "empty attribute_name should fail validation")
}
func TestCustomAttributeDefinitionService_Create_AllValidTypes(t *testing.T) {
svc, accountID := setupCustomAttrDefService(t)
validTypes := []string{"text", "number", "date", "checkbox", "list", "link"}
for _, attrType := range validTypes {
req := &CreateCustomAttributeDefinitionRequest{
AttributeKey: "attr_" + attrType,
AttributeDisplayName: "Attr " + attrType,
AttributeDisplayType: attrType,
AttributeModel: "conversation",
}
def, err := svc.Create(context.Background(), accountID, req)
require.NoError(t, err, "attribute_type=%s should be valid", attrType)
assert.Equal(t, attrType, def.AttributeType)
}
}
// ========== Get ==========
func TestCustomAttributeDefinitionService_Get(t *testing.T) {
svc, accountID := setupCustomAttrDefService(t)
req := &CreateCustomAttributeDefinitionRequest{
AttributeKey: "status",
AttributeDisplayName: "Status",
AttributeDisplayType: "list",
AttributeModel: "contact",
}
def, err := svc.Create(context.Background(), accountID, req)
require.NoError(t, err)
found, err := svc.Get(context.Background(), accountID, def.ID)
require.NoError(t, err)
assert.Equal(t, def.ID, found.ID)
assert.Equal(t, "status", found.AttributeName)
}
func TestCustomAttributeDefinitionService_Get_WrongAccount(t *testing.T) {
svc, accountID := setupCustomAttrDefService(t)
req := &CreateCustomAttributeDefinitionRequest{
AttributeKey: "private_attr",
AttributeDisplayName: "Private",
AttributeDisplayType: "text",
AttributeModel: "conversation",
}
def, err := svc.Create(context.Background(), accountID, req)
require.NoError(t, err)
// Trying to get from a different account should fail
_, err = svc.Get(context.Background(), 9999, def.ID)
assert.Error(t, err, "should not find definition belonging to different account")
}
func TestCustomAttributeDefinitionService_Get_NotFound(t *testing.T) {
svc, _ := setupCustomAttrDefService(t)
_, err := svc.Get(context.Background(), 1, 9999)
assert.Error(t, err)
}
// ========== List ==========
func TestCustomAttributeDefinitionService_List_All(t *testing.T) {
svc, accountID := setupCustomAttrDefService(t)
for _, name := range []string{"attr_a", "attr_b"} {
req := &CreateCustomAttributeDefinitionRequest{
AttributeKey: name,
AttributeDisplayName: name,
AttributeDisplayType: "text",
AttributeModel: "conversation",
}
_, err := svc.Create(context.Background(), accountID, req)
require.NoError(t, err)
}
// Create a contact attribute
req := &CreateCustomAttributeDefinitionRequest{
AttributeKey: "contact_level",
AttributeDisplayName: "Contact Level",
AttributeDisplayType: "text",
AttributeModel: "contact",
}
_, err := svc.Create(context.Background(), accountID, req)
require.NoError(t, err)
defs, total, err := svc.List(context.Background(), accountID, "", 0, 10)
require.NoError(t, err)
assert.Equal(t, int64(3), total)
assert.Len(t, defs, 3)
}
func TestCustomAttributeDefinitionService_List_FilterByModel(t *testing.T) {
svc, accountID := setupCustomAttrDefService(t)
// Create conversation attributes
for _, name := range []string{"conv_a", "conv_b"} {
req := &CreateCustomAttributeDefinitionRequest{
AttributeKey: name,
AttributeDisplayName: name,
AttributeDisplayType: "text",
AttributeModel: "conversation",
}
_, err := svc.Create(context.Background(), accountID, req)
require.NoError(t, err)
}
// Create contact attribute
req := &CreateCustomAttributeDefinitionRequest{
AttributeKey: "contact_c",
AttributeDisplayName: "Contact C",
AttributeDisplayType: "text",
AttributeModel: "contact",
}
_, err := svc.Create(context.Background(), accountID, req)
require.NoError(t, err)
convDefs, convTotal, err := svc.List(context.Background(), accountID, "conversation", 0, 10)
require.NoError(t, err)
assert.Equal(t, int64(2), convTotal)
assert.Len(t, convDefs, 2)
contactDefs, contactTotal, err := svc.List(context.Background(), accountID, "contact", 0, 10)
require.NoError(t, err)
assert.Equal(t, int64(1), contactTotal)
assert.Len(t, contactDefs, 1)
}
// ========== Update ==========
func TestCustomAttributeDefinitionService_Update(t *testing.T) {
svc, accountID := setupCustomAttrDefService(t)
createReq := &CreateCustomAttributeDefinitionRequest{
AttributeKey: "custom_priority_score",
AttributeDisplayName: "Custom Priority Score",
AttributeDisplayType: "text",
AttributeModel: "conversation",
}
def, err := svc.Create(context.Background(), accountID, createReq)
require.NoError(t, err)
updateReq := &UpdateCustomAttributeDefinitionRequest{
AttributeDisplayName: "Priority Level",
AttributeDisplayType: "number",
AttributeDescription: "Updated description",
}
updated, err := svc.Update(context.Background(), accountID, def.ID, updateReq)
require.NoError(t, err)
assert.Equal(t, "Priority Level", updated.AttributeDisplayName)
assert.Equal(t, "number", updated.AttributeType)
assert.Equal(t, "Updated description", updated.Description)
// Name and model should stay unchanged
assert.Equal(t, "custom_priority_score", updated.AttributeName)
assert.Equal(t, "conversation", updated.AttributeModel)
}
func TestCustomAttributeDefinitionService_Update_WrongAccount(t *testing.T) {
svc, accountID := setupCustomAttrDefService(t)
createReq := &CreateCustomAttributeDefinitionRequest{
AttributeKey: "private_attr",
AttributeDisplayName: "Private",
AttributeDisplayType: "text",
AttributeModel: "conversation",
}
def, err := svc.Create(context.Background(), accountID, createReq)
require.NoError(t, err)
updateReq := &UpdateCustomAttributeDefinitionRequest{
AttributeDisplayName: "Hacked",
}
_, err = svc.Update(context.Background(), 9999, def.ID, updateReq)
assert.Error(t, err, "should not update definition belonging to different account")
}
func TestCustomAttributeDefinitionService_Update_NotFound(t *testing.T) {
svc, accountID := setupCustomAttrDefService(t)
updateReq := &UpdateCustomAttributeDefinitionRequest{
AttributeDisplayName: "Nonexistent",
}
_, err := svc.Update(context.Background(), accountID, 9999, updateReq)
assert.Error(t, err)
}
// ========== Delete ==========
func TestCustomAttributeDefinitionService_Delete(t *testing.T) {
svc, accountID := setupCustomAttrDefService(t)
createReq := &CreateCustomAttributeDefinitionRequest{
AttributeKey: "temp_attr",
AttributeDisplayName: "Temp",
AttributeDisplayType: "text",
AttributeModel: "conversation",
}
def, err := svc.Create(context.Background(), accountID, createReq)
require.NoError(t, err)
err = svc.Delete(context.Background(), accountID, def.ID)
require.NoError(t, err)
// Should no longer be accessible via Get
_, err = svc.Get(context.Background(), accountID, def.ID)
assert.Error(t, err)
}
func TestCustomAttributeDefinitionService_Delete_WrongAccount(t *testing.T) {
svc, accountID := setupCustomAttrDefService(t)
createReq := &CreateCustomAttributeDefinitionRequest{
AttributeKey: "private_attr",
AttributeDisplayName: "Private",
AttributeDisplayType: "text",
AttributeModel: "conversation",
}
def, err := svc.Create(context.Background(), accountID, createReq)
require.NoError(t, err)
err = svc.Delete(context.Background(), 9999, def.ID)
assert.Error(t, err, "should not delete definition belonging to different account")
}
func TestCustomAttributeDefinitionService_Delete_NotFound(t *testing.T) {
svc, accountID := setupCustomAttrDefService(t)
err := svc.Delete(context.Background(), accountID, 9999)
assert.Error(t, err)
}