Refactor order and tenant ledger models to use consts for Currency and Type fields; add new UserStatus values; implement comprehensive test cases for content, creator, order, super, and wallet services.
This commit is contained in:
125
backend/app/services/order_test.go
Normal file
125
backend/app/services/order_test.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"quyun/v2/app/commands/testx"
|
||||
order_dto "quyun/v2/app/http/v1/dto"
|
||||
"quyun/v2/database"
|
||||
"quyun/v2/database/models"
|
||||
"quyun/v2/pkg/consts"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"go.ipao.vip/atom/contracts"
|
||||
"go.uber.org/dig"
|
||||
)
|
||||
|
||||
type OrderTestSuiteInjectParams struct {
|
||||
dig.In
|
||||
|
||||
DB *sql.DB
|
||||
Initials []contracts.Initial `group:"initials"`
|
||||
}
|
||||
|
||||
type OrderTestSuite struct {
|
||||
suite.Suite
|
||||
OrderTestSuiteInjectParams
|
||||
}
|
||||
|
||||
func Test_Order(t *testing.T) {
|
||||
providers := testx.Default().With(Provide)
|
||||
|
||||
testx.Serve(providers, t, func(p OrderTestSuiteInjectParams) {
|
||||
suite.Run(t, &OrderTestSuite{OrderTestSuiteInjectParams: p})
|
||||
})
|
||||
}
|
||||
|
||||
func (s *OrderTestSuite) Test_PurchaseFlow() {
|
||||
Convey("Purchase Flow", s.T(), func() {
|
||||
ctx := s.T().Context()
|
||||
database.Truncate(ctx, s.DB,
|
||||
models.TableNameOrder, models.TableNameOrderItem, models.TableNameUser,
|
||||
models.TableNameContent, models.TableNameContentPrice, models.TableNameTenant,
|
||||
models.TableNameContentAccess, models.TableNameTenantLedger,
|
||||
)
|
||||
|
||||
// 1. Setup Data
|
||||
// Creator
|
||||
creator := &models.User{Username: "creator", Phone: "13800000001"}
|
||||
models.UserQuery.WithContext(ctx).Create(creator)
|
||||
// Tenant
|
||||
tenant := &models.Tenant{UserID: creator.ID, Name: "Music Shop", Code: "shop1", Status: consts.TenantStatusVerified}
|
||||
models.TenantQuery.WithContext(ctx).Create(tenant)
|
||||
// Content
|
||||
content := &models.Content{TenantID: tenant.ID, UserID: creator.ID, Title: "Song A", Status: consts.ContentStatusPublished}
|
||||
models.ContentQuery.WithContext(ctx).Create(content)
|
||||
// Price (10.00 CNY = 1000 cents)
|
||||
price := &models.ContentPrice{TenantID: tenant.ID, ContentID: content.ID, PriceAmount: 1000, Currency: consts.CurrencyCNY}
|
||||
models.ContentPriceQuery.WithContext(ctx).Create(price)
|
||||
|
||||
// Buyer
|
||||
buyer := &models.User{Username: "buyer", Phone: "13900000001", Balance: 2000} // Has 20.00
|
||||
models.UserQuery.WithContext(ctx).Create(buyer)
|
||||
|
||||
buyerCtx := context.WithValue(ctx, consts.CtxKeyUser, buyer.ID)
|
||||
|
||||
Convey("should create and pay order successfully", func() {
|
||||
// Step 1: Create Order
|
||||
form := &order_dto.OrderCreateForm{ContentID: cast.ToString(content.ID)}
|
||||
createRes, err := Order.Create(buyerCtx, form)
|
||||
So(err, ShouldBeNil)
|
||||
So(createRes.OrderID, ShouldNotBeEmpty)
|
||||
|
||||
// Verify created status
|
||||
oid := cast.ToInt64(createRes.OrderID)
|
||||
o, _ := models.OrderQuery.WithContext(ctx).Where(models.OrderQuery.ID.Eq(oid)).First()
|
||||
So(o.Status, ShouldEqual, consts.OrderStatusCreated)
|
||||
So(o.AmountPaid, ShouldEqual, 1000)
|
||||
|
||||
// Step 2: Pay Order
|
||||
payForm := &order_dto.OrderPayForm{Method: "balance"}
|
||||
_, err = Order.Pay(buyerCtx, createRes.OrderID, payForm)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
// Verify Order Paid
|
||||
o, _ = models.OrderQuery.WithContext(ctx).Where(models.OrderQuery.ID.Eq(oid)).First()
|
||||
So(o.Status, ShouldEqual, consts.OrderStatusPaid)
|
||||
So(o.PaidAt, ShouldNotBeZeroValue)
|
||||
|
||||
// Verify Balance Deducted
|
||||
b, _ := models.UserQuery.WithContext(ctx).Where(models.UserQuery.ID.Eq(buyer.ID)).First()
|
||||
So(b.Balance, ShouldEqual, 1000) // 2000 - 1000
|
||||
|
||||
// Verify Access Granted
|
||||
access, _ := models.ContentAccessQuery.WithContext(ctx).Where(models.ContentAccessQuery.UserID.Eq(buyer.ID), models.ContentAccessQuery.ContentID.Eq(content.ID)).First()
|
||||
So(access, ShouldNotBeNil)
|
||||
So(access.Status, ShouldEqual, consts.ContentAccessStatusActive)
|
||||
|
||||
// Verify Ledger Created (Creator received money logic?)
|
||||
// Note: My implementation credits the TENANT OWNER (creator.ID).
|
||||
l, _ := models.TenantLedgerQuery.WithContext(ctx).Where(models.TenantLedgerQuery.OrderID.Eq(o.ID)).First()
|
||||
So(l, ShouldNotBeNil)
|
||||
So(l.UserID, ShouldEqual, creator.ID)
|
||||
So(l.Amount, ShouldEqual, 1000)
|
||||
So(l.Type, ShouldEqual, consts.TenantLedgerTypeDebitPurchase)
|
||||
})
|
||||
|
||||
Convey("should fail pay if insufficient balance", func() {
|
||||
// Set balance to 5.00
|
||||
models.UserQuery.WithContext(ctx).Where(models.UserQuery.ID.Eq(buyer.ID)).Update(models.UserQuery.Balance, 500)
|
||||
|
||||
form := &order_dto.OrderCreateForm{ContentID: cast.ToString(content.ID)}
|
||||
createRes, err := Order.Create(buyerCtx, form)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
payForm := &order_dto.OrderPayForm{Method: "balance"}
|
||||
_, err = Order.Pay(buyerCtx, createRes.OrderID, payForm)
|
||||
So(err, ShouldNotBeNil)
|
||||
// Error should be QuotaExceeded or similar
|
||||
})
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user