package service import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/gochat/gochat/internal/model" ) // ========== ListByAccount ========== func TestContactService_ListByAccount_成功(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) // 创建2个联系人 contact1 := &model.Contact{AccountID: account.ID, Name: "联系人1", Email: "c1@test.com"} contact2 := &model.Contact{AccountID: account.ID, Name: "联系人2", Email: "c2@test.com"} require.NoError(t, db.Create(contact1).Error) require.NoError(t, db.Create(contact2).Error) contacts, total, err := svc.ListByAccount(context.Background(), account.ID, 0, 10, "") require.NoError(t, err) assert.Equal(t, int64(2), total) assert.Len(t, contacts, 2) } func TestContactService_ListByAccount_空列表(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) contacts, total, err := svc.ListByAccount(context.Background(), account.ID, 0, 10, "") require.NoError(t, err) assert.Equal(t, int64(0), total) assert.Len(t, contacts, 0) } // ========== Search ========== func TestContactService_Search_空查询返回全部(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) contact := &model.Contact{AccountID: account.ID, Name: "测试联系人", Email: "search@test.com"} require.NoError(t, db.Create(contact).Error) // 空查询应等同于 ListByAccount contacts, total, err := svc.Search(context.Background(), account.ID, "", 0, 10, "") require.NoError(t, err) assert.Equal(t, int64(1), total) assert.Len(t, contacts, 1) } func TestContactService_Search_有查询需PG(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) contact := &model.Contact{AccountID: account.ID, Name: "张三", Email: "zhang@test.com"} require.NoError(t, db.Create(contact).Error) // Search 使用 ILIKE,SQLite 不支持 _, total, err := svc.Search(context.Background(), account.ID, "张", 0, 10, "") if err != nil { t.Logf("SQLite 下 Search 因 ILIKE 不支持而失败: %v", err) t.Skip("跳过: SQLite 不支持 ILIKE (ContactRepo.Search 需要 PG)") } require.NoError(t, err) assert.GreaterOrEqual(t, total, int64(1)) } // ========== GetByID ========== func TestContactService_GetByID_成功(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) contact := createTestContact(t, db, account.ID) result, err := svc.GetByID(context.Background(), contact.ID) require.NoError(t, err) assert.Equal(t, contact.ID, result.ID) assert.Equal(t, "测试联系人", result.Name) } func TestContactService_GetByID_不存在(t *testing.T) { _, _, svc := setupContactService(t) result, err := svc.GetByID(context.Background(), 9999) assert.Error(t, err) assert.Nil(t, result) } // ========== GetByAccountAndID ========== func TestContactService_GetByAccountAndID_成功(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) contact := createTestContact(t, db, account.ID) result, err := svc.GetByAccountAndID(context.Background(), account.ID, contact.ID) require.NoError(t, err) assert.Equal(t, contact.ID, result.ID) } func TestContactService_GetByAccountAndID_账户不匹配(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) otherAccount := createTestAccount(t, db) contact := createTestContact(t, db, account.ID) result, err := svc.GetByAccountAndID(context.Background(), otherAccount.ID, contact.ID) assert.Error(t, err) assert.Nil(t, result) } // ========== Create ========== func TestContactService_Create_成功(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) req := CreateContactRequest{ Name: "新建联系人", Email: "new@test.com", Phone: "13800138000", Identifier: "user_001", AvatarURL: "https://example.com/avatar.jpg", } result, err := svc.Create(context.Background(), account.ID, req) require.NoError(t, err) assert.Equal(t, "新建联系人", result.Name) assert.Equal(t, "new@test.com", result.Email) assert.Equal(t, account.ID, result.AccountID) assert.NotZero(t, result.ID) } func TestContactService_Create_验证失败_名称为空(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) req := CreateContactRequest{ Name: "", // required,min=1 } result, err := svc.Create(context.Background(), account.ID, req) assert.Error(t, err) assert.Nil(t, result) } func TestContactService_Create_验证失败_无效邮箱(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) req := CreateContactRequest{ Name: "有效名称", Email: "not-an-email", // email 格式校验 } result, err := svc.Create(context.Background(), account.ID, req) assert.Error(t, err) assert.Nil(t, result) } func TestContactService_Create_最小字段(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) req := CreateContactRequest{ Name: "最小联系人", } result, err := svc.Create(context.Background(), account.ID, req) require.NoError(t, err) assert.Equal(t, "最小联系人", result.Name) assert.Equal(t, account.ID, result.AccountID) } // ========== Update ========== func TestContactService_Update_成功(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) contact := createTestContact(t, db, account.ID) req := UpdateContactRequest{ Name: "更新后的名称", Email: "updated@test.com", Phone: "13900139000", Identifier: "upd_001", } result, err := svc.Update(context.Background(), account.ID, contact.ID, req) require.NoError(t, err) assert.Equal(t, "更新后的名称", result.Name) assert.Equal(t, "updated@test.com", result.Email) assert.Equal(t, "13900139000", result.PhoneNumber) assert.Equal(t, "upd_001", result.Identifier) } func TestContactService_Update_部分字段(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) contact := createTestContact(t, db, account.ID) req := UpdateContactRequest{ Name: "仅更新名称", } result, err := svc.Update(context.Background(), account.ID, contact.ID, req) require.NoError(t, err) assert.Equal(t, "仅更新名称", result.Name) // 其他字段保持原值 assert.Equal(t, contact.Email, result.Email) } func TestContactService_Update_验证失败_名称过短(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) contact := createTestContact(t, db, account.ID) // 注意: omitempty,min=1 → 空字符串时 omitempty 优先,不会触发 min=1 // 这里实际不会报错,但让我们测试不合理的邮箱 req2 := UpdateContactRequest{ Email: "bad-email", } result2, err2 := svc.Update(context.Background(), account.ID, contact.ID, req2) assert.Error(t, err2) assert.Nil(t, result2) } func TestContactService_Update_不存在(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) req := UpdateContactRequest{ Name: "不存在", } result, err := svc.Update(context.Background(), account.ID, 9999, req) assert.Error(t, err) assert.Nil(t, result) } func TestContactService_Update_账户不匹配(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) otherAccount := createTestAccount(t, db) contact := createTestContact(t, db, account.ID) req := UpdateContactRequest{ Name: "错误账户", } result, err := svc.Update(context.Background(), otherAccount.ID, contact.ID, req) assert.Error(t, err) assert.Nil(t, result) } // ========== Delete ========== func TestContactService_Delete_成功(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) contact := createTestContact(t, db, account.ID) err := svc.Delete(context.Background(), account.ID, contact.ID) require.NoError(t, err) // 验证软删除后无法找到 result, err := svc.GetByID(context.Background(), contact.ID) assert.Error(t, err) assert.Nil(t, result) } func TestContactService_Delete_不存在(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) err := svc.Delete(context.Background(), account.ID, 9999) assert.Error(t, err) } func TestContactService_Delete_账户不匹配(t *testing.T) { db := setupServiceTestDB(t) db, _, svc := setupContactService(t) account := createTestAccount(t, db) otherAccount := createTestAccount(t, db) contact := createTestContact(t, db, account.ID) err := svc.Delete(context.Background(), otherAccount.ID, contact.ID) assert.Error(t, err) } // ========== ListNotes (占位) ========== func TestContactService_ListNotes_占位实现(t *testing.T) { _, _, svc := setupContactService(t) notes, err := svc.ListNotes(context.Background(), 1, 1) require.NoError(t, err) assert.Len(t, notes, 0) } // ========== CreateNote (占位) ========== func TestContactService_CreateNote_成功(t *testing.T) { _, _, svc := setupContactService(t) req := CreateNoteRequest{ Content: "这是一条备注", } note, err := svc.CreateNote(context.Background(), 1, 1, 1, req) require.NoError(t, err) assert.Equal(t, "这是一条备注", note.Content) assert.Equal(t, uint(1), note.ContactID) assert.Equal(t, uint(1), note.CreatedBy) } func TestContactService_CreateNote_验证失败_内容为空(t *testing.T) { _, _, svc := setupContactService(t) req := CreateNoteRequest{ Content: "", // required,min=1 } note, err := svc.CreateNote(context.Background(), 1, 1, 1, req) assert.Error(t, err) assert.Nil(t, note) }