sysDeptModel_test.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. package dept
  2. import (
  3. "context"
  4. "errors"
  5. "math"
  6. "testing"
  7. "time"
  8. "perms-system-server/internal/testutil"
  9. "github.com/stretchr/testify/assert"
  10. "github.com/stretchr/testify/require"
  11. "github.com/zeromicro/go-zero/core/stores/sqlx"
  12. )
  13. // TC-0267: 正常插入
  14. func TestSysDeptModel_CRUD(t *testing.T) {
  15. ctx := context.Background()
  16. conn := testutil.GetTestSqlConn()
  17. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  18. now := time.Now().Unix()
  19. data := &SysDept{
  20. ParentId: 0,
  21. Name: "dept_crud_" + testutil.UniqueId(),
  22. Path: "/crud/" + testutil.UniqueId() + "/",
  23. Sort: 10,
  24. Remark: "r",
  25. Status: 1,
  26. CreateTime: now,
  27. UpdateTime: now,
  28. }
  29. res, err := m.Insert(ctx, data)
  30. require.NoError(t, err)
  31. id, err := res.LastInsertId()
  32. require.NoError(t, err)
  33. tbl := m.TableName()
  34. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id) })
  35. found, err := m.FindOne(ctx, id)
  36. require.NoError(t, err)
  37. require.NotNil(t, found)
  38. assert.Equal(t, id, found.Id)
  39. assert.Equal(t, data.Name, found.Name)
  40. found.Remark = "updated"
  41. found.UpdateTime = now + 1
  42. require.NoError(t, m.Update(ctx, found))
  43. after, err := m.FindOne(ctx, id)
  44. require.NoError(t, err)
  45. assert.Equal(t, "updated", after.Remark)
  46. require.NoError(t, m.Delete(ctx, id))
  47. _, err = m.FindOne(ctx, id)
  48. require.Error(t, err)
  49. assert.True(t, errors.Is(err, ErrNotFound))
  50. }
  51. // TC-0388: 正常查询
  52. func TestSysDeptModel_FindAll_OrderBySortAscIdAsc(t *testing.T) {
  53. ctx := context.Background()
  54. conn := testutil.GetTestSqlConn()
  55. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  56. base := testutil.UniqueId()
  57. now := time.Now().Unix()
  58. rows := []*SysDept{
  59. {ParentId: 0, Name: "a_" + base, Path: "/fa/" + base + "/a/", Sort: 30, Remark: "", Status: 1, CreateTime: now, UpdateTime: now},
  60. {ParentId: 0, Name: "b_" + base, Path: "/fa/" + base + "/b/", Sort: 10, Remark: "", Status: 1, CreateTime: now, UpdateTime: now},
  61. {ParentId: 0, Name: "c_" + base, Path: "/fa/" + base + "/c/", Sort: 20, Remark: "", Status: 1, CreateTime: now, UpdateTime: now},
  62. }
  63. require.NoError(t, m.BatchInsert(ctx, rows))
  64. prefix := "/fa/" + base
  65. byPath, err := m.FindByPathPrefix(ctx, prefix)
  66. require.NoError(t, err)
  67. require.Len(t, byPath, 3)
  68. ids := []int64{byPath[0].Id, byPath[1].Id, byPath[2].Id}
  69. tbl := m.TableName()
  70. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, ids...) })
  71. all, err := m.FindAll(ctx)
  72. require.NoError(t, err)
  73. idSet := map[int64]struct{}{ids[0]: {}, ids[1]: {}, ids[2]: {}}
  74. var picked []*SysDept
  75. for i := range all {
  76. if _, ok := idSet[all[i].Id]; ok {
  77. picked = append(picked, all[i])
  78. }
  79. }
  80. require.Len(t, picked, 3)
  81. for i := 1; i < len(picked); i++ {
  82. prev, cur := picked[i-1], picked[i]
  83. if prev.Sort == cur.Sort {
  84. assert.Less(t, prev.Id, cur.Id)
  85. } else {
  86. assert.Less(t, prev.Sort, cur.Sort)
  87. }
  88. }
  89. assert.Equal(t, int64(10), picked[0].Sort)
  90. assert.Equal(t, int64(20), picked[1].Sort)
  91. assert.Equal(t, int64(30), picked[2].Sort)
  92. }
  93. // TC-0390: 正常查询
  94. func TestSysDeptModel_FindByParentId_FoundAndNotFound(t *testing.T) {
  95. ctx := context.Background()
  96. conn := testutil.GetTestSqlConn()
  97. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  98. now := time.Now().Unix()
  99. parent := &SysDept{
  100. ParentId: 0,
  101. Name: "p_" + testutil.UniqueId(),
  102. Path: "/fp/" + testutil.UniqueId() + "/",
  103. Sort: 1,
  104. Remark: "",
  105. Status: 1,
  106. CreateTime: now,
  107. UpdateTime: now,
  108. }
  109. res, err := m.Insert(ctx, parent)
  110. require.NoError(t, err)
  111. pid, err := res.LastInsertId()
  112. require.NoError(t, err)
  113. child := &SysDept{
  114. ParentId: pid,
  115. Name: "c_" + testutil.UniqueId(),
  116. Path: parent.Path + "sub/",
  117. Sort: 1,
  118. Remark: "",
  119. Status: 1,
  120. CreateTime: now,
  121. UpdateTime: now,
  122. }
  123. cres, err := m.Insert(ctx, child)
  124. require.NoError(t, err)
  125. cid, err := cres.LastInsertId()
  126. require.NoError(t, err)
  127. tbl := m.TableName()
  128. t.Cleanup(func() {
  129. testutil.CleanTable(ctx, conn, tbl, cid, pid)
  130. })
  131. list, err := m.FindByParentId(ctx, pid)
  132. require.NoError(t, err)
  133. require.Len(t, list, 1)
  134. assert.Equal(t, cid, list[0].Id)
  135. empty, err := m.FindByParentId(ctx, math.MaxInt64)
  136. require.NoError(t, err)
  137. assert.Len(t, empty, 0)
  138. }
  139. // TC-0392: 正常查询
  140. func TestSysDeptModel_FindByPathPrefix_LikePrefix(t *testing.T) {
  141. ctx := context.Background()
  142. conn := testutil.GetTestSqlConn()
  143. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  144. now := time.Now().Unix()
  145. pfx := "/pfx/" + testutil.UniqueId()
  146. d1 := &SysDept{ParentId: 0, Name: "n1_" + testutil.UniqueId(), Path: pfx + "/a/", Sort: 1, Remark: "", Status: 1, CreateTime: now, UpdateTime: now}
  147. d2 := &SysDept{ParentId: 0, Name: "n2_" + testutil.UniqueId(), Path: pfx + "/b/", Sort: 2, Remark: "", Status: 1, CreateTime: now, UpdateTime: now}
  148. other := &SysDept{ParentId: 0, Name: "o_" + testutil.UniqueId(), Path: "/other/" + testutil.UniqueId() + "/", Sort: 1, Remark: "", Status: 1, CreateTime: now, UpdateTime: now}
  149. r1, err := m.Insert(ctx, d1)
  150. require.NoError(t, err)
  151. id1, _ := r1.LastInsertId()
  152. r2, err := m.Insert(ctx, d2)
  153. require.NoError(t, err)
  154. id2, _ := r2.LastInsertId()
  155. r3, err := m.Insert(ctx, other)
  156. require.NoError(t, err)
  157. id3, _ := r3.LastInsertId()
  158. tbl := m.TableName()
  159. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id1, id2, id3) })
  160. list, err := m.FindByPathPrefix(ctx, pfx)
  161. require.NoError(t, err)
  162. require.Len(t, list, 2)
  163. names := map[string]struct{}{list[0].Name: {}, list[1].Name: {}}
  164. assert.Contains(t, names, d1.Name)
  165. assert.Contains(t, names, d2.Name)
  166. }
  167. // TC-0290: 多条记录(3条)
  168. func TestSysDeptModel_BatchInsert_BatchDelete(t *testing.T) {
  169. ctx := context.Background()
  170. conn := testutil.GetTestSqlConn()
  171. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  172. now := time.Now().Unix()
  173. tag := testutil.UniqueId()
  174. batch := []*SysDept{
  175. {ParentId: 0, Name: "b1_" + tag, Path: "/bi/" + tag + "/1/", Sort: 1, Remark: "", Status: 1, CreateTime: now, UpdateTime: now},
  176. {ParentId: 0, Name: "b2_" + tag, Path: "/bi/" + tag + "/2/", Sort: 2, Remark: "", Status: 1, CreateTime: now, UpdateTime: now},
  177. }
  178. require.NoError(t, m.BatchInsert(ctx, batch))
  179. got, err := m.FindByPathPrefix(ctx, "/bi/"+tag)
  180. require.NoError(t, err)
  181. require.Len(t, got, 2)
  182. ids := []int64{got[0].Id, got[1].Id}
  183. tbl := m.TableName()
  184. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, ids...) })
  185. require.NoError(t, m.BatchDelete(ctx, ids))
  186. for _, id := range ids {
  187. _, err := m.FindOne(ctx, id)
  188. require.Error(t, err)
  189. assert.True(t, errors.Is(err, ErrNotFound))
  190. }
  191. }
  192. // TC-0281: 事务内更新
  193. func TestSysDeptModel_TransactCtx_InsertWithTx_UpdateWithTx(t *testing.T) {
  194. ctx := context.Background()
  195. conn := testutil.GetTestSqlConn()
  196. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  197. now := time.Now().Unix()
  198. d := &SysDept{
  199. ParentId: 0,
  200. Name: "tx_" + testutil.UniqueId(),
  201. Path: "/tx/" + testutil.UniqueId() + "/",
  202. Sort: 1,
  203. Remark: "before",
  204. Status: 1,
  205. CreateTime: now,
  206. UpdateTime: now,
  207. }
  208. var finalId int64
  209. err := m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error {
  210. res, err := m.InsertWithTx(c, s, d)
  211. if err != nil {
  212. return err
  213. }
  214. lid, err := res.LastInsertId()
  215. if err != nil {
  216. return err
  217. }
  218. finalId = lid
  219. d.Id = finalId
  220. d.Remark = "after_tx"
  221. d.UpdateTime = now + 2
  222. return m.UpdateWithTx(c, s, d)
  223. })
  224. require.NoError(t, err)
  225. tbl := m.TableName()
  226. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, finalId) })
  227. out, err := m.FindOne(ctx, finalId)
  228. require.NoError(t, err)
  229. assert.Equal(t, "after_tx", out.Remark)
  230. }
  231. // TC-0287: 获取表名
  232. func TestSysDeptModel_TableName(t *testing.T) {
  233. conn := testutil.GetTestSqlConn()
  234. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  235. assert.Equal(t, "`sys_dept`", m.TableName())
  236. }
  237. // TC-0274: 记录不存在
  238. func TestSysDeptModel_FindOne_NotFound(t *testing.T) {
  239. conn := testutil.GetTestSqlConn()
  240. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  241. _, err := m.FindOne(context.Background(), 999999999999)
  242. require.ErrorIs(t, err, ErrNotFound)
  243. }
  244. // TC-0280: 记录不存在
  245. func TestSysDeptModel_Update_NonExistentRow_NoError(t *testing.T) {
  246. conn := testutil.GetTestSqlConn()
  247. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  248. err := m.Update(context.Background(), &SysDept{
  249. Id: 999999999999, Name: "ghost", Path: "/x/", Status: 1,
  250. CreateTime: time.Now().Unix(), UpdateTime: time.Now().Unix(),
  251. })
  252. require.NoError(t, err)
  253. }
  254. // TC-0283: 记录不存在
  255. func TestSysDeptModel_Delete_NonExistentRow_NoError(t *testing.T) {
  256. conn := testutil.GetTestSqlConn()
  257. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  258. err := m.Delete(context.Background(), 999999999999)
  259. require.NoError(t, err)
  260. }
  261. // TC-0288: 空列表
  262. func TestSysDeptModel_BatchInsert_Empty(t *testing.T) {
  263. conn := testutil.GetTestSqlConn()
  264. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  265. require.NoError(t, m.BatchInsert(context.Background(), nil))
  266. require.NoError(t, m.BatchInsert(context.Background(), []*SysDept{}))
  267. }
  268. // TC-0305: 空ids
  269. func TestSysDeptModel_BatchDelete_Empty(t *testing.T) {
  270. conn := testutil.GetTestSqlConn()
  271. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  272. require.NoError(t, m.BatchDelete(context.Background(), nil))
  273. require.NoError(t, m.BatchDelete(context.Background(), []int64{}))
  274. }
  275. // TC-0394: 无匹配
  276. func TestSysDeptModel_FindByPathPrefix_NoMatch(t *testing.T) {
  277. conn := testutil.GetTestSqlConn()
  278. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  279. list, err := m.FindByPathPrefix(context.Background(), "/notexist_"+testutil.UniqueId())
  280. require.NoError(t, err)
  281. assert.Empty(t, list)
  282. }
  283. // TC-0271: 事务回滚后无数据
  284. func TestSysDeptModel_InsertWithTx_Rollback(t *testing.T) {
  285. ctx := context.Background()
  286. conn := testutil.GetTestSqlConn()
  287. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  288. now := time.Now().Unix()
  289. path := "/txrb/" + testutil.UniqueId() + "/"
  290. d := &SysDept{
  291. ParentId: 0,
  292. Name: "txrb_" + testutil.UniqueId(),
  293. Path: path,
  294. Sort: 1,
  295. Status: 1,
  296. CreateTime: now,
  297. UpdateTime: now,
  298. }
  299. err := m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error {
  300. if _, e := m.InsertWithTx(c, s, d); e != nil {
  301. return e
  302. }
  303. return errors.New("force rollback")
  304. })
  305. require.Error(t, err)
  306. list, err := m.FindByPathPrefix(ctx, path)
  307. require.NoError(t, err)
  308. assert.Empty(t, list)
  309. }
  310. // TC-0284: 事务内删除
  311. func TestSysDeptModel_DeleteWithTx(t *testing.T) {
  312. ctx := context.Background()
  313. conn := testutil.GetTestSqlConn()
  314. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  315. now := time.Now().Unix()
  316. d := &SysDept{
  317. ParentId: 0,
  318. Name: "deltx_" + testutil.UniqueId(),
  319. Path: "/deltx/" + testutil.UniqueId() + "/",
  320. Sort: 1,
  321. Status: 1,
  322. CreateTime: now,
  323. UpdateTime: now,
  324. }
  325. res, err := m.Insert(ctx, d)
  326. require.NoError(t, err)
  327. id, _ := res.LastInsertId()
  328. tbl := m.TableName()
  329. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id) })
  330. err = m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error {
  331. return m.DeleteWithTx(c, s, id)
  332. })
  333. require.NoError(t, err)
  334. _, err = m.FindOne(ctx, id)
  335. require.ErrorIs(t, err, ErrNotFound)
  336. }
  337. // TC-0286: fn返回错误
  338. func TestSysDeptModel_TransactCtx_Rollback(t *testing.T) {
  339. ctx := context.Background()
  340. conn := testutil.GetTestSqlConn()
  341. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  342. now := time.Now().Unix()
  343. path := "/txrb2/" + testutil.UniqueId() + "/"
  344. d := &SysDept{
  345. ParentId: 0,
  346. Name: "txrb2_" + testutil.UniqueId(),
  347. Path: path,
  348. Sort: 1,
  349. Status: 1,
  350. CreateTime: now,
  351. UpdateTime: now,
  352. }
  353. err := m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error {
  354. if _, e := m.InsertWithTx(c, s, d); e != nil {
  355. return e
  356. }
  357. return errors.New("force rollback")
  358. })
  359. require.Error(t, err)
  360. require.Contains(t, err.Error(), "force rollback")
  361. list, err := m.FindByPathPrefix(ctx, path)
  362. require.NoError(t, err)
  363. assert.Empty(t, list)
  364. }
  365. // TC-0296: 空列表
  366. func TestSysDeptModel_BatchUpdate_Empty(t *testing.T) {
  367. conn := testutil.GetTestSqlConn()
  368. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  369. require.NoError(t, m.BatchUpdate(context.Background(), nil))
  370. require.NoError(t, m.BatchUpdate(context.Background(), []*SysDept{}))
  371. }
  372. // TC-0298: 多条记录(3条)
  373. func TestSysDeptModel_BatchUpdate_Multi(t *testing.T) {
  374. ctx := context.Background()
  375. conn := testutil.GetTestSqlConn()
  376. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  377. now := time.Now().Unix()
  378. tag := testutil.UniqueId()
  379. d1 := &SysDept{ParentId: 0, Name: "bu1_" + tag, Path: "/bu/" + tag + "/1/", Sort: 1, Remark: "r1", Status: 1, CreateTime: now, UpdateTime: now}
  380. d2 := &SysDept{ParentId: 0, Name: "bu2_" + tag, Path: "/bu/" + tag + "/2/", Sort: 2, Remark: "r2", Status: 1, CreateTime: now, UpdateTime: now}
  381. r1, err := m.Insert(ctx, d1)
  382. require.NoError(t, err)
  383. id1, _ := r1.LastInsertId()
  384. r2, err := m.Insert(ctx, d2)
  385. require.NoError(t, err)
  386. id2, _ := r2.LastInsertId()
  387. tbl := m.TableName()
  388. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id1, id2) })
  389. now2 := time.Now().Unix()
  390. upd := []*SysDept{
  391. {Id: id1, ParentId: 0, Name: "bu1_upd", Path: d1.Path, Sort: 10, Remark: "updated", Status: 1, CreateTime: now, UpdateTime: now2},
  392. {Id: id2, ParentId: 0, Name: "bu2_upd", Path: d2.Path, Sort: 20, Remark: "updated", Status: 2, CreateTime: now, UpdateTime: now2},
  393. }
  394. require.NoError(t, m.BatchUpdate(ctx, upd))
  395. g1, err := m.FindOne(ctx, id1)
  396. require.NoError(t, err)
  397. assert.Equal(t, "bu1_upd", g1.Name)
  398. assert.Equal(t, int64(10), g1.Sort)
  399. g2, err := m.FindOne(ctx, id2)
  400. require.NoError(t, err)
  401. assert.Equal(t, "bu2_upd", g2.Name)
  402. assert.Equal(t, int64(2), g2.Status)
  403. }
  404. // TC-0289: 单条记录
  405. func TestSysDeptModel_BatchInsert_Single(t *testing.T) {
  406. ctx := context.Background()
  407. conn := testutil.GetTestSqlConn()
  408. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  409. now := time.Now().Unix()
  410. path := "/bis/" + testutil.UniqueId() + "/"
  411. d := &SysDept{ParentId: 0, Name: "bis_" + testutil.UniqueId(), Path: path, Sort: 1, Status: 1, CreateTime: now, UpdateTime: now}
  412. require.NoError(t, m.BatchInsert(ctx, []*SysDept{d}))
  413. list, err := m.FindByPathPrefix(ctx, path)
  414. require.NoError(t, err)
  415. require.Len(t, list, 1)
  416. tbl := m.TableName()
  417. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, list[0].Id) })
  418. assert.Equal(t, d.Name, list[0].Name)
  419. }
  420. // TC-0294: 正常多条
  421. func TestSysDeptModel_BatchInsertWithTx_Normal(t *testing.T) {
  422. ctx := context.Background()
  423. conn := testutil.GetTestSqlConn()
  424. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  425. now := time.Now().Unix()
  426. tag := testutil.UniqueId()
  427. batch := []*SysDept{
  428. {ParentId: 0, Name: "bitx1_" + tag, Path: "/bitx/" + tag + "/1/", Sort: 1, Status: 1, CreateTime: now, UpdateTime: now},
  429. {ParentId: 0, Name: "bitx2_" + tag, Path: "/bitx/" + tag + "/2/", Sort: 2, Status: 1, CreateTime: now, UpdateTime: now},
  430. }
  431. err := m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error {
  432. return m.BatchInsertWithTx(c, s, batch)
  433. })
  434. require.NoError(t, err)
  435. list, err := m.FindByPathPrefix(ctx, "/bitx/"+tag)
  436. require.NoError(t, err)
  437. require.Len(t, list, 2)
  438. tbl := m.TableName()
  439. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, list[0].Id, list[1].Id) })
  440. }
  441. // TC-0293: 空列表
  442. func TestSysDeptModel_BatchInsertWithTx_Empty(t *testing.T) {
  443. conn := testutil.GetTestSqlConn()
  444. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  445. err := m.TransactCtx(context.Background(), func(c context.Context, s sqlx.Session) error {
  446. return m.BatchInsertWithTx(c, s, nil)
  447. })
  448. require.NoError(t, err)
  449. }
  450. // TC-0295: 事务回滚
  451. func TestSysDeptModel_BatchInsertWithTx_Rollback(t *testing.T) {
  452. ctx := context.Background()
  453. conn := testutil.GetTestSqlConn()
  454. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  455. now := time.Now().Unix()
  456. path := "/bitxrb/" + testutil.UniqueId() + "/"
  457. batch := []*SysDept{
  458. {ParentId: 0, Name: "rbn_" + testutil.UniqueId(), Path: path + "1/", Sort: 1, Status: 1, CreateTime: now, UpdateTime: now},
  459. }
  460. err := m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error {
  461. if e := m.BatchInsertWithTx(c, s, batch); e != nil {
  462. return e
  463. }
  464. return errors.New("force rollback")
  465. })
  466. require.Error(t, err)
  467. list, err := m.FindByPathPrefix(ctx, path)
  468. require.NoError(t, err)
  469. assert.Empty(t, list)
  470. }
  471. // TC-0301: 正常多条
  472. func TestSysDeptModel_BatchUpdateWithTx_Normal(t *testing.T) {
  473. ctx := context.Background()
  474. conn := testutil.GetTestSqlConn()
  475. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  476. now := time.Now().Unix()
  477. tag := testutil.UniqueId()
  478. d1 := &SysDept{ParentId: 0, Name: "butx1_" + tag, Path: "/butx/" + tag + "/1/", Sort: 1, Remark: "old", Status: 1, CreateTime: now, UpdateTime: now}
  479. d2 := &SysDept{ParentId: 0, Name: "butx2_" + tag, Path: "/butx/" + tag + "/2/", Sort: 2, Remark: "old", Status: 1, CreateTime: now, UpdateTime: now}
  480. r1, err := m.Insert(ctx, d1)
  481. require.NoError(t, err)
  482. id1, _ := r1.LastInsertId()
  483. r2, err := m.Insert(ctx, d2)
  484. require.NoError(t, err)
  485. id2, _ := r2.LastInsertId()
  486. tbl := m.TableName()
  487. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id1, id2) })
  488. now2 := time.Now().Unix()
  489. err = m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error {
  490. return m.BatchUpdateWithTx(c, s, []*SysDept{
  491. {Id: id1, ParentId: 0, Name: "butx1_new", Path: d1.Path, Sort: 10, Remark: "new", Status: 1, CreateTime: now, UpdateTime: now2},
  492. {Id: id2, ParentId: 0, Name: "butx2_new", Path: d2.Path, Sort: 20, Remark: "new", Status: 1, CreateTime: now, UpdateTime: now2},
  493. })
  494. })
  495. require.NoError(t, err)
  496. g1, err := m.FindOne(ctx, id1)
  497. require.NoError(t, err)
  498. assert.Equal(t, "butx1_new", g1.Name)
  499. assert.Equal(t, int64(10), g1.Sort)
  500. g2, err := m.FindOne(ctx, id2)
  501. require.NoError(t, err)
  502. assert.Equal(t, "butx2_new", g2.Name)
  503. }
  504. // TC-0300: 空列表
  505. func TestSysDeptModel_BatchUpdateWithTx_Empty(t *testing.T) {
  506. conn := testutil.GetTestSqlConn()
  507. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  508. err := m.TransactCtx(context.Background(), func(c context.Context, s sqlx.Session) error {
  509. return m.BatchUpdateWithTx(c, s, nil)
  510. })
  511. require.NoError(t, err)
  512. }
  513. // TC-0306: 单个id
  514. func TestSysDeptModel_BatchDelete_Single(t *testing.T) {
  515. ctx := context.Background()
  516. conn := testutil.GetTestSqlConn()
  517. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  518. now := time.Now().Unix()
  519. d := &SysDept{ParentId: 0, Name: "bds_" + testutil.UniqueId(), Path: "/bds/" + testutil.UniqueId() + "/", Sort: 1, Status: 1, CreateTime: now, UpdateTime: now}
  520. res, err := m.Insert(ctx, d)
  521. require.NoError(t, err)
  522. id, _ := res.LastInsertId()
  523. tbl := m.TableName()
  524. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id) })
  525. require.NoError(t, m.BatchDelete(ctx, []int64{id}))
  526. _, err = m.FindOne(ctx, id)
  527. require.ErrorIs(t, err, ErrNotFound)
  528. }
  529. // TC-0308: 包含不存在id
  530. func TestSysDeptModel_BatchDelete_ContainsNonExist(t *testing.T) {
  531. ctx := context.Background()
  532. conn := testutil.GetTestSqlConn()
  533. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  534. now := time.Now().Unix()
  535. d := &SysDept{ParentId: 0, Name: "bdne_" + testutil.UniqueId(), Path: "/bdne/" + testutil.UniqueId() + "/", Sort: 1, Status: 1, CreateTime: now, UpdateTime: now}
  536. res, err := m.Insert(ctx, d)
  537. require.NoError(t, err)
  538. id, _ := res.LastInsertId()
  539. tbl := m.TableName()
  540. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id) })
  541. require.NoError(t, m.BatchDelete(ctx, []int64{id, 999999999}))
  542. _, err = m.FindOne(ctx, id)
  543. require.ErrorIs(t, err, ErrNotFound)
  544. }
  545. // TC-0310: 正常多条
  546. func TestSysDeptModel_BatchDeleteWithTx_Normal(t *testing.T) {
  547. ctx := context.Background()
  548. conn := testutil.GetTestSqlConn()
  549. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  550. now := time.Now().Unix()
  551. tag := testutil.UniqueId()
  552. d1 := &SysDept{ParentId: 0, Name: "bdtx1_" + tag, Path: "/bdtx/" + tag + "/1/", Sort: 1, Status: 1, CreateTime: now, UpdateTime: now}
  553. d2 := &SysDept{ParentId: 0, Name: "bdtx2_" + tag, Path: "/bdtx/" + tag + "/2/", Sort: 2, Status: 1, CreateTime: now, UpdateTime: now}
  554. r1, err := m.Insert(ctx, d1)
  555. require.NoError(t, err)
  556. id1, _ := r1.LastInsertId()
  557. r2, err := m.Insert(ctx, d2)
  558. require.NoError(t, err)
  559. id2, _ := r2.LastInsertId()
  560. tbl := m.TableName()
  561. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id1, id2) })
  562. err = m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error {
  563. return m.BatchDeleteWithTx(c, s, []int64{id1, id2})
  564. })
  565. require.NoError(t, err)
  566. _, err = m.FindOne(ctx, id1)
  567. require.ErrorIs(t, err, ErrNotFound)
  568. _, err = m.FindOne(ctx, id2)
  569. require.ErrorIs(t, err, ErrNotFound)
  570. }
  571. // TC-0309: 空ids
  572. func TestSysDeptModel_BatchDeleteWithTx_Empty(t *testing.T) {
  573. conn := testutil.GetTestSqlConn()
  574. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  575. err := m.TransactCtx(context.Background(), func(c context.Context, s sqlx.Session) error {
  576. return m.BatchDeleteWithTx(c, s, nil)
  577. })
  578. require.NoError(t, err)
  579. }
  580. // TC-0393: LIKE注入已修复 — % 和 _ 被转义,不再作为通配符
  581. func TestSysDeptModel_FindByPathPrefix_LikeInjection(t *testing.T) {
  582. ctx := context.Background()
  583. conn := testutil.GetTestSqlConn()
  584. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  585. now := time.Now().Unix()
  586. d := &SysDept{ParentId: 0, Name: "li_" + testutil.UniqueId(), Path: "/test/", Sort: 1, Status: 1, CreateTime: now, UpdateTime: now}
  587. res, err := m.Insert(ctx, d)
  588. require.NoError(t, err)
  589. id, _ := res.LastInsertId()
  590. tbl := m.TableName()
  591. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, id) })
  592. list, err := m.FindByPathPrefix(ctx, "%")
  593. require.NoError(t, err)
  594. assert.Empty(t, list, "% 不应再作为通配符匹配所有记录")
  595. list2, err := m.FindByPathPrefix(ctx, "/test/")
  596. require.NoError(t, err)
  597. assert.GreaterOrEqual(t, len(list2), 1, "正常前缀仍应匹配")
  598. }
  599. // TC-0278: 事务内可见性
  600. func TestSysDeptModel_FindOneWithTx_InsertThenFind(t *testing.T) {
  601. ctx := context.Background()
  602. conn := testutil.GetTestSqlConn()
  603. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  604. now := time.Now().Unix()
  605. var foundInTx *SysDept
  606. var insertedId int64
  607. err := m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error {
  608. res, err := m.InsertWithTx(c, s, &SysDept{
  609. ParentId: 0, Name: "ftx_" + testutil.UniqueId(), Path: "/ftx/" + testutil.UniqueId() + "/",
  610. Sort: 1, DeptType: "NORMAL", Status: 1, CreateTime: now, UpdateTime: now,
  611. })
  612. if err != nil {
  613. return err
  614. }
  615. insertedId, _ = res.LastInsertId()
  616. foundInTx, err = m.FindOneWithTx(c, s, insertedId)
  617. return err
  618. })
  619. require.NoError(t, err)
  620. tbl := m.TableName()
  621. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, insertedId) })
  622. require.NotNil(t, foundInTx)
  623. assert.Equal(t, insertedId, foundInTx.Id)
  624. }
  625. // TC-0277: 事务内记录不存在
  626. func TestSysDeptModel_FindOneWithTx_NotFound(t *testing.T) {
  627. ctx := context.Background()
  628. conn := testutil.GetTestSqlConn()
  629. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  630. err := m.TransactCtx(ctx, func(c context.Context, s sqlx.Session) error {
  631. _, err := m.FindOneWithTx(c, s, 999999999999)
  632. return err
  633. })
  634. require.ErrorIs(t, err, ErrNotFound)
  635. }
  636. // TC-0391: FindByParentId 无子部门
  637. func TestSysDeptModel_FindByParentId_NoChildren(t *testing.T) {
  638. ctx := context.Background()
  639. conn := testutil.GetTestSqlConn()
  640. m := NewSysDeptModel(conn, testutil.GetTestCacheConf(), testutil.GetTestCachePrefix())
  641. now := time.Now().Unix()
  642. parent := &SysDept{
  643. ParentId: 0,
  644. Name: "nochild_" + testutil.UniqueId(),
  645. Path: "/nochild/" + testutil.UniqueId() + "/",
  646. Sort: 1,
  647. Status: 1,
  648. CreateTime: now,
  649. UpdateTime: now,
  650. }
  651. res, err := m.Insert(ctx, parent)
  652. require.NoError(t, err)
  653. pid, err := res.LastInsertId()
  654. require.NoError(t, err)
  655. tbl := m.TableName()
  656. t.Cleanup(func() { testutil.CleanTable(ctx, conn, tbl, pid) })
  657. list, err := m.FindByParentId(ctx, pid)
  658. require.NoError(t, err)
  659. require.Empty(t, list)
  660. }