sysUserRoleModel_test.go 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284
  1. package userrole
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "github.com/go-sql-driver/mysql"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/stretchr/testify/require"
  9. "github.com/zeromicro/go-zero/core/stores/redis"
  10. "github.com/zeromicro/go-zero/core/stores/sqlx"
  11. "math/rand"
  12. "perms-system-server/internal/testutil"
  13. "sort"
  14. "testing"
  15. "time"
  16. )
  17. func randUserRoleId() int64 {
  18. return int64(900000 + rand.Intn(100000))
  19. }
  20. func sortedInt64Copy(a []int64) []int64 {
  21. b := append([]int64(nil), a...)
  22. sort.Slice(b, func(i, j int) bool { return b[i] < b[j] })
  23. return b
  24. }
  25. func int64SliceEqualIgnoreOrder(a, b []int64) bool {
  26. if len(a) != len(b) {
  27. return false
  28. }
  29. aa, bb := sortedInt64Copy(a), sortedInt64Copy(b)
  30. for i := range aa {
  31. if aa[i] != bb[i] {
  32. return false
  33. }
  34. }
  35. return true
  36. }
  37. func insertTestRole(t *testing.T, ctx context.Context, conn sqlx.SqlConn, productCode, name string) int64 {
  38. t.Helper()
  39. ts := time.Now().Unix()
  40. q := "INSERT INTO `sys_role` (`productCode`, `name`, `remark`, `status`, `permsLevel`, `createTime`, `updateTime`) VALUES (?, ?, '', 1, 0, ?, ?)"
  41. res, err := conn.ExecCtx(ctx, q, productCode, name, ts, ts)
  42. if err != nil {
  43. t.Fatalf("insert sys_role: %v", err)
  44. }
  45. id, err := res.LastInsertId()
  46. if err != nil {
  47. t.Fatalf("role LastInsertId: %v", err)
  48. }
  49. return id
  50. }
  51. // TC-0310: 正常插入
  52. func TestSysUserRoleModel_CRUD(t *testing.T) {
  53. ctx := context.Background()
  54. conn := testutil.GetTestSqlConn()
  55. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  56. userId := randUserRoleId()
  57. roleId := randUserRoleId()
  58. ts := time.Now().Unix()
  59. data := &SysUserRole{
  60. UserId: userId,
  61. RoleId: roleId,
  62. CreateTime: ts,
  63. UpdateTime: ts,
  64. }
  65. res, err := m.Insert(ctx, data)
  66. if err != nil {
  67. t.Fatalf("Insert: %v", err)
  68. }
  69. id, err := res.LastInsertId()
  70. if err != nil {
  71. t.Fatalf("LastInsertId: %v", err)
  72. }
  73. defer testutil.CleanTable(ctx, conn, "sys_user_role", id)
  74. got, err := m.FindOne(ctx, id)
  75. if err != nil {
  76. t.Fatalf("FindOne: %v", err)
  77. }
  78. if got.UserId != userId || got.RoleId != roleId {
  79. t.Fatalf("FindOne mismatch: %+v", got)
  80. }
  81. byPair, err := m.FindOneByUserIdRoleId(ctx, userId, roleId)
  82. if err != nil {
  83. t.Fatalf("FindOneByUserIdRoleId: %v", err)
  84. }
  85. if byPair.Id != id {
  86. t.Fatalf("FindOneByUserIdRoleId id want %d got %d", id, byPair.Id)
  87. }
  88. newTs := ts + 1
  89. got.UpdateTime = newTs
  90. if err := m.Update(ctx, got); err != nil {
  91. t.Fatalf("Update: %v", err)
  92. }
  93. updated, err := m.FindOne(ctx, id)
  94. if err != nil {
  95. t.Fatalf("FindOne after update: %v", err)
  96. }
  97. if updated.UpdateTime != newTs {
  98. t.Fatalf("UpdateTime want %d got %d", newTs, updated.UpdateTime)
  99. }
  100. if err := m.Delete(ctx, id); err != nil {
  101. t.Fatalf("Delete: %v", err)
  102. }
  103. if _, err := m.FindOne(ctx, id); err != ErrNotFound {
  104. t.Fatalf("after Delete want ErrNotFound got %v", err)
  105. }
  106. }
  107. // TC-0467: 正常查询
  108. func TestSysUserRoleModel_FindRoleIdsByUserId(t *testing.T) {
  109. ctx := context.Background()
  110. conn := testutil.GetTestSqlConn()
  111. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  112. userId := randUserRoleId()
  113. r1, r2 := randUserRoleId(), randUserRoleId()
  114. ts := time.Now().Unix()
  115. var ids []int64
  116. for _, roleId := range []int64{r1, r2} {
  117. res, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  118. if err != nil {
  119. t.Fatalf("Insert: %v", err)
  120. }
  121. id, _ := res.LastInsertId()
  122. ids = append(ids, id)
  123. }
  124. defer func() {
  125. for _, id := range ids {
  126. testutil.CleanTable(ctx, conn, "sys_user_role", id)
  127. }
  128. }()
  129. got, err := m.FindRoleIdsByUserId(ctx, userId)
  130. if err != nil {
  131. t.Fatalf("FindRoleIdsByUserId: %v", err)
  132. }
  133. if !int64SliceEqualIgnoreOrder(got, []int64{r1, r2}) {
  134. t.Fatalf("got %v want %v", got, []int64{r1, r2})
  135. }
  136. }
  137. // TC-0444: 正常查询
  138. // TC-0469: 正常删除
  139. func TestSysUserRoleModel_DeleteByRoleIdTx(t *testing.T) {
  140. ctx := context.Background()
  141. conn := testutil.GetTestSqlConn()
  142. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  143. userId := randUserRoleId()
  144. roleId := randUserRoleId()
  145. ts := time.Now().Unix()
  146. res, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  147. if err != nil {
  148. t.Fatalf("Insert: %v", err)
  149. }
  150. id, _ := res.LastInsertId()
  151. defer testutil.CleanTable(ctx, conn, "sys_user_role", id)
  152. err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  153. return m.DeleteByRoleIdTx(c, session, roleId)
  154. })
  155. if err != nil {
  156. t.Fatalf("DeleteByRoleIdTx: %v", err)
  157. }
  158. if _, err := m.FindOne(ctx, id); err != ErrNotFound {
  159. t.Fatalf("after tx delete want ErrNotFound got %v", err)
  160. }
  161. }
  162. // TC-0319: 记录不存在
  163. func TestSysUserRoleModel_FindOne_NotFound(t *testing.T) {
  164. conn := testutil.GetTestSqlConn()
  165. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  166. _, err := m.FindOne(context.Background(), 999999999999)
  167. if err != ErrNotFound {
  168. t.Fatalf("want ErrNotFound got %v", err)
  169. }
  170. }
  171. // TC-0388: FindOneByUserIdRoleId
  172. func TestSysUserRoleModel_FindOneByUserIdRoleId_NotFound(t *testing.T) {
  173. conn := testutil.GetTestSqlConn()
  174. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  175. _, err := m.FindOneByUserIdRoleId(context.Background(), 999999999, 999999999)
  176. if err != ErrNotFound {
  177. t.Fatalf("want ErrNotFound got %v", err)
  178. }
  179. }
  180. // TC-0468: 无绑定
  181. func TestSysUserRoleModel_FindRoleIdsByUserId_Empty(t *testing.T) {
  182. conn := testutil.GetTestSqlConn()
  183. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  184. got, err := m.FindRoleIdsByUserId(context.Background(), 999999999)
  185. if err != nil {
  186. t.Fatalf("err: %v", err)
  187. }
  188. if len(got) != 0 {
  189. t.Fatalf("want empty got %v", got)
  190. }
  191. }
  192. // TC-0470: 事务内跨产品删除
  193. func TestSysUserRoleModel_DeleteByUserIdForProductTx(t *testing.T) {
  194. ctx := context.Background()
  195. conn := testutil.GetTestSqlConn()
  196. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  197. pcA := "t_urt_a_" + testutil.UniqueId()
  198. pcB := "t_urt_b_" + testutil.UniqueId()
  199. nameA := "n_ta_" + testutil.UniqueId()
  200. nameB := "n_tb_" + testutil.UniqueId()
  201. roleA := insertTestRole(t, ctx, conn, pcA, nameA)
  202. roleB := insertTestRole(t, ctx, conn, pcB, nameB)
  203. defer testutil.CleanTable(ctx, conn, "sys_role", roleA, roleB)
  204. userId := randUserRoleId()
  205. ts := time.Now().Unix()
  206. resA, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: roleA, CreateTime: ts, UpdateTime: ts})
  207. if err != nil {
  208. t.Fatalf("Insert A: %v", err)
  209. }
  210. idA, _ := resA.LastInsertId()
  211. resB, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: roleB, CreateTime: ts, UpdateTime: ts})
  212. if err != nil {
  213. t.Fatalf("Insert B: %v", err)
  214. }
  215. idB, _ := resB.LastInsertId()
  216. defer testutil.CleanTable(ctx, conn, "sys_user_role", idA, idB)
  217. err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  218. return m.DeleteByUserIdForProductTx(c, session, userId, pcA)
  219. })
  220. if err != nil {
  221. t.Fatalf("DeleteByUserIdForProductTx: %v", err)
  222. }
  223. if _, err := m.FindOne(ctx, idA); err != ErrNotFound {
  224. t.Fatalf("pcA row should delete: %v", err)
  225. }
  226. if _, err := m.FindOne(ctx, idB); err != nil {
  227. t.Fatalf("pcB row should remain: %v", err)
  228. }
  229. }
  230. // TC-0334: 空列表
  231. func TestSysUserRoleModel_BatchInsert_Empty(t *testing.T) {
  232. conn := testutil.GetTestSqlConn()
  233. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  234. if err := m.BatchInsert(context.Background(), nil); err != nil {
  235. t.Fatalf("nil: %v", err)
  236. }
  237. if err := m.BatchInsert(context.Background(), []*SysUserRole{}); err != nil {
  238. t.Fatalf("empty: %v", err)
  239. }
  240. }
  241. // TC-0353: 空ids
  242. func TestSysUserRoleModel_BatchDelete_Empty(t *testing.T) {
  243. conn := testutil.GetTestSqlConn()
  244. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  245. if err := m.BatchDelete(context.Background(), nil); err != nil {
  246. t.Fatalf("nil: %v", err)
  247. }
  248. if err := m.BatchDelete(context.Background(), []int64{}); err != nil {
  249. t.Fatalf("empty: %v", err)
  250. }
  251. }
  252. // TC-0312: 唯一索引冲突
  253. func TestSysUserRoleModel_Insert_UniqueConflict(t *testing.T) {
  254. ctx := context.Background()
  255. conn := testutil.GetTestSqlConn()
  256. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  257. userId := randUserRoleId()
  258. roleId := randUserRoleId()
  259. ts := time.Now().Unix()
  260. res, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  261. if err != nil {
  262. t.Fatalf("Insert: %v", err)
  263. }
  264. id, _ := res.LastInsertId()
  265. defer testutil.CleanTable(ctx, conn, "sys_user_role", id)
  266. _, err = m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  267. if err == nil {
  268. t.Fatal("second Insert want error")
  269. }
  270. var me *mysql.MySQLError
  271. if !errors.As(err, &me) || me.Number != 1062 {
  272. t.Fatalf("want duplicate key 1062, got %v", err)
  273. }
  274. }
  275. // TC-0314: 事务内插入
  276. func TestSysUserRoleModel_InsertWithTx_Normal(t *testing.T) {
  277. ctx := context.Background()
  278. conn := testutil.GetTestSqlConn()
  279. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  280. userId := randUserRoleId()
  281. roleId := randUserRoleId()
  282. ts := time.Now().Unix()
  283. var insertedId int64
  284. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  285. res, err := m.InsertWithTx(c, session, &SysUserRole{UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  286. if err != nil {
  287. return err
  288. }
  289. insertedId, _ = res.LastInsertId()
  290. return nil
  291. })
  292. if err != nil {
  293. t.Fatalf("TransactCtx: %v", err)
  294. }
  295. defer testutil.CleanTable(ctx, conn, "sys_user_role", insertedId)
  296. got, err := m.FindOne(ctx, insertedId)
  297. if err != nil {
  298. t.Fatalf("FindOne: %v", err)
  299. }
  300. if got.UserId != userId || got.RoleId != roleId {
  301. t.Fatalf("mismatch: %+v", got)
  302. }
  303. }
  304. // TC-0316: 事务回滚后无数据
  305. func TestSysUserRoleModel_InsertWithTx_Rollback(t *testing.T) {
  306. ctx := context.Background()
  307. conn := testutil.GetTestSqlConn()
  308. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  309. userId := randUserRoleId()
  310. roleId := randUserRoleId()
  311. ts := time.Now().Unix()
  312. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  313. _, err := m.InsertWithTx(c, session, &SysUserRole{UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  314. if err != nil {
  315. return err
  316. }
  317. return errors.New("rollback")
  318. })
  319. if err == nil || err.Error() != "rollback" {
  320. t.Fatalf("want rollback error got %v", err)
  321. }
  322. _, err = m.FindOneByUserIdRoleId(ctx, userId, roleId)
  323. if err != ErrNotFound {
  324. t.Fatalf("after rollback want ErrNotFound got %v", err)
  325. }
  326. }
  327. // TC-0326: 记录不存在
  328. func TestSysUserRoleModel_Update_NotFound(t *testing.T) {
  329. conn := testutil.GetTestSqlConn()
  330. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  331. ts := time.Now().Unix()
  332. err := m.Update(context.Background(), &SysUserRole{
  333. Id: 999999999, UserId: 1, RoleId: 1, CreateTime: ts, UpdateTime: ts,
  334. })
  335. if err != ErrNotFound {
  336. t.Fatalf("want ErrNotFound got %v", err)
  337. }
  338. }
  339. // TC-0327: 事务内更新
  340. func TestSysUserRoleModel_UpdateWithTx(t *testing.T) {
  341. ctx := context.Background()
  342. conn := testutil.GetTestSqlConn()
  343. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  344. userId := randUserRoleId()
  345. roleId := randUserRoleId()
  346. ts := time.Now().Unix()
  347. res, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  348. if err != nil {
  349. t.Fatalf("Insert: %v", err)
  350. }
  351. id, _ := res.LastInsertId()
  352. defer testutil.CleanTable(ctx, conn, "sys_user_role", id)
  353. newTs := ts + 100
  354. err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  355. return m.UpdateWithTx(c, session, &SysUserRole{
  356. Id: id, UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: newTs,
  357. })
  358. })
  359. if err != nil {
  360. t.Fatalf("UpdateWithTx: %v", err)
  361. }
  362. got, err := m.FindOne(ctx, id)
  363. if err != nil {
  364. t.Fatalf("FindOne: %v", err)
  365. }
  366. if got.UpdateTime != newTs {
  367. t.Fatalf("UpdateTime want %d got %d", newTs, got.UpdateTime)
  368. }
  369. }
  370. // TC-0329: 记录不存在
  371. func TestSysUserRoleModel_Delete_NotFound(t *testing.T) {
  372. conn := testutil.GetTestSqlConn()
  373. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  374. err := m.Delete(context.Background(), 999999999)
  375. if err != ErrNotFound {
  376. t.Fatalf("want ErrNotFound got %v", err)
  377. }
  378. }
  379. // TC-0330: 事务内删除
  380. func TestSysUserRoleModel_DeleteWithTx(t *testing.T) {
  381. ctx := context.Background()
  382. conn := testutil.GetTestSqlConn()
  383. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  384. userId := randUserRoleId()
  385. roleId := randUserRoleId()
  386. ts := time.Now().Unix()
  387. res, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  388. if err != nil {
  389. t.Fatalf("Insert: %v", err)
  390. }
  391. id, _ := res.LastInsertId()
  392. defer testutil.CleanTable(ctx, conn, "sys_user_role", id)
  393. err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  394. return m.DeleteWithTx(c, session, id)
  395. })
  396. if err != nil {
  397. t.Fatalf("DeleteWithTx: %v", err)
  398. }
  399. if _, err := m.FindOne(ctx, id); err != ErrNotFound {
  400. t.Fatalf("after DeleteWithTx want ErrNotFound got %v", err)
  401. }
  402. }
  403. // TC-0332: fn返回错误
  404. func TestSysUserRoleModel_TransactCtx_Rollback(t *testing.T) {
  405. ctx := context.Background()
  406. conn := testutil.GetTestSqlConn()
  407. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  408. userId := randUserRoleId()
  409. roleId := randUserRoleId()
  410. ts := time.Now().Unix()
  411. res, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  412. if err != nil {
  413. t.Fatalf("Insert: %v", err)
  414. }
  415. id, _ := res.LastInsertId()
  416. defer testutil.CleanTable(ctx, conn, "sys_user_role", id)
  417. err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  418. if err := m.DeleteWithTx(c, session, id); err != nil {
  419. return err
  420. }
  421. return errors.New("rollback")
  422. })
  423. if err == nil || err.Error() != "rollback" {
  424. t.Fatalf("want rollback error got %v", err)
  425. }
  426. got, err := m.FindOne(ctx, id)
  427. if err != nil {
  428. t.Fatalf("after rollback FindOne: %v", err)
  429. }
  430. if got.Id != id {
  431. t.Fatalf("row should still exist")
  432. }
  433. }
  434. // TC-0333: 获取表名
  435. func TestSysUserRoleModel_TableName(t *testing.T) {
  436. conn := testutil.GetTestSqlConn()
  437. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  438. if m.TableName() != "`sys_user_role`" {
  439. t.Fatalf("want `sys_user_role` got %s", m.TableName())
  440. }
  441. }
  442. // TC-0335: 单条记录
  443. func TestSysUserRoleModel_BatchInsert_Single(t *testing.T) {
  444. ctx := context.Background()
  445. conn := testutil.GetTestSqlConn()
  446. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  447. userId := randUserRoleId()
  448. roleId := randUserRoleId()
  449. ts := time.Now().Unix()
  450. if err := m.BatchInsert(ctx, []*SysUserRole{
  451. {UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts},
  452. }); err != nil {
  453. t.Fatalf("BatchInsert: %v", err)
  454. }
  455. got, err := m.FindOneByUserIdRoleId(ctx, userId, roleId)
  456. if err != nil {
  457. t.Fatalf("FindOneByUserIdRoleId: %v", err)
  458. }
  459. defer testutil.CleanTable(ctx, conn, "sys_user_role", got.Id)
  460. if got.UserId != userId || got.RoleId != roleId {
  461. t.Fatalf("mismatch: %+v", got)
  462. }
  463. }
  464. // TC-0336: 多条记录(3条)
  465. func TestSysUserRoleModel_BatchInsert_Multi(t *testing.T) {
  466. ctx := context.Background()
  467. conn := testutil.GetTestSqlConn()
  468. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  469. u1 := randUserRoleId()
  470. r1, r2, r3 := randUserRoleId(), randUserRoleId(), randUserRoleId()
  471. ts := time.Now().Unix()
  472. if err := m.BatchInsert(ctx, []*SysUserRole{
  473. {UserId: u1, RoleId: r1, CreateTime: ts, UpdateTime: ts},
  474. {UserId: u1, RoleId: r2, CreateTime: ts, UpdateTime: ts},
  475. {UserId: u1, RoleId: r3, CreateTime: ts, UpdateTime: ts},
  476. }); err != nil {
  477. t.Fatalf("BatchInsert: %v", err)
  478. }
  479. var ids []int64
  480. for _, roleId := range []int64{r1, r2, r3} {
  481. got, err := m.FindOneByUserIdRoleId(ctx, u1, roleId)
  482. if err != nil {
  483. t.Fatalf("FindOneByUserIdRoleId(%d,%d): %v", u1, roleId, err)
  484. }
  485. ids = append(ids, got.Id)
  486. }
  487. defer func() {
  488. for _, id := range ids {
  489. testutil.CleanTable(ctx, conn, "sys_user_role", id)
  490. }
  491. }()
  492. if len(ids) != 3 {
  493. t.Fatalf("want 3 rows got %d", len(ids))
  494. }
  495. }
  496. // TC-0338: 唯一索引冲突
  497. func TestSysUserRoleModel_BatchInsert_UniqueConflict(t *testing.T) {
  498. ctx := context.Background()
  499. conn := testutil.GetTestSqlConn()
  500. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  501. userId := randUserRoleId()
  502. roleId := randUserRoleId()
  503. ts := time.Now().Unix()
  504. err := m.BatchInsert(ctx, []*SysUserRole{
  505. {UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts},
  506. {UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts},
  507. })
  508. if err == nil {
  509. t.Fatal("want error for duplicate")
  510. }
  511. var me *mysql.MySQLError
  512. if !errors.As(err, &me) || me.Number != 1062 {
  513. t.Fatalf("want duplicate key 1062, got %v", err)
  514. }
  515. }
  516. // TC-0343: 空列表
  517. func TestSysUserRoleModel_BatchUpdate_Empty(t *testing.T) {
  518. conn := testutil.GetTestSqlConn()
  519. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  520. if err := m.BatchUpdate(context.Background(), nil); err != nil {
  521. t.Fatalf("nil: %v", err)
  522. }
  523. if err := m.BatchUpdate(context.Background(), []*SysUserRole{}); err != nil {
  524. t.Fatalf("empty: %v", err)
  525. }
  526. }
  527. // TC-0345: 多条记录(3条)
  528. func TestSysUserRoleModel_BatchUpdate_Multi(t *testing.T) {
  529. ctx := context.Background()
  530. conn := testutil.GetTestSqlConn()
  531. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  532. userId := randUserRoleId()
  533. r1, r2 := randUserRoleId(), randUserRoleId()
  534. ts := time.Now().Unix()
  535. res1, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: r1, CreateTime: ts, UpdateTime: ts})
  536. if err != nil {
  537. t.Fatalf("Insert1: %v", err)
  538. }
  539. id1, _ := res1.LastInsertId()
  540. res2, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: r2, CreateTime: ts, UpdateTime: ts})
  541. if err != nil {
  542. t.Fatalf("Insert2: %v", err)
  543. }
  544. id2, _ := res2.LastInsertId()
  545. defer testutil.CleanTable(ctx, conn, "sys_user_role", id1, id2)
  546. newTs := ts + 100
  547. newR1, newR2 := randUserRoleId(), randUserRoleId()
  548. err = m.BatchUpdate(ctx, []*SysUserRole{
  549. {Id: id1, UserId: userId, RoleId: newR1, CreateTime: ts, UpdateTime: newTs},
  550. {Id: id2, UserId: userId, RoleId: newR2, CreateTime: ts, UpdateTime: newTs},
  551. })
  552. if err != nil {
  553. t.Fatalf("BatchUpdate: %v", err)
  554. }
  555. got1, err := m.FindOne(ctx, id1)
  556. if err != nil {
  557. t.Fatalf("FindOne1: %v", err)
  558. }
  559. if got1.UpdateTime != newTs || got1.RoleId != newR1 {
  560. t.Fatalf("got1 mismatch: %+v", got1)
  561. }
  562. got2, err := m.FindOne(ctx, id2)
  563. if err != nil {
  564. t.Fatalf("FindOne2: %v", err)
  565. }
  566. if got2.UpdateTime != newTs || got2.RoleId != newR2 {
  567. t.Fatalf("got2 mismatch: %+v", got2)
  568. }
  569. }
  570. // TC-0355: 多个id(3个)
  571. func TestSysUserRoleModel_BatchDelete_Multi(t *testing.T) {
  572. ctx := context.Background()
  573. conn := testutil.GetTestSqlConn()
  574. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  575. ts := time.Now().Unix()
  576. var ids []int64
  577. for i := 0; i < 3; i++ {
  578. res, err := m.Insert(ctx, &SysUserRole{
  579. UserId: randUserRoleId(), RoleId: randUserRoleId(), CreateTime: ts, UpdateTime: ts,
  580. })
  581. if err != nil {
  582. t.Fatalf("Insert: %v", err)
  583. }
  584. id, _ := res.LastInsertId()
  585. ids = append(ids, id)
  586. }
  587. defer func() {
  588. for _, id := range ids {
  589. testutil.CleanTable(ctx, conn, "sys_user_role", id)
  590. }
  591. }()
  592. if err := m.BatchDelete(ctx, ids); err != nil {
  593. t.Fatalf("BatchDelete: %v", err)
  594. }
  595. for _, id := range ids {
  596. if _, err := m.FindOne(ctx, id); err != ErrNotFound {
  597. t.Fatalf("id %d should be deleted: %v", id, err)
  598. }
  599. }
  600. }
  601. // TC-0354: 单个id
  602. func TestSysUserRoleModel_BatchDelete_Single(t *testing.T) {
  603. ctx := context.Background()
  604. conn := testutil.GetTestSqlConn()
  605. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  606. userId := randUserRoleId()
  607. roleId := randUserRoleId()
  608. ts := time.Now().Unix()
  609. res, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  610. if err != nil {
  611. t.Fatalf("Insert: %v", err)
  612. }
  613. id, _ := res.LastInsertId()
  614. defer testutil.CleanTable(ctx, conn, "sys_user_role", id)
  615. if err := m.BatchDelete(ctx, []int64{id}); err != nil {
  616. t.Fatalf("BatchDelete: %v", err)
  617. }
  618. if _, err := m.FindOne(ctx, id); err != ErrNotFound {
  619. t.Fatalf("want ErrNotFound got %v", err)
  620. }
  621. }
  622. // TC-0356: 包含不存在id
  623. func TestSysUserRoleModel_BatchDelete_ContainsNonExist(t *testing.T) {
  624. ctx := context.Background()
  625. conn := testutil.GetTestSqlConn()
  626. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  627. userId := randUserRoleId()
  628. roleId := randUserRoleId()
  629. ts := time.Now().Unix()
  630. res, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  631. if err != nil {
  632. t.Fatalf("Insert: %v", err)
  633. }
  634. id, _ := res.LastInsertId()
  635. defer testutil.CleanTable(ctx, conn, "sys_user_role", id)
  636. if err := m.BatchDelete(ctx, []int64{id, 999999999}); err != nil {
  637. t.Fatalf("BatchDelete: %v", err)
  638. }
  639. if _, err := m.FindOne(ctx, id); err != ErrNotFound {
  640. t.Fatalf("want ErrNotFound got %v", err)
  641. }
  642. }
  643. // TC-0341: 正常多条
  644. func TestSysUserRoleModel_BatchInsertWithTx_Normal(t *testing.T) {
  645. ctx := context.Background()
  646. conn := testutil.GetTestSqlConn()
  647. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  648. u1 := randUserRoleId()
  649. r1, r2 := randUserRoleId(), randUserRoleId()
  650. ts := time.Now().Unix()
  651. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  652. return m.BatchInsertWithTx(c, session, []*SysUserRole{
  653. {UserId: u1, RoleId: r1, CreateTime: ts, UpdateTime: ts},
  654. {UserId: u1, RoleId: r2, CreateTime: ts, UpdateTime: ts},
  655. })
  656. })
  657. if err != nil {
  658. t.Fatalf("BatchInsertWithTx: %v", err)
  659. }
  660. got1, err := m.FindOneByUserIdRoleId(ctx, u1, r1)
  661. if err != nil {
  662. t.Fatalf("FindOne r1: %v", err)
  663. }
  664. got2, err := m.FindOneByUserIdRoleId(ctx, u1, r2)
  665. if err != nil {
  666. t.Fatalf("FindOne r2: %v", err)
  667. }
  668. defer testutil.CleanTable(ctx, conn, "sys_user_role", got1.Id, got2.Id)
  669. }
  670. // TC-0340: 空列表
  671. func TestSysUserRoleModel_BatchInsertWithTx_Empty(t *testing.T) {
  672. ctx := context.Background()
  673. conn := testutil.GetTestSqlConn()
  674. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  675. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  676. if err := m.BatchInsertWithTx(c, session, nil); err != nil {
  677. return err
  678. }
  679. return m.BatchInsertWithTx(c, session, []*SysUserRole{})
  680. })
  681. if err != nil {
  682. t.Fatalf("BatchInsertWithTx empty: %v", err)
  683. }
  684. }
  685. // TC-0342: 事务回滚
  686. func TestSysUserRoleModel_BatchInsertWithTx_Rollback(t *testing.T) {
  687. ctx := context.Background()
  688. conn := testutil.GetTestSqlConn()
  689. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  690. userId := randUserRoleId()
  691. roleId := randUserRoleId()
  692. ts := time.Now().Unix()
  693. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  694. if err := m.BatchInsertWithTx(c, session, []*SysUserRole{
  695. {UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts},
  696. }); err != nil {
  697. return err
  698. }
  699. return errors.New("rollback")
  700. })
  701. if err == nil || err.Error() != "rollback" {
  702. t.Fatalf("want rollback error got %v", err)
  703. }
  704. _, err = m.FindOneByUserIdRoleId(ctx, userId, roleId)
  705. if err != ErrNotFound {
  706. t.Fatalf("after rollback want ErrNotFound got %v", err)
  707. }
  708. }
  709. // TC-0349: 正常多条
  710. func TestSysUserRoleModel_BatchUpdateWithTx_Normal(t *testing.T) {
  711. ctx := context.Background()
  712. conn := testutil.GetTestSqlConn()
  713. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  714. userId := randUserRoleId()
  715. r1, r2 := randUserRoleId(), randUserRoleId()
  716. ts := time.Now().Unix()
  717. res1, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: r1, CreateTime: ts, UpdateTime: ts})
  718. if err != nil {
  719. t.Fatalf("Insert1: %v", err)
  720. }
  721. id1, _ := res1.LastInsertId()
  722. res2, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: r2, CreateTime: ts, UpdateTime: ts})
  723. if err != nil {
  724. t.Fatalf("Insert2: %v", err)
  725. }
  726. id2, _ := res2.LastInsertId()
  727. defer testutil.CleanTable(ctx, conn, "sys_user_role", id1, id2)
  728. newTs := ts + 200
  729. err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  730. return m.BatchUpdateWithTx(c, session, []*SysUserRole{
  731. {Id: id1, UserId: userId, RoleId: r1, CreateTime: ts, UpdateTime: newTs},
  732. {Id: id2, UserId: userId, RoleId: r2, CreateTime: ts, UpdateTime: newTs},
  733. })
  734. })
  735. if err != nil {
  736. t.Fatalf("BatchUpdateWithTx: %v", err)
  737. }
  738. got1, err := m.FindOne(ctx, id1)
  739. if err != nil {
  740. t.Fatalf("FindOne1: %v", err)
  741. }
  742. if got1.UpdateTime != newTs {
  743. t.Fatalf("got1 UpdateTime want %d got %d", newTs, got1.UpdateTime)
  744. }
  745. }
  746. // TC-0348: 空列表
  747. func TestSysUserRoleModel_BatchUpdateWithTx_Empty(t *testing.T) {
  748. ctx := context.Background()
  749. conn := testutil.GetTestSqlConn()
  750. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  751. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  752. if err := m.BatchUpdateWithTx(c, session, nil); err != nil {
  753. return err
  754. }
  755. return m.BatchUpdateWithTx(c, session, []*SysUserRole{})
  756. })
  757. if err != nil {
  758. t.Fatalf("BatchUpdateWithTx empty: %v", err)
  759. }
  760. }
  761. // TC-0358: 正常多条
  762. func TestSysUserRoleModel_BatchDeleteWithTx_Normal(t *testing.T) {
  763. ctx := context.Background()
  764. conn := testutil.GetTestSqlConn()
  765. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  766. ts := time.Now().Unix()
  767. var ids []int64
  768. for i := 0; i < 2; i++ {
  769. res, err := m.Insert(ctx, &SysUserRole{
  770. UserId: randUserRoleId(), RoleId: randUserRoleId(), CreateTime: ts, UpdateTime: ts,
  771. })
  772. if err != nil {
  773. t.Fatalf("Insert: %v", err)
  774. }
  775. id, _ := res.LastInsertId()
  776. ids = append(ids, id)
  777. }
  778. defer func() {
  779. for _, id := range ids {
  780. testutil.CleanTable(ctx, conn, "sys_user_role", id)
  781. }
  782. }()
  783. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  784. return m.BatchDeleteWithTx(c, session, ids)
  785. })
  786. if err != nil {
  787. t.Fatalf("BatchDeleteWithTx: %v", err)
  788. }
  789. for _, id := range ids {
  790. if _, err := m.FindOne(ctx, id); err != ErrNotFound {
  791. t.Fatalf("id %d should be deleted: %v", id, err)
  792. }
  793. }
  794. }
  795. // TC-0357: 空ids
  796. func TestSysUserRoleModel_BatchDeleteWithTx_Empty(t *testing.T) {
  797. ctx := context.Background()
  798. conn := testutil.GetTestSqlConn()
  799. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  800. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  801. if err := m.BatchDeleteWithTx(c, session, nil); err != nil {
  802. return err
  803. }
  804. return m.BatchDeleteWithTx(c, session, []int64{})
  805. })
  806. if err != nil {
  807. t.Fatalf("BatchDeleteWithTx empty: %v", err)
  808. }
  809. }
  810. // TC-0323: 事务内可见性
  811. func TestSysUserRoleModel_FindOneWithTx_InsertThenFind(t *testing.T) {
  812. ctx := context.Background()
  813. conn := testutil.GetTestSqlConn()
  814. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  815. userId := randUserRoleId()
  816. roleId := randUserRoleId()
  817. ts := time.Now().Unix()
  818. var insertedId int64
  819. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  820. res, err := m.InsertWithTx(c, session, &SysUserRole{UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  821. if err != nil {
  822. return err
  823. }
  824. insertedId, err = res.LastInsertId()
  825. if err != nil {
  826. return err
  827. }
  828. got, err := m.FindOneWithTx(c, session, insertedId)
  829. require.NoError(t, err)
  830. assert.Equal(t, insertedId, got.Id)
  831. assert.Equal(t, userId, got.UserId)
  832. assert.Equal(t, roleId, got.RoleId)
  833. return nil
  834. })
  835. require.NoError(t, err)
  836. defer testutil.CleanTable(ctx, conn, "sys_user_role", insertedId)
  837. }
  838. // TC-0322: 事务内记录不存在
  839. func TestSysUserRoleModel_FindOneWithTx_NotFound(t *testing.T) {
  840. ctx := context.Background()
  841. m := NewSysUserRoleModel(testutil.GetTestSqlConn(), testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  842. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  843. _, err := m.FindOneWithTx(c, session, 999999999999)
  844. require.ErrorIs(t, err, ErrNotFound)
  845. return nil
  846. })
  847. require.NoError(t, err)
  848. }
  849. // TC-0389: FindOneByUserIdRoleIdWithTx
  850. func TestSysUserRoleModel_FindOneByUserIdRoleIdWithTx_InsertThenFind(t *testing.T) {
  851. ctx := context.Background()
  852. conn := testutil.GetTestSqlConn()
  853. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  854. userId := randUserRoleId()
  855. roleId := randUserRoleId()
  856. ts := time.Now().Unix()
  857. var insertedId int64
  858. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  859. res, err := m.InsertWithTx(c, session, &SysUserRole{UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  860. if err != nil {
  861. return err
  862. }
  863. insertedId, err = res.LastInsertId()
  864. if err != nil {
  865. return err
  866. }
  867. got, err := m.FindOneByUserIdRoleIdWithTx(c, session, userId, roleId)
  868. require.NoError(t, err)
  869. assert.Equal(t, insertedId, got.Id)
  870. assert.Equal(t, userId, got.UserId)
  871. assert.Equal(t, roleId, got.RoleId)
  872. return nil
  873. })
  874. require.NoError(t, err)
  875. defer testutil.CleanTable(ctx, conn, "sys_user_role", insertedId)
  876. }
  877. // TC-0390: FindOneByUserIdRoleIdWithTx
  878. func TestSysUserRoleModel_FindOneByUserIdRoleIdWithTx_NotFound(t *testing.T) {
  879. ctx := context.Background()
  880. m := NewSysUserRoleModel(testutil.GetTestSqlConn(), testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  881. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  882. _, err := m.FindOneByUserIdRoleIdWithTx(c, session, 999999999, 999999999)
  883. require.ErrorIs(t, err, ErrNotFound)
  884. return nil
  885. })
  886. require.NoError(t, err)
  887. }
  888. // TC-0472: FindUserIdsByRoleId 正常返回角色下用户ID列表
  889. func TestSysUserRoleModel_FindUserIdsByRoleId_Normal(t *testing.T) {
  890. ctx := context.Background()
  891. conn := testutil.GetTestSqlConn()
  892. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  893. roleId := randUserRoleId()
  894. u1, u2 := randUserRoleId(), randUserRoleId()
  895. ts := time.Now().Unix()
  896. var ids []int64
  897. for _, userId := range []int64{u1, u2} {
  898. res, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  899. require.NoError(t, err)
  900. id, _ := res.LastInsertId()
  901. ids = append(ids, id)
  902. }
  903. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_user_role", ids...) })
  904. got, err := m.FindUserIdsByRoleId(ctx, roleId)
  905. require.NoError(t, err)
  906. require.Len(t, got, 2)
  907. assert.True(t, int64SliceEqualIgnoreOrder(got, []int64{u1, u2}))
  908. }
  909. // TC-0473: FindUserIdsByRoleId 无绑定返回空
  910. func TestSysUserRoleModel_FindUserIdsByRoleId_Empty(t *testing.T) {
  911. conn := testutil.GetTestSqlConn()
  912. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  913. got, err := m.FindUserIdsByRoleId(context.Background(), 999999998)
  914. require.NoError(t, err)
  915. require.Empty(t, got)
  916. }
  917. // =============================================================================
  918. // audit 回归:FindRoleIdsByUserIdForProduct 必须过滤 r.status=1(禁用角色不返回)
  919. // =============================================================================
  920. // TC-0706: 同一产品下同时存在启用/禁用两个角色,接口只返回启用的那个
  921. func TestSysUserRoleModel_FindRoleIdsByUserIdForProduct_FiltersDisabledRole(t *testing.T) {
  922. ctx := context.Background()
  923. conn := testutil.GetTestSqlConn()
  924. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  925. userId := randUserRoleId()
  926. productCode := testutil.UniqueId()
  927. ts := time.Now().Unix()
  928. // 启用角色
  929. enabledRoleId := insertTestRole(t, ctx, conn, productCode, "enabled_"+testutil.UniqueId())
  930. // 禁用角色(默认 insertTestRole 状态=1,需要手动改为 2)
  931. disabledRoleId := insertTestRole(t, ctx, conn, productCode, "disabled_"+testutil.UniqueId())
  932. _, err := conn.ExecCtx(ctx, "UPDATE `sys_role` SET `status`=2 WHERE `id`=?", disabledRoleId)
  933. require.NoError(t, err)
  934. var recIds []int64
  935. for _, rid := range []int64{enabledRoleId, disabledRoleId} {
  936. res, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: rid, CreateTime: ts, UpdateTime: ts})
  937. require.NoError(t, err)
  938. id, _ := res.LastInsertId()
  939. recIds = append(recIds, id)
  940. }
  941. defer func() {
  942. for _, id := range recIds {
  943. testutil.CleanTable(ctx, conn, "sys_user_role", id)
  944. }
  945. testutil.CleanTable(ctx, conn, "sys_role", enabledRoleId, disabledRoleId)
  946. }()
  947. got, err := m.FindRoleIdsByUserIdForProduct(ctx, userId, productCode)
  948. require.NoError(t, err)
  949. // 修复后:仅返回启用角色
  950. assert.ElementsMatch(t, []int64{enabledRoleId}, got,
  951. "FindRoleIdsByUserIdForProduct 必须过滤 r.status=1,禁用角色不应出现在返回中")
  952. assert.NotContains(t, got, disabledRoleId,
  953. "禁用角色的 id 不应被返回")
  954. }
  955. // =============================================================================
  956. // audit 回归:DeleteByUserIdAndRoleIdsTx 批量删除正确性
  957. // =============================================================================
  958. // TC-0707: 批量删除指定 (userId, roleIds) 组合;未命中的绑定不受影响
  959. func TestSysUserRoleModel_DeleteByUserIdAndRoleIdsTx(t *testing.T) {
  960. ctx := context.Background()
  961. conn := testutil.GetTestSqlConn()
  962. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  963. userId := randUserRoleId()
  964. r1, r2, r3 := randUserRoleId(), randUserRoleId(), randUserRoleId()
  965. ts := time.Now().Unix()
  966. var recIds []int64
  967. for _, rid := range []int64{r1, r2, r3} {
  968. res, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: rid, CreateTime: ts, UpdateTime: ts})
  969. require.NoError(t, err)
  970. id, _ := res.LastInsertId()
  971. recIds = append(recIds, id)
  972. }
  973. defer func() {
  974. for _, id := range recIds {
  975. testutil.CleanTable(ctx, conn, "sys_user_role", id)
  976. }
  977. }()
  978. // 删除 r1 和 r3,保留 r2
  979. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  980. return m.DeleteByUserIdAndRoleIdsTx(c, session, userId, []int64{r1, r3})
  981. })
  982. require.NoError(t, err)
  983. got, err := m.FindRoleIdsByUserId(ctx, userId)
  984. require.NoError(t, err)
  985. assert.ElementsMatch(t, []int64{r2}, got, "仅保留未被删除的 r2")
  986. }
  987. // TC-0708: DeleteByUserIdAndRoleIdsTx 空列表时应立即返回 nil,不执行 SQL
  988. func TestSysUserRoleModel_DeleteByUserIdAndRoleIdsTx_EmptyIsNoop(t *testing.T) {
  989. ctx := context.Background()
  990. conn := testutil.GetTestSqlConn()
  991. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  992. userId := randUserRoleId()
  993. roleId := randUserRoleId()
  994. ts := time.Now().Unix()
  995. res, err := m.Insert(ctx, &SysUserRole{UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  996. require.NoError(t, err)
  997. id, _ := res.LastInsertId()
  998. defer testutil.CleanTable(ctx, conn, "sys_user_role", id)
  999. // 空列表
  1000. err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  1001. return m.DeleteByUserIdAndRoleIdsTx(c, session, userId, []int64{})
  1002. })
  1003. require.NoError(t, err)
  1004. // 原有绑定未被删除
  1005. got, err := m.FindRoleIdsByUserId(ctx, userId)
  1006. require.NoError(t, err)
  1007. assert.Contains(t, got, roleId, "空 roleIds 列表不应删除任何数据")
  1008. }
  1009. // TC-0709: DeleteByUserIdAndRoleIdsTx 不影响其它 userId 的同 roleId 绑定(WHERE userId 约束)
  1010. func TestSysUserRoleModel_DeleteByUserIdAndRoleIdsTx_OtherUserNotAffected(t *testing.T) {
  1011. ctx := context.Background()
  1012. conn := testutil.GetTestSqlConn()
  1013. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  1014. u1, u2 := randUserRoleId(), randUserRoleId()
  1015. roleId := randUserRoleId()
  1016. ts := time.Now().Unix()
  1017. res1, err := m.Insert(ctx, &SysUserRole{UserId: u1, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  1018. require.NoError(t, err)
  1019. id1, _ := res1.LastInsertId()
  1020. res2, err := m.Insert(ctx, &SysUserRole{UserId: u2, RoleId: roleId, CreateTime: ts, UpdateTime: ts})
  1021. require.NoError(t, err)
  1022. id2, _ := res2.LastInsertId()
  1023. defer func() {
  1024. testutil.CleanTable(ctx, conn, "sys_user_role", id1, id2)
  1025. }()
  1026. err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  1027. return m.DeleteByUserIdAndRoleIdsTx(c, session, u1, []int64{roleId})
  1028. })
  1029. require.NoError(t, err)
  1030. // u1 的被删,u2 的保留
  1031. got1, _ := m.FindRoleIdsByUserId(ctx, u1)
  1032. got2, _ := m.FindRoleIdsByUserId(ctx, u2)
  1033. assert.NotContains(t, got1, roleId)
  1034. assert.Contains(t, got2, roleId)
  1035. }
  1036. func seedUserRoleWithPrimedCache(t *testing.T, ctx context.Context, m SysUserRoleModel, userId, roleId int64) int64 {
  1037. t.Helper()
  1038. ts := time.Now().Unix()
  1039. res, err := m.Insert(ctx, &SysUserRole{
  1040. UserId: userId, RoleId: roleId, CreateTime: ts, UpdateTime: ts,
  1041. })
  1042. require.NoError(t, err)
  1043. id, err := res.LastInsertId()
  1044. require.NoError(t, err)
  1045. _, err = m.FindOne(ctx, id)
  1046. require.NoError(t, err, "FindOne 为 id 维度缓存预热")
  1047. _, err = m.FindOneByUserIdRoleId(ctx, userId, roleId)
  1048. require.NoError(t, err, "FindOneByUserIdRoleId 为复合维度缓存预热")
  1049. return id
  1050. }
  1051. func assertCacheKeysGone(t *testing.T, rds *redis.Redis, idKey, compositeKey string) {
  1052. t.Helper()
  1053. got, err := rds.Get(idKey)
  1054. require.NoError(t, err)
  1055. assert.Empty(t, got, "id 维度缓存 key %q 应被 DELETE 一并失效", idKey)
  1056. got, err = rds.Get(compositeKey)
  1057. require.NoError(t, err)
  1058. assert.Empty(t, got, "复合维度缓存 key %q 应被 DELETE 一并失效", compositeKey)
  1059. }
  1060. // TC-1062: DeleteByRoleIdTx 对多行同时失效 id + composite 缓存
  1061. func TestSysUserRoleModel_DeleteByRoleIdTx_InvalidatesAllKeyCols(t *testing.T) {
  1062. ctx := context.Background()
  1063. conn := testutil.GetTestSqlConn()
  1064. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  1065. rds := redis.MustNewRedis(testutil.GetTestConfig().CacheRedis.Nodes[0].RedisConf)
  1066. cachePrefix := testutil.GetTestCachePrefix()
  1067. roleId := randUserRoleId()
  1068. u1, u2 := randUserRoleId(), randUserRoleId()
  1069. if u1 == u2 {
  1070. u2 = u1 + 1
  1071. }
  1072. id1 := seedUserRoleWithPrimedCache(t, ctx, m, u1, roleId)
  1073. id2 := seedUserRoleWithPrimedCache(t, ctx, m, u2, roleId)
  1074. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_user_role", id1, id2) })
  1075. require.NoError(t,
  1076. m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  1077. return m.DeleteByRoleIdTx(c, session, roleId)
  1078. }))
  1079. // 裁剪 SELECT 为三列后,构造缓存 key 需要的 id/userId/roleId 仍被携带回来。
  1080. // 任何一条遗漏都会让这里的断言炸掉(旧行的缓存还在,FindOne 就能从缓存拿到"幽灵行")。
  1081. for _, c := range []struct {
  1082. id, uid int64
  1083. }{{id1, u1}, {id2, u2}} {
  1084. idKey := fmt.Sprintf("%s:cache:sysUserRole:id:%v", cachePrefix, c.id)
  1085. compKey := fmt.Sprintf("%s:cache:sysUserRole:userId:roleId:%v:%v", cachePrefix, c.uid, roleId)
  1086. assertCacheKeysGone(t, rds, idKey, compKey)
  1087. }
  1088. // 终态真相:两行都不应再存在于 DB。
  1089. for _, id := range []int64{id1, id2} {
  1090. _, err := m.FindOne(ctx, id)
  1091. assert.ErrorIs(t, err, ErrNotFound,
  1092. "DELETE 已提交,FindOne 必须回落 DB 读到 ErrNotFound;"+
  1093. "若仍查到旧行说明裁剪后的 SELECT 漏掉了 id 列,id 维度缓存仍在")
  1094. }
  1095. }
  1096. // TC-1063: DeleteByUserIdAndRoleIdsTx 的批量 IN 路径
  1097. func TestSysUserRoleModel_DeleteByUserIdAndRoleIdsTx_InvalidatesAllKeyCols(t *testing.T) {
  1098. ctx := context.Background()
  1099. conn := testutil.GetTestSqlConn()
  1100. m := NewSysUserRoleModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  1101. rds := redis.MustNewRedis(testutil.GetTestConfig().CacheRedis.Nodes[0].RedisConf)
  1102. cachePrefix := testutil.GetTestCachePrefix()
  1103. userId := randUserRoleId()
  1104. r1, r2, r3 := randUserRoleId(), randUserRoleId()+1, randUserRoleId()+2
  1105. id1 := seedUserRoleWithPrimedCache(t, ctx, m, userId, r1)
  1106. id2 := seedUserRoleWithPrimedCache(t, ctx, m, userId, r2)
  1107. // r3 作为"不在删除集合内"的对照组:这一行不得被误伤
  1108. id3 := seedUserRoleWithPrimedCache(t, ctx, m, userId, r3)
  1109. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "sys_user_role", id1, id2, id3) })
  1110. require.NoError(t,
  1111. m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  1112. return m.DeleteByUserIdAndRoleIdsTx(c, session, userId, []int64{r1, r2})
  1113. }))
  1114. // 被删的两条:id+composite 都必须失效。
  1115. for _, c := range []struct {
  1116. id, rid int64
  1117. }{{id1, r1}, {id2, r2}} {
  1118. idKey := fmt.Sprintf("%s:cache:sysUserRole:id:%v", cachePrefix, c.id)
  1119. compKey := fmt.Sprintf("%s:cache:sysUserRole:userId:roleId:%v:%v", cachePrefix, userId, c.rid)
  1120. assertCacheKeysGone(t, rds, idKey, compKey)
  1121. _, err := m.FindOne(ctx, c.id)
  1122. assert.ErrorIs(t, err, ErrNotFound)
  1123. }
  1124. // 未被删的第 3 条:DB 仍在,FindOne 必须成功。
  1125. got, err := m.FindOne(ctx, id3)
  1126. require.NoError(t, err,
  1127. "防误伤:IN (r1, r2) 不得把 r3 的行带走;"+
  1128. "若失败,说明裁剪后 SELECT 把对照组的 key 也返回并被 ExecCtx 一并失效")
  1129. assert.Equal(t, id3, got.Id)
  1130. assert.Equal(t, r3, got.RoleId)
  1131. }