membership_repo_test.go 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. // MembershipRepo 仓储集成测试(t_membership 成员关系,登录两步协议支撑)。
  2. //
  3. // 依赖 test_helpers_test.go 的 setup/insertFixture;未设置 UNIFIED_TEST_DSN 时跳过。
  4. package repository
  5. import (
  6. "context"
  7. stderrors "errors"
  8. "testing"
  9. apperrors "yuansiwei/unified-backend/internal/infra/errors"
  10. )
  11. // TestMembershipRepo_ListByCanonicalUserID 多 membership 按 created_at 升序 / 无 membership 返非 nil 空切片。
  12. func TestMembershipRepo_ListByCanonicalUserID(t *testing.T) {
  13. gdb := setup(t)
  14. ctx := context.Background()
  15. fx := insertFixture(t, gdb, ctx)
  16. repo := NewMembershipRepo(gdb)
  17. t.Run("命中_按created_at升序", func(t *testing.T) {
  18. list, err := repo.ListByCanonicalUserID(ctx, fx.canonical)
  19. if err != nil {
  20. t.Fatalf("ListByCanonicalUserID: %v", err)
  21. }
  22. if len(list) != 3 {
  23. t.Fatalf("期望 3 条 membership,实际 %d", len(list))
  24. }
  25. // created_at 升序:instB(academic,-30s) → instA(teacher,-20s) → instA(finance,-10s)
  26. if list[0].InstitutionID != fx.instB || list[0].Role != "academic" {
  27. t.Errorf("第 1 条 = (%d, %s),期望 (%d, academic)", list[0].InstitutionID, list[0].Role, fx.instB)
  28. }
  29. if list[1].InstitutionID != fx.instA || list[1].Role != "teacher" {
  30. t.Errorf("第 2 条 = (%d, %s),期望 (%d, teacher)", list[1].InstitutionID, list[1].Role, fx.instA)
  31. }
  32. if list[2].InstitutionID != fx.instA || list[2].Role != "finance" {
  33. t.Errorf("第 3 条 = (%d, %s),期望 (%d, finance)", list[2].InstitutionID, list[2].Role, fx.instA)
  34. }
  35. })
  36. t.Run("无membership_返回非nil空切片", func(t *testing.T) {
  37. list, err := repo.ListByCanonicalUserID(ctx, fx.other)
  38. if err != nil {
  39. t.Fatalf("ListByCanonicalUserID(无 membership 用户): %v", err)
  40. }
  41. if list == nil {
  42. t.Errorf("期望非 nil 空切片(JSON 序列化为 [] 而非 null),实际 nil")
  43. }
  44. if len(list) != 0 {
  45. t.Errorf("期望 0 条,实际 %d", len(list))
  46. }
  47. })
  48. }
  49. // TestMembershipRepo_GetByUserInstitutionCampus 命中 / 同作用域多角色取最新 / 未命中。
  50. func TestMembershipRepo_GetByUserInstitutionCampus(t *testing.T) {
  51. gdb := setup(t)
  52. ctx := context.Background()
  53. fx := insertFixture(t, gdb, ctx)
  54. repo := NewMembershipRepo(gdb)
  55. t.Run("命中_单角色", func(t *testing.T) {
  56. // instB 下只有 academic 一条
  57. m, err := repo.GetByUserInstitutionCampus(ctx, fx.canonical, fx.instB, 0)
  58. if err != nil {
  59. t.Fatalf("GetByUserInstitutionCampus(instB): %v", err)
  60. }
  61. if m.Role != "academic" {
  62. t.Errorf("instB role = %q,期望 academic", m.Role)
  63. }
  64. })
  65. t.Run("同作用域多角色_取created_at最新", func(t *testing.T) {
  66. // instA 下有 teacher(-20s) + finance(-10s),finance 较新 → 应取 finance
  67. m, err := repo.GetByUserInstitutionCampus(ctx, fx.canonical, fx.instA, 0)
  68. if err != nil {
  69. t.Fatalf("GetByUserInstitutionCampus(instA): %v", err)
  70. }
  71. if m.Role != "finance" {
  72. t.Errorf("instA 多角色应取最新(finance),实际 %q", m.Role)
  73. }
  74. })
  75. t.Run("未命中_返回ErrNotFound", func(t *testing.T) {
  76. // 该用户在 instC(不存在的机构)下无 membership
  77. instC := int64(999)
  78. _, err := repo.GetByUserInstitutionCampus(ctx, fx.canonical, instC, 0)
  79. if !stderrors.Is(err, apperrors.ErrNotFound) {
  80. t.Fatalf("期望 ErrNotFound,实际 %v", err)
  81. }
  82. })
  83. }