package product import ( "errors" "sync" "testing" productModel "perms-system-server/internal/model/product" "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-0045: 正常创建 func TestCreateProduct_Success(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() code := testutil.UniqueId() logic := NewCreateProductLogic(ctx, svcCtx) resp, err := logic.CreateProduct(&types.CreateProductReq{ Code: code, Name: "测试产品", Remark: "集成测试", }) require.NoError(t, err) require.NotNil(t, resp) t.Cleanup(func() { testutil.CleanTableByField(ctx, conn, "`sys_product_member`", "productCode", code) testutil.CleanTableByField(ctx, conn, "`sys_user`", "username", "admin_"+code) testutil.CleanTable(ctx, conn, "`sys_product`", resp.Id) }) assert.True(t, resp.Id > 0) assert.Equal(t, code, resp.Code) assert.NotEmpty(t, resp.AppKey) assert.NotEmpty(t, resp.AppSecret) assert.Equal(t, "admin_"+code, resp.AdminUser) assert.NotEmpty(t, resp.AdminPassword) } // TC-0045: 正常创建 func TestCreateProduct_VerifyDB(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() code := testutil.UniqueId() logic := NewCreateProductLogic(ctx, svcCtx) resp, err := logic.CreateProduct(&types.CreateProductReq{ Code: code, Name: "DB验证产品", Remark: "验证数据库记录", }) require.NoError(t, err) t.Cleanup(func() { testutil.CleanTableByField(ctx, conn, "`sys_product_member`", "productCode", code) testutil.CleanTableByField(ctx, conn, "`sys_user`", "username", "admin_"+code) testutil.CleanTable(ctx, conn, "`sys_product`", resp.Id) }) product, err := svcCtx.SysProductModel.FindOne(ctx, resp.Id) require.NoError(t, err) assert.Equal(t, code, product.Code) assert.Equal(t, "DB验证产品", product.Name) assert.Equal(t, resp.AppKey, product.AppKey) assert.Equal(t, resp.AppSecret, product.AppSecret) assert.Equal(t, int64(1), product.Status) var userCount int64 err = conn.QueryRowCtx(ctx, &userCount, "SELECT COUNT(*) FROM `sys_user` WHERE `username` = ?", "admin_"+code) require.NoError(t, err) assert.Equal(t, int64(1), userCount) var memberCount int64 err = conn.QueryRowCtx(ctx, &memberCount, "SELECT COUNT(*) FROM `sys_product_member` WHERE `productCode` = ? AND `memberType` = 'ADMIN'", code) require.NoError(t, err) assert.Equal(t, int64(1), memberCount) } // TC-0048: 编码已存在 func TestCreateProduct_DuplicateCode(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() code := testutil.UniqueId() logic := NewCreateProductLogic(ctx, svcCtx) resp, err := logic.CreateProduct(&types.CreateProductReq{ Code: code, Name: "第一个产品", }) require.NoError(t, err) t.Cleanup(func() { testutil.CleanTableByField(ctx, conn, "`sys_product_member`", "productCode", code) testutil.CleanTableByField(ctx, conn, "`sys_user`", "username", "admin_"+code) testutil.CleanTable(ctx, conn, "`sys_product`", resp.Id) }) logic2 := NewCreateProductLogic(ctx, svcCtx) _, err = logic2.CreateProduct(&types.CreateProductReq{ Code: code, Name: "重复产品", }) require.Error(t, err) var codeErr *response.CodeError require.True(t, errors.As(err, &codeErr)) assert.Equal(t, 409, codeErr.Code()) assert.Equal(t, "产品编码已存在", codeErr.Error()) } // TC-0049: 并发创建同编码 func TestCreateProduct_ConcurrentSameCode(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() code := testutil.UniqueId() t.Cleanup(func() { testutil.CleanTableByField(ctx, conn, "`sys_product_member`", "productCode", code) testutil.CleanTableByField(ctx, conn, "`sys_user`", "username", "admin_"+code) testutil.CleanTableByField(ctx, conn, "`sys_product`", "code", code) }) var wg sync.WaitGroup results := make(chan error, 2) for i := 0; i < 2; i++ { wg.Add(1) go func() { defer wg.Done() logic := NewCreateProductLogic(ctx, svcCtx) _, err := logic.CreateProduct(&types.CreateProductReq{ Code: code, Name: "并发测试产品", }) results <- err }() } wg.Wait() close(results) var errs []error for err := range results { errs = append(errs, err) } require.Len(t, errs, 2) successCount := 0 failCount := 0 for _, err := range errs { if err == 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)") } // TC-0484: createProduct非超管拒绝 func TestCreateProduct_NonSuperAdminRejected(t *testing.T) { ctx := ctxhelper.AdminCtx("test_product") svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) logic := NewCreateProductLogic(ctx, svcCtx) _, err := logic.CreateProduct(&types.CreateProductReq{Code: "test", Name: "test"}) require.Error(t, err) var ce *response.CodeError require.True(t, errors.As(err, &ce)) assert.Equal(t, 403, ce.Code()) } // suppress unused import var _ = (*productModel.SysProduct)(nil)