diff --git a/modules/dop/dao/autotest_scene.go b/modules/dop/dao/autotest_scene.go index 7a9b260f0be..dedfb9b9a6c 100644 --- a/modules/dop/dao/autotest_scene.go +++ b/modules/dop/dao/autotest_scene.go @@ -219,72 +219,161 @@ func (db *DBClient) DeleteAutoTestScene(id uint64) (err error) { // like linklist change to node index func (db *DBClient) MoveAutoTestScene(id, newPreID, newSetID uint64, tx *gorm.DB) (err error) { - return func() error { - var scene, next, oldNext AutoTestScene - // get scene - if err := tx.Where("id = ?", id).Find(&scene).Error; err != nil { - return err - } - // get next scene - if err := tx.Where("pre_id = ?", id).Find(&oldNext).Error; err != nil { - // not have next scene jump over update next scene - if gorm.IsRecordNotFoundError(err) { - goto LABEL1 - } + // a < b < c < d < e + // to + // a < d < c < b < e + + // a < d + // d < b + // c < e + + var changeScene, nextScene AutoTestScene + var newPreScene, newNextScene AutoTestScene + + // get d and pre_id c + changeScene, err = getScene(tx, id) + if err != nil { + return err + } + + // get e + nextScene, err = getSceneByPreID(tx, id, 0) + if err != nil { + return err + } + + if newPreID > 0 { + // get a + newPreScene, err = getScene(tx, newPreID) + if err != nil { return err } - // next scene link this scene pre scene - oldNext.PreID = scene.PreID - if err := tx.Save(&oldNext).Error; err != nil { + + // get b + newNextScene, err = getSceneByPreID(tx, newPreID, 0) + if err != nil { return err } - - defer func() { - err = checkSamePreID(tx, oldNext.SetID, oldNext.PreID) + } else { + if newSetID != 0 { + // get b + newNextScene, err = getSceneByPreID(tx, newPreID, newSetID) if err != nil { - err = fmt.Errorf("set_id %v have same pre_id %v, please refresh", oldNext.SetID, oldNext.PreID) + return err } - }() - - LABEL1: - // get new next scene pre scene - if err := tx.Where("pre_id = ?", newPreID).Where("set_id = ?", newSetID).Find(&next).Error; err != nil { - // not have new next scene jump over update new next scene - if gorm.IsRecordNotFoundError(err) { - goto LABEL2 + } else { + // get b + newNextScene, err = getSceneByPreID(tx, newPreID, changeScene.SetID) + if err != nil { + return err } - return err } - // new next scene link this scene - next.PreID = scene.ID - if err := tx.Save(&next).Error; err != nil { + } + + var a = newPreScene.ID + var b = newNextScene.ID + var c = changeScene.PreID + var d = changeScene.ID + var e = nextScene.ID + + // a < d + if a == d { + return fmt.Errorf("the pre_id of the scene cannot be itself") + } + + err = updateScenePreID(tx, d, c, a, newSetID) + if err != nil { + return err + } + + // c < e + if e > 0 { + if c == e { + return fmt.Errorf("the pre_id of the scene cannot be itself") + } + + err = updateScenePreID(tx, e, d, c, 0) + if err != nil { return err } + } - defer func() { - err = checkSamePreID(tx, next.SetID, next.PreID) - if err != nil { - err = fmt.Errorf("set_id %v have same pre_id %v, please refresh", next.SetID, next.PreID) - } - }() + // d < b + if b > 0 { + if d == b { + return fmt.Errorf("the pre_id of the scene cannot be itself") + } - LABEL2: - // this scene link new next scene pre scene - scene.SetID = newSetID - scene.PreID = newPreID - if err := tx.Save(&scene).Error; err != nil { + err = updateScenePreID(tx, b, a, d, 0) + if err != nil { return err } + } - defer func() { - err = checkSamePreID(tx, scene.SetID, scene.PreID) - if err != nil { - err = fmt.Errorf("set_id %v have same pre_id %v, please refresh", next.SetID, next.PreID) - } - }() + var setID = changeScene.SetID + if newSetID != 0 { + setID = newSetID + } - return nil - }() + err = checkSceneSetNotHaveSamePreID(tx, setID) + if err != nil { + return err + } + + return nil +} + +func getScene(tx *gorm.DB, id uint64) (AutoTestScene, error) { + var scene AutoTestScene + if err := tx.Where("id = ?", id).Find(&scene).Error; err != nil { + return scene, err + } + return scene, nil +} + +func getSceneByPreID(tx *gorm.DB, preID uint64, setID uint64) (AutoTestScene, error) { + var scene AutoTestScene + + tx = tx.Where("pre_id = ?", preID) + if setID > 0 { + tx = tx.Where("set_id = ?", setID) + } + + err := tx.First(&scene).Error + if !gorm.IsRecordNotFoundError(err) { + return scene, err + } + return scene, nil +} + +func updateScenePreID(tx *gorm.DB, id uint64, preID uint64, newPreID uint64, newSetID uint64) error { + tx = tx.Table(AutoTestScene{}.TableName()).Where("id = ? and pre_id = ?", id, preID) + + var updateMap = map[string]interface{}{} + + updateMap["pre_id"] = newPreID + if newSetID > 0 { + updateMap["set_id"] = newSetID + } + + err := tx.Updates(updateMap).Error + if err != nil { + return err + } + return nil +} + +func checkSceneSetNotHaveSamePreID(tx *gorm.DB, setID uint64) error { + rows, err := tx.Table(AutoTestScene{}.TableName()).Select("count(*) as num, pre_id").Where("set_id = ?", setID).Group("pre_id").Having("num > ?", 1).Rows() + if err != nil { + if !gorm.IsRecordNotFoundError(err) { + return err + } + } + if rows.Next() { + return fmt.Errorf("there is a broken link between scenes, please refresh the interface and try again\n") + } + return nil } // check sceneSet linked list not have same pre_id diff --git a/modules/dop/dao/autotest_scene_test.go b/modules/dop/dao/autotest_scene_test.go new file mode 100644 index 00000000000..b4da5b12c74 --- /dev/null +++ b/modules/dop/dao/autotest_scene_test.go @@ -0,0 +1,114 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dao + +import ( + "fmt" + "testing" + + "bou.ke/monkey" + "github.com/jinzhu/gorm" + + "github.com/erda-project/erda/pkg/database/dbengine" +) + +func TestDBClient_MoveAutoTestScene(t *testing.T) { + type args struct { + id uint64 + newPreID uint64 + newSetID uint64 + tx *gorm.DB + } + + // a < b < c < d < e + // 1 < 2 < 3 < 4 < 5 + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "change a b", + args: args{ + id: 4, + newPreID: 1, + newSetID: 0, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + db := &DBClient{} + patch := monkey.Patch(getScene, func(tx *gorm.DB, id uint64) (AutoTestScene, error) { + if tt.name == "change a b" { + if id == tt.args.id { + return AutoTestScene{ + BaseModel: dbengine.BaseModel{ + ID: tt.args.id, + }, + PreID: 3, + }, nil + } + if id == tt.args.newPreID { + return AutoTestScene{ + BaseModel: dbengine.BaseModel{ + ID: tt.args.newPreID, + }, + }, nil + } + } + return AutoTestScene{}, fmt.Errorf("not find") + }) + defer patch.Unpatch() + + patch1 := monkey.Patch(getSceneByPreID, func(tx *gorm.DB, preID uint64, setID uint64) (AutoTestScene, error) { + if tt.name == "change a b" { + if preID == tt.args.id { + return AutoTestScene{ + BaseModel: dbengine.BaseModel{ + ID: 5, + }, + PreID: tt.args.id, + }, nil + } + if preID == tt.args.newPreID { + return AutoTestScene{ + BaseModel: dbengine.BaseModel{ + ID: 2, + }, + PreID: tt.args.newPreID, + }, nil + } + } + return AutoTestScene{}, fmt.Errorf("not find") + }) + defer patch1.Unpatch() + + patch2 := monkey.Patch(updateScenePreID, func(tx *gorm.DB, id uint64, preID uint64, newPreID uint64, newSetID uint64) error { + return nil + }) + defer patch2.Unpatch() + + patch3 := monkey.Patch(checkSceneSetNotHaveSamePreID, func(tx *gorm.DB, setID uint64) error { + return nil + }) + defer patch3.Unpatch() + + if err := db.MoveAutoTestScene(tt.args.id, tt.args.newPreID, tt.args.newSetID, tt.args.tx); (err != nil) != tt.wantErr { + t.Errorf("MoveAutoTestScene() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/modules/dop/endpoints/autotests.http b/modules/dop/endpoints/autotests.http index 38d6346c559..8da226990a4 100644 --- a/modules/dop/endpoints/autotests.http +++ b/modules/dop/endpoints/autotests.http @@ -1,6 +1,18 @@ +### + GET http://localhost:3033/api/autotests/scenes/2/actions/list-input Accept: application/json User-ID: 2 +### + +PUT http://127.0.0.1:9527/api/autotests/scenes/actions/move-scene +Content-Type: application/json +User-ID: 2 + +{ + "id": 1626, + "target": 1627 +} ### PUT http://localhost:3033/api/autotests/scenes/2/actions/update-input