1. 增加重复顺序校验
2. 增加测试用例
This commit is contained in:
@@ -125,6 +125,11 @@ func (r *gormPlanRepository) Create(plan *models.Plan) error {
|
||||
return ErrMixedContent
|
||||
}
|
||||
|
||||
// 检查是否有重复的执行顺序
|
||||
if err := plan.ValidateExecutionOrder(); err != nil {
|
||||
return fmt.Errorf("计划 (ID: %d) 的执行顺序无效: %w", plan.ID, err)
|
||||
}
|
||||
|
||||
// 如果是子计划类型,验证所有子计划是否存在且ID不为0
|
||||
if plan.ContentType == models.PlanContentTypeSubPlans {
|
||||
childIDsToValidate := make(map[uint]bool)
|
||||
@@ -156,28 +161,6 @@ func (r *gormPlanRepository) Create(plan *models.Plan) error {
|
||||
if err := tx.Create(plan).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. 处理子计划关联 (如果 ContentType 是 sub_plans)
|
||||
// GORM 不会自动创建 SubPlan 关联记录,需要手动处理
|
||||
if plan.ContentType == models.PlanContentTypeSubPlans {
|
||||
// 收集所有 SubPlan 关联,设置 ParentPlanID
|
||||
var subPlanLinksToCreate []models.SubPlan
|
||||
for i := range plan.SubPlans {
|
||||
plan.SubPlans[i].ParentPlanID = plan.ID // 设置父计划ID
|
||||
// 确保 ChildPlanID 被正确设置,因为 ChildPlan 对象可能只在内存中
|
||||
if plan.SubPlans[i].ChildPlanID == 0 && plan.SubPlans[i].ChildPlan != nil {
|
||||
plan.SubPlans[i].ChildPlanID = plan.SubPlans[i].ChildPlan.ID
|
||||
}
|
||||
subPlanLinksToCreate = append(subPlanLinksToCreate, plan.SubPlans[i])
|
||||
}
|
||||
|
||||
// 批量创建 SubPlan 关联记录
|
||||
if len(subPlanLinksToCreate) > 0 {
|
||||
if err := tx.CreateInBatches(subPlanLinksToCreate, 100).Error; err != nil { // 批量大小100
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -214,7 +197,12 @@ func (r *gormPlanRepository) validatePlanTree(tx *gorm.DB, plan *models.Plan) er
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. 一次性数据库存在性校验
|
||||
// 3. 检查是否有重复的执行顺序
|
||||
if err := plan.ValidateExecutionOrder(); err != nil {
|
||||
return fmt.Errorf("计划 (ID: %d) 的执行顺序无效: %w", plan.ID, err)
|
||||
}
|
||||
|
||||
// 4. 一次性数据库存在性校验
|
||||
var idsToCheck []uint
|
||||
for id := range allIDs {
|
||||
idsToCheck = append(idsToCheck, id)
|
||||
@@ -229,7 +217,6 @@ func (r *gormPlanRepository) validatePlanTree(tx *gorm.DB, plan *models.Plan) er
|
||||
return ErrNodeDoesNotExist
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -432,8 +432,8 @@ func TestUpdatePlan_Validation(t *testing.T) {
|
||||
planC.SubPlans = []models.SubPlan{{ChildPlanID: 4, ChildPlan: planD}}
|
||||
planA.ContentType = models.PlanContentTypeSubPlans
|
||||
planA.SubPlans = []models.SubPlan{
|
||||
{ChildPlanID: 2, ChildPlan: planB},
|
||||
{ChildPlanID: 3, ChildPlan: planC},
|
||||
{ChildPlanID: 2, ChildPlan: planB, ExecutionOrder: 1},
|
||||
{ChildPlanID: 3, ChildPlan: planC, ExecutionOrder: 2},
|
||||
}
|
||||
return planA
|
||||
},
|
||||
@@ -466,8 +466,8 @@ func TestUpdatePlan_Validation(t *testing.T) {
|
||||
buildInput: func() *models.Plan {
|
||||
planA.ContentType = models.PlanContentTypeSubPlans
|
||||
planA.SubPlans = []models.SubPlan{
|
||||
{ChildPlanID: 2, ChildPlan: planB},
|
||||
{ChildPlanID: 3, ChildPlan: planC}, // C 不存在
|
||||
{ChildPlanID: 2, ChildPlan: planB, ExecutionOrder: 1},
|
||||
{ChildPlanID: 3, ChildPlan: planC, ExecutionOrder: 2}, // C 不存在
|
||||
}
|
||||
return planA
|
||||
},
|
||||
@@ -532,6 +532,37 @@ func TestUpdatePlan_Validation(t *testing.T) {
|
||||
},
|
||||
expectedError: "不能同时包含任务和子计划",
|
||||
},
|
||||
{
|
||||
name: "错误-任务执行顺序重复",
|
||||
setupDB: func(db *gorm.DB) {
|
||||
db.Create(&models.Plan{Model: gorm.Model{ID: 1}})
|
||||
},
|
||||
buildInput: func() *models.Plan {
|
||||
planA.ContentType = models.PlanContentTypeTasks
|
||||
planA.Tasks = []models.Task{
|
||||
{Name: "Task 1", ExecutionOrder: 1},
|
||||
{Name: "Task 2", ExecutionOrder: 1}, // 重复的顺序
|
||||
}
|
||||
return planA
|
||||
},
|
||||
expectedError: fmt.Sprintf("任务执行顺序重复: %d", 1),
|
||||
}, {
|
||||
name: "错误-子计划执行顺序重复",
|
||||
setupDB: func(db *gorm.DB) {
|
||||
db.Create(&models.Plan{Model: gorm.Model{ID: 1}})
|
||||
db.Create(&models.Plan{Model: gorm.Model{ID: 10}})
|
||||
db.Create(&models.Plan{Model: gorm.Model{ID: 11}})
|
||||
},
|
||||
buildInput: func() *models.Plan {
|
||||
planA.ContentType = models.PlanContentTypeSubPlans
|
||||
planA.SubPlans = []models.SubPlan{
|
||||
{ChildPlanID: 10, ChildPlan: &models.Plan{Model: gorm.Model{ID: 10}}, ExecutionOrder: 1},
|
||||
{ChildPlanID: 11, ChildPlan: &models.Plan{Model: gorm.Model{ID: 11}}, ExecutionOrder: 1}, // 重复的顺序
|
||||
}
|
||||
return planA
|
||||
},
|
||||
expectedError: fmt.Sprintf("子计划执行顺序重复: %d", 1),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
@@ -1024,3 +1055,243 @@ func TestUpdatePlan_Reconciliation(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// createExistingPlan 辅助函数,用于在数据库中创建已存在的计划
|
||||
func createExistingPlan(db *gorm.DB, name string, contentType models.PlanContentType) *models.Plan {
|
||||
plan := &models.Plan{
|
||||
Name: name,
|
||||
ContentType: contentType,
|
||||
}
|
||||
db.Create(plan)
|
||||
return plan
|
||||
}
|
||||
|
||||
func TestPlanRepository_Create(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
setupDB func(db *gorm.DB) // 准备数据库的初始状态
|
||||
inputPlan *models.Plan // 传入 Create 方法的计划对象
|
||||
expectedError error // 期望的错误类型
|
||||
verifyDB func(t *testing.T, db *gorm.DB, createdPlan *models.Plan) // 验证数据库状态
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
name: "成功创建-只包含基本信息",
|
||||
setupDB: func(db *gorm.DB) {
|
||||
// 无需额外设置
|
||||
},
|
||||
inputPlan: &models.Plan{
|
||||
Name: "简单计划",
|
||||
Description: "一个不包含任务或子计划的简单计划",
|
||||
ContentType: models.PlanContentTypeTasks, // 修改为有效的 ContentType
|
||||
Tasks: []models.Task{}, // 明确为空任务列表
|
||||
},
|
||||
expectedError: nil,
|
||||
verifyDB: func(t *testing.T, db *gorm.DB, createdPlan *models.Plan) {
|
||||
assert.NotZero(t, createdPlan.ID, "创建后计划ID不应为0")
|
||||
var foundPlan models.Plan
|
||||
err := db.First(&foundPlan, createdPlan.ID).Error
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "简单计划", foundPlan.Name)
|
||||
assert.Equal(t, models.PlanContentTypeTasks, foundPlan.ContentType)
|
||||
var tasks []models.Task
|
||||
db.Where("plan_id = ?", createdPlan.ID).Find(&tasks)
|
||||
assert.Len(t, tasks, 0, "不应创建任何任务")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "成功创建-包含任务",
|
||||
setupDB: func(db *gorm.DB) {
|
||||
// 无需额外设置
|
||||
},
|
||||
inputPlan: &models.Plan{
|
||||
Name: "任务计划",
|
||||
ContentType: models.PlanContentTypeTasks,
|
||||
Tasks: []models.Task{
|
||||
{Name: "任务A", ExecutionOrder: 1},
|
||||
{Name: "任务B", ExecutionOrder: 2},
|
||||
},
|
||||
},
|
||||
expectedError: nil,
|
||||
verifyDB: func(t *testing.T, db *gorm.DB, createdPlan *models.Plan) {
|
||||
assert.NotZero(t, createdPlan.ID, "计划ID不应为0")
|
||||
var foundPlan models.Plan
|
||||
db.Preload("Tasks").First(&foundPlan, createdPlan.ID)
|
||||
assert.Len(t, foundPlan.Tasks, 2, "应创建两个任务")
|
||||
assert.NotZero(t, foundPlan.Tasks[0].ID, "任务ID不应为0")
|
||||
assert.Equal(t, "任务A", foundPlan.Tasks[0].Name)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "成功创建-包含子计划关联",
|
||||
setupDB: func(db *gorm.DB) {
|
||||
// 预先创建子计划实体,使用有效的 ContentType
|
||||
createExistingPlan(db, "子计划1", models.PlanContentTypeTasks)
|
||||
createExistingPlan(db, "子计划2", models.PlanContentTypeTasks)
|
||||
},
|
||||
inputPlan: &models.Plan{
|
||||
Name: "父计划",
|
||||
ContentType: models.PlanContentTypeSubPlans,
|
||||
SubPlans: []models.SubPlan{
|
||||
{ChildPlanID: 1, ExecutionOrder: 1, ChildPlan: &models.Plan{Model: gorm.Model{ID: 1}}}, // 关联已存在的子计划1
|
||||
{ChildPlanID: 2, ExecutionOrder: 2, ChildPlan: &models.Plan{Model: gorm.Model{ID: 2}}}, // 关联已存在的子计划2
|
||||
},
|
||||
},
|
||||
expectedError: nil,
|
||||
verifyDB: func(t *testing.T, db *gorm.DB, createdPlan *models.Plan) {
|
||||
assert.NotZero(t, createdPlan.ID, "创建后计划ID不应为0")
|
||||
|
||||
// 直接查询 SubPlan 关联记录
|
||||
var foundSubPlanLinks []models.SubPlan
|
||||
err := db.Where("parent_plan_id = ?", createdPlan.ID).Find(&foundSubPlanLinks).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Len(t, foundSubPlanLinks, 2, "应创建两个子计划关联")
|
||||
assert.NotZero(t, foundSubPlanLinks[0].ID, "子计划关联ID不应为0")
|
||||
assert.Equal(t, createdPlan.ID, foundSubPlanLinks[0].ParentPlanID)
|
||||
assert.Equal(t, uint(1), foundSubPlanLinks[0].ChildPlanID)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "失败-计划ID不为0",
|
||||
setupDB: func(db *gorm.DB) {
|
||||
// 无需额外设置
|
||||
},
|
||||
inputPlan: &models.Plan{
|
||||
Model: gorm.Model{ID: 100}, // ID不为0
|
||||
Name: "无效计划",
|
||||
},
|
||||
expectedError: repository.ErrCreateWithNonZeroID,
|
||||
verifyDB: func(t *testing.T, db *gorm.DB, createdPlan *models.Plan) {
|
||||
// 验证数据库中没有创建该计划
|
||||
var count int64
|
||||
db.Model(&models.Plan{}).Where("id = ?", 100).Count(&count)
|
||||
assert.Equal(t, int64(0), count, "计划不应被创建")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "失败-同时包含任务和子计划",
|
||||
setupDB: func(db *gorm.DB) {
|
||||
createExistingPlan(db, "子计划", models.PlanContentTypeTasks) // 使用有效的 ContentType
|
||||
},
|
||||
inputPlan: &models.Plan{
|
||||
Name: "混合内容计划",
|
||||
ContentType: models.PlanContentTypeTasks, // 声明为任务类型
|
||||
Tasks: []models.Task{{Name: "任务A"}},
|
||||
SubPlans: []models.SubPlan{{ChildPlanID: 1, ChildPlan: &models.Plan{Model: gorm.Model{ID: 1}}}}, // 但也包含子计划
|
||||
},
|
||||
expectedError: repository.ErrMixedContent,
|
||||
verifyDB: func(t *testing.T, db *gorm.DB, createdPlan *models.Plan) {
|
||||
// 验证数据库中没有创建该计划
|
||||
var count int64
|
||||
db.Model(&models.Plan{}).Where("name = ?", "混合内容计划").Count(&count)
|
||||
assert.Equal(t, int64(0), count, "计划不应被创建")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "失败-子计划ID为0",
|
||||
setupDB: func(db *gorm.DB) {
|
||||
// 无需额外设置
|
||||
},
|
||||
inputPlan: &models.Plan{
|
||||
Name: "无效子计划关联",
|
||||
ContentType: models.PlanContentTypeSubPlans,
|
||||
SubPlans: []models.SubPlan{
|
||||
{ChildPlanID: 0, ChildPlan: &models.Plan{Model: gorm.Model{ID: 0}}}, // 子计划ID为0
|
||||
},
|
||||
},
|
||||
expectedError: repository.ErrSubPlanIDIsZeroOnCreate,
|
||||
verifyDB: func(t *testing.T, db *gorm.DB, createdPlan *models.Plan) {
|
||||
var count int64
|
||||
db.Model(&models.Plan{}).Where("name = ?", "无效子计划关联").Count(&count)
|
||||
assert.Equal(t, int64(0), count, "计划不应被创建")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "失败-子计划在数据库中不存在",
|
||||
setupDB: func(db *gorm.DB) {
|
||||
// 不创建ID为999的计划
|
||||
},
|
||||
inputPlan: &models.Plan{
|
||||
Name: "不存在的子计划",
|
||||
ContentType: models.PlanContentTypeSubPlans,
|
||||
SubPlans: []models.SubPlan{
|
||||
{ChildPlanID: 999, ChildPlan: &models.Plan{Model: gorm.Model{ID: 999}}}, // 关联一个不存在的ID
|
||||
},
|
||||
},
|
||||
expectedError: repository.ErrNodeDoesNotExist,
|
||||
verifyDB: func(t *testing.T, db *gorm.DB, createdPlan *models.Plan) {
|
||||
var count int64
|
||||
db.Model(&models.Plan{}).Where("name = ?", "不存在的子计划").Count(&count)
|
||||
assert.Equal(t, int64(0), count, "计划不应被创建")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "失败-任务执行顺序重复",
|
||||
setupDB: func(db *gorm.DB) {
|
||||
// 无需额外设置
|
||||
},
|
||||
inputPlan: &models.Plan{
|
||||
Name: "重复任务顺序计划",
|
||||
ContentType: models.PlanContentTypeTasks,
|
||||
Tasks: []models.Task{
|
||||
{Name: "Task 1", ExecutionOrder: 1},
|
||||
{Name: "Task 2", ExecutionOrder: 1}, // 重复的顺序
|
||||
},
|
||||
},
|
||||
expectedError: fmt.Errorf("任务执行顺序重复: %d", 1), // 假设 Create 方法会返回此错误
|
||||
verifyDB: func(t *testing.T, db *gorm.DB, createdPlan *models.Plan) {
|
||||
var count int64
|
||||
db.Model(&models.Plan{}).Where("name = ?", "重复任务顺序计划").Count(&count)
|
||||
assert.Equal(t, int64(0), count, "重复任务顺序的计划不应被创建")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "失败-子计划执行顺序重复",
|
||||
setupDB: func(db *gorm.DB) {
|
||||
createExistingPlan(db, "子计划A", models.PlanContentTypeTasks)
|
||||
createExistingPlan(db, "子计划B", models.PlanContentTypeTasks)
|
||||
},
|
||||
inputPlan: &models.Plan{
|
||||
Name: "重复子计划顺序计划",
|
||||
ContentType: models.PlanContentTypeSubPlans,
|
||||
SubPlans: []models.SubPlan{
|
||||
{ChildPlanID: 1, ExecutionOrder: 1},
|
||||
{ChildPlanID: 2, ExecutionOrder: 1}, // 重复的顺序
|
||||
},
|
||||
},
|
||||
expectedError: fmt.Errorf("子计划执行顺序重复: %d", 1), // 假设 Create 方法会返回此错误
|
||||
verifyDB: func(t *testing.T, db *gorm.DB, createdPlan *models.Plan) {
|
||||
var count int64
|
||||
db.Model(&models.Plan{}).Where("name = ?", "重复子计划顺序计划").Count(&count)
|
||||
assert.Equal(t, int64(0), count, "重复子计划顺序的计划不应被创建")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
repo := repository.NewGormPlanRepository(db)
|
||||
|
||||
// 准备数据库状态
|
||||
tc.setupDB(db)
|
||||
|
||||
// 执行 Create 操作
|
||||
err := repo.Create(tc.inputPlan)
|
||||
|
||||
// 断言错误
|
||||
if tc.expectedError != nil {
|
||||
assert.Error(t, err)
|
||||
// 使用 Contains 检查错误信息,因为 fmt.Errorf 会创建新的错误实例
|
||||
assert.Contains(t, err.Error(), tc.expectedError.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// 验证数据库状态
|
||||
tc.verifyDB(t, db, tc.inputPlan)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user