sysUserModel_test.go 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033
  1. package user_test
  2. import (
  3. "context"
  4. "database/sql"
  5. "errors"
  6. "strings"
  7. "testing"
  8. "time"
  9. "github.com/go-sql-driver/mysql"
  10. "github.com/stretchr/testify/assert"
  11. "github.com/stretchr/testify/require"
  12. "perms-system-server/internal/model/user"
  13. "perms-system-server/internal/testutil"
  14. "github.com/zeromicro/go-zero/core/stores/sqlx"
  15. )
  16. func newTestSysUser(username string, deptId int64) *user.SysUser {
  17. now := time.Now().Unix()
  18. return &user.SysUser{
  19. Username: username,
  20. Password: "hashed",
  21. Nickname: "nick",
  22. Avatar: sql.NullString{Valid: false},
  23. Email: "[email protected]",
  24. Phone: "13800000000",
  25. Remark: "",
  26. DeptId: deptId,
  27. IsSuperAdmin: 2,
  28. MustChangePassword: 2,
  29. Status: 1,
  30. CreateTime: now,
  31. UpdateTime: now,
  32. }
  33. }
  34. func newModel(t *testing.T) (user.SysUserModel, sqlx.SqlConn) {
  35. t.Helper()
  36. conn := testutil.GetTestSqlConn()
  37. m := user.NewSysUserModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  38. return m, conn
  39. }
  40. // TC-0304: 获取表名
  41. func TestSysUserModel_TableName(t *testing.T) {
  42. m, _ := newModel(t)
  43. require.Equal(t, "`sys_user`", m.TableName())
  44. }
  45. // TC-0281: 正常插入
  46. func TestSysUserModel_CRUD(t *testing.T) {
  47. ctx := context.Background()
  48. m, conn := newModel(t)
  49. username := "crud_" + testutil.UniqueId()
  50. data := newTestSysUser(username, 1)
  51. res, err := m.Insert(ctx, data)
  52. require.NoError(t, err)
  53. id, err := res.LastInsertId()
  54. require.NoError(t, err)
  55. require.Greater(t, id, int64(0))
  56. defer testutil.CleanTable(ctx, conn, m.TableName(), id)
  57. got, err := m.FindOne(ctx, id)
  58. require.NoError(t, err)
  59. require.Equal(t, username, got.Username)
  60. require.Equal(t, data.Email, got.Email)
  61. data.Id = id
  62. data.Nickname = "updated_nick"
  63. data.UpdateTime = time.Now().Unix()
  64. require.NoError(t, m.Update(ctx, data))
  65. after, err := m.FindOne(ctx, id)
  66. require.NoError(t, err)
  67. require.Equal(t, "updated_nick", after.Nickname)
  68. require.NoError(t, m.Delete(ctx, id))
  69. _, err = m.FindOne(ctx, id)
  70. require.ErrorIs(t, err, user.ErrNotFound)
  71. }
  72. // TC-0330: FindOneByUsername
  73. func TestSysUserModel_FindOneByUsername(t *testing.T) {
  74. ctx := context.Background()
  75. m, conn := newModel(t)
  76. username := "findname_" + testutil.UniqueId()
  77. data := newTestSysUser(username, 1)
  78. res, err := m.Insert(ctx, data)
  79. require.NoError(t, err)
  80. id, err := res.LastInsertId()
  81. require.NoError(t, err)
  82. defer testutil.CleanTable(ctx, conn, m.TableName(), id)
  83. found, err := m.FindOneByUsername(ctx, username)
  84. require.NoError(t, err)
  85. require.Equal(t, id, found.Id)
  86. require.Equal(t, username, found.Username)
  87. _, err = m.FindOneByUsername(ctx, "no_such_"+testutil.UniqueId())
  88. require.ErrorIs(t, err, user.ErrNotFound)
  89. }
  90. // TC-0307: 多条记录(3条)
  91. func TestSysUserModel_BatchInsert_BatchDelete(t *testing.T) {
  92. ctx := context.Background()
  93. m, conn := newModel(t)
  94. names := []string{
  95. "batch_a_" + testutil.UniqueId(),
  96. "batch_b_" + testutil.UniqueId(),
  97. "batch_c_" + testutil.UniqueId(),
  98. }
  99. list := []*user.SysUser{
  100. newTestSysUser(names[0], 10),
  101. newTestSysUser(names[1], 10),
  102. newTestSysUser(names[2], 10),
  103. }
  104. require.NoError(t, m.BatchInsert(ctx, list))
  105. var ids []int64
  106. for _, name := range names {
  107. u, err := m.FindOneByUsername(ctx, name)
  108. require.NoError(t, err)
  109. ids = append(ids, u.Id)
  110. }
  111. defer testutil.CleanTable(ctx, conn, m.TableName(), ids...)
  112. require.NoError(t, m.BatchDelete(ctx, ids))
  113. for _, name := range names {
  114. _, err := m.FindOneByUsername(ctx, name)
  115. require.ErrorIs(t, err, user.ErrNotFound)
  116. }
  117. }
  118. // TC-0316: 多条记录(3条)
  119. func TestSysUserModel_BatchUpdate(t *testing.T) {
  120. ctx := context.Background()
  121. m, conn := newModel(t)
  122. u1 := "bupd1_" + testutil.UniqueId()
  123. u2 := "bupd2_" + testutil.UniqueId()
  124. d1 := newTestSysUser(u1, 20)
  125. d2 := newTestSysUser(u2, 20)
  126. r1, err := m.Insert(ctx, d1)
  127. require.NoError(t, err)
  128. id1, err := r1.LastInsertId()
  129. require.NoError(t, err)
  130. r2, err := m.Insert(ctx, d2)
  131. require.NoError(t, err)
  132. id2, err := r2.LastInsertId()
  133. require.NoError(t, err)
  134. defer testutil.CleanTable(ctx, conn, m.TableName(), id1, id2)
  135. now := time.Now().Unix()
  136. upd := []*user.SysUser{
  137. {Id: id1, Username: u1, Password: d1.Password, Nickname: "n1_new", Avatar: sql.NullString{}, Email: d1.Email, Phone: d1.Phone, Remark: d1.Remark, DeptId: 21, IsSuperAdmin: 2, MustChangePassword: 2, Status: 1, CreateTime: d1.CreateTime, UpdateTime: now},
  138. {Id: id2, Username: u2, Password: d2.Password, Nickname: "n2_new", Avatar: sql.NullString{}, Email: d2.Email, Phone: d2.Phone, Remark: d2.Remark, DeptId: 22, IsSuperAdmin: 2, MustChangePassword: 2, Status: 2, CreateTime: d2.CreateTime, UpdateTime: now},
  139. }
  140. require.NoError(t, m.BatchUpdate(ctx, upd))
  141. g1, err := m.FindOne(ctx, id1)
  142. require.NoError(t, err)
  143. require.Equal(t, "n1_new", g1.Nickname)
  144. require.Equal(t, int64(21), g1.DeptId)
  145. g2, err := m.FindOne(ctx, id2)
  146. require.NoError(t, err)
  147. require.Equal(t, "n2_new", g2.Nickname)
  148. require.Equal(t, int64(22), g2.DeptId)
  149. require.Equal(t, int64(2), g2.Status)
  150. }
  151. // TC-0302: 正常事务
  152. func TestSysUserModel_TransactCtx_Commit(t *testing.T) {
  153. ctx := context.Background()
  154. m, conn := newModel(t)
  155. username := "tx_ok_" + testutil.UniqueId()
  156. data := newTestSysUser(username, 3)
  157. var insertedID int64
  158. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  159. res, err := m.InsertWithTx(c, session, data)
  160. if err != nil {
  161. return err
  162. }
  163. insertedID, err = res.LastInsertId()
  164. return err
  165. })
  166. require.NoError(t, err)
  167. require.Greater(t, insertedID, int64(0))
  168. defer testutil.CleanTable(ctx, conn, m.TableName(), insertedID)
  169. got, err := m.FindOne(ctx, insertedID)
  170. require.NoError(t, err)
  171. require.Equal(t, username, got.Username)
  172. }
  173. // TC-0303: fn返回错误
  174. func TestSysUserModel_TransactCtx_Rollback(t *testing.T) {
  175. ctx := context.Background()
  176. m, _ := newModel(t)
  177. username := "tx_rb_" + testutil.UniqueId()
  178. data := newTestSysUser(username, 3)
  179. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  180. if _, e := m.InsertWithTx(c, session, data); e != nil {
  181. return e
  182. }
  183. return errors.New("force rollback")
  184. })
  185. require.Error(t, err)
  186. require.Contains(t, err.Error(), "force rollback")
  187. _, err = m.FindOneByUsername(ctx, username)
  188. require.ErrorIs(t, err, user.ErrNotFound)
  189. }
  190. // TC-0285: 事务内插入
  191. func TestSysUserModel_InsertWithTx_DeleteWithTx_SameTransaction(t *testing.T) {
  192. ctx := context.Background()
  193. m, conn := newModel(t)
  194. username := "tx_del_" + testutil.UniqueId()
  195. data := newTestSysUser(username, 4)
  196. // DeleteWithTx 会先 FindOne;未提交事务内的插入对默认连接不可见,因此分两个 TransactCtx:
  197. // 先提交插入,再在独立事务中 DeleteWithTx。
  198. var insertedID int64
  199. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  200. res, err := m.InsertWithTx(c, session, data)
  201. if err != nil {
  202. return err
  203. }
  204. insertedID, err = res.LastInsertId()
  205. return err
  206. })
  207. require.NoError(t, err)
  208. require.Greater(t, insertedID, int64(0))
  209. defer testutil.CleanTable(ctx, conn, m.TableName(), insertedID)
  210. err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  211. return m.DeleteWithTx(c, session, insertedID)
  212. })
  213. require.NoError(t, err)
  214. _, err = m.FindOne(ctx, insertedID)
  215. require.ErrorIs(t, err, user.ErrNotFound)
  216. }
  217. // TC-0376: 正常分页
  218. func TestSysUserModel_FindListByPage(t *testing.T) {
  219. ctx := context.Background()
  220. m, conn := newModel(t)
  221. var cnt int64
  222. err := conn.QueryRowCtx(ctx, &cnt, "SELECT COUNT(*) FROM "+m.TableName())
  223. require.NoError(t, err)
  224. username := "page_" + testutil.UniqueId()
  225. res, err := m.Insert(ctx, newTestSysUser(username, 5))
  226. require.NoError(t, err)
  227. id, err := res.LastInsertId()
  228. require.NoError(t, err)
  229. defer testutil.CleanTable(ctx, conn, m.TableName(), id)
  230. list, total, err := m.FindListByPage(ctx, 1, 10)
  231. require.NoError(t, err)
  232. var cntAfter int64
  233. require.NoError(t, conn.QueryRowCtx(ctx, &cntAfter, "SELECT COUNT(*) FROM "+m.TableName()))
  234. require.Equal(t, cntAfter, total)
  235. require.GreaterOrEqual(t, len(list), 1)
  236. require.LessOrEqual(t, len(list), 10)
  237. list2, total2, err := m.FindListByPage(ctx, 1, 1)
  238. require.NoError(t, err)
  239. require.Equal(t, cntAfter, total2)
  240. require.Len(t, list2, 1)
  241. }
  242. // TC-0381: 正常查询
  243. func TestSysUserModel_FindListByDeptIds(t *testing.T) {
  244. ctx := context.Background()
  245. m, conn := newModel(t)
  246. list, total, err := m.FindListByDeptIds(ctx, []int64{}, 1, 10)
  247. require.NoError(t, err)
  248. require.Nil(t, list)
  249. require.Equal(t, int64(0), total)
  250. deptID := time.Now().UnixNano() % 1_000_000_000
  251. if deptID < 0 {
  252. deptID = -deptID
  253. }
  254. deptID += 700_000_000
  255. u1 := "dept_" + testutil.UniqueId()
  256. u2 := "dept_" + testutil.UniqueId()
  257. otherDept := deptID + 99999
  258. r1, err := m.Insert(ctx, newTestSysUser(u1, deptID))
  259. require.NoError(t, err)
  260. id1, err := r1.LastInsertId()
  261. require.NoError(t, err)
  262. r2, err := m.Insert(ctx, newTestSysUser(u2, deptID))
  263. require.NoError(t, err)
  264. id2, err := r2.LastInsertId()
  265. require.NoError(t, err)
  266. r3, err := m.Insert(ctx, newTestSysUser("dept_other_"+testutil.UniqueId(), otherDept))
  267. require.NoError(t, err)
  268. id3, err := r3.LastInsertId()
  269. require.NoError(t, err)
  270. defer testutil.CleanTable(ctx, conn, m.TableName(), id1, id2, id3)
  271. list, total, err = m.FindListByDeptIds(ctx, []int64{deptID}, 1, 10)
  272. require.NoError(t, err)
  273. require.GreaterOrEqual(t, total, int64(2))
  274. found := map[int64]struct{}{}
  275. for _, u := range list {
  276. found[u.Id] = struct{}{}
  277. }
  278. _, ok1 := found[id1]
  279. _, ok2 := found[id2]
  280. require.True(t, ok1 && ok2, "expected both dept users in result set")
  281. list2, total, err := m.FindListByDeptIds(ctx, []int64{deptID, otherDept}, 1, 10)
  282. require.NoError(t, err)
  283. require.GreaterOrEqual(t, total, int64(3))
  284. require.GreaterOrEqual(t, len(list2), 3)
  285. }
  286. // TC-0385: 正常批量查询
  287. func TestSysUserModel_FindByIds(t *testing.T) {
  288. ctx := context.Background()
  289. m, conn := newModel(t)
  290. list, err := m.FindByIds(ctx, nil)
  291. require.NoError(t, err)
  292. require.Nil(t, list)
  293. list, err = m.FindByIds(ctx, []int64{})
  294. require.NoError(t, err)
  295. require.Nil(t, list)
  296. r1, err := m.Insert(ctx, newTestSysUser("fid1_"+testutil.UniqueId(), 6))
  297. require.NoError(t, err)
  298. id1, err := r1.LastInsertId()
  299. require.NoError(t, err)
  300. r2, err := m.Insert(ctx, newTestSysUser("fid2_"+testutil.UniqueId(), 6))
  301. require.NoError(t, err)
  302. id2, err := r2.LastInsertId()
  303. require.NoError(t, err)
  304. defer testutil.CleanTable(ctx, conn, m.TableName(), id1, id2)
  305. list, err = m.FindByIds(ctx, []int64{id1, id2})
  306. require.NoError(t, err)
  307. require.Len(t, list, 2)
  308. ids := map[int64]struct{}{list[0].Id: {}, list[1].Id: {}}
  309. _, ok1 := ids[id1]
  310. _, ok2 := ids[id2]
  311. require.True(t, ok1 && ok2)
  312. list, err = m.FindByIds(ctx, []int64{id1, 999999999999999})
  313. require.NoError(t, err)
  314. require.Len(t, list, 1)
  315. require.Equal(t, id1, list[0].Id)
  316. }
  317. // TC-0283: 唯一索引冲突
  318. func TestSysUserModel_Insert_DuplicateUsername(t *testing.T) {
  319. ctx := context.Background()
  320. m, conn := newModel(t)
  321. username := "dup_" + testutil.UniqueId()
  322. data := newTestSysUser(username, 7)
  323. res, err := m.Insert(ctx, data)
  324. require.NoError(t, err)
  325. id, err := res.LastInsertId()
  326. require.NoError(t, err)
  327. defer testutil.CleanTable(ctx, conn, m.TableName(), id)
  328. _, err = m.Insert(ctx, newTestSysUser(username, 8))
  329. require.Error(t, err)
  330. var me *mysql.MySQLError
  331. if errors.As(err, &me) {
  332. require.Equal(t, uint16(1062), me.Number)
  333. } else {
  334. require.True(t, strings.Contains(strings.ToLower(err.Error()), "duplicate"), "expected duplicate key error, got: %v", err)
  335. }
  336. }
  337. // TC-0290: 记录不存在
  338. func TestSysUserModel_FindOne_NotFound(t *testing.T) {
  339. m, _ := newModel(t)
  340. _, err := m.FindOne(context.Background(), 999999999999)
  341. require.ErrorIs(t, err, user.ErrNotFound)
  342. }
  343. // TC-0297: 记录不存在
  344. func TestSysUserModel_Update_NotFound(t *testing.T) {
  345. m, _ := newModel(t)
  346. err := m.Update(context.Background(), &user.SysUser{
  347. Id: 999999999999, Username: "ghost", Password: "x",
  348. Nickname: "n", Email: "e", Phone: "p",
  349. IsSuperAdmin: 2, MustChangePassword: 2, Status: 1,
  350. CreateTime: time.Now().Unix(), UpdateTime: time.Now().Unix(),
  351. })
  352. require.ErrorIs(t, err, user.ErrNotFound)
  353. }
  354. // TC-0300: 记录不存在
  355. func TestSysUserModel_Delete_NotFound(t *testing.T) {
  356. m, _ := newModel(t)
  357. err := m.Delete(context.Background(), 999999999999)
  358. require.ErrorIs(t, err, user.ErrNotFound)
  359. }
  360. // TC-0305: 空列表
  361. func TestSysUserModel_BatchInsert_Empty(t *testing.T) {
  362. m, _ := newModel(t)
  363. require.NoError(t, m.BatchInsert(context.Background(), nil))
  364. require.NoError(t, m.BatchInsert(context.Background(), []*user.SysUser{}))
  365. }
  366. // TC-0314: 空列表
  367. func TestSysUserModel_BatchUpdate_Empty(t *testing.T) {
  368. m, _ := newModel(t)
  369. require.NoError(t, m.BatchUpdate(context.Background(), nil))
  370. require.NoError(t, m.BatchUpdate(context.Background(), []*user.SysUser{}))
  371. }
  372. // TC-0324: 空ids
  373. func TestSysUserModel_BatchDelete_Empty(t *testing.T) {
  374. m, _ := newModel(t)
  375. require.NoError(t, m.BatchDelete(context.Background(), nil))
  376. require.NoError(t, m.BatchDelete(context.Background(), []int64{}))
  377. }
  378. // TC-0377: 第二页
  379. func TestSysUserModel_FindListByPage_SecondPage(t *testing.T) {
  380. ctx := context.Background()
  381. m, conn := newModel(t)
  382. var ids []int64
  383. for i := 0; i < 3; i++ {
  384. res, err := m.Insert(ctx, newTestSysUser("p2_"+testutil.UniqueId(), 0))
  385. require.NoError(t, err)
  386. id, _ := res.LastInsertId()
  387. ids = append(ids, id)
  388. }
  389. t.Cleanup(func() { testutil.CleanTable(ctx, conn, m.TableName(), ids...) })
  390. _, total, err := m.FindListByPage(ctx, 1, 1)
  391. require.NoError(t, err)
  392. if total >= 2 {
  393. list2, _, err := m.FindListByPage(ctx, 2, 1)
  394. require.NoError(t, err)
  395. require.Len(t, list2, 1)
  396. }
  397. }
  398. // TC-0384: deptId不存在
  399. func TestSysUserModel_FindListByDeptIds_NotExistDept(t *testing.T) {
  400. m, _ := newModel(t)
  401. list, total, err := m.FindListByDeptIds(context.Background(), []int64{999999999}, 1, 10)
  402. require.NoError(t, err)
  403. require.Equal(t, int64(0), total)
  404. require.Len(t, list, 0)
  405. }
  406. // TC-0298: 事务内更新
  407. func TestSysUserModel_UpdateWithTx(t *testing.T) {
  408. ctx := context.Background()
  409. m, conn := newModel(t)
  410. username := "upd_tx_" + testutil.UniqueId()
  411. data := newTestSysUser(username, 1)
  412. res, err := m.Insert(ctx, data)
  413. require.NoError(t, err)
  414. id, err := res.LastInsertId()
  415. require.NoError(t, err)
  416. defer testutil.CleanTable(ctx, conn, m.TableName(), id)
  417. err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  418. data.Id = id
  419. data.Nickname = "tx_updated"
  420. data.UpdateTime = time.Now().Unix()
  421. return m.UpdateWithTx(c, session, data)
  422. })
  423. require.NoError(t, err)
  424. got, err := m.FindOne(ctx, id)
  425. require.NoError(t, err)
  426. require.Equal(t, "tx_updated", got.Nickname)
  427. }
  428. // TC-0306: 单条记录
  429. func TestSysUserModel_BatchInsert_Single(t *testing.T) {
  430. ctx := context.Background()
  431. m, conn := newModel(t)
  432. username := "bi_single_" + testutil.UniqueId()
  433. list := []*user.SysUser{newTestSysUser(username, 1)}
  434. require.NoError(t, m.BatchInsert(ctx, list))
  435. found, err := m.FindOneByUsername(ctx, username)
  436. require.NoError(t, err)
  437. defer testutil.CleanTable(ctx, conn, m.TableName(), found.Id)
  438. require.Equal(t, username, found.Username)
  439. }
  440. // TC-0309: 唯一索引冲突
  441. func TestSysUserModel_BatchInsert_UniqueConflict(t *testing.T) {
  442. ctx := context.Background()
  443. m, conn := newModel(t)
  444. username := "bi_dup_" + testutil.UniqueId()
  445. list := []*user.SysUser{
  446. newTestSysUser(username, 1),
  447. newTestSysUser(username, 2),
  448. }
  449. err := m.BatchInsert(ctx, list)
  450. require.Error(t, err)
  451. t.Cleanup(func() {
  452. if found, e := m.FindOneByUsername(ctx, username); e == nil {
  453. testutil.CleanTable(ctx, conn, m.TableName(), found.Id)
  454. }
  455. })
  456. var me *mysql.MySQLError
  457. if errors.As(err, &me) {
  458. require.Equal(t, uint16(1062), me.Number)
  459. } else {
  460. require.True(t, strings.Contains(strings.ToLower(err.Error()), "duplicate"), "expected duplicate key error, got: %v", err)
  461. }
  462. }
  463. // TC-0312: 正常多条
  464. func TestSysUserModel_BatchInsertWithTx_Normal(t *testing.T) {
  465. ctx := context.Background()
  466. m, conn := newModel(t)
  467. u1 := "bitx_a_" + testutil.UniqueId()
  468. u2 := "bitx_b_" + testutil.UniqueId()
  469. list := []*user.SysUser{
  470. newTestSysUser(u1, 1),
  471. newTestSysUser(u2, 1),
  472. }
  473. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  474. return m.BatchInsertWithTx(c, session, list)
  475. })
  476. require.NoError(t, err)
  477. f1, err := m.FindOneByUsername(ctx, u1)
  478. require.NoError(t, err)
  479. f2, err := m.FindOneByUsername(ctx, u2)
  480. require.NoError(t, err)
  481. defer testutil.CleanTable(ctx, conn, m.TableName(), f1.Id, f2.Id)
  482. require.Equal(t, u1, f1.Username)
  483. require.Equal(t, u2, f2.Username)
  484. }
  485. // TC-0311: 空列表
  486. func TestSysUserModel_BatchInsertWithTx_Empty(t *testing.T) {
  487. ctx := context.Background()
  488. m, _ := newModel(t)
  489. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  490. return m.BatchInsertWithTx(c, session, nil)
  491. })
  492. require.NoError(t, err)
  493. }
  494. // TC-0313: 事务回滚
  495. func TestSysUserModel_BatchInsertWithTx_Rollback(t *testing.T) {
  496. ctx := context.Background()
  497. m, _ := newModel(t)
  498. u1 := "bitx_rb_" + testutil.UniqueId()
  499. u2 := "bitx_rb_" + testutil.UniqueId()
  500. list := []*user.SysUser{
  501. newTestSysUser(u1, 1),
  502. newTestSysUser(u2, 1),
  503. }
  504. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  505. if e := m.BatchInsertWithTx(c, session, list); e != nil {
  506. return e
  507. }
  508. return errors.New("force rollback")
  509. })
  510. require.Error(t, err)
  511. _, err = m.FindOneByUsername(ctx, u1)
  512. require.ErrorIs(t, err, user.ErrNotFound)
  513. _, err = m.FindOneByUsername(ctx, u2)
  514. require.ErrorIs(t, err, user.ErrNotFound)
  515. }
  516. // TC-0320: 正常多条
  517. func TestSysUserModel_BatchUpdateWithTx_Normal(t *testing.T) {
  518. ctx := context.Background()
  519. m, conn := newModel(t)
  520. u1 := "butx_a_" + testutil.UniqueId()
  521. u2 := "butx_b_" + testutil.UniqueId()
  522. r1, err := m.Insert(ctx, newTestSysUser(u1, 1))
  523. require.NoError(t, err)
  524. id1, _ := r1.LastInsertId()
  525. r2, err := m.Insert(ctx, newTestSysUser(u2, 1))
  526. require.NoError(t, err)
  527. id2, _ := r2.LastInsertId()
  528. defer testutil.CleanTable(ctx, conn, m.TableName(), id1, id2)
  529. now := time.Now().Unix()
  530. err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  531. return m.BatchUpdateWithTx(c, session, []*user.SysUser{
  532. {Id: id1, Username: u1, Password: "hashed", Nickname: "new1", Avatar: sql.NullString{}, Email: "[email protected]", Phone: "13800000000", DeptId: 1, IsSuperAdmin: 2, MustChangePassword: 2, Status: 1, CreateTime: now, UpdateTime: now},
  533. {Id: id2, Username: u2, Password: "hashed", Nickname: "new2", Avatar: sql.NullString{}, Email: "[email protected]", Phone: "13800000000", DeptId: 1, IsSuperAdmin: 2, MustChangePassword: 2, Status: 1, CreateTime: now, UpdateTime: now},
  534. })
  535. })
  536. require.NoError(t, err)
  537. g1, err := m.FindOne(ctx, id1)
  538. require.NoError(t, err)
  539. require.Equal(t, "new1", g1.Nickname)
  540. g2, err := m.FindOne(ctx, id2)
  541. require.NoError(t, err)
  542. require.Equal(t, "new2", g2.Nickname)
  543. }
  544. // TC-0319: 空列表
  545. func TestSysUserModel_BatchUpdateWithTx_Empty(t *testing.T) {
  546. ctx := context.Background()
  547. m, _ := newModel(t)
  548. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  549. return m.BatchUpdateWithTx(c, session, nil)
  550. })
  551. require.NoError(t, err)
  552. }
  553. // TC-0325: 单个id
  554. func TestSysUserModel_BatchDelete_Single(t *testing.T) {
  555. ctx := context.Background()
  556. m, conn := newModel(t)
  557. username := "bd_single_" + testutil.UniqueId()
  558. res, err := m.Insert(ctx, newTestSysUser(username, 1))
  559. require.NoError(t, err)
  560. id, _ := res.LastInsertId()
  561. defer testutil.CleanTable(ctx, conn, m.TableName(), id)
  562. require.NoError(t, m.BatchDelete(ctx, []int64{id}))
  563. _, err = m.FindOne(ctx, id)
  564. require.ErrorIs(t, err, user.ErrNotFound)
  565. }
  566. // TC-0327: 包含不存在id
  567. func TestSysUserModel_BatchDelete_ContainsNonExist(t *testing.T) {
  568. ctx := context.Background()
  569. m, conn := newModel(t)
  570. username := "bd_nonex_" + testutil.UniqueId()
  571. res, err := m.Insert(ctx, newTestSysUser(username, 1))
  572. require.NoError(t, err)
  573. id, _ := res.LastInsertId()
  574. defer testutil.CleanTable(ctx, conn, m.TableName(), id)
  575. require.NoError(t, m.BatchDelete(ctx, []int64{id, 999999999}))
  576. _, err = m.FindOne(ctx, id)
  577. require.ErrorIs(t, err, user.ErrNotFound)
  578. }
  579. // TC-0329: 正常多条
  580. func TestSysUserModel_BatchDeleteWithTx_Normal(t *testing.T) {
  581. ctx := context.Background()
  582. m, conn := newModel(t)
  583. u1 := "bdtx_a_" + testutil.UniqueId()
  584. u2 := "bdtx_b_" + testutil.UniqueId()
  585. r1, err := m.Insert(ctx, newTestSysUser(u1, 1))
  586. require.NoError(t, err)
  587. id1, _ := r1.LastInsertId()
  588. r2, err := m.Insert(ctx, newTestSysUser(u2, 1))
  589. require.NoError(t, err)
  590. id2, _ := r2.LastInsertId()
  591. defer testutil.CleanTable(ctx, conn, m.TableName(), id1, id2)
  592. err = m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  593. return m.BatchDeleteWithTx(c, session, []int64{id1, id2})
  594. })
  595. require.NoError(t, err)
  596. _, err = m.FindOne(ctx, id1)
  597. require.ErrorIs(t, err, user.ErrNotFound)
  598. _, err = m.FindOne(ctx, id2)
  599. require.ErrorIs(t, err, user.ErrNotFound)
  600. }
  601. // TC-0328: 空ids
  602. func TestSysUserModel_BatchDeleteWithTx_Empty(t *testing.T) {
  603. ctx := context.Background()
  604. m, _ := newModel(t)
  605. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  606. return m.BatchDeleteWithTx(c, session, nil)
  607. })
  608. require.NoError(t, err)
  609. }
  610. // TC-0294: 事务内可见性
  611. func TestSysUserModel_FindOneWithTx_InsertThenFind(t *testing.T) {
  612. ctx := context.Background()
  613. m, conn := newModel(t)
  614. username := "fone_tx_" + testutil.UniqueId()
  615. data := newTestSysUser(username, 1)
  616. var insertedID int64
  617. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  618. res, err := m.InsertWithTx(c, session, data)
  619. if err != nil {
  620. return err
  621. }
  622. insertedID, err = res.LastInsertId()
  623. if err != nil {
  624. return err
  625. }
  626. got, err := m.FindOneWithTx(c, session, insertedID)
  627. if err != nil {
  628. return err
  629. }
  630. require.Equal(t, insertedID, got.Id)
  631. require.Equal(t, username, got.Username)
  632. assert.Equal(t, data.Email, got.Email)
  633. assert.Equal(t, data.Phone, got.Phone)
  634. assert.Equal(t, data.DeptId, got.DeptId)
  635. return nil
  636. })
  637. require.NoError(t, err)
  638. defer testutil.CleanTable(ctx, conn, m.TableName(), insertedID)
  639. }
  640. // TC-0293: 事务内记录不存在
  641. func TestSysUserModel_FindOneWithTx_NotFound(t *testing.T) {
  642. ctx := context.Background()
  643. m, _ := newModel(t)
  644. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  645. _, err := m.FindOneWithTx(c, session, 999999999999)
  646. require.ErrorIs(t, err, user.ErrNotFound)
  647. return nil
  648. })
  649. require.NoError(t, err)
  650. }
  651. // TC-0332: FindOneByUsernameWithTx
  652. func TestSysUserModel_FindOneByUsernameWithTx_InsertThenFind(t *testing.T) {
  653. ctx := context.Background()
  654. m, conn := newModel(t)
  655. username := "fuser_tx_" + testutil.UniqueId()
  656. data := newTestSysUser(username, 1)
  657. var insertedID int64
  658. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  659. res, err := m.InsertWithTx(c, session, data)
  660. if err != nil {
  661. return err
  662. }
  663. insertedID, err = res.LastInsertId()
  664. if err != nil {
  665. return err
  666. }
  667. got, err := m.FindOneByUsernameWithTx(c, session, username)
  668. if err != nil {
  669. return err
  670. }
  671. require.Equal(t, insertedID, got.Id)
  672. require.Equal(t, username, got.Username)
  673. assert.Equal(t, data.Email, got.Email)
  674. return nil
  675. })
  676. require.NoError(t, err)
  677. defer testutil.CleanTable(ctx, conn, m.TableName(), insertedID)
  678. }
  679. // TC-0333: FindOneByUsernameWithTx
  680. func TestSysUserModel_FindOneByUsernameWithTx_NotFound(t *testing.T) {
  681. ctx := context.Background()
  682. m, _ := newModel(t)
  683. err := m.TransactCtx(ctx, func(c context.Context, session sqlx.Session) error {
  684. _, err := m.FindOneByUsernameWithTx(c, session, "no_such_"+testutil.UniqueId())
  685. require.ErrorIs(t, err, user.ErrNotFound)
  686. return nil
  687. })
  688. require.NoError(t, err)
  689. }
  690. // TC-0389: FindIdsByDeptId 正常返回部门下用户ID列表
  691. func TestSysUserModel_FindIdsByDeptId_Normal(t *testing.T) {
  692. ctx := context.Background()
  693. m, conn := newModel(t)
  694. deptId := time.Now().UnixNano()%100_000_000 + 600_000_000
  695. u1 := "fbd1_" + testutil.UniqueId()
  696. u2 := "fbd2_" + testutil.UniqueId()
  697. r1, err := m.Insert(ctx, newTestSysUser(u1, deptId))
  698. require.NoError(t, err)
  699. id1, err := r1.LastInsertId()
  700. require.NoError(t, err)
  701. r2, err := m.Insert(ctx, newTestSysUser(u2, deptId))
  702. require.NoError(t, err)
  703. id2, err := r2.LastInsertId()
  704. require.NoError(t, err)
  705. t.Cleanup(func() { testutil.CleanTable(ctx, conn, m.TableName(), id1, id2) })
  706. ids, err := m.FindIdsByDeptId(ctx, deptId)
  707. require.NoError(t, err)
  708. require.Len(t, ids, 2)
  709. assert.ElementsMatch(t, []int64{id1, id2}, ids)
  710. }
  711. // TC-0390: FindIdsByDeptId 部门无用户返回空
  712. func TestSysUserModel_FindIdsByDeptId_Empty(t *testing.T) {
  713. m, _ := newModel(t)
  714. deptId := time.Now().UnixNano()%100_000_000 + 700_000_000
  715. ids, err := m.FindIdsByDeptId(context.Background(), deptId)
  716. require.NoError(t, err)
  717. require.Empty(t, ids)
  718. }
  719. // TC-0380: FindListByPage list查询失败(DB异常)
  720. func TestSysUserModel_FindListByPage_DBError(t *testing.T) {
  721. badConn := sqlx.NewMysql("root:bad@tcp(127.0.0.1:1)/bad?timeout=1s")
  722. m := user.NewSysUserModel(badConn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  723. _, _, err := m.FindListByPage(context.Background(), 1, 10)
  724. require.Error(t, err)
  725. }
  726. // TC-0388: FindByIds DB异常
  727. func TestSysUserModel_FindByIds_DBError(t *testing.T) {
  728. badConn := sqlx.NewMysql("root:bad@tcp(127.0.0.1:1)/bad?timeout=1s")
  729. m := user.NewSysUserModel(badConn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  730. list, err := m.FindByIds(context.Background(), []int64{1, 2, 3})
  731. require.Error(t, err)
  732. require.Nil(t, list)
  733. }
  734. // TC-0378: FindListByPage - 空结果页
  735. func TestSysUserModel_FindListByPage_EmptyPage(t *testing.T) {
  736. ctx := context.Background()
  737. m, _ := newModel(t)
  738. list, total, err := m.FindListByPage(ctx, 999999, 10)
  739. require.NoError(t, err)
  740. require.GreaterOrEqual(t, total, int64(0))
  741. require.Empty(t, list)
  742. }
  743. // TC-0382: FindListByDeptIds - 空deptIds
  744. func TestSysUserModel_FindListByDeptIds_EmptyDeptIds(t *testing.T) {
  745. ctx := context.Background()
  746. m, _ := newModel(t)
  747. list, total, err := m.FindListByDeptIds(ctx, []int64{}, 1, 10)
  748. require.NoError(t, err)
  749. require.Equal(t, int64(0), total)
  750. require.Nil(t, list)
  751. }
  752. // TC-0383: FindListByDeptIds - 单个deptId
  753. func TestSysUserModel_FindListByDeptIds_SingleDeptId(t *testing.T) {
  754. ctx := context.Background()
  755. m, conn := newModel(t)
  756. deptId := time.Now().UnixNano()%100_000_000 + 800_000_000
  757. username := "single_dept_" + testutil.UniqueId()
  758. res, err := m.Insert(ctx, newTestSysUser(username, deptId))
  759. require.NoError(t, err)
  760. id, err := res.LastInsertId()
  761. require.NoError(t, err)
  762. t.Cleanup(func() { testutil.CleanTable(ctx, conn, m.TableName(), id) })
  763. list, total, err := m.FindListByDeptIds(ctx, []int64{deptId}, 1, 10)
  764. require.NoError(t, err)
  765. require.GreaterOrEqual(t, total, int64(1))
  766. require.GreaterOrEqual(t, len(list), 1)
  767. found := false
  768. for _, u := range list {
  769. if u.Id == id {
  770. found = true
  771. assert.Equal(t, username, u.Username)
  772. assert.Equal(t, deptId, u.DeptId)
  773. break
  774. }
  775. }
  776. require.True(t, found, "expected user with id %d in result set", id)
  777. }
  778. // TC-0282: Insert 正常插入含TokenVersion
  779. func TestSysUserModel_Insert_WithTokenVersion(t *testing.T) {
  780. ctx := context.Background()
  781. m, conn := newModel(t)
  782. username := "tv_insert_" + testutil.UniqueId()
  783. data := newTestSysUser(username, 0)
  784. res, err := m.Insert(ctx, data)
  785. require.NoError(t, err, "Insert should include tokenVersion in SQL parameters")
  786. id, err := res.LastInsertId()
  787. require.NoError(t, err)
  788. defer testutil.CleanTable(ctx, conn, m.TableName(), id)
  789. got, err := m.FindOne(ctx, id)
  790. require.NoError(t, err)
  791. assert.Equal(t, int64(0), got.TokenVersion, "default tokenVersion should be 0")
  792. }
  793. // TC-0286: InsertWithTx 事务内插入含TokenVersion
  794. func TestSysUserModel_InsertWithTx_WithTokenVersion(t *testing.T) {
  795. ctx := context.Background()
  796. m, conn := newModel(t)
  797. username := "tv_instx_" + testutil.UniqueId()
  798. data := newTestSysUser(username, 0)
  799. var insertedId int64
  800. err := m.TransactCtx(ctx, func(txCtx context.Context, session sqlx.Session) error {
  801. res, err := m.InsertWithTx(txCtx, session, data)
  802. if err != nil {
  803. return err
  804. }
  805. insertedId, _ = res.LastInsertId()
  806. return nil
  807. })
  808. require.NoError(t, err, "InsertWithTx should include tokenVersion in SQL parameters")
  809. defer testutil.CleanTable(ctx, conn, m.TableName(), insertedId)
  810. got, err := m.FindOne(ctx, insertedId)
  811. require.NoError(t, err)
  812. assert.Equal(t, int64(0), got.TokenVersion)
  813. }
  814. // TC-0296: Update 正常更新含TokenVersion
  815. func TestSysUserModel_Update_WithTokenVersion(t *testing.T) {
  816. ctx := context.Background()
  817. m, conn := newModel(t)
  818. username := "tv_update_" + testutil.UniqueId()
  819. data := newTestSysUser(username, 0)
  820. res, err := m.Insert(ctx, data)
  821. require.NoError(t, err)
  822. id, _ := res.LastInsertId()
  823. defer testutil.CleanTable(ctx, conn, m.TableName(), id)
  824. got, err := m.FindOne(ctx, id)
  825. require.NoError(t, err)
  826. got.TokenVersion = 5
  827. got.Nickname = "updated_nick"
  828. err = m.Update(ctx, got)
  829. require.NoError(t, err, "Update should include tokenVersion in SQL parameters")
  830. updated, err := m.FindOne(ctx, id)
  831. require.NoError(t, err)
  832. assert.Equal(t, int64(5), updated.TokenVersion)
  833. assert.Equal(t, "updated_nick", updated.Nickname)
  834. }
  835. // TC-0308: BatchInsert 批量插入含TokenVersion
  836. func TestSysUserModel_BatchInsert_WithTokenVersion(t *testing.T) {
  837. ctx := context.Background()
  838. m, conn := newModel(t)
  839. dataList := make([]*user.SysUser, 3)
  840. for i := range dataList {
  841. dataList[i] = newTestSysUser("tv_batch_"+testutil.UniqueId(), 0)
  842. }
  843. err := m.BatchInsert(ctx, dataList)
  844. require.NoError(t, err, "BatchInsert should include tokenVersion in SQL parameters")
  845. for _, d := range dataList {
  846. got, err := m.FindOneByUsername(ctx, d.Username)
  847. require.NoError(t, err)
  848. assert.Equal(t, int64(0), got.TokenVersion)
  849. t.Cleanup(func() { testutil.CleanTable(ctx, conn, m.TableName(), got.Id) })
  850. }
  851. }
  852. // TC-0317: BatchUpdate 批量更新不污染数据
  853. func TestSysUserModel_BatchUpdate_NoDataCorruption(t *testing.T) {
  854. ctx := context.Background()
  855. m, conn := newModel(t)
  856. now := time.Now().Unix()
  857. dataList := make([]*user.SysUser, 2)
  858. var ids []int64
  859. for i := range dataList {
  860. dataList[i] = newTestSysUser("tv_bupd_"+testutil.UniqueId(), 0)
  861. res, err := m.Insert(ctx, dataList[i])
  862. require.NoError(t, err)
  863. id, _ := res.LastInsertId()
  864. ids = append(ids, id)
  865. dataList[i].Id = id
  866. }
  867. t.Cleanup(func() { testutil.CleanTable(ctx, conn, m.TableName(), ids...) })
  868. dataList[0].TokenVersion = 10
  869. dataList[0].Nickname = "batch_updated_0"
  870. dataList[0].UpdateTime = now + 100
  871. dataList[1].TokenVersion = 20
  872. dataList[1].Nickname = "batch_updated_1"
  873. dataList[1].UpdateTime = now + 200
  874. err := m.BatchUpdate(ctx, dataList)
  875. require.NoError(t, err, "BatchUpdate should correctly assign values without offset")
  876. for i, id := range ids {
  877. got, err := m.FindOne(ctx, id)
  878. require.NoError(t, err)
  879. assert.Equal(t, dataList[i].TokenVersion, got.TokenVersion,
  880. "tokenVersion must not be corrupted (should not contain createTime value)")
  881. assert.Equal(t, dataList[i].Nickname, got.Nickname)
  882. assert.NotEqual(t, got.Id, got.UpdateTime,
  883. "updateTime must not be corrupted (should not contain Id value)")
  884. }
  885. }