package service import ( "context" "errors" "testing" "git.lzcwy.cn/zhengchiliu/zt-closet-backend/global" "git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/mysqlmodel" "git.lzcwy.cn/zhengchiliu/zt-closet-backend/pkg/errcode" "github.com/DATA-DOG/go-sqlmock" "go.opentelemetry.io/otel/trace/noop" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" ) func setupOrderQuotaTest(t *testing.T) sqlmock.Sqlmock { t.Helper() conn, mock, err := sqlmock.New() if err != nil { t.Fatal(err) } db, err := gorm.Open(mysql.New(mysql.Config{Conn: conn, SkipInitializeWithVersion: true}), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)}) if err != nil { t.Fatal(err) } oldDB, oldTracer := global.MysqlEngine, global.Tracer global.MysqlEngine, global.Tracer = db, noop.NewTracerProvider().Tracer("order-test") t.Cleanup(func() { global.MysqlEngine, global.Tracer = oldDB, oldTracer; conn.Close() }) return mock } func TestOrderQuotaGoodsReadFailureDoesNotGrant(t *testing.T) { mock := setupOrderQuotaTest(t) mock.ExpectQuery("SELECT .* FROM `orders`").WillReturnRows(sqlmock.NewRows([]string{"order_no", "goods_type", "quota_valid_days"}).AddRow("RENEW", 0, 0)) mock.ExpectQuery("SELECT .* FROM `goods`").WillReturnError(errors.New("database unavailable")) err := NewOrderService().grantQuotaGrant(context.Background(), &mysqlmodel.BenefitGrantTask{UserID: 7, GoodsID: 9, OrderNo: "RENEW", Amount: 100}, QuotaTypeCos) if err != errcode.DatabaseError { t.Fatalf("got %v, want DatabaseError", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func TestOrderRenewalAndCompensationSkipQuota(t *testing.T) { mock := setupOrderQuotaTest(t) svc := NewOrderService() // 首购凭证已存在:新续费订单和该订单补偿执行均不得插入额度或流水。 for i := 0; i < 2; i++ { mock.ExpectQuery("SELECT .* FROM `orders`").WillReturnRows(sqlmock.NewRows([]string{"order_no", "goods_type", "quota_valid_days"}).AddRow("RENEW", GoodsTypeMember, 30)) mock.ExpectQuery("SELECT .* FROM `users`").WillReturnRows(sqlmock.NewRows([]string{"id", "vip_expire"}).AddRow(7, nowUnix()+86400)) mock.ExpectQuery("SELECT .* FROM `quota_grants`").WillReturnRows(sqlmock.NewRows([]string{"id", "source", "amount", "used", "expire_time"}).AddRow(1, QuotaGrantSourceMember, 100, 40, 0)) mock.ExpectQuery("SELECT .* FROM `user_quotas`").WillReturnRows(sqlmock.NewRows([]string{"used"}).AddRow(0)) mock.ExpectBegin() mock.ExpectQuery("SELECT .* FROM `users` .*FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(7)) mock.ExpectQuery("SELECT quota_grants.* .*FOR UPDATE").WithArgs(7, 9, QuotaTypeCos, QuotaGrantSourceMember, 1). WillReturnRows(sqlmock.NewRows([]string{"id", "amount", "used", "ref_id"}).AddRow(1, 100, 40, "FIRST")) mock.ExpectCommit() err := svc.doGrantBenefit(context.Background(), &mysqlmodel.BenefitGrantTask{UserID: 7, GoodsID: 9, OrderNo: "RENEW", Amount: 100, BenefitType: BenefitTypeCos}) if err != nil { t.Fatal(err) } } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } }