实现UpdatePlan单测
This commit is contained in:
@@ -1,12 +1,20 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 定义仓库层可导出的公共错误
|
||||||
|
var (
|
||||||
|
ErrUpdateWithInvalidRoot = errors.New("更新操作的目标根计划无效或ID为0")
|
||||||
|
ErrNewSubPlanInUpdate = errors.New("计划树中包含一个ID为0的新子计划,更新操作只允许关联已存在的计划")
|
||||||
|
ErrNodeDoesNotExist = errors.New("计划树中包含一个或多个在数据库中不存在的计划")
|
||||||
|
)
|
||||||
|
|
||||||
// PlanRepository 定义了与计划模型相关的数据库操作接口
|
// PlanRepository 定义了与计划模型相关的数据库操作接口
|
||||||
// 这是为了让业务逻辑层依赖于抽象,而不是具体的数据库实现
|
// 这是为了让业务逻辑层依赖于抽象,而不是具体的数据库实现
|
||||||
type PlanRepository interface {
|
type PlanRepository interface {
|
||||||
@@ -118,7 +126,7 @@ func (r *gormPlanRepository) updatePlanTx(tx *gorm.DB, plan *models.Plan) error
|
|||||||
func (r *gormPlanRepository) validatePlanTree(tx *gorm.DB, plan *models.Plan) error {
|
func (r *gormPlanRepository) validatePlanTree(tx *gorm.DB, plan *models.Plan) error {
|
||||||
// 1. 检查根节点
|
// 1. 检查根节点
|
||||||
if plan == nil || plan.ID == 0 {
|
if plan == nil || plan.ID == 0 {
|
||||||
return fmt.Errorf("更新操作的目标计划无效或ID为0")
|
return ErrUpdateWithInvalidRoot
|
||||||
}
|
}
|
||||||
if len(plan.Tasks) > 0 && len(plan.SubPlans) > 0 {
|
if len(plan.Tasks) > 0 && len(plan.SubPlans) > 0 {
|
||||||
return fmt.Errorf("计划 (ID: %d) 不能同时包含任务和子计划", plan.ID)
|
return fmt.Errorf("计划 (ID: %d) 不能同时包含任务和子计划", plan.ID)
|
||||||
@@ -143,7 +151,7 @@ func (r *gormPlanRepository) validatePlanTree(tx *gorm.DB, plan *models.Plan) er
|
|||||||
return fmt.Errorf("检查计划存在性时出错: %w", err)
|
return fmt.Errorf("检查计划存在性时出错: %w", err)
|
||||||
}
|
}
|
||||||
if int(count) != len(idsToCheck) {
|
if int(count) != len(idsToCheck) {
|
||||||
return fmt.Errorf("计划树中包含一个或多个在数据库中不存在的计划")
|
return ErrNodeDoesNotExist
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +164,7 @@ func validateNodeAndDetectCycles(plan *models.Plan, allIDs, recursionStack map[u
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if plan.ID == 0 {
|
if plan.ID == 0 {
|
||||||
return fmt.Errorf("错误:计划树中包含一个ID为0的新计划,更新操作只允许操作已存在的计划")
|
return ErrNewSubPlanInUpdate
|
||||||
}
|
}
|
||||||
if recursionStack[plan.ID] {
|
if recursionStack[plan.ID] {
|
||||||
return fmt.Errorf("检测到循环引用:计划 (ID: %d) 是其自身的祖先", plan.ID)
|
return fmt.Errorf("检测到循环引用:计划 (ID: %d) 是其自身的祖先", plan.ID)
|
||||||
|
|||||||
@@ -398,3 +398,400 @@ func cleanPlanForComparison(p *models.Plan) {
|
|||||||
cleanPlanForComparison(p.SubPlans[i].ChildPlan)
|
cleanPlanForComparison(p.SubPlans[i].ChildPlan)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestUpdatePlan_Validation 专注于测试 UpdatePlan 中前置检查逻辑的各种失败和成功场景。
|
||||||
|
func TestUpdatePlan_Validation(t *testing.T) {
|
||||||
|
// 定义Go测试中使用的计划实体
|
||||||
|
planA := &models.Plan{Model: gorm.Model{ID: 1}, Name: "Plan A", ContentType: models.PlanContentTypeSubPlans}
|
||||||
|
planB := &models.Plan{Model: gorm.Model{ID: 2}, Name: "Plan B", ContentType: models.PlanContentTypeSubPlans}
|
||||||
|
planC := &models.Plan{Model: gorm.Model{ID: 3}, Name: "Plan C", ContentType: models.PlanContentTypeSubPlans}
|
||||||
|
planD := &models.Plan{Model: gorm.Model{ID: 4}, Name: "Plan D", ContentType: models.PlanContentTypeTasks}
|
||||||
|
planNew := &models.Plan{Model: gorm.Model{ID: 0}, Name: "New Plan"} // ID为0的新计划
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
name string
|
||||||
|
setupDB func(db *gorm.DB)
|
||||||
|
buildInput func() *models.Plan // 修改为构建函数
|
||||||
|
expectedError string // 保持 string 类型
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []testCase{
|
||||||
|
{
|
||||||
|
name: "成功-合法的菱形依赖树",
|
||||||
|
setupDB: func(db *gorm.DB) {
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 1}})
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 2}})
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 3}})
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 4}})
|
||||||
|
},
|
||||||
|
buildInput: func() *models.Plan {
|
||||||
|
planD.ContentType = models.PlanContentTypeTasks
|
||||||
|
planB.ContentType = models.PlanContentTypeSubPlans
|
||||||
|
planB.SubPlans = []models.SubPlan{{ChildPlanID: 4, ChildPlan: planD}}
|
||||||
|
planC.ContentType = models.PlanContentTypeSubPlans
|
||||||
|
planC.SubPlans = []models.SubPlan{{ChildPlanID: 4, ChildPlan: planD}}
|
||||||
|
planA.ContentType = models.PlanContentTypeSubPlans
|
||||||
|
planA.SubPlans = []models.SubPlan{
|
||||||
|
{ChildPlanID: 2, ChildPlan: planB},
|
||||||
|
{ChildPlanID: 3, ChildPlan: planC},
|
||||||
|
}
|
||||||
|
return planA
|
||||||
|
},
|
||||||
|
expectedError: "", // 期望没有错误
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "错误-根节点ID为零",
|
||||||
|
setupDB: func(db *gorm.DB) {},
|
||||||
|
buildInput: func() *models.Plan { return planNew },
|
||||||
|
expectedError: repository.ErrUpdateWithInvalidRoot.Error(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "错误-子计划ID为零",
|
||||||
|
setupDB: func(db *gorm.DB) {
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 1}})
|
||||||
|
},
|
||||||
|
buildInput: func() *models.Plan {
|
||||||
|
planA.ContentType = models.PlanContentTypeSubPlans
|
||||||
|
planA.SubPlans = []models.SubPlan{{ChildPlan: planNew}}
|
||||||
|
return planA
|
||||||
|
},
|
||||||
|
expectedError: repository.ErrNewSubPlanInUpdate.Error(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "错误-节点在数据库中不存在",
|
||||||
|
setupDB: func(db *gorm.DB) {
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 1}})
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 2}})
|
||||||
|
},
|
||||||
|
buildInput: func() *models.Plan {
|
||||||
|
planA.ContentType = models.PlanContentTypeSubPlans
|
||||||
|
planA.SubPlans = []models.SubPlan{
|
||||||
|
{ChildPlanID: 2, ChildPlan: planB},
|
||||||
|
{ChildPlanID: 3, ChildPlan: planC}, // C 不存在
|
||||||
|
}
|
||||||
|
return planA
|
||||||
|
},
|
||||||
|
expectedError: repository.ErrNodeDoesNotExist.Error(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "错误-简单循环引用(A->B->A)",
|
||||||
|
setupDB: func(db *gorm.DB) {
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 1}})
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 2}})
|
||||||
|
},
|
||||||
|
buildInput: func() *models.Plan {
|
||||||
|
planA.ContentType = models.PlanContentTypeSubPlans
|
||||||
|
planA.SubPlans = []models.SubPlan{{ChildPlanID: 2, ChildPlan: planB}}
|
||||||
|
planB.ContentType = models.PlanContentTypeSubPlans
|
||||||
|
planB.SubPlans = []models.SubPlan{{ChildPlanID: 1, ChildPlan: planA}}
|
||||||
|
return planA
|
||||||
|
},
|
||||||
|
expectedError: "检测到循环引用:计划 (ID: 1)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "错误-复杂循环引用(A->B->C->A)",
|
||||||
|
setupDB: func(db *gorm.DB) {
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 1}})
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 2}})
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 3}})
|
||||||
|
},
|
||||||
|
buildInput: func() *models.Plan {
|
||||||
|
planA.ContentType = models.PlanContentTypeSubPlans
|
||||||
|
planA.SubPlans = []models.SubPlan{{ChildPlanID: 2, ChildPlan: planB}}
|
||||||
|
planB.ContentType = models.PlanContentTypeSubPlans
|
||||||
|
planB.SubPlans = []models.SubPlan{{ChildPlanID: 3, ChildPlan: planC}}
|
||||||
|
planC.ContentType = models.PlanContentTypeSubPlans
|
||||||
|
planC.SubPlans = []models.SubPlan{{ChildPlanID: 1, ChildPlan: planA}}
|
||||||
|
return planA
|
||||||
|
},
|
||||||
|
expectedError: "检测到循环引用:计划 (ID: 1)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "错误-自引用(A->A)",
|
||||||
|
setupDB: func(db *gorm.DB) {
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 1}})
|
||||||
|
},
|
||||||
|
buildInput: func() *models.Plan {
|
||||||
|
planA.ContentType = models.PlanContentTypeSubPlans
|
||||||
|
planA.SubPlans = []models.SubPlan{{ChildPlanID: 1, ChildPlan: planA}}
|
||||||
|
return planA
|
||||||
|
},
|
||||||
|
expectedError: "检测到循环引用:计划 (ID: 1)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "错误-根节点内容混合",
|
||||||
|
setupDB: func(db *gorm.DB) {
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 1}})
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 2}})
|
||||||
|
},
|
||||||
|
buildInput: func() *models.Plan {
|
||||||
|
planA.ContentType = models.PlanContentTypeSubPlans
|
||||||
|
planA.SubPlans = []models.SubPlan{{ChildPlanID: 2, ChildPlan: planB}}
|
||||||
|
planA.Tasks = []models.Task{{Name: "A's Task"}}
|
||||||
|
return planA
|
||||||
|
},
|
||||||
|
expectedError: "不能同时包含任务和子计划",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
// 1. 为每个测试用例重置基础对象的状态
|
||||||
|
*planA = models.Plan{Model: gorm.Model{ID: 1}, Name: "Plan A"}
|
||||||
|
*planB = models.Plan{Model: gorm.Model{ID: 2}, Name: "Plan B"}
|
||||||
|
*planC = models.Plan{Model: gorm.Model{ID: 3}, Name: "Plan C"}
|
||||||
|
*planD = models.Plan{Model: gorm.Model{ID: 4}, Name: "Plan D"}
|
||||||
|
*planNew = models.Plan{Model: gorm.Model{ID: 0}, Name: "New Plan"}
|
||||||
|
|
||||||
|
// 2. 设置数据库
|
||||||
|
db := setupTestDB(t)
|
||||||
|
sqlDB, _ := db.DB()
|
||||||
|
defer sqlDB.Close()
|
||||||
|
tc.setupDB(db)
|
||||||
|
|
||||||
|
// 3. 在对象重置后,构建本次测试需要的输入结构
|
||||||
|
input := tc.buildInput()
|
||||||
|
|
||||||
|
// 4. 执行测试
|
||||||
|
repo := repository.NewGormPlanRepository(db)
|
||||||
|
err := repo.UpdatePlan(input)
|
||||||
|
|
||||||
|
// 5. 断言结果
|
||||||
|
if tc.expectedError != "" {
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), tc.expectedError)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestUpdatePlan_Reconciliation 专注于测试 UpdatePlan 成功执行后,数据库状态是否与预期一致。
|
||||||
|
func TestUpdatePlan_Reconciliation(t *testing.T) {
|
||||||
|
type testCase struct {
|
||||||
|
name string
|
||||||
|
setupDB func(db *gorm.DB) (rootPlanID uint)
|
||||||
|
buildInput func(db *gorm.DB) *models.Plan
|
||||||
|
verifyDB func(t *testing.T, db *gorm.DB, rootPlanID uint)
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []testCase{
|
||||||
|
{
|
||||||
|
name: "任务协调-新增一个任务",
|
||||||
|
setupDB: func(db *gorm.DB) uint {
|
||||||
|
plan := models.Plan{Model: gorm.Model{ID: 1}, Name: "Plan With Tasks", ContentType: models.PlanContentTypeTasks}
|
||||||
|
db.Create(&plan)
|
||||||
|
db.Create(&models.Task{PlanID: 1, Name: "Task 1", ExecutionOrder: 1})
|
||||||
|
return 1
|
||||||
|
},
|
||||||
|
buildInput: func(db *gorm.DB) *models.Plan {
|
||||||
|
return &models.Plan{
|
||||||
|
Model: gorm.Model{ID: 1},
|
||||||
|
Name: "Plan With Tasks",
|
||||||
|
ContentType: models.PlanContentTypeTasks,
|
||||||
|
Tasks: []models.Task{
|
||||||
|
{Model: gorm.Model{ID: 1}, PlanID: 1, Name: "Task 1", ExecutionOrder: 1},
|
||||||
|
{Name: "New Task 2", ExecutionOrder: 2},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
verifyDB: func(t *testing.T, db *gorm.DB, rootPlanID uint) {
|
||||||
|
var finalPlan models.Plan
|
||||||
|
db.Preload("Tasks").First(&finalPlan, rootPlanID)
|
||||||
|
assert.Len(t, finalPlan.Tasks, 2)
|
||||||
|
assert.Equal(t, "New Task 2", finalPlan.Tasks[1].Name)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "任务协调-删除一个任务",
|
||||||
|
setupDB: func(db *gorm.DB) uint {
|
||||||
|
plan := models.Plan{Model: gorm.Model{ID: 1}, Name: "Plan With Tasks", ContentType: models.PlanContentTypeTasks}
|
||||||
|
db.Create(&plan)
|
||||||
|
db.Create(&models.Task{Model: gorm.Model{ID: 10}, PlanID: 1, Name: "Task 1", ExecutionOrder: 1})
|
||||||
|
db.Create(&models.Task{Model: gorm.Model{ID: 11}, PlanID: 1, Name: "Task to Delete", ExecutionOrder: 2})
|
||||||
|
return 1
|
||||||
|
},
|
||||||
|
buildInput: func(db *gorm.DB) *models.Plan {
|
||||||
|
return &models.Plan{
|
||||||
|
Model: gorm.Model{ID: 1},
|
||||||
|
Name: "Plan With Tasks",
|
||||||
|
ContentType: models.PlanContentTypeTasks,
|
||||||
|
Tasks: []models.Task{
|
||||||
|
{Model: gorm.Model{ID: 10}, PlanID: 1, Name: "Task 1", ExecutionOrder: 1},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
verifyDB: func(t *testing.T, db *gorm.DB, rootPlanID uint) {
|
||||||
|
var tasks []models.Task
|
||||||
|
db.Where("plan_id = ?", rootPlanID).Find(&tasks)
|
||||||
|
assert.Len(t, tasks, 1)
|
||||||
|
var count int64
|
||||||
|
db.Model(&models.Task{}).Where("id = ?", 11).Count(&count)
|
||||||
|
assert.Equal(t, int64(0), count, "被删除的任务不应再存在")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "任务协调-更新并重排序任务",
|
||||||
|
setupDB: func(db *gorm.DB) uint {
|
||||||
|
plan := models.Plan{Model: gorm.Model{ID: 1}, Name: "Plan", ContentType: models.PlanContentTypeTasks}
|
||||||
|
db.Create(&plan)
|
||||||
|
db.Create(&models.Task{Model: gorm.Model{ID: 10}, PlanID: 1, Name: "A", ExecutionOrder: 1})
|
||||||
|
db.Create(&models.Task{Model: gorm.Model{ID: 11}, PlanID: 1, Name: "B", ExecutionOrder: 2})
|
||||||
|
return 1
|
||||||
|
},
|
||||||
|
buildInput: func(db *gorm.DB) *models.Plan {
|
||||||
|
return &models.Plan{
|
||||||
|
Model: gorm.Model{ID: 1},
|
||||||
|
Name: "Plan",
|
||||||
|
ContentType: models.PlanContentTypeTasks,
|
||||||
|
Tasks: []models.Task{
|
||||||
|
{Model: gorm.Model{ID: 11}, PlanID: 1, Name: "B Updated", ExecutionOrder: 1},
|
||||||
|
{Model: gorm.Model{ID: 10}, PlanID: 1, Name: "A", ExecutionOrder: 2},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
verifyDB: func(t *testing.T, db *gorm.DB, rootPlanID uint) {
|
||||||
|
var finalPlan models.Plan
|
||||||
|
db.Preload("Tasks", func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Order("execution_order")
|
||||||
|
}).First(&finalPlan, rootPlanID)
|
||||||
|
assert.Len(t, finalPlan.Tasks, 2)
|
||||||
|
assert.Equal(t, "B Updated", finalPlan.Tasks[0].Name)
|
||||||
|
assert.Equal(t, uint(11), finalPlan.Tasks[0].ID)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "子计划协调-新增一个关联",
|
||||||
|
setupDB: func(db *gorm.DB) uint {
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 1}, Name: "Parent", ContentType: models.PlanContentTypeSubPlans})
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 2}, Name: "Existing Child"})
|
||||||
|
return 1
|
||||||
|
},
|
||||||
|
buildInput: func(db *gorm.DB) *models.Plan {
|
||||||
|
return &models.Plan{
|
||||||
|
Model: gorm.Model{ID: 1},
|
||||||
|
Name: "Parent",
|
||||||
|
ContentType: models.PlanContentTypeSubPlans,
|
||||||
|
SubPlans: []models.SubPlan{{ChildPlanID: 2}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
verifyDB: func(t *testing.T, db *gorm.DB, rootPlanID uint) {
|
||||||
|
var links []models.SubPlan
|
||||||
|
db.Where("parent_plan_id = ?", rootPlanID).Find(&links)
|
||||||
|
assert.Len(t, links, 1)
|
||||||
|
assert.Equal(t, uint(2), links[0].ChildPlanID)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "子计划协调-删除一个关联",
|
||||||
|
setupDB: func(db *gorm.DB) uint {
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 1}, Name: "Parent", ContentType: models.PlanContentTypeSubPlans})
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 2}, Name: "Child To Unlink"})
|
||||||
|
db.Create(&models.SubPlan{ParentPlanID: 1, ChildPlanID: 2})
|
||||||
|
return 1
|
||||||
|
},
|
||||||
|
buildInput: func(db *gorm.DB) *models.Plan {
|
||||||
|
return &models.Plan{
|
||||||
|
Model: gorm.Model{ID: 1},
|
||||||
|
Name: "Parent",
|
||||||
|
ContentType: models.PlanContentTypeSubPlans,
|
||||||
|
SubPlans: []models.SubPlan{},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
verifyDB: func(t *testing.T, db *gorm.DB, rootPlanID uint) {
|
||||||
|
var linkCount int64
|
||||||
|
db.Model(&models.SubPlan{}).Where("parent_plan_id = ?", rootPlanID).Count(&linkCount)
|
||||||
|
assert.Equal(t, int64(0), linkCount)
|
||||||
|
|
||||||
|
var planCount int64
|
||||||
|
db.Model(&models.Plan{}).Where("id = ?", 2).Count(&planCount)
|
||||||
|
assert.Equal(t, int64(1), planCount, "子计划本身不应被删除")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "类型转换-从任务切换到子计划",
|
||||||
|
setupDB: func(db *gorm.DB) uint {
|
||||||
|
plan := models.Plan{Model: gorm.Model{ID: 1}, Name: "Plan", ContentType: models.PlanContentTypeTasks}
|
||||||
|
db.Create(&plan)
|
||||||
|
db.Create(&models.Task{PlanID: 1, Name: "Old Task"})
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 10}, Name: "New Child"})
|
||||||
|
return 1
|
||||||
|
},
|
||||||
|
buildInput: func(db *gorm.DB) *models.Plan {
|
||||||
|
return &models.Plan{
|
||||||
|
Model: gorm.Model{ID: 1},
|
||||||
|
Name: "Plan",
|
||||||
|
ContentType: models.PlanContentTypeSubPlans,
|
||||||
|
SubPlans: []models.SubPlan{{ChildPlanID: 10}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
verifyDB: func(t *testing.T, db *gorm.DB, rootPlanID uint) {
|
||||||
|
var taskCount int64
|
||||||
|
db.Model(&models.Task{}).Where("plan_id = ?", rootPlanID).Count(&taskCount)
|
||||||
|
assert.Equal(t, int64(0), taskCount, "旧任务应被清理")
|
||||||
|
|
||||||
|
var linkCount int64
|
||||||
|
db.Model(&models.SubPlan{}).Where("parent_plan_id = ?", rootPlanID).Count(&linkCount)
|
||||||
|
assert.Equal(t, int64(1), linkCount, "新关联应被创建")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "递归更新-深层节点的变更",
|
||||||
|
setupDB: func(db *gorm.DB) uint {
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 1}, Name: "A", ContentType: models.PlanContentTypeSubPlans})
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 2}, Name: "B", ContentType: models.PlanContentTypeSubPlans})
|
||||||
|
db.Create(&models.Plan{Model: gorm.Model{ID: 3}, Name: "C", ContentType: models.PlanContentTypeTasks})
|
||||||
|
db.Create(&models.SubPlan{Model: gorm.Model{ID: 101}, ParentPlanID: 1, ChildPlanID: 2, ExecutionOrder: 1})
|
||||||
|
db.Create(&models.SubPlan{Model: gorm.Model{ID: 102}, ParentPlanID: 2, ChildPlanID: 3, ExecutionOrder: 1})
|
||||||
|
return 1
|
||||||
|
},
|
||||||
|
buildInput: func(db *gorm.DB) *models.Plan {
|
||||||
|
planC := &models.Plan{Model: gorm.Model{ID: 3}, Name: "C Updated", ContentType: models.PlanContentTypeTasks}
|
||||||
|
planB := &models.Plan{
|
||||||
|
Model: gorm.Model{ID: 2},
|
||||||
|
Name: "B",
|
||||||
|
ContentType: models.PlanContentTypeSubPlans,
|
||||||
|
SubPlans: []models.SubPlan{{Model: gorm.Model{ID: 102}, ParentPlanID: 2, ChildPlanID: 3, ChildPlan: planC, ExecutionOrder: 2}},
|
||||||
|
}
|
||||||
|
planA := &models.Plan{
|
||||||
|
Model: gorm.Model{ID: 1},
|
||||||
|
Name: "A Updated",
|
||||||
|
ContentType: models.PlanContentTypeSubPlans,
|
||||||
|
SubPlans: []models.SubPlan{{Model: gorm.Model{ID: 101}, ParentPlanID: 1, ChildPlanID: 2, ChildPlan: planB, ExecutionOrder: 1}},
|
||||||
|
}
|
||||||
|
return planA
|
||||||
|
},
|
||||||
|
verifyDB: func(t *testing.T, db *gorm.DB, rootPlanID uint) {
|
||||||
|
var finalA, finalC models.Plan
|
||||||
|
var finalLinkB models.SubPlan
|
||||||
|
|
||||||
|
db.First(&finalA, 1)
|
||||||
|
assert.Equal(t, "A Updated", finalA.Name)
|
||||||
|
|
||||||
|
db.First(&finalLinkB, 102)
|
||||||
|
assert.Equal(t, 2, finalLinkB.ExecutionOrder)
|
||||||
|
|
||||||
|
db.First(&finalC, 3)
|
||||||
|
assert.Equal(t, "C Updated", finalC.Name)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
db := setupTestDB(t)
|
||||||
|
sqlDB, _ := db.DB()
|
||||||
|
defer sqlDB.Close()
|
||||||
|
|
||||||
|
rootID := tc.setupDB(db)
|
||||||
|
input := tc.buildInput(db)
|
||||||
|
|
||||||
|
repo := repository.NewGormPlanRepository(db)
|
||||||
|
err := repo.UpdatePlan(input)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
tc.verifyDB(t, db, rootID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user