package dept import ( "context" "errors" "fmt" "testing" "time" deptModel "perms-system-server/internal/model/dept" "perms-system-server/internal/response" "perms-system-server/internal/svc" "perms-system-server/internal/testutil" "perms-system-server/internal/testutil/ctxhelper" "perms-system-server/internal/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func insertDeptRaw(ctx context.Context, svcCtx *svc.ServiceContext, parentId int64, name, path string) (int64, error) { now := time.Now().Unix() result, err := svcCtx.SysDeptModel.Insert(ctx, &deptModel.SysDept{ ParentId: parentId, Name: name, Path: path, Sort: 0, DeptType: "NORMAL", Status: 1, CreateTime: now, UpdateTime: now, }) if err != nil { return 0, err } id, _ := result.LastInsertId() d, err := svcCtx.SysDeptModel.FindOne(ctx, id) if err != nil { return id, err } d.Path = fmt.Sprintf("%s%d/", path, id) d.UpdateTime = now return id, svcCtx.SysDeptModel.Update(ctx, d) } // TC-0081: 父部门不存在 func TestCreateDept_ParentNotFound(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) l := NewCreateDeptLogic(ctx, svcCtx) resp, err := l.CreateDept(&types.CreateDeptReq{ ParentId: 999999999, Name: "orphan_" + testutil.UniqueId(), }) assert.Nil(t, resp) require.Error(t, err) var ce *response.CodeError require.True(t, errors.As(err, &ce)) assert.Equal(t, 404, ce.Code()) assert.Contains(t, ce.Error(), "父部门不存在") } // TC-0079: 创建顶级部门 func TestCreateDept_TopLevel_ViaRawInsert(t *testing.T) { ctx := context.Background() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() name := "dept_top_" + testutil.UniqueId() id, err := insertDeptRaw(ctx, svcCtx, 0, name, "/") require.NoError(t, err) require.Greater(t, id, int64(0)) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_dept`", id) }) dept, err := svcCtx.SysDeptModel.FindOne(ctx, id) require.NoError(t, err) assert.Equal(t, name, dept.Name) assert.Equal(t, int64(0), dept.ParentId) assert.Equal(t, fmt.Sprintf("/%d/", id), dept.Path) assert.Equal(t, int64(1), dept.Status) } // TC-0080: 创建子部门 func TestCreateDept_Child_ViaRawInsert(t *testing.T) { ctx := context.Background() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() parentId, err := insertDeptRaw(ctx, svcCtx, 0, "par_"+testutil.UniqueId(), "/") require.NoError(t, err) parentDept, _ := svcCtx.SysDeptModel.FindOne(ctx, parentId) childId, err := insertDeptRaw(ctx, svcCtx, parentId, "child_"+testutil.UniqueId(), parentDept.Path) require.NoError(t, err) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_dept`", childId, parentId) }) child, err := svcCtx.SysDeptModel.FindOne(ctx, childId) require.NoError(t, err) assert.Equal(t, parentId, child.ParentId) assert.Equal(t, fmt.Sprintf("/%d/%d/", parentId, childId), child.Path) } // TC-0087: 多层嵌套(5层) func TestCreateDept_MultiLevel(t *testing.T) { ctx := context.Background() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() l1Id, err := insertDeptRaw(ctx, svcCtx, 0, "L1_"+testutil.UniqueId(), "/") require.NoError(t, err) l1, _ := svcCtx.SysDeptModel.FindOne(ctx, l1Id) l2Id, err := insertDeptRaw(ctx, svcCtx, l1Id, "L2_"+testutil.UniqueId(), l1.Path) require.NoError(t, err) l2, _ := svcCtx.SysDeptModel.FindOne(ctx, l2Id) l3Id, err := insertDeptRaw(ctx, svcCtx, l2Id, "L3_"+testutil.UniqueId(), l2.Path) require.NoError(t, err) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_dept`", l3Id, l2Id, l1Id) }) d3, err := svcCtx.SysDeptModel.FindOne(ctx, l3Id) require.NoError(t, err) assert.Equal(t, fmt.Sprintf("/%d/%d/%d/", l1Id, l2Id, l3Id), d3.Path) } // TC-0087: 多层嵌套(5层) func TestCreateDept_FiveLevelNesting(t *testing.T) { ctx := context.Background() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() var ids []int64 parentId := int64(0) parentPath := "/" for level := 1; level <= 5; level++ { id, err := insertDeptRaw(ctx, svcCtx, parentId, fmt.Sprintf("L%d_%s", level, testutil.UniqueId()), parentPath) require.NoError(t, err) ids = append(ids, id) dept, err := svcCtx.SysDeptModel.FindOne(ctx, id) require.NoError(t, err) parentId = id parentPath = dept.Path } t.Cleanup(func() { for i := len(ids) - 1; i >= 0; i-- { testutil.CleanTable(ctx, conn, "`sys_dept`", ids[i]) } }) deepest, err := svcCtx.SysDeptModel.FindOne(ctx, ids[4]) require.NoError(t, err) expected := fmt.Sprintf("/%d/%d/%d/%d/%d/", ids[0], ids[1], ids[2], ids[3], ids[4]) assert.Equal(t, expected, deepest.Path) } // TC-0082: 不传DeptType默认NORMAL func TestCreateDept_DefaultDeptType(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() l := NewCreateDeptLogic(ctx, svcCtx) resp, err := l.CreateDept(&types.CreateDeptReq{ ParentId: 0, Name: "deftype_" + testutil.UniqueId(), }) require.NoError(t, err) require.NotNil(t, resp) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_dept`", resp.Id) }) d, err := svcCtx.SysDeptModel.FindOne(ctx, resp.Id) require.NoError(t, err) assert.Equal(t, "NORMAL", d.DeptType) assert.Contains(t, d.Path, fmt.Sprintf("/%d/", resp.Id)) } // TC-0083: 传DeptType=DEV func TestCreateDept_DevDeptType(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() l := NewCreateDeptLogic(ctx, svcCtx) resp, err := l.CreateDept(&types.CreateDeptReq{ ParentId: 0, Name: "devtype_" + testutil.UniqueId(), DeptType: "DEV", }) require.NoError(t, err) require.NotNil(t, resp) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_dept`", resp.Id) }) d, err := svcCtx.SysDeptModel.FindOne(ctx, resp.Id) require.NoError(t, err) assert.Equal(t, "DEV", d.DeptType) assert.Contains(t, d.Path, fmt.Sprintf("/%d/", resp.Id)) } // TC-0088: 通过Logic创建+验证Path func TestCreateDept_ViaLogic_PathCorrect(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() l := NewCreateDeptLogic(ctx, svcCtx) parentResp, err := l.CreateDept(&types.CreateDeptReq{ ParentId: 0, Name: "parent_" + testutil.UniqueId(), }) require.NoError(t, err) childResp, err := l.CreateDept(&types.CreateDeptReq{ ParentId: parentResp.Id, Name: "child_" + testutil.UniqueId(), }) require.NoError(t, err) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_dept`", childResp.Id, parentResp.Id) }) parent, err := svcCtx.SysDeptModel.FindOne(ctx, parentResp.Id) require.NoError(t, err) assert.Equal(t, fmt.Sprintf("/%d/", parentResp.Id), parent.Path) child, err := svcCtx.SysDeptModel.FindOne(ctx, childResp.Id) require.NoError(t, err) assert.Equal(t, fmt.Sprintf("/%d/%d/", parentResp.Id, childResp.Id), child.Path) } // TC-0511: createDept非超管拒绝 func TestCreateDept_NonSuperAdminRejected(t *testing.T) { ctx := ctxhelper.AdminCtx("test_product") svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) l := NewCreateDeptLogic(ctx, svcCtx) _, err := l.CreateDept(&types.CreateDeptReq{Name: "test", Sort: 1}) require.Error(t, err) var ce *response.CodeError require.True(t, errors.As(err, &ce)) assert.Equal(t, 403, ce.Code()) } var _ = deptModel.ErrNotFound