package member import ( "database/sql" "sync" "testing" "time" productModel "perms-system-server/internal/model/product" memberModel "perms-system-server/internal/model/productmember" userModel "perms-system-server/internal/model/user" "perms-system-server/internal/response" "perms-system-server/internal/svc" "perms-system-server/internal/testutil" "perms-system-server/internal/testutil/ctxhelper" "perms-system-server/internal/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TC-0213: 正常添加 func TestAddMember_Normal(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() now := time.Now().Unix() uid := testutil.UniqueId() pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{ Code: uid, Name: "test_prod", AppKey: uid, AppSecret: "s1", Status: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) pId, _ := pRes.LastInsertId() uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{ Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick", Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2, Status: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) uId, _ := uRes.LastInsertId() t.Cleanup(func() { testutil.CleanTableByField(ctx, conn, "`sys_product_member`", "productCode", uid) testutil.CleanTable(ctx, conn, "`sys_user`", uId) testutil.CleanTable(ctx, conn, "`sys_product`", pId) }) logic := NewAddMemberLogic(ctx, svcCtx) resp, err := logic.AddMember(&types.AddMemberReq{ ProductCode: uid, UserId: uId, MemberType: "MEMBER", }) require.NoError(t, err) assert.True(t, resp.Id > 0) member, err := svcCtx.SysProductMemberModel.FindOne(ctx, resp.Id) require.NoError(t, err) assert.Equal(t, uid, member.ProductCode) assert.Equal(t, uId, member.UserId) assert.Equal(t, "MEMBER", member.MemberType) assert.Equal(t, int64(1), member.Status) } // TC-0214: 产品不存在 func TestAddMember_ProductNotFound(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) logic := NewAddMemberLogic(ctx, svcCtx) _, err := logic.AddMember(&types.AddMemberReq{ ProductCode: "nonexistent_product_xyz", UserId: 1, MemberType: "MEMBER", }) require.Error(t, err) ce, ok := err.(*response.CodeError) require.True(t, ok) assert.Equal(t, 404, ce.Code()) assert.Equal(t, "产品不存在", ce.Error()) } // TC-0215: 用户不存在 func TestAddMember_UserNotFound(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() now := time.Now().Unix() uid := testutil.UniqueId() pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{ Code: uid, Name: "test_prod", AppKey: uid, AppSecret: "s1", Status: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) pId, _ := pRes.LastInsertId() t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_product`", pId) }) logic := NewAddMemberLogic(ctx, svcCtx) _, err = logic.AddMember(&types.AddMemberReq{ ProductCode: uid, UserId: 999999999, MemberType: "MEMBER", }) require.Error(t, err) ce, ok := err.(*response.CodeError) require.True(t, ok) assert.Equal(t, 404, ce.Code()) assert.Equal(t, "用户不存在", ce.Error()) } // TC-0216: 已是成员 func TestAddMember_AlreadyMember(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() now := time.Now().Unix() uid := testutil.UniqueId() pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{ Code: uid, Name: "test_prod", AppKey: uid, AppSecret: "s1", Status: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) pId, _ := pRes.LastInsertId() uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{ Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick", Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2, Status: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) uId, _ := uRes.LastInsertId() mRes, err := svcCtx.SysProductMemberModel.Insert(ctx, &memberModel.SysProductMember{ ProductCode: uid, UserId: uId, MemberType: "MEMBER", Status: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) mId, _ := mRes.LastInsertId() t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_product_member`", mId) testutil.CleanTable(ctx, conn, "`sys_user`", uId) testutil.CleanTable(ctx, conn, "`sys_product`", pId) }) logic := NewAddMemberLogic(ctx, svcCtx) _, err = logic.AddMember(&types.AddMemberReq{ ProductCode: uid, UserId: uId, MemberType: "ADMIN", }) require.Error(t, err) ce, ok := err.(*response.CodeError) require.True(t, ok) assert.Equal(t, 409, ce.Code()) assert.Equal(t, "该用户已是该产品成员", ce.Error()) } // TC-0218: 无效MemberType func TestAddMember_InvalidMemberType(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() now := time.Now().Unix() uid := testutil.UniqueId() pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{ Code: uid, Name: "test_prod", AppKey: uid, AppSecret: "s1", Status: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) pId, _ := pRes.LastInsertId() uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{ Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick", Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2, Status: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) uId, _ := uRes.LastInsertId() t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", uId) testutil.CleanTable(ctx, conn, "`sys_product`", pId) }) logic := NewAddMemberLogic(ctx, svcCtx) _, err = logic.AddMember(&types.AddMemberReq{ ProductCode: uid, UserId: uId, MemberType: "INVALID", }) require.Error(t, err) ce, ok := err.(*response.CodeError) require.True(t, ok) assert.Equal(t, 400, ce.Code()) assert.Equal(t, "无效的成员类型", ce.Error()) } // TC-0217: 并发添加 func TestAddMember_ConcurrentSameUserProduct(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() now := time.Now().Unix() uid := testutil.UniqueId() pRes, err := svcCtx.SysProductModel.Insert(ctx, &productModel.SysProduct{ Code: uid, Name: "concurrent_prod", AppKey: uid, AppSecret: "s1", Status: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) pId, _ := pRes.LastInsertId() uRes, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{ Username: uid, Password: testutil.HashPassword("pass123"), Nickname: "nick", Avatar: sql.NullString{}, IsSuperAdmin: 2, MustChangePassword: 2, Status: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) uId, _ := uRes.LastInsertId() t.Cleanup(func() { testutil.CleanTableByField(ctx, conn, "`sys_product_member`", "productCode", uid) testutil.CleanTable(ctx, conn, "`sys_user`", uId) testutil.CleanTable(ctx, conn, "`sys_product`", pId) }) var wg sync.WaitGroup results := make(chan error, 2) for i := 0; i < 2; i++ { wg.Add(1) go func() { defer wg.Done() logic := NewAddMemberLogic(ctx, svcCtx) _, err := logic.AddMember(&types.AddMemberReq{ ProductCode: uid, UserId: uId, MemberType: "MEMBER", }) results <- err }() } wg.Wait() close(results) var errs []error for e := range results { errs = append(errs, e) } require.Len(t, errs, 2) successCount := 0 failCount := 0 for _, e := range errs { if e == nil { successCount++ } else { failCount++ } } assert.Equal(t, 1, successCount, "exactly one goroutine should succeed") assert.Equal(t, 1, failCount, "exactly one goroutine should fail (409 or DB duplicate)") }