Files
gochat/backend/internal/repository/contact_inbox_repo_test.go
T
rogee aeddedf2a3 Reorganize repo: backend/, deploy/, docs/ layout + AGENTS.md
Restructure the monorepo into clear top-level directories:
- backend/: Go module root (cmd, internal, pkg, configs, migrations,
  docs/swagger, scripts, tests, go.mod, Makefile, .air.toml)
- deploy/: Docker (Dockerfile, docker-compose*), quickstart, fluentd
- docs/: project documentation + reports/ (moved from repo root)
- AGENTS.md: new AI coding-agent guide at repo root

Update all references to the new layout:
- Dockerfile: COPY backend/go.mod, COPY backend/ (context = repo root)
- docker-compose files: context ../.., dockerfile deploy/docker/Dockerfile,
  env_file ../../.env, volume mounts ../../backend:/app
- deploy/quickstart/compose.yaml: dockerfile deploy/docker/Dockerfile
- CI: working-directory: backend for go commands, file deploy/docker/Dockerfile,
  coverage path backend/coverage.out, health_check backend/scripts/
- backend/Makefile: docker target uses -f ../deploy/docker/Dockerfile ../
- README: architecture tree, quickstart, config paths updated

Move root stray scripts (rename_models.*, run_m11_tests.sh, verify_build.sh,
gorm_bool_main.go) to backend/scripts/legacy/. All moves via git mv to
preserve history. Build, vet, SQLite tests, and docker compose config verified.
2026-07-07 14:44:12 +08:00

307 lines
9.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package repository
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"github.com/gochat/gochat/internal/model"
)
// Helper: create a test ContactInbox with prerequisites (Account, Contact, Inbox).
func createTestContactInbox(t *testing.T, db *gorm.DB, sourceID, pubsubToken string) *model.ContactInbox {
t.Helper()
account := &model.Account{Name: "CITestOrg", Locale: "en", Active: true}
require.NoError(t, db.Create(account).Error)
contact := &model.Contact{AccountID: account.ID, Name: "CIContact", Email: "ci@example.com"}
require.NoError(t, db.Create(contact).Error)
inbox := &model.Inbox{AccountID: account.ID, Name: "CIInbox", ChannelType: "web_widget", ChannelID: 1}
require.NoError(t, db.Create(inbox).Error)
ci := &model.ContactInbox{
ContactID: contact.ID,
InboxID: inbox.ID,
SourceID: sourceID,
PubsubToken: pubsubToken,
}
require.NoError(t, db.Create(ci).Error)
return ci
}
// 1. FindByID success
func TestContactInboxRepo_FindByID(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
ci := createTestContactInbox(t, db, "src-001", "pub-001")
found, err := repo.FindByID(context.Background(), ci.ID)
assert.NoError(t, err)
assert.Equal(t, ci.ID, found.ID)
assert.Equal(t, ci.ContactID, found.ContactID)
assert.Equal(t, ci.InboxID, found.InboxID)
assert.Equal(t, "src-001", found.SourceID)
assert.Equal(t, "pub-001", found.PubsubToken)
}
// 2. FindByID not found
func TestContactInboxRepo_FindByID_NotFound(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
found, err := repo.FindByID(context.Background(), 9999)
assert.Error(t, err)
assert.Nil(t, found)
}
// 3. FindByContactAndInbox success
func TestContactInboxRepo_FindByContactAndInbox(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
ci := createTestContactInbox(t, db, "src-002", "pub-002")
found, err := repo.FindByContactAndInbox(context.Background(), ci.ContactID, ci.InboxID)
assert.NoError(t, err)
assert.Equal(t, ci.ID, found.ID)
assert.Equal(t, ci.ContactID, found.ContactID)
assert.Equal(t, ci.InboxID, found.InboxID)
}
// 4. FindByContactAndInbox not found
func TestContactInboxRepo_FindByContactAndInbox_NotFound(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
found, err := repo.FindByContactAndInbox(context.Background(), 8888, 9999)
assert.Error(t, err)
assert.Nil(t, found)
}
func TestContactInboxRepo_FindByContactInboxSource(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
ci := createTestContactInbox(t, db, "browser-abc", "pub-source")
found, err := repo.FindByContactInboxSource(context.Background(), ci.ContactID, ci.InboxID, "browser-abc")
assert.NoError(t, err)
assert.Equal(t, ci.ID, found.ID)
assert.Equal(t, ci.SourceID, found.SourceID)
assert.Equal(t, ci.InboxID, found.Inbox.ID)
}
// 5. FindByPubsubToken success (with Contact preloaded)
func TestContactInboxRepo_FindByPubsubToken(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
ci := createTestContactInbox(t, db, "src-003", "pub-003")
found, err := repo.FindByPubsubToken(context.Background(), "pub-003")
assert.NoError(t, err)
assert.Equal(t, ci.ID, found.ID)
assert.Equal(t, "pub-003", found.PubsubToken)
// Verify Contact is preloaded
assert.Equal(t, ci.ContactID, found.Contact.ID)
assert.Equal(t, "CIContact", found.Contact.Name)
}
// 6. FindByPubsubToken not found
func TestContactInboxRepo_FindByPubsubToken_NotFound(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
found, err := repo.FindByPubsubToken(context.Background(), "nonexistent-token")
assert.Error(t, err)
assert.Nil(t, found)
}
// 7. FindByContact returns multiple contact_inboxes for same contact
func TestContactInboxRepo_FindByContact(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
account := &model.Account{Name: "CIListOrg", Locale: "en", Active: true}
require.NoError(t, db.Create(account).Error)
contact := &model.Contact{AccountID: account.ID, Name: "CIListContact"}
require.NoError(t, db.Create(contact).Error)
inbox1 := &model.Inbox{AccountID: account.ID, Name: "CIListInbox1", ChannelType: "web_widget", ChannelID: 1}
require.NoError(t, db.Create(inbox1).Error)
inbox2 := &model.Inbox{AccountID: account.ID, Name: "CIListInbox2", ChannelType: "facebook", ChannelID: 2}
require.NoError(t, db.Create(inbox2).Error)
ci1 := &model.ContactInbox{ContactID: contact.ID, InboxID: inbox1.ID, SourceID: "src-a", PubsubToken: "pub-a"}
require.NoError(t, db.Create(ci1).Error)
ci2 := &model.ContactInbox{ContactID: contact.ID, InboxID: inbox2.ID, SourceID: "src-b", PubsubToken: "pub-b"}
require.NoError(t, db.Create(ci2).Error)
results, err := repo.FindByContact(context.Background(), contact.ID)
assert.NoError(t, err)
assert.Len(t, results, 2)
// Ordered by id ASC, so ci1 comes first
assert.Equal(t, ci1.ID, results[0].ID)
assert.Equal(t, ci2.ID, results[1].ID)
}
// 8. FindByContact no results
func TestContactInboxRepo_FindByContact_Empty(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
results, err := repo.FindByContact(context.Background(), 9999)
assert.NoError(t, err)
assert.Len(t, results, 0)
}
// 9. FindByInbox with pagination
func TestContactInboxRepo_FindByInbox(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
account := &model.Account{Name: "CIInboxOrg", Locale: "en", Active: true}
require.NoError(t, db.Create(account).Error)
inbox := &model.Inbox{AccountID: account.ID, Name: "CIInboxPag", ChannelType: "web_widget", ChannelID: 1}
require.NoError(t, db.Create(inbox).Error)
// Create 3 contacts with contact_inboxes in the same inbox
for i := 0; i < 3; i++ {
contact := &model.Contact{AccountID: account.ID, Name: "CIInboxContact" + string(rune('A'+i))}
require.NoError(t, db.Create(contact).Error)
ci := &model.ContactInbox{ContactID: contact.ID, InboxID: inbox.ID, SourceID: "src-inbox-" + string(rune('A'+i))}
require.NoError(t, db.Create(ci).Error)
}
// Page 1: offset=0, limit=2
results, total, err := repo.FindByInbox(context.Background(), inbox.ID, 0, 2)
assert.NoError(t, err)
assert.Equal(t, int64(3), total)
assert.Len(t, results, 2)
// Page 2: offset=2, limit=2
results2, total2, err := repo.FindByInbox(context.Background(), inbox.ID, 2, 2)
assert.NoError(t, err)
assert.Equal(t, int64(3), total2)
assert.Len(t, results2, 1)
}
// 10. FindBySourceID success
func TestContactInboxRepo_FindBySourceID(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
ci := createTestContactInbox(t, db, "src-unique-007", "pub-007")
found, err := repo.FindBySourceID(context.Background(), ci.InboxID, "src-unique-007")
assert.NoError(t, err)
assert.Equal(t, ci.ID, found.ID)
assert.Equal(t, "src-unique-007", found.SourceID)
assert.Equal(t, ci.InboxID, found.InboxID)
}
// 11. FindBySourceID not found
func TestContactInboxRepo_FindBySourceID_NotFound(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
found, err := repo.FindBySourceID(context.Background(), 9999, "nonexistent-source")
assert.Error(t, err)
assert.Nil(t, found)
}
// 12. Create success
func TestContactInboxRepo_Create(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
account := &model.Account{Name: "CICreateOrg", Locale: "en", Active: true}
require.NoError(t, db.Create(account).Error)
contact := &model.Contact{AccountID: account.ID, Name: "CICreateContact"}
require.NoError(t, db.Create(contact).Error)
inbox := &model.Inbox{AccountID: account.ID, Name: "CICreateInbox", ChannelType: "web_widget", ChannelID: 1}
require.NoError(t, db.Create(inbox).Error)
ci := &model.ContactInbox{
ContactID: contact.ID,
InboxID: inbox.ID,
SourceID: "src-create",
PubsubToken: "pub-create",
HMACToken: "hmac-create",
}
err := repo.Create(context.Background(), ci)
assert.NoError(t, err)
assert.NotZero(t, ci.ID)
// Verify persisted
found, err := repo.FindByID(context.Background(), ci.ID)
assert.NoError(t, err)
assert.Equal(t, "src-create", found.SourceID)
assert.Equal(t, "pub-create", found.PubsubToken)
assert.Equal(t, "hmac-create", found.HMACToken)
}
// 13. Update success
func TestContactInboxRepo_Update(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
ci := createTestContactInbox(t, db, "src-update-old", "pub-update-old")
// Modify fields
ci.SourceID = "src-update-new"
ci.PubsubToken = "pub-update-new"
err := repo.Update(context.Background(), ci)
assert.NoError(t, err)
// Verify persisted
found, err := repo.FindByID(context.Background(), ci.ID)
assert.NoError(t, err)
assert.Equal(t, "src-update-new", found.SourceID)
assert.Equal(t, "pub-update-new", found.PubsubToken)
}
// 14. Delete success
func TestContactInboxRepo_Delete(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
ci := createTestContactInbox(t, db, "src-delete", "pub-delete")
err := repo.Delete(context.Background(), ci.ID)
assert.NoError(t, err)
// Verify soft-deleted (FindByID should fail due to soft delete)
found, err := repo.FindByID(context.Background(), ci.ID)
assert.Error(t, err)
assert.Nil(t, found)
}
// 15. DeleteByContactAndInbox success
func TestContactInboxRepo_DeleteByContactAndInbox(t *testing.T) {
db := setupTestDB(t)
repo := NewContactInboxRepo(db)
ci := createTestContactInbox(t, db, "src-del-ci", "pub-del-ci")
err := repo.DeleteByContactAndInbox(context.Background(), ci.ContactID, ci.InboxID)
assert.NoError(t, err)
// Verify soft-deleted
found, err := repo.FindByContactAndInbox(context.Background(), ci.ContactID, ci.InboxID)
assert.Error(t, err)
assert.Nil(t, found)
}