增加TestGetPlanByID单测
This commit is contained in:
@@ -2,7 +2,9 @@ package repository_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
|
"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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user