From 01b11b6e42cfe4e1cfb1efcc31178b8c21dfbb0a Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Sat, 13 Sep 2025 15:42:03 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0TestGetPlanByID=E5=8D=95?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infra/repository/plan_repository_test.go | 264 ++++++++++++++++++ 1 file changed, 264 insertions(+) diff --git a/internal/infra/repository/plan_repository_test.go b/internal/infra/repository/plan_repository_test.go index 7752ff1..91c6751 100644 --- a/internal/infra/repository/plan_repository_test.go +++ b/internal/infra/repository/plan_repository_test.go @@ -2,7 +2,9 @@ package repository_test import ( "errors" + "fmt" "testing" + "time" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" "git.huangwc.com/pig/pig-farm-controller/internal/infra/repository" @@ -134,3 +136,265 @@ func TestGetBasicPlanByID(t *testing.T) { }) } } + +// TestGetPlanByID 测试 GetPlanByID 方法,确保它能根据ID正确返回计划的完整信息,包括关联数据。 +func TestGetPlanByID(t *testing.T) { + type testCase struct { + name string + setupData func(db *gorm.DB) // 用于在测试前插入数据 + planID uint + expectedPlan *models.Plan + expectedError string + } + + testCases := []testCase{ + { + name: "PlanNotFound", + setupData: func(db *gorm.DB) { + // 不插入任何数据 + }, + planID: 999, + expectedPlan: nil, + expectedError: "record not found", + }, + { + name: "PlanWithTasks", + setupData: func(db *gorm.DB) { + // 使用硬编码的ID创建计划,使测试可预测 + plan := models.Plan{ + Model: gorm.Model{ID: 1}, + Name: "Plan A", + ContentType: models.PlanContentTypeTasks, + } + db.Create(&plan) + // 创建任务,它们的ID将由数据库自动生成 + db.Create(&models.Task{PlanID: 1, Name: "Task 2", ExecutionOrder: 1}) + db.Create(&models.Task{PlanID: 1, Name: "Task 1", ExecutionOrder: 2}) + }, + planID: 1, + expectedPlan: &models.Plan{ + Model: gorm.Model{ID: 1}, + Name: "Plan A", + ContentType: models.PlanContentTypeTasks, + Tasks: []models.Task{ + // 期望按 "order" 字段升序排序 + {PlanID: 1, Name: "Task 2", ExecutionOrder: 1}, + {PlanID: 1, Name: "Task 1", ExecutionOrder: 2}, + }, + }, + expectedError: "", + }, + { + name: "PlanWithMultiLevelSubPlans", + setupData: func(db *gorm.DB) { + // 创建一个三层结构的计划 + db.Create(&models.Plan{Model: gorm.Model{ID: 20}, Name: "Grandparent Plan", ContentType: models.PlanContentTypeSubPlans}) + db.Create(&models.Plan{Model: gorm.Model{ID: 21}, Name: "Parent Plan", ContentType: models.PlanContentTypeSubPlans}) + db.Create(&models.Plan{Model: gorm.Model{ID: 22}, Name: "Child Plan With Tasks", ContentType: models.PlanContentTypeTasks}) + db.Create(&models.Task{PlanID: 22, Name: "Grandchild's Task", ExecutionOrder: 1}) + + // 创建关联关系 + db.Create(&models.SubPlan{ParentPlanID: 20, ChildPlanID: 21, ExecutionOrder: 1}) + db.Create(&models.SubPlan{ParentPlanID: 21, ChildPlanID: 22, ExecutionOrder: 1}) + }, + planID: 20, + expectedPlan: &models.Plan{ + Model: gorm.Model{ID: 20}, + Name: "Grandparent Plan", + ContentType: models.PlanContentTypeSubPlans, + SubPlans: []models.SubPlan{ + { + ParentPlanID: 20, + ChildPlanID: 21, + ExecutionOrder: 1, + ChildPlan: &models.Plan{ + Model: gorm.Model{ID: 21}, + Name: "Parent Plan", + ContentType: models.PlanContentTypeSubPlans, + SubPlans: []models.SubPlan{ + { + ParentPlanID: 21, + ChildPlanID: 22, + ExecutionOrder: 1, + ChildPlan: &models.Plan{ + Model: gorm.Model{ID: 22}, + Name: "Child Plan With Tasks", + ContentType: models.PlanContentTypeTasks, + Tasks: []models.Task{ + {PlanID: 22, Name: "Grandchild's Task", ExecutionOrder: 1}, + }, + }, + }, + }, + }, + }, + }, + }, + expectedError: "", + }, + { + name: "UnknownContentType", + setupData: func(db *gorm.DB) { + db.Create(&models.Plan{ + Model: gorm.Model{ID: 30}, + Name: "Unknown Type Plan", + ContentType: "INVALID_TYPE", + }) + }, + planID: 30, + expectedPlan: nil, + expectedError: fmt.Sprintf("未知的计划内容类型: INVALID_TYPE; 计划ID: 30"), + }, + // 新增场景:测试空的关联数据 + { + name: "PlanWithTasksType_ButNoTasks", + setupData: func(db *gorm.DB) { + db.Create(&models.Plan{ + Model: gorm.Model{ID: 50}, + Name: "Plan with empty tasks", + ContentType: models.PlanContentTypeTasks, + }) + }, + planID: 50, + expectedPlan: &models.Plan{ + Model: gorm.Model{ID: 50}, + Name: "Plan with empty tasks", + ContentType: models.PlanContentTypeTasks, + Tasks: []models.Task{}, // 期望一个空的切片 + }, + expectedError: "", + }, + // 新增场景:测试复杂的同级排序 + { + name: "PlanWithSubPlans_ComplexSorting", + setupData: func(db *gorm.DB) { + db.Create(&models.Plan{Model: gorm.Model{ID: 60}, Name: "Main Plan For Sorting", ContentType: models.PlanContentTypeSubPlans}) + db.Create(&models.Plan{Model: gorm.Model{ID: 61}, Name: "SubPlan C", ContentType: models.PlanContentTypeTasks}) + db.Create(&models.Plan{Model: gorm.Model{ID: 62}, Name: "SubPlan A", ContentType: models.PlanContentTypeTasks}) + db.Create(&models.Plan{Model: gorm.Model{ID: 63}, Name: "SubPlan B", ContentType: models.PlanContentTypeTasks}) + // 故意打乱顺序插入 + db.Create(&models.SubPlan{ParentPlanID: 60, ChildPlanID: 61, ExecutionOrder: 3}) + db.Create(&models.SubPlan{ParentPlanID: 60, ChildPlanID: 62, ExecutionOrder: 1}) + db.Create(&models.SubPlan{ParentPlanID: 60, ChildPlanID: 63, ExecutionOrder: 2}) + }, + planID: 60, + expectedPlan: &models.Plan{ + Model: gorm.Model{ID: 60}, + Name: "Main Plan For Sorting", + ContentType: models.PlanContentTypeSubPlans, + SubPlans: []models.SubPlan{ + {ParentPlanID: 60, ChildPlanID: 62, ExecutionOrder: 1, ChildPlan: &models.Plan{Model: gorm.Model{ID: 62}, Name: "SubPlan A", ContentType: models.PlanContentTypeTasks, Tasks: []models.Task{}}}, + {ParentPlanID: 60, ChildPlanID: 63, ExecutionOrder: 2, ChildPlan: &models.Plan{Model: gorm.Model{ID: 63}, Name: "SubPlan B", ContentType: models.PlanContentTypeTasks, Tasks: []models.Task{}}}, + {ParentPlanID: 60, ChildPlanID: 61, ExecutionOrder: 3, ChildPlan: &models.Plan{Model: gorm.Model{ID: 61}, Name: "SubPlan C", ContentType: models.PlanContentTypeTasks, Tasks: []models.Task{}}}, + }, + }, + expectedError: "", + }, + // 新增场景:测试混合内容的子计划树 + { + name: "PlanWithSubPlans_MixedContentTypes", + setupData: func(db *gorm.DB) { + db.Create(&models.Plan{Model: gorm.Model{ID: 70}, Name: "Mixed Main Plan", ContentType: models.PlanContentTypeSubPlans}) + db.Create(&models.Plan{Model: gorm.Model{ID: 71}, Name: "Child with SubPlans", ContentType: models.PlanContentTypeSubPlans}) + db.Create(&models.Plan{Model: gorm.Model{ID: 72}, Name: "Grandchild with Tasks", ContentType: models.PlanContentTypeTasks}) + db.Create(&models.Task{PlanID: 72, Name: "Grandchild's Task", ExecutionOrder: 1}) + db.Create(&models.Plan{Model: gorm.Model{ID: 73}, Name: "Child with Tasks", ContentType: models.PlanContentTypeTasks}) + db.Create(&models.Task{PlanID: 73, Name: "Child's Task", ExecutionOrder: 1}) + + // 创建关联 + db.Create(&models.SubPlan{ParentPlanID: 70, ChildPlanID: 71, ExecutionOrder: 1}) // Main -> Child with SubPlans + db.Create(&models.SubPlan{ParentPlanID: 70, ChildPlanID: 73, ExecutionOrder: 2}) // Main -> Child with Tasks + db.Create(&models.SubPlan{ParentPlanID: 71, ChildPlanID: 72, ExecutionOrder: 1}) // Child with SubPlans -> Grandchild + }, + planID: 70, + expectedPlan: &models.Plan{ + Model: gorm.Model{ID: 70}, + Name: "Mixed Main Plan", + ContentType: models.PlanContentTypeSubPlans, + SubPlans: []models.SubPlan{ + { + ParentPlanID: 70, ChildPlanID: 71, ExecutionOrder: 1, + ChildPlan: &models.Plan{ + Model: gorm.Model{ID: 71}, Name: "Child with SubPlans", ContentType: models.PlanContentTypeSubPlans, + SubPlans: []models.SubPlan{ + {ParentPlanID: 71, ChildPlanID: 72, ExecutionOrder: 1, ChildPlan: &models.Plan{ + Model: gorm.Model{ID: 72}, Name: "Grandchild with Tasks", ContentType: models.PlanContentTypeTasks, + Tasks: []models.Task{{PlanID: 72, Name: "Grandchild's Task", ExecutionOrder: 1}}, + }}, + }, + }, + }, + { + ParentPlanID: 70, ChildPlanID: 73, ExecutionOrder: 2, + ChildPlan: &models.Plan{ + Model: gorm.Model{ID: 73}, Name: "Child with Tasks", ContentType: models.PlanContentTypeTasks, + Tasks: []models.Task{{PlanID: 73, Name: "Child's Task", ExecutionOrder: 1}}, + }, + }, + }, + }, + expectedError: "", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + db := setupTestDB(t) + // 使用 defer 确保数据库连接在测试结束后关闭 + sqlDB, _ := db.DB() + defer sqlDB.Close() + + tc.setupData(db) + + repo := repository.NewGormPlanRepository(db) + plan, err := repo.GetPlanByID(tc.planID) + + if tc.expectedError != "" { + assert.Error(t, err) + assert.Contains(t, err.Error(), tc.expectedError) + assert.Nil(t, plan) + } else { + assert.NoError(t, err) + assert.NotNil(t, plan) + + // 在比较之前,清理实际结果和期望结果中所有不确定的、由数据库自动生成的字段 + cleanPlanForComparison(plan) + cleanPlanForComparison(tc.expectedPlan) + + assert.Equal(t, tc.expectedPlan, plan) + } + }) + } +} + +// cleanPlanForComparison 递归地重置 Plan 对象及其关联对象中由数据库自动生成的字段(如ID和时间戳), +// 以便在测试中断言它们与期望值相等。 +func cleanPlanForComparison(p *models.Plan) { + if p == nil { + return + } + + // 重置 Plan 自身的时间戳 + p.CreatedAt = time.Time{} + p.UpdatedAt = time.Time{} + p.DeletedAt = gorm.DeletedAt{} + + // 重置所有 Task 的自动生成字段 + for i := range p.Tasks { + p.Tasks[i].ID = 0 // ID是自动生成的,必须重置为0才能与期望值匹配 + p.Tasks[i].CreatedAt = time.Time{} + p.Tasks[i].UpdatedAt = time.Time{} + p.Tasks[i].DeletedAt = gorm.DeletedAt{} + } + + // 重置所有 SubPlan 的自动生成字段,并递归清理子计划 + for i := range p.SubPlans { + p.SubPlans[i].ID = 0 // SubPlan 连接记录的ID也是自动生成的 + p.SubPlans[i].CreatedAt = time.Time{} + p.SubPlans[i].UpdatedAt = time.Time{} + p.SubPlans[i].DeletedAt = gorm.DeletedAt{} + + // 递归调用以清理嵌套的子计划 + cleanPlanForComparison(p.SubPlans[i].ChildPlan) + } +}