* H-300: wire Captain Skills into Web runtime * H-300: enforce effective model and conservative skill budget * H-300: fix CI gosec step * ci: extend golangci-lint timeout * fix lint findings across backend * fix(push): resolve delivery protocol blockers * test(repository): close SQLite test databases * test(repository): reuse SQLite schema per package * H-307: restore backend Go cache in CI * H-307: prefetch modules before cold lint * H-307: resolve govulncheck security gate * H-307: build lint with patched Go toolchain * H-307: clear remaining security scan findings --------- Co-authored-by: Rogee <rogee@ipao.vip>
267 lines
7.2 KiB
Go
267 lines
7.2 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 setupAgentBotInboxService(t *testing.T) (*AgentBotInboxService, *gorm.DB) {
|
|
t.Helper()
|
|
|
|
db, err := gorm.Open(sqlite.Open("file::memory:"), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Silent),
|
|
})
|
|
require.NoError(t, err, "failed to open SQLite test db")
|
|
|
|
require.NoError(t, db.AutoMigrate(
|
|
&model.Account{},
|
|
&model.AgentBot{},
|
|
&model.Inbox{},
|
|
&model.AgentBotInbox{},
|
|
), "failed to auto-migrate")
|
|
|
|
t.Cleanup(func() {
|
|
sqlDB, _ := db.DB()
|
|
sqlDB.Close()
|
|
})
|
|
|
|
repo := repository.NewAgentBotInboxRepo(db)
|
|
botRepo := repository.NewAgentBotRepo(db)
|
|
svc := NewAgentBotInboxService(repo, botRepo)
|
|
return svc, db
|
|
}
|
|
|
|
// Helper: create prerequisite Account, AgentBot, and Inbox for service tests.
|
|
func createSvcPrereqs(t *testing.T, db *gorm.DB) (*model.Account, *model.AgentBot, *model.Inbox) {
|
|
t.Helper()
|
|
|
|
account := &model.Account{Name: "SvcTestOrg", Locale: "en", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
|
|
bot := &model.AgentBot{
|
|
AccountID: &account.ID,
|
|
Name: "SvcTestBot",
|
|
BotType: "default",
|
|
Secret: "secret-svcbot",
|
|
AccessToken: "token-svcbot",
|
|
}
|
|
require.NoError(t, db.Create(bot).Error)
|
|
|
|
inbox := &model.Inbox{
|
|
AccountID: account.ID,
|
|
Name: "SvcTestInbox",
|
|
ChannelType: "web_widget",
|
|
ChannelID: 1,
|
|
}
|
|
require.NoError(t, db.Create(inbox).Error)
|
|
|
|
return account, bot, inbox
|
|
}
|
|
|
|
// ========== 1. Bind ==========
|
|
|
|
func TestAgentBotInboxService_Bind(t *testing.T) {
|
|
svc, db := setupAgentBotInboxService(t)
|
|
_, bot, inbox := createSvcPrereqs(t, db)
|
|
|
|
abi, err := svc.Bind(context.Background(), inbox.AccountID, BindBotToInboxRequest{
|
|
AgentBotID: bot.ID,
|
|
InboxID: inbox.ID,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.NotZero(t, abi.ID)
|
|
assert.Equal(t, bot.ID, abi.AgentBotID)
|
|
assert.Equal(t, inbox.ID, abi.InboxID)
|
|
assert.Equal(t, model.AgentBotInboxActive, abi.Status)
|
|
}
|
|
|
|
func TestAgentBotInboxService_Bind_BotNotFound(t *testing.T) {
|
|
svc, _ := setupAgentBotInboxService(t)
|
|
|
|
abi, err := svc.Bind(context.Background(), 1, BindBotToInboxRequest{
|
|
AgentBotID: 9999,
|
|
InboxID: 1,
|
|
})
|
|
assert.Error(t, err)
|
|
assert.Nil(t, abi)
|
|
assert.Contains(t, err.Error(), "agent bot not found")
|
|
}
|
|
|
|
func TestAgentBotInboxService_Bind_Duplicate(t *testing.T) {
|
|
svc, db := setupAgentBotInboxService(t)
|
|
_, bot, inbox := createSvcPrereqs(t, db)
|
|
|
|
// First bind succeeds
|
|
_, err := svc.Bind(context.Background(), inbox.AccountID, BindBotToInboxRequest{
|
|
AgentBotID: bot.ID,
|
|
InboxID: inbox.ID,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Second bind fails (duplicate)
|
|
abi, err := svc.Bind(context.Background(), inbox.AccountID, BindBotToInboxRequest{
|
|
AgentBotID: bot.ID,
|
|
InboxID: inbox.ID,
|
|
})
|
|
assert.Error(t, err)
|
|
assert.Nil(t, abi)
|
|
assert.Contains(t, err.Error(), "already bound")
|
|
}
|
|
|
|
// ========== 2. Unbind ==========
|
|
|
|
func TestAgentBotInboxService_Unbind(t *testing.T) {
|
|
svc, db := setupAgentBotInboxService(t)
|
|
_, bot, inbox := createSvcPrereqs(t, db)
|
|
|
|
abi, err := svc.Bind(context.Background(), inbox.AccountID, BindBotToInboxRequest{
|
|
AgentBotID: bot.ID,
|
|
InboxID: inbox.ID,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = svc.Unbind(context.Background(), abi.ID)
|
|
assert.NoError(t, err)
|
|
|
|
// Verify binding is gone
|
|
abis, err := svc.ListByInbox(context.Background(), inbox.ID)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, abis, 0)
|
|
}
|
|
|
|
func TestAgentBotInboxService_Unbind_NotFound(t *testing.T) {
|
|
svc, _ := setupAgentBotInboxService(t)
|
|
|
|
err := svc.Unbind(context.Background(), 9999)
|
|
assert.NoError(t, err) // GORM delete on non-existent row returns no error
|
|
}
|
|
|
|
// ========== 3. UpdateStatus ==========
|
|
|
|
func TestAgentBotInboxService_UpdateStatus(t *testing.T) {
|
|
svc, db := setupAgentBotInboxService(t)
|
|
_, bot, inbox := createSvcPrereqs(t, db)
|
|
|
|
abi, err := svc.Bind(context.Background(), inbox.AccountID, BindBotToInboxRequest{
|
|
AgentBotID: bot.ID,
|
|
InboxID: inbox.ID,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, model.AgentBotInboxActive, abi.Status)
|
|
|
|
// Toggle to inactive
|
|
updated, err := svc.UpdateStatus(context.Background(), abi.ID, UpdateBindingStatusRequest{Status: 1})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, model.AgentBotInboxInactive, updated.Status)
|
|
|
|
// Toggle back to active
|
|
updated2, err := svc.UpdateStatus(context.Background(), abi.ID, UpdateBindingStatusRequest{Status: 0})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, model.AgentBotInboxActive, updated2.Status)
|
|
}
|
|
|
|
func TestAgentBotInboxService_UpdateStatus_NotFound(t *testing.T) {
|
|
svc, _ := setupAgentBotInboxService(t)
|
|
|
|
abi, err := svc.UpdateStatus(context.Background(), 9999, UpdateBindingStatusRequest{Status: 1})
|
|
assert.Error(t, err)
|
|
assert.Nil(t, abi)
|
|
assert.Contains(t, err.Error(), "binding not found")
|
|
}
|
|
|
|
// ========== 4. ListByInbox ==========
|
|
|
|
func TestAgentBotInboxService_ListByInbox(t *testing.T) {
|
|
svc, db := setupAgentBotInboxService(t)
|
|
_, bot, inbox := createSvcPrereqs(t, db)
|
|
|
|
_, err := svc.Bind(context.Background(), inbox.AccountID, BindBotToInboxRequest{
|
|
AgentBotID: bot.ID,
|
|
InboxID: inbox.ID,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
abis, err := svc.ListByInbox(context.Background(), inbox.ID)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, abis, 1)
|
|
assert.Equal(t, bot.ID, abis[0].AgentBotID)
|
|
}
|
|
|
|
func TestAgentBotInboxService_ListByInbox_Empty(t *testing.T) {
|
|
svc, _ := setupAgentBotInboxService(t)
|
|
|
|
abis, err := svc.ListByInbox(context.Background(), 9999)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, abis, 0)
|
|
}
|
|
|
|
// ========== 5. ListActiveByInbox ==========
|
|
|
|
func TestAgentBotInboxService_ListActiveByInbox(t *testing.T) {
|
|
svc, db := setupAgentBotInboxService(t)
|
|
_, bot, inbox := createSvcPrereqs(t, db)
|
|
|
|
abi, err := svc.Bind(context.Background(), inbox.AccountID, BindBotToInboxRequest{
|
|
AgentBotID: bot.ID,
|
|
InboxID: inbox.ID,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Initially active
|
|
abis, err := svc.ListActiveByInbox(context.Background(), inbox.ID)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, abis, 1)
|
|
|
|
// Toggle to inactive
|
|
_, err = svc.UpdateStatus(context.Background(), abi.ID, UpdateBindingStatusRequest{Status: 1})
|
|
require.NoError(t, err)
|
|
|
|
// No active bindings now
|
|
abis, err = svc.ListActiveByInbox(context.Background(), inbox.ID)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, abis, 0)
|
|
|
|
// But ListByInbox still shows 1
|
|
allAbis, err := svc.ListByInbox(context.Background(), inbox.ID)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, allAbis, 1)
|
|
}
|
|
|
|
// ========== 6. ListByBot ==========
|
|
|
|
func TestAgentBotInboxService_ListByBot(t *testing.T) {
|
|
svc, db := setupAgentBotInboxService(t)
|
|
_, bot, inbox := createSvcPrereqs(t, db)
|
|
|
|
_, err := svc.Bind(context.Background(), inbox.AccountID, BindBotToInboxRequest{
|
|
AgentBotID: bot.ID,
|
|
InboxID: inbox.ID,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
abis, err := svc.ListByBot(context.Background(), bot.ID)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, abis, 1)
|
|
assert.Equal(t, inbox.ID, abis[0].InboxID)
|
|
}
|
|
|
|
func TestAgentBotInboxService_ListByBot_Empty(t *testing.T) {
|
|
svc, db := setupAgentBotInboxService(t)
|
|
_, bot, _ := createSvcPrereqs(t, db)
|
|
|
|
abis, err := svc.ListByBot(context.Background(), bot.ID)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, abis, 0)
|
|
}
|