package dept import ( "context" "errors" "math" "testing" "time" "perms-system-server/internal/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/zeromicro/go-zero/core/stores/sqlx" ) // TC-0267: 正常插入 func TestSysDeptModel_CRUD(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() data := &SysDept{ ParentId: 0, Name: "dept_crud_" + testutil.UniqueId(), Path: "/crud/" + testutil.UniqueId() + "/", Sort: 10, Remark: "r", Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id) }) found, err := m.FindOne(ctx, id) require.NoError(t, err) require.NotNil(t, found) assert.Equal(t, id, found.Id) assert.Equal(t, data.Name, found.Name) found.Remark = "updated" found.UpdateTime = now + 1 require.NoError(t, m.Update(ctx, found)) after, err := m.FindOne(ctx, id) require.NoError(t, err) assert.Equal(t, "updated", after.Remark) require.NoError(t, m.Delete(ctx, id)) _, err = m.FindOne(ctx, id) require.Error(t, err) assert.True(t, errors.Is(err, ErrNotFound)) } // TC-0388: 正常查询 func TestSysDeptModel_FindAll_OrderBySortAscIdAsc(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) base := testutil.UniqueId() now := time.Now().Unix() rows := []*SysDept{ {ParentId: 0, Name: "a_" + base, Path: "/fa/" + base + "/a/", Sort: 30, Remark: "", Status: 1, CreateTime: now, UpdateTime: now}, {ParentId: 0, Name: "b_" + base, Path: "/fa/" + base + "/b/", Sort: 10, Remark: "", Status: 1, CreateTime: now, UpdateTime: now}, {ParentId: 0, Name: "c_" + base, Path: "/fa/" + base + "/c/", Sort: 20, Remark: "", Status: 1, CreateTime: now, UpdateTime: now}, } require.NoError(t, m.BatchInsert(ctx, rows)) prefix := "/fa/" + base byPath, err := m.FindByPathPrefix(ctx, prefix) require.NoError(t, err) require.Len(t, byPath, 3) ids := []int64{byPath[0].Id, byPath[1].Id, byPath[2].Id} tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, ids...) }) all, err := m.FindAll(ctx) require.NoError(t, err) idSet := map[int64]struct{}{ids[0]: {}, ids[1]: {}, ids[2]: {}} var picked []*SysDept for i := range all { if _, ok := idSet[all[i].Id]; ok { picked = append(picked, all[i]) } } require.Len(t, picked, 3) for i := 1; i < len(picked); i++ { prev, cur := picked[i-1], picked[i] if prev.Sort == cur.Sort { assert.Less(t, prev.Id, cur.Id) } else { assert.Less(t, prev.Sort, cur.Sort) } } assert.Equal(t, int64(10), picked[0].Sort) assert.Equal(t, int64(20), picked[1].Sort) assert.Equal(t, int64(30), picked[2].Sort) } // TC-0390: 正常查询 func TestSysDeptModel_FindByParentId_FoundAndNotFound(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() parent := &SysDept{ ParentId: 0, Name: "p_" + testutil.UniqueId(), Path: "/fp/" + testutil.UniqueId() + "/", Sort: 1, Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, parent) require.NoError(t, err) pid, err := res.LastInsertId() require.NoError(t, err) child := &SysDept{ ParentId: pid, Name: "c_" + testutil.UniqueId(), Path: parent.Path + "sub/", Sort: 1, Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } cres, err := m.Insert(ctx, child) require.NoError(t, err) cid, err := cres.LastInsertId() require.NoError(t, err) tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, cid, pid) }) list, err := m.FindByParentId(ctx, pid) require.NoError(t, err) require.Len(t, list, 1) assert.Equal(t, cid, list[0].Id) empty, err := m.FindByParentId(ctx, math.MaxInt64) require.NoError(t, err) assert.Len(t, empty, 0) } // TC-0392: 正常查询 func TestSysDeptModel_FindByPathPrefix_LikePrefix(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() pfx := "/pfx/" + testutil.UniqueId() d1 := &SysDept{ParentId: 0, Name: "n1_" + testutil.UniqueId(), Path: pfx + "/a/", Sort: 1, Remark: "", Status: 1, CreateTime: now, UpdateTime: now} d2 := &SysDept{ParentId: 0, Name: "n2_" + testutil.UniqueId(), Path: pfx + "/b/", Sort: 2, Remark: "", Status: 1, CreateTime: now, UpdateTime: now} other := &SysDept{ParentId: 0, Name: "o_" + testutil.UniqueId(), Path: "/other/" + testutil.UniqueId() + "/", Sort: 1, Remark: "", Status: 1, CreateTime: now, UpdateTime: now} r1, err := m.Insert(ctx, d1) require.NoError(t, err) id1, _ := r1.LastInsertId() r2, err := m.Insert(ctx, d2) require.NoError(t, err) id2, _ := r2.LastInsertId() r3, err := m.Insert(ctx, other) require.NoError(t, err) id3, _ := r3.LastInsertId() tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id1, id2, id3) }) list, err := m.FindByPathPrefix(ctx, pfx) require.NoError(t, err) require.Len(t, list, 2) names := map[string]struct{}{list[0].Name: {}, list[1].Name: {}} assert.Contains(t, names, d1.Name) assert.Contains(t, names, d2.Name) } // TC-0290: 多条记录(3条) func TestSysDeptModel_BatchInsert_BatchDelete(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() tag := testutil.UniqueId() batch := []*SysDept{ {ParentId: 0, Name: "b1_" + tag, Path: "/bi/" + tag + "/1/", Sort: 1, Remark: "", Status: 1, CreateTime: now, UpdateTime: now}, {ParentId: 0, Name: "b2_" + tag, Path: "/bi/" + tag + "/2/", Sort: 2, Remark: "", Status: 1, CreateTime: now, UpdateTime: now}, } require.NoError(t, m.BatchInsert(ctx, batch)) got, err := m.FindByPathPrefix(ctx, "/bi/"+tag) require.NoError(t, err) require.Len(t, got, 2) ids := []int64{got[0].Id, got[1].Id} tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, ids...) }) require.NoError(t, m.BatchDelete(ctx, ids)) for _, id := range ids { _, err := m.FindOne(ctx, id) require.Error(t, err) assert.True(t, errors.Is(err, ErrNotFound)) } } // TC-0281: 事务内更新 func TestSysDeptModel_TransactCtx_InsertWithTx_UpdateWithTx(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() d := &SysDept{ ParentId: 0, Name: "tx_" + testutil.UniqueId(), Path: "/tx/" + testutil.UniqueId() + "/", Sort: 1, Remark: "before", Status: 1, CreateTime: now, UpdateTime: now, } var finalId int64 err := m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error { res, err := m.InsertWithTx(c, s, d) if err != nil { return err } lid, err := res.LastInsertId() if err != nil { return err } finalId = lid d.Id = finalId d.Remark = "after_tx" d.UpdateTime = now + 2 return m.UpdateWithTx(c, s, d) }) require.NoError(t, err) tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, finalId) }) out, err := m.FindOne(ctx, finalId) require.NoError(t, err) assert.Equal(t, "after_tx", out.Remark) } // TC-0287: 获取表名 func TestSysDeptModel_TableName(t *testing.T) { conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) assert.Equal(t, "`sys_dept`", m.TableName()) } // TC-0274: 记录不存在 func TestSysDeptModel_FindOne_NotFound(t *testing.T) { conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) _, err := m.FindOne(context.Background(), 999999999999) require.ErrorIs(t, err, ErrNotFound) } // TC-0280: 记录不存在 func TestSysDeptModel_Update_NonExistentRow_NoError(t *testing.T) { conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) err := m.Update(context.Background(), &SysDept{ Id: 999999999999, Name: "ghost", Path: "/x/", Status: 1, CreateTime: time.Now().Unix(), UpdateTime: time.Now().Unix(), }) require.NoError(t, err) } // TC-0283: 记录不存在 func TestSysDeptModel_Delete_NonExistentRow_NoError(t *testing.T) { conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) err := m.Delete(context.Background(), 999999999999) require.NoError(t, err) } // TC-0288: 空列表 func TestSysDeptModel_BatchInsert_Empty(t *testing.T) { conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) require.NoError(t, m.BatchInsert(context.Background(), nil)) require.NoError(t, m.BatchInsert(context.Background(), []*SysDept{})) } // TC-0305: 空ids func TestSysDeptModel_BatchDelete_Empty(t *testing.T) { conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) require.NoError(t, m.BatchDelete(context.Background(), nil)) require.NoError(t, m.BatchDelete(context.Background(), []int64{})) } // TC-0394: 无匹配 func TestSysDeptModel_FindByPathPrefix_NoMatch(t *testing.T) { conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) list, err := m.FindByPathPrefix(context.Background(), "/notexist_"+testutil.UniqueId()) require.NoError(t, err) assert.Empty(t, list) } // TC-0271: 事务回滚后无数据 func TestSysDeptModel_InsertWithTx_Rollback(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() path := "/txrb/" + testutil.UniqueId() + "/" d := &SysDept{ ParentId: 0, Name: "txrb_" + testutil.UniqueId(), Path: path, Sort: 1, Status: 1, CreateTime: now, UpdateTime: now, } err := m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error { if _, e := m.InsertWithTx(c, s, d); e != nil { return e } return errors.New("force rollback") }) require.Error(t, err) list, err := m.FindByPathPrefix(ctx, path) require.NoError(t, err) assert.Empty(t, list) } // TC-0284: 事务内删除 func TestSysDeptModel_DeleteWithTx(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() d := &SysDept{ ParentId: 0, Name: "deltx_" + testutil.UniqueId(), Path: "/deltx/" + testutil.UniqueId() + "/", Sort: 1, Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, d) require.NoError(t, err) id, _ := res.LastInsertId() tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id) }) err = m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error { return m.DeleteWithTx(c, s, id) }) require.NoError(t, err) _, err = m.FindOne(ctx, id) require.ErrorIs(t, err, ErrNotFound) } // TC-0286: fn返回错误 func TestSysDeptModel_TransactCtx_Rollback(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() path := "/txrb2/" + testutil.UniqueId() + "/" d := &SysDept{ ParentId: 0, Name: "txrb2_" + testutil.UniqueId(), Path: path, Sort: 1, Status: 1, CreateTime: now, UpdateTime: now, } err := m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error { if _, e := m.InsertWithTx(c, s, d); e != nil { return e } return errors.New("force rollback") }) require.Error(t, err) require.Contains(t, err.Error(), "force rollback") list, err := m.FindByPathPrefix(ctx, path) require.NoError(t, err) assert.Empty(t, list) } // TC-0296: 空列表 func TestSysDeptModel_BatchUpdate_Empty(t *testing.T) { conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) require.NoError(t, m.BatchUpdate(context.Background(), nil)) require.NoError(t, m.BatchUpdate(context.Background(), []*SysDept{})) } // TC-0298: 多条记录(3条) func TestSysDeptModel_BatchUpdate_Multi(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() tag := testutil.UniqueId() d1 := &SysDept{ParentId: 0, Name: "bu1_" + tag, Path: "/bu/" + tag + "/1/", Sort: 1, Remark: "r1", Status: 1, CreateTime: now, UpdateTime: now} d2 := &SysDept{ParentId: 0, Name: "bu2_" + tag, Path: "/bu/" + tag + "/2/", Sort: 2, Remark: "r2", Status: 1, CreateTime: now, UpdateTime: now} r1, err := m.Insert(ctx, d1) require.NoError(t, err) id1, _ := r1.LastInsertId() r2, err := m.Insert(ctx, d2) require.NoError(t, err) id2, _ := r2.LastInsertId() tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id1, id2) }) now2 := time.Now().Unix() upd := []*SysDept{ {Id: id1, ParentId: 0, Name: "bu1_upd", Path: d1.Path, Sort: 10, Remark: "updated", Status: 1, CreateTime: now, UpdateTime: now2}, {Id: id2, ParentId: 0, Name: "bu2_upd", Path: d2.Path, Sort: 20, Remark: "updated", Status: 2, CreateTime: now, UpdateTime: now2}, } require.NoError(t, m.BatchUpdate(ctx, upd)) g1, err := m.FindOne(ctx, id1) require.NoError(t, err) assert.Equal(t, "bu1_upd", g1.Name) assert.Equal(t, int64(10), g1.Sort) g2, err := m.FindOne(ctx, id2) require.NoError(t, err) assert.Equal(t, "bu2_upd", g2.Name) assert.Equal(t, int64(2), g2.Status) } // TC-0289: 单条记录 func TestSysDeptModel_BatchInsert_Single(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() path := "/bis/" + testutil.UniqueId() + "/" d := &SysDept{ParentId: 0, Name: "bis_" + testutil.UniqueId(), Path: path, Sort: 1, Status: 1, CreateTime: now, UpdateTime: now} require.NoError(t, m.BatchInsert(ctx, []*SysDept{d})) list, err := m.FindByPathPrefix(ctx, path) require.NoError(t, err) require.Len(t, list, 1) tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, list[0].Id) }) assert.Equal(t, d.Name, list[0].Name) } // TC-0294: 正常多条 func TestSysDeptModel_BatchInsertWithTx_Normal(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() tag := testutil.UniqueId() batch := []*SysDept{ {ParentId: 0, Name: "bitx1_" + tag, Path: "/bitx/" + tag + "/1/", Sort: 1, Status: 1, CreateTime: now, UpdateTime: now}, {ParentId: 0, Name: "bitx2_" + tag, Path: "/bitx/" + tag + "/2/", Sort: 2, Status: 1, CreateTime: now, UpdateTime: now}, } err := m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error { return m.BatchInsertWithTx(c, s, batch) }) require.NoError(t, err) list, err := m.FindByPathPrefix(ctx, "/bitx/"+tag) require.NoError(t, err) require.Len(t, list, 2) tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, list[0].Id, list[1].Id) }) } // TC-0293: 空列表 func TestSysDeptModel_BatchInsertWithTx_Empty(t *testing.T) { conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) err := m.TransactCtx(context.Background(), func(c context.Context, s sqlx.Session) error { return m.BatchInsertWithTx(c, s, nil) }) require.NoError(t, err) } // TC-0295: 事务回滚 func TestSysDeptModel_BatchInsertWithTx_Rollback(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() path := "/bitxrb/" + testutil.UniqueId() + "/" batch := []*SysDept{ {ParentId: 0, Name: "rbn_" + testutil.UniqueId(), Path: path + "1/", Sort: 1, Status: 1, CreateTime: now, UpdateTime: now}, } err := m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error { if e := m.BatchInsertWithTx(c, s, batch); e != nil { return e } return errors.New("force rollback") }) require.Error(t, err) list, err := m.FindByPathPrefix(ctx, path) require.NoError(t, err) assert.Empty(t, list) } // TC-0301: 正常多条 func TestSysDeptModel_BatchUpdateWithTx_Normal(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() tag := testutil.UniqueId() d1 := &SysDept{ParentId: 0, Name: "butx1_" + tag, Path: "/butx/" + tag + "/1/", Sort: 1, Remark: "old", Status: 1, CreateTime: now, UpdateTime: now} d2 := &SysDept{ParentId: 0, Name: "butx2_" + tag, Path: "/butx/" + tag + "/2/", Sort: 2, Remark: "old", Status: 1, CreateTime: now, UpdateTime: now} r1, err := m.Insert(ctx, d1) require.NoError(t, err) id1, _ := r1.LastInsertId() r2, err := m.Insert(ctx, d2) require.NoError(t, err) id2, _ := r2.LastInsertId() tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id1, id2) }) now2 := time.Now().Unix() err = m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error { return m.BatchUpdateWithTx(c, s, []*SysDept{ {Id: id1, ParentId: 0, Name: "butx1_new", Path: d1.Path, Sort: 10, Remark: "new", Status: 1, CreateTime: now, UpdateTime: now2}, {Id: id2, ParentId: 0, Name: "butx2_new", Path: d2.Path, Sort: 20, Remark: "new", Status: 1, CreateTime: now, UpdateTime: now2}, }) }) require.NoError(t, err) g1, err := m.FindOne(ctx, id1) require.NoError(t, err) assert.Equal(t, "butx1_new", g1.Name) assert.Equal(t, int64(10), g1.Sort) g2, err := m.FindOne(ctx, id2) require.NoError(t, err) assert.Equal(t, "butx2_new", g2.Name) } // TC-0300: 空列表 func TestSysDeptModel_BatchUpdateWithTx_Empty(t *testing.T) { conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) err := m.TransactCtx(context.Background(), func(c context.Context, s sqlx.Session) error { return m.BatchUpdateWithTx(c, s, nil) }) require.NoError(t, err) } // TC-0306: 单个id func TestSysDeptModel_BatchDelete_Single(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() d := &SysDept{ParentId: 0, Name: "bds_" + testutil.UniqueId(), Path: "/bds/" + testutil.UniqueId() + "/", Sort: 1, Status: 1, CreateTime: now, UpdateTime: now} res, err := m.Insert(ctx, d) require.NoError(t, err) id, _ := res.LastInsertId() tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id) }) require.NoError(t, m.BatchDelete(ctx, []int64{id})) _, err = m.FindOne(ctx, id) require.ErrorIs(t, err, ErrNotFound) } // TC-0308: 包含不存在id func TestSysDeptModel_BatchDelete_ContainsNonExist(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() d := &SysDept{ParentId: 0, Name: "bdne_" + testutil.UniqueId(), Path: "/bdne/" + testutil.UniqueId() + "/", Sort: 1, Status: 1, CreateTime: now, UpdateTime: now} res, err := m.Insert(ctx, d) require.NoError(t, err) id, _ := res.LastInsertId() tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id) }) require.NoError(t, m.BatchDelete(ctx, []int64{id, 999999999})) _, err = m.FindOne(ctx, id) require.ErrorIs(t, err, ErrNotFound) } // TC-0310: 正常多条 func TestSysDeptModel_BatchDeleteWithTx_Normal(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() tag := testutil.UniqueId() d1 := &SysDept{ParentId: 0, Name: "bdtx1_" + tag, Path: "/bdtx/" + tag + "/1/", Sort: 1, Status: 1, CreateTime: now, UpdateTime: now} d2 := &SysDept{ParentId: 0, Name: "bdtx2_" + tag, Path: "/bdtx/" + tag + "/2/", Sort: 2, Status: 1, CreateTime: now, UpdateTime: now} r1, err := m.Insert(ctx, d1) require.NoError(t, err) id1, _ := r1.LastInsertId() r2, err := m.Insert(ctx, d2) require.NoError(t, err) id2, _ := r2.LastInsertId() tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id1, id2) }) err = m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error { return m.BatchDeleteWithTx(c, s, []int64{id1, id2}) }) require.NoError(t, err) _, err = m.FindOne(ctx, id1) require.ErrorIs(t, err, ErrNotFound) _, err = m.FindOne(ctx, id2) require.ErrorIs(t, err, ErrNotFound) } // TC-0309: 空ids func TestSysDeptModel_BatchDeleteWithTx_Empty(t *testing.T) { conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) err := m.TransactCtx(context.Background(), func(c context.Context, s sqlx.Session) error { return m.BatchDeleteWithTx(c, s, nil) }) require.NoError(t, err) } // TC-0393: LIKE注入已修复 — % 和 _ 被转义,不再作为通配符 func TestSysDeptModel_FindByPathPrefix_LikeInjection(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() d := &SysDept{ParentId: 0, Name: "li_" + testutil.UniqueId(), Path: "/test/", Sort: 1, Status: 1, CreateTime: now, UpdateTime: now} res, err := m.Insert(ctx, d) require.NoError(t, err) id, _ := res.LastInsertId() tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id) }) list, err := m.FindByPathPrefix(ctx, "%") require.NoError(t, err) assert.Empty(t, list, "% 不应再作为通配符匹配所有记录") list2, err := m.FindByPathPrefix(ctx, "/test/") require.NoError(t, err) assert.GreaterOrEqual(t, len(list2), 1, "正常前缀仍应匹配") } // TC-0278: 事务内可见性 func TestSysDeptModel_FindOneWithTx_InsertThenFind(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() var foundInTx *SysDept var insertedId int64 err := m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error { res, err := m.InsertWithTx(c, s, &SysDept{ ParentId: 0, Name: "ftx_" + testutil.UniqueId(), Path: "/ftx/" + testutil.UniqueId() + "/", Sort: 1, DeptType: "NORMAL", Status: 1, CreateTime: now, UpdateTime: now, }) if err != nil { return err } insertedId, _ = res.LastInsertId() foundInTx, err = m.FindOneWithTx(c, s, insertedId) return err }) require.NoError(t, err) tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, insertedId) }) require.NotNil(t, foundInTx) assert.Equal(t, insertedId, foundInTx.Id) } // TC-0277: 事务内记录不存在 func TestSysDeptModel_FindOneWithTx_NotFound(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) err := m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error { _, err := m.FindOneWithTx(c, s, 999999999999) return err }) require.ErrorIs(t, err, ErrNotFound) } // TC-0391: FindByParentId 无子部门 func TestSysDeptModel_FindByParentId_NoChildren(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix()) now := time.Now().Unix() parent := &SysDept{ ParentId: 0, Name: "nochild_" + testutil.UniqueId(), Path: "/nochild/" + testutil.UniqueId() + "/", Sort: 1, Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, parent) require.NoError(t, err) pid, err := res.LastInsertId() require.NoError(t, err) tbl := m.TableName() t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, pid) }) list, err := m.FindByParentId(ctx, pid) require.NoError(t, err) require.Empty(t, list) }