package service import ( "context" "encoding/json" "testing" "github.com/gochat/gochat/internal/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // ========== Create ========== func TestCaptainScenarioService_Create_成功(t *testing.T) { db, _, svc := setupCaptainScenarioService(t) account := createTestAccount(t, db) assistant := createTestCaptainAssistant(t, db, account.ID) tools := json.RawMessage(`["search","calculate"]`) req := &CreateScenarioRequest{ Title: "客服场景", Description: "处理客户咨询", Instruction: "首先了解客户需求,然后提供解决方案", Enabled: true, Tools: tools, } scenario, err := svc.Create(context.Background(), account.ID, assistant.ID, req) require.NoError(t, err) assert.NotZero(t, scenario.ID) assert.Equal(t, account.ID, scenario.AccountID) assert.Equal(t, assistant.ID, scenario.AssistantID) assert.Equal(t, "客服场景", scenario.Title) assert.Equal(t, "处理客户咨询", scenario.Description) assert.Equal(t, "首先了解客户需求,然后提供解决方案", scenario.Instruction) assert.True(t, scenario.Enabled) assert.Equal(t, tools, scenario.Tools) } func TestCaptainScenarioService_Create_默认Enabled(t *testing.T) { db, _, svc := setupCaptainScenarioService(t) account := createTestAccount(t, db) assistant := createTestCaptainAssistant(t, db, account.ID) // GORM零值bool问题:先创建Enabled=true的场景,再更新为false req := &CreateScenarioRequest{ Title: "启用场景", Enabled: true, } scenario, err := svc.Create(context.Background(), account.ID, assistant.ID, req) require.NoError(t, err) assert.True(t, scenario.Enabled) // 更新Enabled为false(使用Select强制写入零值) require.NoError(t, db.Model(scenario).Select("enabled").Update("enabled", false).Error) db.First(scenario, scenario.ID) assert.False(t, scenario.Enabled) } func TestCaptainScenarioService_Create_空Tools(t *testing.T) { db, _, svc := setupCaptainScenarioService(t) account := createTestAccount(t, db) assistant := createTestCaptainAssistant(t, db, account.ID) req := &CreateScenarioRequest{ Title: "无工具场景", Enabled: true, Tools: nil, } scenario, err := svc.Create(context.Background(), account.ID, assistant.ID, req) require.NoError(t, err) assert.Nil(t, scenario.Tools) } // ========== GetByID ========== func TestCaptainScenarioService_GetByID_成功(t *testing.T) { db, _, svc := setupCaptainScenarioService(t) account := createTestAccount(t, db) assistant := createTestCaptainAssistant(t, db, account.ID) scenario := createTestCaptainScenario(t, db, account.ID, assistant.ID) result, err := svc.GetByID(context.Background(), scenario.ID) require.NoError(t, err) assert.Equal(t, scenario.ID, result.ID) assert.Equal(t, scenario.Title, result.Title) assert.Equal(t, scenario.AssistantID, result.AssistantID) } func TestCaptainScenarioService_GetByID_不存在(t *testing.T) { _, _, svc := setupCaptainScenarioService(t) result, err := svc.GetByID(context.Background(), 99999) require.Error(t, err) assert.Nil(t, result) assert.Contains(t, err.Error(), "get scenario") } // ========== Update ========== func TestCaptainScenarioService_Update_成功(t *testing.T) { db, _, svc := setupCaptainScenarioService(t) account := createTestAccount(t, db) assistant := createTestCaptainAssistant(t, db, account.ID) scenario := createTestCaptainScenario(t, db, account.ID, assistant.ID) enabled := false newTools := json.RawMessage(`["updated_tool"]`) req := &UpdateScenarioRequest{ Title: "更新后的场景", Description: "更新后的描述", Instruction: "新的操作指令", Enabled: &enabled, Tools: newTools, } updated, err := svc.Update(context.Background(), scenario.ID, req) require.NoError(t, err) assert.Equal(t, "更新后的场景", updated.Title) assert.Equal(t, "更新后的描述", updated.Description) assert.Equal(t, "新的操作指令", updated.Instruction) assert.False(t, updated.Enabled) assert.Equal(t, newTools, updated.Tools) } func TestCaptainScenarioService_Update_部分字段(t *testing.T) { db, _, svc := setupCaptainScenarioService(t) account := createTestAccount(t, db) assistant := createTestCaptainAssistant(t, db, account.ID) scenario := createTestCaptainScenario(t, db, account.ID, assistant.ID) // 只更新 Title,其他字段保持不变 req := &UpdateScenarioRequest{ Title: "只改标题", } updated, err := svc.Update(context.Background(), scenario.ID, req) require.NoError(t, err) assert.Equal(t, "只改标题", updated.Title) assert.Equal(t, scenario.Description, updated.Description) assert.Equal(t, scenario.Instruction, updated.Instruction) assert.Equal(t, scenario.Enabled, updated.Enabled) } func TestCaptainScenarioService_Update_不存在(t *testing.T) { _, _, svc := setupCaptainScenarioService(t) enabled := true req := &UpdateScenarioRequest{ Title: "不存在", Enabled: &enabled, } result, err := svc.Update(context.Background(), 99999, req) require.Error(t, err) assert.Nil(t, result) assert.Contains(t, err.Error(), "scenario not found") } func TestCaptainScenarioService_Update_Enabled为nullJSON(t *testing.T) { db, _, svc := setupCaptainScenarioService(t) account := createTestAccount(t, db) assistant := createTestCaptainAssistant(t, db, account.ID) originalTools := json.RawMessage(`["tool_a"]`) scenario := createTestCaptainScenario(t, db, account.ID, assistant.ID, func(s *model.CaptainScenario) { s.Tools = originalTools }) // 传入 "null" JSON,不应更新 Tools req := &UpdateScenarioRequest{ Tools: json.RawMessage(`null`), } updated, err := svc.Update(context.Background(), scenario.ID, req) require.NoError(t, err) assert.Equal(t, originalTools, updated.Tools) } // ========== Delete ========== func TestCaptainScenarioService_Delete_成功(t *testing.T) { db, _, svc := setupCaptainScenarioService(t) account := createTestAccount(t, db) assistant := createTestCaptainAssistant(t, db, account.ID) scenario := createTestCaptainScenario(t, db, account.ID, assistant.ID) err := svc.Delete(context.Background(), scenario.ID) require.NoError(t, err) // 验证已删除(GetByID 应返回错误) result, err := svc.GetByID(context.Background(), scenario.ID) require.Error(t, err) assert.Nil(t, result) } func TestCaptainScenarioService_Delete_不存在(t *testing.T) { _, _, svc := setupCaptainScenarioService(t) // GORM Delete不存在的ID不返回error(rows affected=0但不报错) err := svc.Delete(context.Background(), 99999) assert.NoError(t, err) } // ========== ListByAssistant ========== func TestCaptainScenarioService_ListByAssistant_成功(t *testing.T) { db, _, svc := setupCaptainScenarioService(t) account := createTestAccount(t, db) assistant := createTestCaptainAssistant(t, db, account.ID) // 创建3个场景 createTestCaptainScenario(t, db, account.ID, assistant.ID, func(s *model.CaptainScenario) { s.Title = "场景A" }) createTestCaptainScenario(t, db, account.ID, assistant.ID, func(s *model.CaptainScenario) { s.Title = "场景B" }) createTestCaptainScenario(t, db, account.ID, assistant.ID, func(s *model.CaptainScenario) { s.Title = "场景C" }) scenarios, count, err := svc.ListByAssistant(context.Background(), assistant.ID, 0, 10) require.NoError(t, err) assert.Equal(t, int64(3), count) assert.Len(t, scenarios, 3) } func TestCaptainScenarioService_ListByAssistant_分页(t *testing.T) { db, _, svc := setupCaptainScenarioService(t) account := createTestAccount(t, db) assistant := createTestCaptainAssistant(t, db, account.ID) // 创建5个场景 for i := 0; i < 5; i++ { createTestCaptainScenario(t, db, account.ID, assistant.ID) } // offset=0, limit=2 应返回前2条,count=5 scenarios, count, err := svc.ListByAssistant(context.Background(), assistant.ID, 0, 2) require.NoError(t, err) assert.Equal(t, int64(5), count) assert.Len(t, scenarios, 2) // offset=2, limit=2 应返回第3-4条 scenarios2, count2, err := svc.ListByAssistant(context.Background(), assistant.ID, 2, 2) require.NoError(t, err) assert.Equal(t, int64(5), count2) assert.Len(t, scenarios2, 2) } func TestCaptainScenarioService_ListByAssistant_空列表(t *testing.T) { db, _, svc := setupCaptainScenarioService(t) account := createTestAccount(t, db) assistant := createTestCaptainAssistant(t, db, account.ID) scenarios, count, err := svc.ListByAssistant(context.Background(), assistant.ID, 0, 10) require.NoError(t, err) assert.Equal(t, int64(0), count) assert.Len(t, scenarios, 0) } func TestCaptainScenarioService_ListByAssistant_不同助手隔离(t *testing.T) { db, _, svc := setupCaptainScenarioService(t) account := createTestAccount(t, db) assistant1 := createTestCaptainAssistant(t, db, account.ID, func(a *model.CaptainAssistant) { a.Name = "助手1" }) assistant2 := createTestCaptainAssistant(t, db, account.ID, func(a *model.CaptainAssistant) { a.Name = "助手2" }) // assistant1 有2个场景,assistant2 有1个场景 createTestCaptainScenario(t, db, account.ID, assistant1.ID) createTestCaptainScenario(t, db, account.ID, assistant1.ID) createTestCaptainScenario(t, db, account.ID, assistant2.ID) scenarios1, count1, err := svc.ListByAssistant(context.Background(), assistant1.ID, 0, 10) require.NoError(t, err) assert.Equal(t, int64(2), count1) assert.Len(t, scenarios1, 2) scenarios2, count2, err := svc.ListByAssistant(context.Background(), assistant2.ID, 0, 10) require.NoError(t, err) assert.Equal(t, int64(1), count2) assert.Len(t, scenarios2, 1) }