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

656 lines
17 KiB
Go

package repository
import (
"context"
"fmt"
"testing"
"gorm.io/gorm"
"github.com/gochat/gochat/internal/model"
"github.com/gochat/gochat/pkg/crypto"
)
// createBenchmarkAccount creates a test account for benchmarks.
func createBenchmarkAccount(b *testing.B, db *gorm.DB, name string) *model.Account {
b.Helper()
account := &model.Account{Name: name, Locale: "en", Active: true}
if err := db.Create(account).Error; err != nil {
b.Fatalf("failed to create account: %v", err)
}
return account
}
// createBenchmarkUser creates a test user for benchmarks.
func createBenchmarkUser(b *testing.B, db *gorm.DB, accountID uint, email string) *model.User {
b.Helper()
password, err := crypto.HashPassword("BenchmarkPass123!")
if err != nil {
b.Fatalf("failed to hash password: %v", err)
}
user := &model.User{
AccountID: accountID,
Name: "Benchmark User",
Email: email,
Password: password,
Role: "agent",
Active: true,
}
if err := db.Create(user).Error; err != nil {
b.Fatalf("failed to create user: %v", err)
}
return user
}
// createBenchmarkInbox creates a test inbox for benchmarks.
func createBenchmarkInbox(b *testing.B, db *gorm.DB, accountID uint, name string) *model.Inbox {
b.Helper()
inbox := &model.Inbox{
AccountID: accountID,
Name: name,
ChannelType: "web_widget",
EnableAutoAssignment: false,
}
if err := db.Create(inbox).Error; err != nil {
b.Fatalf("failed to create inbox: %v", err)
}
return inbox
}
// createBenchmarkContact creates a test contact for benchmarks.
func createBenchmarkContact(b *testing.B, db *gorm.DB, accountID uint, email string) *model.Contact {
b.Helper()
contact := &model.Contact{
AccountID: accountID,
Name: "Benchmark Contact",
Email: email,
}
if err := db.Create(contact).Error; err != nil {
b.Fatalf("failed to create contact: %v", err)
}
return contact
}
// ============================================================================
// User Repository Benchmarks
// ============================================================================
func BenchmarkUserRepo_Create(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "User Bench Org")
repo := NewUserRepo(db)
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
user := &model.User{
AccountID: account.ID,
Name: fmt.Sprintf("BenchUser_%d", i),
Email: fmt.Sprintf("bench_user_%d@test.com", i),
Password: "hashed_password_placeholder",
Role: "agent",
Active: true,
}
if err := repo.Create(ctx, user); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkUserRepo_FindByID(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "User FindBench Org")
repo := NewUserRepo(db)
ctx := context.Background()
// Pre-create a user to find
user := createBenchmarkUser(b, db, account.ID, "find_by_id@test.com")
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := repo.FindByID(ctx, user.ID)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkUserRepo_FindByEmail(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "User EmailBench Org")
repo := NewUserRepo(db)
ctx := context.Background()
_ = createBenchmarkUser(b, db, account.ID, "find_by_email@test.com")
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := repo.FindByEmail(ctx, "find_by_email@test.com")
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkUserRepo_Update(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "User UpdateBench Org")
repo := NewUserRepo(db)
ctx := context.Background()
user := createBenchmarkUser(b, db, account.ID, "update_user@test.com")
b.ResetTimer()
for i := 0; i < b.N; i++ {
user.Name = fmt.Sprintf("UpdatedName_%d", i)
if err := repo.Update(ctx, user); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkUserRepo_Delete(b *testing.B) {
// For delete benchmark, we need to create a user per iteration
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "User DeleteBench Org")
repo := NewUserRepo(db)
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
user := &model.User{
AccountID: account.ID,
Name: fmt.Sprintf("DeleteUser_%d", i),
Email: fmt.Sprintf("delete_user_%d@test.com", i),
Password: "hashed_placeholder",
Role: "agent",
Active: true,
}
if err := repo.Create(ctx, user); err != nil {
b.Fatal(err)
}
if err := repo.Delete(ctx, user.ID); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkUserRepo_List(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "User ListBench Org")
repo := NewUserRepo(db)
ctx := context.Background()
// Pre-create 100 users for list benchmark
for i := 0; i < 100; i++ {
user := &model.User{
AccountID: account.ID,
Name: fmt.Sprintf("ListUser_%d", i),
Email: fmt.Sprintf("list_user_%d@test.com", i),
Password: "hashed_placeholder",
Role: "agent",
Active: true,
}
if err := repo.Create(ctx, user); err != nil {
b.Fatal(err)
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, err := repo.List(ctx, 0, 25)
if err != nil {
b.Fatal(err)
}
}
}
// ============================================================================
// Conversation Repository Benchmarks
// ============================================================================
func BenchmarkConversationRepo_Create(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "Conv Bench Org")
inbox := createBenchmarkInbox(b, db, account.ID, "Conv Bench Inbox")
contact := createBenchmarkContact(b, db, account.ID, "conv_bench@contact.com")
repo := NewConversationRepo(db)
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
conv := &model.Conversation{
AccountID: account.ID,
InboxID: inbox.ID,
ContactID: contact.ID,
Status: "open",
ChannelType: "web_widget",
Channel: "web_widget",
}
if err := repo.Create(ctx, conv); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkConversationRepo_FindByID(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "Conv FindBench Org")
inbox := createBenchmarkInbox(b, db, account.ID, "Conv FindBench Inbox")
contact := createBenchmarkContact(b, db, account.ID, "conv_find@contact.com")
repo := NewConversationRepo(db)
ctx := context.Background()
// Pre-create a conversation
conv := &model.Conversation{
AccountID: account.ID,
InboxID: inbox.ID,
ContactID: contact.ID,
Status: "open",
ChannelType: "web_widget",
Channel: "web_widget",
}
if err := db.Create(conv).Error; err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := repo.FindByID(ctx, conv.ID)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkConversationRepo_FindByAccount(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "Conv AcctBench Org")
inbox := createBenchmarkInbox(b, db, account.ID, "Conv AcctBench Inbox")
contact := createBenchmarkContact(b, db, account.ID, "conv_acct@contact.com")
repo := NewConversationRepo(db)
ctx := context.Background()
// Pre-create 50 conversations
for i := 0; i < 50; i++ {
conv := &model.Conversation{
AccountID: account.ID,
InboxID: inbox.ID,
ContactID: contact.ID,
Status: "open",
ChannelType: "web_widget",
Channel: "web_widget",
}
if err := db.Create(conv).Error; err != nil {
b.Fatal(err)
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, err := repo.FindByAccount(ctx, account.ID, 0, 25)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkConversationRepo_Update(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "Conv UpdateBench Org")
inbox := createBenchmarkInbox(b, db, account.ID, "Conv UpdateBench Inbox")
contact := createBenchmarkContact(b, db, account.ID, "conv_update@contact.com")
repo := NewConversationRepo(db)
ctx := context.Background()
conv := &model.Conversation{
AccountID: account.ID,
InboxID: inbox.ID,
ContactID: contact.ID,
Status: "open",
ChannelType: "web_widget",
Channel: "web_widget",
}
if err := db.Create(conv).Error; err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
conv.Status = "resolved"
if err := repo.Update(ctx, conv); err != nil {
b.Fatal(err)
}
conv.Status = "open"
}
}
func BenchmarkConversationRepo_Delete(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "Conv DelBench Org")
inbox := createBenchmarkInbox(b, db, account.ID, "Conv DelBench Inbox")
contact := createBenchmarkContact(b, db, account.ID, "conv_del@contact.com")
repo := NewConversationRepo(db)
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
conv := &model.Conversation{
AccountID: account.ID,
InboxID: inbox.ID,
ContactID: contact.ID,
Status: "open",
ChannelType: "web_widget",
Channel: "web_widget",
}
if err := db.Create(conv).Error; err != nil {
b.Fatal(err)
}
if err := repo.Delete(ctx, conv.ID); err != nil {
b.Fatal(err)
}
}
}
// ============================================================================
// Message Repository Benchmarks
// ============================================================================
func BenchmarkMessageRepo_Create(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "Msg Bench Org")
inbox := createBenchmarkInbox(b, db, account.ID, "Msg Bench Inbox")
contact := createBenchmarkContact(b, db, account.ID, "msg_bench@contact.com")
conv := &model.Conversation{
AccountID: account.ID,
InboxID: inbox.ID,
ContactID: contact.ID,
Status: "open",
ChannelType: "web_widget",
Channel: "web_widget",
}
if err := db.Create(conv).Error; err != nil {
b.Fatal(err)
}
repo := NewMessageRepo(db)
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
msg := &model.Message{
ConversationID: conv.ID,
AccountID: account.ID,
InboxID: inbox.ID,
SenderType: "contact",
Content: fmt.Sprintf("Benchmark message %d", i),
ContentType: "text",
MessageType: "incoming",
}
if err := repo.Create(ctx, msg); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkMessageRepo_FindByID(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "Msg FindBench Org")
inbox := createBenchmarkInbox(b, db, account.ID, "Msg FindBench Inbox")
contact := createBenchmarkContact(b, db, account.ID, "msg_find@contact.com")
conv := &model.Conversation{
AccountID: account.ID,
InboxID: inbox.ID,
ContactID: contact.ID,
Status: "open",
ChannelType: "web_widget",
Channel: "web_widget",
}
if err := db.Create(conv).Error; err != nil {
b.Fatal(err)
}
repo := NewMessageRepo(db)
ctx := context.Background()
msg := &model.Message{
ConversationID: conv.ID,
AccountID: account.ID,
InboxID: inbox.ID,
SenderType: "contact",
Content: "Find benchmark message",
ContentType: "text",
MessageType: "incoming",
}
if err := db.Create(msg).Error; err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := repo.FindByID(ctx, msg.ID)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkMessageRepo_FindByConversation(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "Msg ConvBench Org")
inbox := createBenchmarkInbox(b, db, account.ID, "Msg ConvBench Inbox")
contact := createBenchmarkContact(b, db, account.ID, "msg_conv@contact.com")
conv := &model.Conversation{
AccountID: account.ID,
InboxID: inbox.ID,
ContactID: contact.ID,
Status: "open",
ChannelType: "web_widget",
Channel: "web_widget",
}
if err := db.Create(conv).Error; err != nil {
b.Fatal(err)
}
repo := NewMessageRepo(db)
ctx := context.Background()
// Pre-create 50 messages
for i := 0; i < 50; i++ {
msg := &model.Message{
ConversationID: conv.ID,
AccountID: account.ID,
InboxID: inbox.ID,
SenderType: "contact",
Content: fmt.Sprintf("Pre-created message %d", i),
ContentType: "text",
MessageType: "incoming",
}
if err := db.Create(msg).Error; err != nil {
b.Fatal(err)
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, err := repo.FindByConversation(ctx, conv.ID, 0, 25)
if err != nil {
b.Fatal(err)
}
}
}
// ============================================================================
// Notification Repository Benchmarks
// ============================================================================
func BenchmarkNotificationRepo_Create(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "Notif Bench Org")
user := createBenchmarkUser(b, db, account.ID, "notif_bench@test.com")
repo := NewNotificationRepo(db)
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
notif := &model.Notification{
AccountID: &account.ID,
UserID: user.ID,
NotificationType: "conversation_created",
PrimaryActorType: "Conversation",
PrimaryActorID: uint(i % 1000),
}
if err := repo.Create(ctx, notif); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkNotificationRepo_FindByID(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "Notif FindBench Org")
user := createBenchmarkUser(b, db, account.ID, "notif_find@test.com")
repo := NewNotificationRepo(db)
ctx := context.Background()
notif := &model.Notification{
AccountID: &account.ID,
UserID: user.ID,
NotificationType: "conversation_created",
PrimaryActorType: "Conversation",
PrimaryActorID: 1,
}
if err := db.Create(notif).Error; err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := repo.FindByID(ctx, notif.ID)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkNotificationRepo_ListByUser(b *testing.B) {
db := setupBenchmarkDB(b)
account := createBenchmarkAccount(b, db, "Notif ListBench Org")
user := createBenchmarkUser(b, db, account.ID, "notif_list@test.com")
repo := NewNotificationRepo(db)
ctx := context.Background()
// Pre-create 50 notifications
for i := 0; i < 50; i++ {
notif := &model.Notification{
AccountID: &account.ID,
UserID: user.ID,
NotificationType: "conversation_created",
PrimaryActorType: "Conversation",
PrimaryActorID: uint(i),
}
if err := db.Create(notif).Error; err != nil {
b.Fatal(err)
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, err := repo.ListByUser(ctx, user.ID, 0, 25)
if err != nil {
b.Fatal(err)
}
}
}
// ============================================================================
// BaseRepository Generic CRUD Benchmarks
// ============================================================================
func BenchmarkBaseRepository_Create(b *testing.B) {
db := setupBenchmarkDB(b)
repo := NewBaseRepository[model.Account](db)
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
account := &model.Account{Name: fmt.Sprintf("BenchOrg_%d", i), Locale: "en", Active: true}
if err := repo.Create(ctx, account); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkBaseRepository_GetByID(b *testing.B) {
db := setupBenchmarkDB(b)
repo := NewBaseRepository[model.Account](db)
ctx := context.Background()
account := &model.Account{Name: "BenchGetOrg", Locale: "en", Active: true}
if err := repo.Create(ctx, account); err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := repo.GetByID(ctx, account.ID)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkBaseRepository_Update(b *testing.B) {
db := setupBenchmarkDB(b)
repo := NewBaseRepository[model.Account](db)
ctx := context.Background()
account := &model.Account{Name: "BenchUpdateOrg", Locale: "en", Active: true}
if err := repo.Create(ctx, account); err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
account.Name = fmt.Sprintf("UpdatedOrg_%d", i)
if err := repo.Update(ctx, account); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkBaseRepository_Delete(b *testing.B) {
db := setupBenchmarkDB(b)
repo := NewBaseRepository[model.Account](db)
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
account := &model.Account{Name: fmt.Sprintf("BenchDelOrg_%d", i), Locale: "en", Active: true}
if err := repo.Create(ctx, account); err != nil {
b.Fatal(err)
}
if err := repo.Delete(ctx, account.ID); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkBaseRepository_List(b *testing.B) {
db := setupBenchmarkDB(b)
repo := NewBaseRepository[model.Account](db)
ctx := context.Background()
// Pre-create 100 accounts
for i := 0; i < 100; i++ {
account := &model.Account{Name: fmt.Sprintf("BenchListOrg_%d", i), Locale: "en", Active: true}
if err := repo.Create(ctx, account); err != nil {
b.Fatal(err)
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := repo.List(ctx, 0, 25)
if err != nil {
b.Fatal(err)
}
}
}