package perm_test import ( "context" "errors" "sort" "testing" "time" "github.com/go-sql-driver/mysql" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/zeromicro/go-zero/core/stores/sqlx" "perms-system-server/internal/model/perm" "perms-system-server/internal/testutil" ) func newTestSysPermModel(t *testing.T) perm.SysPermModel { t.Helper() return perm.NewSysPermModel( testutil.GetTestSqlConn(), testutil.GetTestCacheConf(), testutil.GetTestCachePrefix(), ) } func sortPermsByID(list []*perm.SysPerm) { sort.Slice(list, func(i, j int) bool { return list[i].Id < list[j].Id }) } func isDuplicateKeyError(err error) bool { var me *mysql.MySQLError return errors.As(err, &me) && me.Number == 1062 } // TC-0267: 正常插入 func TestSysPermModel_CRUD(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() code := testutil.UniqueId() now := time.Now().Unix() data := &perm.SysPerm{ ProductCode: productCode, Name: "perm-name", Code: code, Remark: "r1", Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) require.NotZero(t, id) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", id) }) one, err := m.FindOne(ctx, id) require.NoError(t, err) require.Equal(t, id, one.Id) require.Equal(t, productCode, one.ProductCode) require.Equal(t, "perm-name", one.Name) require.Equal(t, code, one.Code) require.Equal(t, int64(1), one.Status) now2 := time.Now().Unix() data.Id = id data.Name = "updated-name" data.Remark = "r2" data.Status = 2 data.UpdateTime = now2 require.NoError(t, m.Update(ctx, data)) after, err := m.FindOne(ctx, id) require.NoError(t, err) require.Equal(t, "updated-name", after.Name) require.Equal(t, int64(2), after.Status) require.Equal(t, now2, after.UpdateTime) require.NoError(t, m.Delete(ctx, id)) _, err = m.FindOne(ctx, id) require.ErrorIs(t, err, perm.ErrNotFound) } // TC-0323: FindOneByProductCodeCode func TestSysPermModel_FindOneByProductCodeCode(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() code := testutil.UniqueId() now := time.Now().Unix() data := &perm.SysPerm{ ProductCode: productCode, Name: "n", Code: code, Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", id) }) found, err := m.FindOneByProductCodeCode(ctx, productCode, code) require.NoError(t, err) require.Equal(t, id, found.Id) require.Equal(t, code, found.Code) _, err = m.FindOneByProductCodeCode(ctx, productCode, testutil.UniqueId()+"_missing") require.ErrorIs(t, err, perm.ErrNotFound) } // TC-0373: 正常分页 func TestSysPermModel_FindListByProductCode_Pagination(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() ids := make([]int64, 0, 5) for i := 0; i < 5; i++ { data := &perm.SysPerm{ ProductCode: productCode, Name: "p", Code: testutil.UniqueId(), Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) ids = append(ids, id) } t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", ids...) }) all, cnt, err := m.FindListByProductCode(ctx, productCode, 1, 100) require.NoError(t, err) require.Len(t, all, 5) require.Equal(t, int64(5), cnt) page1, tot, err := m.FindListByProductCode(ctx, productCode, 1, 2) require.NoError(t, err) require.Equal(t, int64(5), tot) require.Len(t, page1, 2) // id desc: first page has largest ids require.Greater(t, page1[0].Id, page1[1].Id) page2, _, err := m.FindListByProductCode(ctx, productCode, 2, 2) require.NoError(t, err) require.Len(t, page2, 2) page3, _, err := m.FindListByProductCode(ctx, productCode, 3, 2) require.NoError(t, err) require.Len(t, page3, 1) } // TC-0375: 正常查询(仅status=1) func TestSysPermModel_FindAllByProductCode_OnlyEnabled(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() ids := make([]int64, 0, 3) for _, st := range []int64{1, 2, 1} { data := &perm.SysPerm{ ProductCode: productCode, Name: "a", Code: testutil.UniqueId(), Remark: "", Status: st, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) ids = append(ids, id) } t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", ids...) }) list, err := m.FindAllByProductCode(ctx, productCode) require.NoError(t, err) require.Len(t, list, 2) for _, p := range list { require.Equal(t, int64(1), p.Status) } } // TC-0377: 正常查询 func TestSysPermModel_FindAllCodesByProductCode_OnlyEnabled(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() codeOn := testutil.UniqueId() codeOff := testutil.UniqueId() ids := make([]int64, 0, 2) ins := []struct { code string status int64 }{ {codeOn, 1}, {codeOff, 2}, } for _, row := range ins { data := &perm.SysPerm{ ProductCode: productCode, Name: "c", Code: row.code, Remark: "", Status: row.status, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) ids = append(ids, id) } t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", ids...) }) codes, err := m.FindAllCodesByProductCode(ctx, productCode) require.NoError(t, err) require.Equal(t, []string{codeOn}, codes) } // TC-0379: 正常 func TestSysPermModel_FindByIds(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() var id1, id2 int64 for i := 0; i < 2; i++ { data := &perm.SysPerm{ ProductCode: productCode, Name: "f", Code: testutil.UniqueId(), Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) if i == 0 { id1 = id } else { id2 = id } } t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", id1, id2) }) t.Run("normal", func(t *testing.T) { list, err := m.FindByIds(ctx, []int64{id1, id2}) require.NoError(t, err) require.Len(t, list, 2) sortPermsByID(list) require.Equal(t, id1, list[0].Id) require.Equal(t, id2, list[1].Id) }) t.Run("empty", func(t *testing.T) { list, err := m.FindByIds(ctx, nil) require.NoError(t, err) require.Nil(t, list) list2, err := m.FindByIds(ctx, []int64{}) require.NoError(t, err) require.Nil(t, list2) }) t.Run("partial", func(t *testing.T) { list, err := m.FindByIds(ctx, []int64{999999999999, id1}) require.NoError(t, err) require.Len(t, list, 1) require.Equal(t, id1, list[0].Id) }) } // TC-0381: 正常查询 func TestSysPermModel_FindMapByProductCode(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() c1 := testutil.UniqueId() c2 := testutil.UniqueId() now := time.Now().Unix() ids := make([]int64, 0, 2) for _, code := range []string{c1, c2} { data := &perm.SysPerm{ ProductCode: productCode, Name: "m", Code: code, Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) ids = append(ids, id) } t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", ids...) }) mp, err := m.FindMapByProductCode(ctx, productCode) require.NoError(t, err) require.Len(t, mp, 2) require.Contains(t, mp, c1) require.Contains(t, mp, c2) require.Equal(t, c1, mp[c1].Code) require.Equal(t, productCode, mp[c2].ProductCode) } // TC-0384: codes非空-正常 func TestSysPermModel_DisableNotInCodes(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() k1 := testutil.UniqueId() k2 := testutil.UniqueId() k3 := testutil.UniqueId() ids := make([]int64, 0, 3) for _, code := range []string{k1, k2, k3} { data := &perm.SysPerm{ ProductCode: productCode, Name: "d", Code: code, Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) ids = append(ids, id) } t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", ids...) }) t.Run("with_codes_keeps_subset", func(t *testing.T) { ts := time.Now().Unix() n, err := m.DisableNotInCodes(ctx, productCode, []string{k1, k2}, ts) require.NoError(t, err) require.Equal(t, int64(1), n) all, err := m.FindAllByProductCode(ctx, productCode) require.NoError(t, err) require.Len(t, all, 2) gotCodes := []string{all[0].Code, all[1].Code} assert.ElementsMatch(t, []string{k1, k2}, gotCodes) p3, err := m.FindOneByProductCodeCode(ctx, productCode, k3) require.NoError(t, err) require.Equal(t, int64(2), p3.Status) require.Equal(t, ts, p3.UpdateTime) }) t.Run("empty_codes_disables_all_enabled", func(t *testing.T) { // reset rows: re-insert three enabled for a fresh product pc := testutil.UniqueId() ids2 := make([]int64, 0, 3) for i := 0; i < 3; i++ { data := &perm.SysPerm{ ProductCode: pc, Name: "d2", Code: testutil.UniqueId(), Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) ids2 = append(ids2, id) } t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", ids2...) }) ts := time.Now().Unix() n, err := m.DisableNotInCodes(ctx, pc, nil, ts) require.NoError(t, err) require.Equal(t, int64(3), n) enabled, err := m.FindAllByProductCode(ctx, pc) require.NoError(t, err) require.Len(t, enabled, 0) }) t.Run("no_need_to_disable", func(t *testing.T) { pc := testutil.UniqueId() ca := testutil.UniqueId() cb := testutil.UniqueId() ids3 := make([]int64, 0, 2) for _, code := range []string{ca, cb} { data := &perm.SysPerm{ ProductCode: pc, Name: "d3", Code: code, Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) ids3 = append(ids3, id) } t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", ids3...) }) ts := time.Now().Unix() n, err := m.DisableNotInCodes(ctx, pc, []string{ca, cb}, ts) require.NoError(t, err) require.Equal(t, int64(0), n) for _, code := range []string{ca, cb} { p, err := m.FindOneByProductCodeCode(ctx, pc, code) require.NoError(t, err) require.Equal(t, int64(1), p.Status) } }) } // TC-0290: 多条记录(3条) func TestSysPermModel_BatchInsert_BatchDelete(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() list := []*perm.SysPerm{ { ProductCode: productCode, Name: "b1", Code: testutil.UniqueId(), Remark: "", Status: 1, CreateTime: now, UpdateTime: now, }, { ProductCode: productCode, Name: "b2", Code: testutil.UniqueId(), Remark: "", Status: 1, CreateTime: now, UpdateTime: now, }, } require.NoError(t, m.BatchInsert(ctx, list)) var ids []int64 for _, code := range []string{list[0].Code, list[1].Code} { p, err := m.FindOneByProductCodeCode(ctx, productCode, code) require.NoError(t, err) ids = append(ids, p.Id) } t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", ids...) }) require.NoError(t, m.BatchDelete(ctx, ids)) for _, id := range ids { _, err := m.FindOne(ctx, id) require.ErrorIs(t, err, perm.ErrNotFound) } } // TC-0268: 唯一索引冲突 func TestSysPermModel_InsertDuplicateProductCodeCode(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() code := testutil.UniqueId() now := time.Now().Unix() data := &perm.SysPerm{ ProductCode: productCode, Name: "dup", Code: code, Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", id) }) dup := &perm.SysPerm{ ProductCode: productCode, Name: "other", Code: code, Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } _, err = m.Insert(ctx, dup) require.Error(t, err) assert.True(t, isDuplicateKeyError(err), "expected MySQL duplicate key error, got: %v", err) } // TC-0274: 记录不存在 func TestSysPermModel_FindOne_NotFound(t *testing.T) { m := newTestSysPermModel(t) _, err := m.FindOne(context.Background(), 999999999999) require.ErrorIs(t, err, perm.ErrNotFound) } // TC-0280: 记录不存在 func TestSysPermModel_Update_NotFound(t *testing.T) { m := newTestSysPermModel(t) err := m.Update(context.Background(), &perm.SysPerm{ Id: 999999999999, ProductCode: "x", Name: "n", Code: "c", Status: 1, CreateTime: time.Now().Unix(), UpdateTime: time.Now().Unix(), }) require.ErrorIs(t, err, perm.ErrNotFound) } // TC-0283: 记录不存在 func TestSysPermModel_Delete_NotFound(t *testing.T) { m := newTestSysPermModel(t) err := m.Delete(context.Background(), 999999999999) require.ErrorIs(t, err, perm.ErrNotFound) } // TC-0288: 空列表 func TestSysPermModel_BatchInsert_Empty(t *testing.T) { m := newTestSysPermModel(t) require.NoError(t, m.BatchInsert(context.Background(), nil)) require.NoError(t, m.BatchInsert(context.Background(), []*perm.SysPerm{})) } // TC-0305: 空ids func TestSysPermModel_BatchDelete_Empty(t *testing.T) { m := newTestSysPermModel(t) require.NoError(t, m.BatchDelete(context.Background(), nil)) require.NoError(t, m.BatchDelete(context.Background(), []int64{})) } // TC-0374: 不存在的productCode func TestSysPermModel_FindListByProductCode_NotExistProduct(t *testing.T) { m := newTestSysPermModel(t) list, total, err := m.FindListByProductCode(context.Background(), "notexist_"+testutil.UniqueId(), 1, 10) require.NoError(t, err) require.Equal(t, int64(0), total) require.Len(t, list, 0) } // TC-0378: 空结果 func TestSysPermModel_FindAllCodesByProductCode_Empty(t *testing.T) { m := newTestSysPermModel(t) codes, err := m.FindAllCodesByProductCode(context.Background(), "empty_"+testutil.UniqueId()) require.NoError(t, err) require.Empty(t, codes) } // TC-0382: 空结果 func TestSysPermModel_FindMapByProductCode_Empty(t *testing.T) { m := newTestSysPermModel(t) mp, err := m.FindMapByProductCode(context.Background(), "empty_"+testutil.UniqueId()) require.NoError(t, err) require.Empty(t, mp) } // TC-0270: 事务内插入 func TestSysPermModel_InsertWithTx_Normal(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() code := testutil.UniqueId() now := time.Now().Unix() data := &perm.SysPerm{ ProductCode: productCode, Name: "tx_insert", Code: code, Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } var insertedID int64 err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error { res, err := m.InsertWithTx(c, session, data) if err != nil { return err } insertedID, err = res.LastInsertId() return err }) require.NoError(t, err) require.Greater(t, insertedID, int64(0)) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", insertedID) }) got, err := m.FindOne(ctx, insertedID) require.NoError(t, err) require.Equal(t, productCode, got.ProductCode) require.Equal(t, code, got.Code) } // TC-0271: 事务回滚后无数据 func TestSysPermModel_InsertWithTx_Rollback(t *testing.T) { ctx := context.Background() m := newTestSysPermModel(t) productCode := testutil.UniqueId() code := testutil.UniqueId() now := time.Now().Unix() data := &perm.SysPerm{ ProductCode: productCode, Name: "tx_rollback", Code: code, Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error { if _, e := m.InsertWithTx(c, session, data); e != nil { return e } return errors.New("force rollback") }) require.Error(t, err) _, err = m.FindOneByProductCodeCode(ctx, productCode, code) require.ErrorIs(t, err, perm.ErrNotFound) } // TC-0281: 事务内更新 func TestSysPermModel_UpdateWithTx(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() code := testutil.UniqueId() now := time.Now().Unix() data := &perm.SysPerm{ ProductCode: productCode, Name: "before", Code: code, Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", id) }) err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error { data.Id = id data.Name = "after" data.UpdateTime = time.Now().Unix() return m.UpdateWithTx(c, session, data) }) require.NoError(t, err) got, err := m.FindOne(ctx, id) require.NoError(t, err) require.Equal(t, "after", got.Name) } // TC-0284: 事务内删除 func TestSysPermModel_DeleteWithTx(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() code := testutil.UniqueId() now := time.Now().Unix() data := &perm.SysPerm{ ProductCode: productCode, Name: "del", Code: code, Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", id) }) err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error { return m.DeleteWithTx(c, session, id) }) require.NoError(t, err) _, err = m.FindOne(ctx, id) require.ErrorIs(t, err, perm.ErrNotFound) } // TC-0285: 正常事务 func TestSysPermModel_TransactCtx_CommitAndRollback(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) now := time.Now().Unix() pc1 := testutil.UniqueId() c1 := testutil.UniqueId() var id1 int64 err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error { res, err := m.InsertWithTx(c, session, &perm.SysPerm{ ProductCode: pc1, Name: "commit", Code: c1, Status: 1, CreateTime: now, UpdateTime: now, }) if err != nil { return err } id1, err = res.LastInsertId() return err }) require.NoError(t, err) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", id1) }) _, err = m.FindOne(ctx, id1) require.NoError(t, err) pc2 := testutil.UniqueId() c2 := testutil.UniqueId() err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error { if _, e := m.InsertWithTx(c, session, &perm.SysPerm{ ProductCode: pc2, Name: "rollback", Code: c2, Status: 1, CreateTime: now, UpdateTime: now, }); e != nil { return e } return errors.New("force rollback") }) require.Error(t, err) _, err = m.FindOneByProductCodeCode(ctx, pc2, c2) require.ErrorIs(t, err, perm.ErrNotFound) } // TC-0287: 获取表名 func TestSysPermModel_TableName(t *testing.T) { m := newTestSysPermModel(t) require.Equal(t, "`sys_perm`", m.TableName()) } // TC-0289: 单条记录 func TestSysPermModel_BatchInsert_Single(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() code := testutil.UniqueId() now := time.Now().Unix() require.NoError(t, m.BatchInsert(ctx, []*perm.SysPerm{ {ProductCode: productCode, Name: "single", Code: code, Status: 1, CreateTime: now, UpdateTime: now}, })) found, err := m.FindOneByProductCodeCode(ctx, productCode, code) require.NoError(t, err) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", found.Id) }) require.Equal(t, code, found.Code) } // TC-0291: 唯一索引冲突 func TestSysPermModel_BatchInsert_UniqueConflict(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() code := testutil.UniqueId() now := time.Now().Unix() list := []*perm.SysPerm{ {ProductCode: productCode, Name: "dup1", Code: code, Status: 1, CreateTime: now, UpdateTime: now}, {ProductCode: productCode, Name: "dup2", Code: code, Status: 1, CreateTime: now, UpdateTime: now}, } err := m.BatchInsert(ctx, list) require.Error(t, err) require.True(t, isDuplicateKeyError(err)) t.Cleanup(func() { if found, e := m.FindOneByProductCodeCode(ctx, productCode, code); e == nil { testutil.CleanTable(ctx, conn, "sys_perm", found.Id) } }) } // TC-0296: 空列表 func TestSysPermModel_BatchUpdate_Empty(t *testing.T) { m := newTestSysPermModel(t) require.NoError(t, m.BatchUpdate(context.Background(), nil)) require.NoError(t, m.BatchUpdate(context.Background(), []*perm.SysPerm{})) } // TC-0298: 多条记录(3条) func TestSysPermModel_BatchUpdate_Multi(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() d1 := &perm.SysPerm{ProductCode: productCode, Name: "u1", Code: testutil.UniqueId(), Status: 1, CreateTime: now, UpdateTime: now} d2 := &perm.SysPerm{ProductCode: productCode, Name: "u2", Code: testutil.UniqueId(), 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() t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", id1, id2) }) now2 := time.Now().Unix() upd := []*perm.SysPerm{ {Id: id1, ProductCode: productCode, Name: "u1_new", Code: d1.Code, Status: 2, CreateTime: now, UpdateTime: now2}, {Id: id2, ProductCode: productCode, Name: "u2_new", Code: d2.Code, Status: 1, CreateTime: now, UpdateTime: now2}, } require.NoError(t, m.BatchUpdate(ctx, upd)) g1, err := m.FindOne(ctx, id1) require.NoError(t, err) require.Equal(t, "u1_new", g1.Name) require.Equal(t, int64(2), g1.Status) g2, err := m.FindOne(ctx, id2) require.NoError(t, err) require.Equal(t, "u2_new", g2.Name) } // TC-0294: 正常多条 func TestSysPermModel_BatchInsertWithTx_Normal(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() c1 := testutil.UniqueId() c2 := testutil.UniqueId() now := time.Now().Unix() err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error { return m.BatchInsertWithTx(c, session, []*perm.SysPerm{ {ProductCode: productCode, Name: "tx1", Code: c1, Status: 1, CreateTime: now, UpdateTime: now}, {ProductCode: productCode, Name: "tx2", Code: c2, Status: 1, CreateTime: now, UpdateTime: now}, }) }) require.NoError(t, err) f1, err := m.FindOneByProductCodeCode(ctx, productCode, c1) require.NoError(t, err) f2, err := m.FindOneByProductCodeCode(ctx, productCode, c2) require.NoError(t, err) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", f1.Id, f2.Id) }) require.Equal(t, c1, f1.Code) require.Equal(t, c2, f2.Code) } // TC-0293: 空列表 func TestSysPermModel_BatchInsertWithTx_Empty(t *testing.T) { m := newTestSysPermModel(t) err := m.TransactCtx(context.Background(), func(c context.Context, session sqlx.Session) error { return m.BatchInsertWithTx(c, session, nil) }) require.NoError(t, err) } // TC-0295: 事务回滚 func TestSysPermModel_BatchInsertWithTx_Rollback(t *testing.T) { ctx := context.Background() m := newTestSysPermModel(t) productCode := testutil.UniqueId() code := testutil.UniqueId() now := time.Now().Unix() err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error { if e := m.BatchInsertWithTx(c, session, []*perm.SysPerm{ {ProductCode: productCode, Name: "rb", Code: code, Status: 1, CreateTime: now, UpdateTime: now}, }); e != nil { return e } return errors.New("force rollback") }) require.Error(t, err) _, err = m.FindOneByProductCodeCode(ctx, productCode, code) require.ErrorIs(t, err, perm.ErrNotFound) } // TC-0301: 正常多条 func TestSysPermModel_BatchUpdateWithTx_Normal(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() d1 := &perm.SysPerm{ProductCode: productCode, Name: "bu1", Code: testutil.UniqueId(), Status: 1, CreateTime: now, UpdateTime: now} d2 := &perm.SysPerm{ProductCode: productCode, Name: "bu2", Code: testutil.UniqueId(), 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() t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", id1, id2) }) now2 := time.Now().Unix() err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error { return m.BatchUpdateWithTx(c, session, []*perm.SysPerm{ {Id: id1, ProductCode: productCode, Name: "bu1_tx", Code: d1.Code, Status: 1, CreateTime: now, UpdateTime: now2}, {Id: id2, ProductCode: productCode, Name: "bu2_tx", Code: d2.Code, Status: 2, CreateTime: now, UpdateTime: now2}, }) }) require.NoError(t, err) g1, err := m.FindOne(ctx, id1) require.NoError(t, err) require.Equal(t, "bu1_tx", g1.Name) g2, err := m.FindOne(ctx, id2) require.NoError(t, err) require.Equal(t, "bu2_tx", g2.Name) require.Equal(t, int64(2), g2.Status) } // TC-0300: 空列表 func TestSysPermModel_BatchUpdateWithTx_Empty(t *testing.T) { m := newTestSysPermModel(t) err := m.TransactCtx(context.Background(), func(c context.Context, session sqlx.Session) error { return m.BatchUpdateWithTx(c, session, nil) }) require.NoError(t, err) } // TC-0306: 单个id func TestSysPermModel_BatchDelete_Single(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() d := &perm.SysPerm{ProductCode: productCode, Name: "bd", Code: testutil.UniqueId(), Status: 1, CreateTime: now, UpdateTime: now} res, err := m.Insert(ctx, d) require.NoError(t, err) id, _ := res.LastInsertId() t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", id) }) require.NoError(t, m.BatchDelete(ctx, []int64{id})) _, err = m.FindOne(ctx, id) require.ErrorIs(t, err, perm.ErrNotFound) } // TC-0308: 包含不存在id func TestSysPermModel_BatchDelete_ContainsNonExist(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() d := &perm.SysPerm{ProductCode: productCode, Name: "bdne", Code: testutil.UniqueId(), Status: 1, CreateTime: now, UpdateTime: now} res, err := m.Insert(ctx, d) require.NoError(t, err) id, _ := res.LastInsertId() t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", id) }) require.NoError(t, m.BatchDelete(ctx, []int64{id, 999999999})) _, err = m.FindOne(ctx, id) require.ErrorIs(t, err, perm.ErrNotFound) } // TC-0310: 正常多条 func TestSysPermModel_BatchDeleteWithTx_Normal(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() d1 := &perm.SysPerm{ProductCode: productCode, Name: "bdtx1", Code: testutil.UniqueId(), Status: 1, CreateTime: now, UpdateTime: now} d2 := &perm.SysPerm{ProductCode: productCode, Name: "bdtx2", Code: testutil.UniqueId(), 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() t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", id1, id2) }) err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error { return m.BatchDeleteWithTx(c, session, []int64{id1, id2}) }) require.NoError(t, err) _, err = m.FindOne(ctx, id1) require.ErrorIs(t, err, perm.ErrNotFound) _, err = m.FindOne(ctx, id2) require.ErrorIs(t, err, perm.ErrNotFound) } // TC-0309: 空ids func TestSysPermModel_BatchDeleteWithTx_Empty(t *testing.T) { m := newTestSysPermModel(t) err := m.TransactCtx(context.Background(), func(c context.Context, session sqlx.Session) error { return m.BatchDeleteWithTx(c, session, nil) }) require.NoError(t, err) } // TC-0376: 无启用权限 func TestSysPermModel_FindAllByProductCode_AllDisabled(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() ids := make([]int64, 0, 3) for i := 0; i < 3; i++ { d := &perm.SysPerm{ ProductCode: productCode, Name: "disabled", Code: testutil.UniqueId(), Status: 2, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, d) require.NoError(t, err) id, _ := res.LastInsertId() ids = append(ids, id) } t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", ids...) }) list, err := m.FindAllByProductCode(ctx, productCode) require.NoError(t, err) require.Empty(t, list) } // TC-0278: 事务内可见性 func TestSysPermModel_FindOneWithTx_InsertThenFind(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() code := testutil.UniqueId() now := time.Now().Unix() var foundInTx *perm.SysPerm var insertedId int64 err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error { res, err := m.InsertWithTx(c, session, &perm.SysPerm{ ProductCode: productCode, Name: "ftx_perm_" + testutil.UniqueId(), Code: code, Remark: "", Status: 1, CreateTime: now, UpdateTime: now, }) if err != nil { return err } insertedId, _ = res.LastInsertId() foundInTx, err = m.FindOneWithTx(c, session, insertedId) return err }) require.NoError(t, err) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", insertedId) }) require.NotNil(t, foundInTx) assert.Equal(t, insertedId, foundInTx.Id) assert.Equal(t, productCode, foundInTx.ProductCode) assert.Equal(t, code, foundInTx.Code) } // TC-0277: 事务内记录不存在 func TestSysPermModel_FindOneWithTx_NotFound(t *testing.T) { ctx := context.Background() m := newTestSysPermModel(t) err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error { _, err := m.FindOneWithTx(c, session, 999999999999) return err }) require.ErrorIs(t, err, perm.ErrNotFound) } // TC-0325: FindOneByProductCodeCodeWithTx func TestSysPermModel_FindOneByProductCodeCodeWithTx_InsertThenFind(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() code := testutil.UniqueId() now := time.Now().Unix() var foundByKey *perm.SysPerm var insertedId int64 err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error { res, err := m.InsertWithTx(c, session, &perm.SysPerm{ ProductCode: productCode, Name: "ftx_bykey_" + testutil.UniqueId(), Code: code, Remark: "", Status: 1, CreateTime: now, UpdateTime: now, }) if err != nil { return err } insertedId, _ = res.LastInsertId() foundByKey, err = m.FindOneByProductCodeCodeWithTx(c, session, productCode, code) return err }) require.NoError(t, err) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", insertedId) }) require.NotNil(t, foundByKey) assert.Equal(t, insertedId, foundByKey.Id) assert.Equal(t, productCode, foundByKey.ProductCode) assert.Equal(t, code, foundByKey.Code) } // TC-0326: FindOneByProductCodeCodeWithTx func TestSysPermModel_FindOneByProductCodeCodeWithTx_NotFound(t *testing.T) { ctx := context.Background() m := newTestSysPermModel(t) err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error { _, err := m.FindOneByProductCodeCodeWithTx(c, session, "notexist_pc_"+testutil.UniqueId(), "notexist_code_"+testutil.UniqueId()) return err }) require.ErrorIs(t, err, perm.ErrNotFound) } // TC-0494: FindAllCodesByProductCode 返回仅启用的权限code func TestSysPermModel_FindAllCodesByProductCode_OnlyEnabledCodes(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() codeA := testutil.UniqueId() codeB := testutil.UniqueId() codeDisabled := testutil.UniqueId() ids := make([]int64, 0, 3) for _, row := range []struct { code string status int64 }{ {codeA, 1}, {codeB, 1}, {codeDisabled, 2}, } { data := &perm.SysPerm{ ProductCode: productCode, Name: "tc0494", Code: row.code, Remark: "", Status: row.status, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) ids = append(ids, id) } t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", ids...) }) codes, err := m.FindAllCodesByProductCode(ctx, productCode) require.NoError(t, err) require.Len(t, codes, 2) assert.ElementsMatch(t, []string{codeA, codeB}, codes) } // TC-0495: FindAllCodesByProductCode 不存在的product返回空 func TestSysPermModel_FindAllCodesByProductCode_NonExistentProduct(t *testing.T) { m := newTestSysPermModel(t) codes, err := m.FindAllCodesByProductCode(context.Background(), "nonexist_"+testutil.UniqueId()) require.NoError(t, err) require.Empty(t, codes) } // TC-0292: BatchInsert 大批量(1000条) func TestSysPermModel_BatchInsert_Bulk1000(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() list := make([]*perm.SysPerm, 1000) for i := range list { list[i] = &perm.SysPerm{ ProductCode: productCode, Name: "bulk", Code: testutil.UniqueId(), Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } } require.NoError(t, m.BatchInsert(ctx, list)) t.Cleanup(func() { conn.ExecCtx(ctx, "DELETE FROM `sys_perm` WHERE `productCode` = ?", productCode) }) _, cnt, err := m.FindListByProductCode(ctx, productCode, 1, 1100) require.NoError(t, err) require.Equal(t, int64(1000), cnt) } // TC-0297: BatchUpdate 单条记录 func TestSysPermModel_BatchUpdate_Single(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() code := testutil.UniqueId() now := time.Now().Unix() d := &perm.SysPerm{ProductCode: productCode, Name: "bu_single", Code: code, Status: 1, CreateTime: now, UpdateTime: now} res, err := m.Insert(ctx, d) require.NoError(t, err) id, _ := res.LastInsertId() t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", id) }) now2 := time.Now().Unix() upd := []*perm.SysPerm{ {Id: id, ProductCode: productCode, Name: "bu_single_new", Code: code, Status: 2, CreateTime: now, UpdateTime: now2}, } require.NoError(t, m.BatchUpdate(ctx, upd)) got, err := m.FindOne(ctx, id) require.NoError(t, err) require.Equal(t, "bu_single_new", got.Name) require.Equal(t, int64(2), got.Status) require.Equal(t, now2, got.UpdateTime) } // TC-0299: BatchUpdate 部分id不存在 func TestSysPermModel_BatchUpdate_PartialIdNotExist(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() d1 := &perm.SysPerm{ProductCode: productCode, Name: "bu_p1", Code: testutil.UniqueId(), Status: 1, CreateTime: now, UpdateTime: now} d2 := &perm.SysPerm{ProductCode: productCode, Name: "bu_p2", Code: testutil.UniqueId(), 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() t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", id1, id2) }) now2 := time.Now().Unix() upd := []*perm.SysPerm{ {Id: id1, ProductCode: productCode, Name: "bu_p1_new", Code: d1.Code, Status: 2, CreateTime: now, UpdateTime: now2}, {Id: id2, ProductCode: productCode, Name: "bu_p2_new", Code: d2.Code, Status: 2, CreateTime: now, UpdateTime: now2}, {Id: 999999999, ProductCode: productCode, Name: "ghost", Code: "ghost_" + testutil.UniqueId(), Status: 1, CreateTime: now, UpdateTime: now2}, } require.NoError(t, m.BatchUpdate(ctx, upd)) g1, err := m.FindOne(ctx, id1) require.NoError(t, err) require.Equal(t, "bu_p1_new", g1.Name) require.Equal(t, int64(2), g1.Status) g2, err := m.FindOne(ctx, id2) require.NoError(t, err) require.Equal(t, "bu_p2_new", g2.Name) } // TC-0383: FindMapByProductCode key唯一性 func TestSysPermModel_FindMapByProductCode_KeyUniqueness(t *testing.T) { ctx := context.Background() conn := testutil.GetTestSqlConn() m := newTestSysPermModel(t) productCode := testutil.UniqueId() now := time.Now().Unix() c1, c2, c3 := testutil.UniqueId(), testutil.UniqueId(), testutil.UniqueId() ids := make([]int64, 0, 3) for _, code := range []string{c1, c2, c3} { data := &perm.SysPerm{ ProductCode: productCode, Name: "map_key", Code: code, Remark: "", Status: 1, CreateTime: now, UpdateTime: now, } res, err := m.Insert(ctx, data) require.NoError(t, err) id, err := res.LastInsertId() require.NoError(t, err) ids = append(ids, id) } t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_perm", ids...) }) mp, err := m.FindMapByProductCode(ctx, productCode) require.NoError(t, err) require.Len(t, mp, 3) require.Contains(t, mp, c1) require.Contains(t, mp, c2) require.Contains(t, mp, c3) require.Equal(t, c1, mp[c1].Code) require.Equal(t, c2, mp[c2].Code) require.Equal(t, c3, mp[c3].Code) }