126 lines
4.4 KiB
Go
126 lines
4.4 KiB
Go
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
|
|
})
|
|
})
|
|
}
|