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.
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/gochat/gochat/internal/model"
|
||||
"github.com/gochat/gochat/internal/model/channel"
|
||||
)
|
||||
|
||||
// === Account fixtures ===
|
||||
|
||||
// NewAccountFixture returns a test Account without persisting it.
|
||||
func NewAccountFixture(name string) *model.Account {
|
||||
return &model.Account{
|
||||
Name: name,
|
||||
Locale: "en",
|
||||
Timezone: "UTC",
|
||||
Active: true,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateAccountFixture creates and persists a test Account.
|
||||
func CreateAccountFixture(t *testing.T, db *gorm.DB, name string) *model.Account {
|
||||
t.Helper()
|
||||
acc := NewAccountFixture(name)
|
||||
if err := db.Create(acc).Error; err != nil {
|
||||
panic(fmt.Sprintf("failed to create test account: %v", err))
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
// === User fixtures ===
|
||||
|
||||
// NewUserFixture returns a test User without persisting it.
|
||||
func NewUserFixture(accountID uint, name, email string) *model.User {
|
||||
return &model.User{
|
||||
AccountID: accountID,
|
||||
Name: name,
|
||||
Email: email,
|
||||
Password: "hashed_password_placeholder",
|
||||
Provider: "email",
|
||||
Role: string(model.AccountUserRoleAgent),
|
||||
Active: true,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateUserFixture creates and persists a test User.
|
||||
func CreateUserFixture(t *testing.T, db *gorm.DB, accountID uint, name, email string) *model.User {
|
||||
t.Helper()
|
||||
user := NewUserFixture(accountID, name, email)
|
||||
if err := db.Create(user).Error; err != nil {
|
||||
panic(fmt.Sprintf("failed to create test user: %v", err))
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
// === AccountUser fixtures ===
|
||||
|
||||
// NewAccountUserFixture returns a test AccountUser without persisting it.
|
||||
func NewAccountUserFixture(userID, accountID uint, role string) *model.AccountUser {
|
||||
return &model.AccountUser{
|
||||
UserID: userID,
|
||||
AccountID: accountID,
|
||||
Role: role,
|
||||
Availability: "online",
|
||||
}
|
||||
}
|
||||
|
||||
// CreateAccountUserFixture creates and persists a test AccountUser.
|
||||
func CreateAccountUserFixture(t *testing.T, db *gorm.DB, userID, accountID uint, role string) *model.AccountUser {
|
||||
t.Helper()
|
||||
au := NewAccountUserFixture(userID, accountID, role)
|
||||
if err := db.Create(au).Error; err != nil {
|
||||
panic(fmt.Sprintf("failed to create test account_user: %v", err))
|
||||
}
|
||||
return au
|
||||
}
|
||||
|
||||
// === Inbox fixtures ===
|
||||
|
||||
// NewInboxFixture returns a test Inbox without persisting it.
|
||||
func NewInboxFixture(accountID uint, name, channelType string) *model.Inbox {
|
||||
return &model.Inbox{
|
||||
AccountID: accountID,
|
||||
Name: name,
|
||||
ChannelType: channelType,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateInboxFixture creates and persists a test Inbox.
|
||||
func CreateInboxFixture(t *testing.T, db *gorm.DB, accountID uint, name, channelType string) *model.Inbox {
|
||||
t.Helper()
|
||||
inbox := NewInboxFixture(accountID, name, channelType)
|
||||
if err := db.Create(inbox).Error; err != nil {
|
||||
panic(fmt.Sprintf("failed to create test inbox: %v", err))
|
||||
}
|
||||
return inbox
|
||||
}
|
||||
|
||||
// === Contact fixtures ===
|
||||
|
||||
// NewContactFixture returns a test Contact without persisting it.
|
||||
func NewContactFixture(accountID uint, name string) *model.Contact {
|
||||
return &model.Contact{
|
||||
AccountID: accountID,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateContactFixture creates and persists a test Contact.
|
||||
func CreateContactFixture(t *testing.T, db *gorm.DB, accountID uint, name string) *model.Contact {
|
||||
t.Helper()
|
||||
contact := NewContactFixture(accountID, name)
|
||||
if err := db.Create(contact).Error; err != nil {
|
||||
panic(fmt.Sprintf("failed to create test contact: %v", err))
|
||||
}
|
||||
return contact
|
||||
}
|
||||
|
||||
// === Conversation fixtures ===
|
||||
|
||||
// NewConversationFixture returns a test Conversation without persisting it.
|
||||
func NewConversationFixture(accountID, inboxID, contactID uint, status string) *model.Conversation {
|
||||
return &model.Conversation{
|
||||
AccountID: accountID,
|
||||
InboxID: inboxID,
|
||||
ContactID: contactID,
|
||||
Status: status,
|
||||
ChannelType: "web_widget",
|
||||
}
|
||||
}
|
||||
|
||||
// CreateConversationFixture creates and persists a test Conversation.
|
||||
func CreateConversationFixture(t *testing.T, db *gorm.DB, accountID, inboxID, contactID uint, status string) *model.Conversation {
|
||||
t.Helper()
|
||||
conv := NewConversationFixture(accountID, inboxID, contactID, status)
|
||||
if err := db.Create(conv).Error; err != nil {
|
||||
panic(fmt.Sprintf("failed to create test conversation: %v", err))
|
||||
}
|
||||
return conv
|
||||
}
|
||||
|
||||
// === Message fixtures ===
|
||||
|
||||
// NewMessageFixture returns a test Message without persisting it.
|
||||
func NewMessageFixture(conversationID, accountID, inboxID uint, content string) *model.Message {
|
||||
return &model.Message{
|
||||
ConversationID: conversationID,
|
||||
AccountID: accountID,
|
||||
InboxID: inboxID,
|
||||
Content: content,
|
||||
ContentType: string(model.MessageContentTypeText),
|
||||
MessageType: string(model.MessageTypeIncoming),
|
||||
SenderType: "contact",
|
||||
}
|
||||
}
|
||||
|
||||
// CreateMessageFixture creates and persists a test Message.
|
||||
func CreateMessageFixture(t *testing.T, db *gorm.DB, conversationID, accountID, inboxID uint, content string) *model.Message {
|
||||
t.Helper()
|
||||
msg := NewMessageFixture(conversationID, accountID, inboxID, content)
|
||||
if err := db.Create(msg).Error; err != nil {
|
||||
panic(fmt.Sprintf("failed to create test message: %v", err))
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// === CustomRole fixtures ===
|
||||
|
||||
// NewCustomRoleFixture returns a test CustomRole without persisting it.
|
||||
func NewCustomRoleFixture(accountID uint, name string) *model.CustomRole {
|
||||
return &model.CustomRole{
|
||||
AccountID: accountID,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateCustomRoleFixture creates and persists a test CustomRole.
|
||||
func CreateCustomRoleFixture(t *testing.T, db *gorm.DB, accountID uint, name string) *model.CustomRole {
|
||||
t.Helper()
|
||||
role := NewCustomRoleFixture(accountID, name)
|
||||
if err := db.Create(role).Error; err != nil {
|
||||
panic(fmt.Sprintf("failed to create test custom_role: %v", err))
|
||||
}
|
||||
return role
|
||||
}
|
||||
|
||||
// === Channel fixtures ===
|
||||
|
||||
// NewChannelTelegramFixture returns a test ChannelTelegram without persisting it.
|
||||
func NewChannelTelegramFixture(accountID uint, botToken string) *channel.ChannelTelegram {
|
||||
return &channel.ChannelTelegram{
|
||||
AccountID: accountID,
|
||||
BotToken: botToken,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateChannelTelegramFixture creates and persists a test ChannelTelegram.
|
||||
func CreateChannelTelegramFixture(t *testing.T, db *gorm.DB, accountID uint, botToken string) *channel.ChannelTelegram {
|
||||
t.Helper()
|
||||
ch := NewChannelTelegramFixture(accountID, botToken)
|
||||
if err := db.Create(ch).Error; err != nil {
|
||||
panic(fmt.Sprintf("failed to create test channel_telegram: %v", err))
|
||||
}
|
||||
return ch
|
||||
}
|
||||
|
||||
// NewChannelWebWidgetFixture returns a test ChannelWebWidget without persisting it.
|
||||
func NewChannelWebWidgetFixture(inboxID uint, websiteURL string) *channel.ChannelWebWidget {
|
||||
return &channel.ChannelWebWidget{
|
||||
InboxID: inboxID,
|
||||
WebsiteURL: websiteURL,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateChannelWebWidgetFixture creates and persists a test ChannelWebWidget.
|
||||
func CreateChannelWebWidgetFixture(t *testing.T, db *gorm.DB, inboxID uint, websiteURL string) *channel.ChannelWebWidget {
|
||||
t.Helper()
|
||||
ch := NewChannelWebWidgetFixture(inboxID, websiteURL)
|
||||
if err := db.Create(ch).Error; err != nil {
|
||||
panic(fmt.Sprintf("failed to create test channel_web_widget: %v", err))
|
||||
}
|
||||
return ch
|
||||
}
|
||||
|
||||
// === Full seed chain ===
|
||||
|
||||
// SeedFullAccountChain creates a complete chain: Account → User → AccountUser
|
||||
// This is the minimal RBAC context needed for most tests.
|
||||
func SeedFullAccountChain(t *testing.T, db *gorm.DB) (*model.Account, *model.User, *model.AccountUser) {
|
||||
t.Helper()
|
||||
acc := CreateAccountFixture(t, db, "Test Account")
|
||||
user := CreateUserFixture(t, db, acc.ID, "Test User", "test@example.com")
|
||||
au := CreateAccountUserFixture(t, db, user.ID, acc.ID, string(model.AccountUserRoleAgent))
|
||||
return acc, user, au
|
||||
}
|
||||
|
||||
// SeedFullConversationChain creates: Account → Inbox → Contact → Conversation
|
||||
func SeedFullConversationChain(t *testing.T, db *gorm.DB) (*model.Account, *model.Inbox, *model.Contact, *model.Conversation) {
|
||||
t.Helper()
|
||||
acc := CreateAccountFixture(t, db, "Test Account")
|
||||
inbox := CreateInboxFixture(t, db, acc.ID, "Test Inbox", "web_widget")
|
||||
contact := CreateContactFixture(t, db, acc.ID, "Test Contact")
|
||||
conv := CreateConversationFixture(t, db, acc.ID, inbox.ID, contact.ID, string(model.ConversationStatusOpen))
|
||||
return acc, inbox, contact, conv
|
||||
}
|
||||
|
||||
// === Helpers for fixture assertions ===
|
||||
|
||||
// AssertFixtureCount asserts that a model table has the expected number of rows.
|
||||
func AssertFixtureCount(t *testing.T, db *gorm.DB, model interface{}, expected int64) {
|
||||
t.Helper()
|
||||
var count int64
|
||||
err := db.Model(model).Count(&count).Error
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, count)
|
||||
}
|
||||
Reference in New Issue
Block a user