@@ -398,3 +398,629 @@ func cleanPlanForComparison(p *models.Plan) {
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 {
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 : "Task 1 (Original)" , ExecutionOrder : 1 } )
db . Create ( & models . Task { Model : gorm . Model { ID : 11 } , PlanID : 1 , Name : "Task 2 (To Be Deleted)" , ExecutionOrder : 2 } )
db . Create ( & models . Task { Model : gorm . Model { ID : 12 } , PlanID : 1 , Name : "Task 3 (Original)" , ExecutionOrder : 3 } )
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 {
// T4 (新) -> T3 (不变) -> T1 (更新)
{ Name : "Task 4 (New)" , ExecutionOrder : 1 } ,
{ Model : gorm . Model { ID : 12 } , PlanID : 1 , Name : "Task 3 (Original)" , ExecutionOrder : 2 } ,
{ Model : gorm . Model { ID : 10 } , PlanID : 1 , Name : "Task 1 (Updated)" , ExecutionOrder : 3 } ,
} ,
}
} ,
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 , 3 )
// 验证被删除的 T2 不存在
var count int64
db . Model ( & models . Task { } ) . Where ( "id = ?" , 11 ) . Count ( & count )
assert . Equal ( t , int64 ( 0 ) , count )
// 验证顺序和内容
assert . Equal ( t , "Task 4 (New)" , finalPlan . Tasks [ 0 ] . Name )
assert . Equal ( t , "Task 3 (Original)" , finalPlan . Tasks [ 1 ] . Name )
assert . Equal ( t , "Task 1 (Updated)" , finalPlan . Tasks [ 2 ] . Name )
} ,
} ,
{
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 : "Plan" , ContentType : models . PlanContentTypeSubPlans } )
db . Create ( & models . Plan { Model : gorm . Model { ID : 10 } , Name : "Old Child" } )
db . Create ( & models . SubPlan { ParentPlanID : 1 , ChildPlanID : 10 } )
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 { { Name : "New Task" } } ,
}
} ,
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 taskCount int64
db . Model ( & models . Task { } ) . Where ( "plan_id = ?" , rootPlanID ) . Count ( & taskCount )
assert . Equal ( t , int64 ( 1 ) , taskCount , "新任务应被创建" )
} ,
} ,
{
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 )
} ,
} ,
{
name : "递归更新-深层节点的类型转换(C从SubPlans变为Tasks)" ,
setupDB : func ( db * gorm . DB ) uint {
// 初始状态: A -> B -> C -> D
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 . PlanContentTypeSubPlans } ) // C的初始类型
db . Create ( & models . Plan { Model : gorm . Model { ID : 4 } , Name : "D" } )
db . Create ( & models . SubPlan { ParentPlanID : 1 , ChildPlanID : 2 } )
db . Create ( & models . SubPlan { ParentPlanID : 2 , ChildPlanID : 3 } )
db . Create ( & models . SubPlan { Model : gorm . Model { ID : 99 } , ParentPlanID : 3 , ChildPlanID : 4 } ) // C->D 的关联
return 1
} ,
buildInput : func ( db * gorm . DB ) * models . Plan {
// 更新操作: C的类型变为Tasks, 并增加一个新Task
planC := & models . Plan {
Model : gorm . Model { ID : 3 } ,
Name : "C" ,
ContentType : models . PlanContentTypeTasks , // 类型变更
Tasks : [ ] models . Task { { Name : "C's New Task" } } ,
}
planB := & models . Plan {
Model : gorm . Model { ID : 2 } ,
Name : "B" ,
ContentType : models . PlanContentTypeSubPlans ,
SubPlans : [ ] models . SubPlan { { ChildPlanID : 3 , ChildPlan : planC } } ,
}
planA := & models . Plan {
Model : gorm . Model { ID : 1 } ,
Name : "A" ,
ContentType : models . PlanContentTypeSubPlans ,
SubPlans : [ ] models . SubPlan { { ChildPlanID : 2 , ChildPlan : planB } } ,
}
return planA
} ,
verifyDB : func ( t * testing . T , db * gorm . DB , rootPlanID uint ) {
// 1. 验证 C->D 的旧关联已被删除
var linkCount int64
db . Model ( & models . SubPlan { } ) . Where ( "id = ?" , 99 ) . Count ( & linkCount )
assert . Equal ( t , int64 ( 0 ) , linkCount , "C的旧子计划关联应被清理" )
// 2. 验证 C 的新Task已被创建
var task models . Task
err := db . Where ( "plan_id = ?" , 3 ) . First ( & task ) . Error
assert . NoError ( t , err , "C的新任务应该被创建" )
assert . Equal ( t , "C's New Task" , task . Name )
// 3. 验证 D 计划本身依然存在
var planDCount int64
db . Model ( & models . Plan { } ) . Where ( "id = ?" , 4 ) . Count ( & planDCount )
assert . Equal ( t , int64 ( 1 ) , planDCount , "计划D本身不应被删除" )
} ,
} , {
name : "递归更新-中间层分支替换(A->B变为A->D)" ,
setupDB : func ( db * gorm . DB ) uint {
// 初始状态: A -> B -> C
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" } )
db . Create ( & models . SubPlan { Model : gorm . Model { ID : 101 } , ParentPlanID : 1 , ChildPlanID : 2 } ) // A->B
db . Create ( & models . SubPlan { ParentPlanID : 2 , ChildPlanID : 3 } ) // B->C
// 准备用于替换的分支: D -> E
db . Create ( & models . Plan { Model : gorm . Model { ID : 4 } , Name : "D" , ContentType : models . PlanContentTypeSubPlans } )
db . Create ( & models . Plan { Model : gorm . Model { ID : 5 } , Name : "E" } )
db . Create ( & models . SubPlan { ParentPlanID : 4 , ChildPlanID : 5 } ) // D->E
return 1
} ,
buildInput : func ( db * gorm . DB ) * models . Plan {
// 更新操作: A的子计划从 B 替换为 D
planE := & models . Plan { Model : gorm . Model { ID : 5 } , Name : "E Updated" } // 同时更新深层节点
planD := & models . Plan {
Model : gorm . Model { ID : 4 } ,
Name : "D" ,
ContentType : models . PlanContentTypeSubPlans ,
SubPlans : [ ] models . SubPlan { { ChildPlanID : 5 , ChildPlan : planE } } ,
}
planA := & models . Plan {
Model : gorm . Model { ID : 1 } ,
Name : "A" ,
ContentType : models . PlanContentTypeSubPlans ,
SubPlans : [ ] models . SubPlan { { ChildPlanID : 4 , ChildPlan : planD } } , // 新关联 A->D
}
return planA
} ,
verifyDB : func ( t * testing . T , db * gorm . DB , rootPlanID uint ) {
// 1. 验证 A->B 的旧关联已被删除
var linkCount int64
db . Model ( & models . SubPlan { } ) . Where ( "id = ?" , 101 ) . Count ( & linkCount )
assert . Equal ( t , int64 ( 0 ) , linkCount , "A->B 的旧关联应被删除" )
// 2. 验证 A->D 的新关联已创建
var newLink models . SubPlan
err := db . Where ( "parent_plan_id = ? AND child_plan_id = ?" , 1 , 4 ) . First ( & newLink ) . Error
assert . NoError ( t , err , "A->D 的新关联应被创建" )
// 3. 验证 B, C, D, E 计划本身都依然存在
var planCount int64
db . Model ( & models . Plan { } ) . Where ( "id IN ?" , [ ] uint { 2 , 3 , 4 , 5 } ) . Count ( & planCount )
assert . Equal ( t , int64 ( 4 ) , planCount , "所有被引用或解引用的计划本身都不应被删除" )
// 4. 验证对新分支深层节点的递归更新已生效
var finalE models . Plan
db . First ( & finalE , 5 )
assert . Equal ( t , "E Updated" , finalE . Name )
} ,
} ,
{
name : "递归更新-菱形依赖下的冲突更新" ,
setupDB : func ( db * gorm . DB ) uint {
// 初始状态: A -> B -> D, A -> C -> D
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 . PlanContentTypeSubPlans } )
db . Create ( & models . Plan { Model : gorm . Model { ID : 4 } , Name : "D (Original)" } ) // D的初始名字
// 创建关联
db . Create ( & models . SubPlan { ParentPlanID : 1 , ChildPlanID : 2 , ExecutionOrder : 1 } ) // A->B
db . Create ( & models . SubPlan { ParentPlanID : 1 , ChildPlanID : 3 , ExecutionOrder : 2 } ) // A->C
db . Create ( & models . SubPlan { ParentPlanID : 2 , ChildPlanID : 4 } ) // B->D
db . Create ( & models . SubPlan { ParentPlanID : 3 , ChildPlanID : 4 } ) // C->D
return 1
} ,
buildInput : func ( db * gorm . DB ) * models . Plan {
// 在一次调用中,通过不同路径对 D 进行不同的更新
planD_fromB := & models . Plan { Model : gorm . Model { ID : 4 } , Name : "D (Updated from B)" }
planD_fromC := & models . Plan { Model : gorm . Model { ID : 4 } , Name : "D (Updated from C)" }
planB := & models . Plan {
Model : gorm . Model { ID : 2 } ,
ContentType : models . PlanContentTypeSubPlans ,
SubPlans : [ ] models . SubPlan { { ChildPlanID : 4 , ChildPlan : planD_fromB } } ,
}
planC := & models . Plan {
Model : gorm . Model { ID : 3 } ,
ContentType : models . PlanContentTypeSubPlans ,
SubPlans : [ ] models . SubPlan { { ChildPlanID : 4 , ChildPlan : planD_fromC } } ,
}
planA := & models . Plan {
Model : gorm . Model { ID : 1 } ,
ContentType : models . PlanContentTypeSubPlans ,
SubPlans : [ ] models . SubPlan {
{ ChildPlanID : 2 , ChildPlan : planB , ExecutionOrder : 1 } ,
{ ChildPlanID : 3 , ChildPlan : planC , ExecutionOrder : 2 } , // C 在 B 之后
} ,
}
return planA
} ,
verifyDB : func ( t * testing . T , db * gorm . DB , rootPlanID uint ) {
// 验证: D 的最终名字应该符合“最后一次更新获胜”的原则
// 由于 planC 在 planB 之后被处理, D 的名字应该是 "D (Updated from C)"
var finalD models . Plan
db . First ( & finalD , 4 )
assert . Equal ( t , "D (Updated from C)" , finalD . Name , "共享下游节点的更新应以后一次执行为准" )
// 确保所有关联依然存在
var linkCount int64
db . Model ( & models . SubPlan { } ) . Where ( "child_plan_id = ?" , 4 ) . Count ( & linkCount )
assert . Equal ( t , int64 ( 2 ) , linkCount , "D 的两个上游关联都应继续存在" )
} ,
} ,
}
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 )
} )
}
}