Files
quyun-v2/backend/app/services/super_test.go

322 lines
9.3 KiB
Go

package services
import (
"database/sql"
"testing"
"time"
"quyun/v2/app/commands/testx"
super_dto "quyun/v2/app/http/super/v1/dto"
"quyun/v2/app/requests"
"quyun/v2/database"
"quyun/v2/database/models"
"quyun/v2/pkg/consts"
"github.com/samber/lo"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/suite"
"go.ipao.vip/atom/contracts"
"go.uber.org/dig"
)
type SuperTestSuiteInjectParams struct {
dig.In
DB *sql.DB
Initials []contracts.Initial `group:"initials"`
}
type SuperTestSuite struct {
suite.Suite
SuperTestSuiteInjectParams
}
func Test_Super(t *testing.T) {
providers := testx.Default().With(Provide)
testx.Serve(providers, t, func(p SuperTestSuiteInjectParams) {
suite.Run(t, &SuperTestSuite{SuperTestSuiteInjectParams: p})
})
}
func (s *SuperTestSuite) Test_ListUsers() {
Convey("ListUsers", s.T(), func() {
ctx := s.T().Context()
database.Truncate(ctx, s.DB, models.TableNameUser)
u1 := &models.User{Username: "user1", Nickname: "Alice"}
u2 := &models.User{Username: "user2", Nickname: "Bob"}
models.UserQuery.WithContext(ctx).Create(u1, u2)
Convey("should list users", func() {
filter := &super_dto.UserListFilter{
Pagination: requests.Pagination{Page: 1, Limit: 10},
}
res, err := Super.ListUsers(ctx, filter)
So(err, ShouldBeNil)
So(res.Total, ShouldEqual, 2)
items := res.Items.([]super_dto.UserItem)
So(items[0].Username, ShouldEqual, "user2") // Desc order
})
Convey("should filter users", func() {
filter := &super_dto.UserListFilter{
Pagination: requests.Pagination{Page: 1, Limit: 10},
Username: lo.ToPtr("Alice"),
}
res, err := Super.ListUsers(ctx, filter)
So(err, ShouldBeNil)
So(res.Total, ShouldEqual, 1)
items := res.Items.([]super_dto.UserItem)
So(items[0].Username, ShouldEqual, "user1")
})
})
}
func (s *SuperTestSuite) Test_CreateTenant() {
Convey("CreateTenant", s.T(), func() {
ctx := s.T().Context()
database.Truncate(ctx, s.DB, models.TableNameUser, models.TableNameTenant)
u := &models.User{Username: "admin1"}
models.UserQuery.WithContext(ctx).Create(u)
Convey("should create tenant", func() {
form := &super_dto.TenantCreateForm{
Name: "Super Tenant",
Code: "st1",
AdminUserID: u.ID,
}
err := Super.CreateTenant(ctx, form)
So(err, ShouldBeNil)
t, _ := models.TenantQuery.WithContext(ctx).Where(models.TenantQuery.Code.Eq("st1")).First()
So(t, ShouldNotBeNil)
So(t.Name, ShouldEqual, "Super Tenant")
So(t.UserID, ShouldEqual, u.ID)
So(t.Status, ShouldEqual, consts.TenantStatusVerified)
})
})
}
func (s *SuperTestSuite) Test_WithdrawalApproval() {
Convey("Withdrawal Approval", s.T(), func() {
ctx := s.T().Context()
database.Truncate(ctx, s.DB, models.TableNameOrder, models.TableNameUser, models.TableNameTenantLedger)
u := &models.User{Username: "user_w", Balance: 1000} // Initial 10.00
admin := &models.User{Username: "admin_w"}
models.UserQuery.WithContext(ctx).Create(u, admin)
// Create Withdrawal Order (Pending)
o1 := &models.Order{
UserID: u.ID,
Type: consts.OrderTypeWithdrawal,
Status: consts.OrderStatusCreated,
AmountPaid: 500,
}
models.OrderQuery.WithContext(ctx).Create(o1)
Convey("should list withdrawals", func() {
filter := &super_dto.SuperOrderListFilter{Pagination: requests.Pagination{Page: 1, Limit: 10}}
res, err := Super.ListWithdrawals(ctx, filter)
So(err, ShouldBeNil)
So(res.Total, ShouldEqual, 1)
})
Convey("should approve withdrawal", func() {
err := Super.ApproveWithdrawal(ctx, admin.ID, o1.ID)
So(err, ShouldBeNil)
oReload, _ := models.OrderQuery.WithContext(ctx).Where(models.OrderQuery.ID.Eq(o1.ID)).First()
So(oReload.Status, ShouldEqual, consts.OrderStatusPaid)
})
Convey("should reject withdrawal and refund", func() {
// Another order
o2 := &models.Order{
UserID: u.ID,
Type: consts.OrderTypeWithdrawal,
Status: consts.OrderStatusCreated,
AmountPaid: 200,
}
models.OrderQuery.WithContext(ctx).Create(o2)
// Assuming user balance was deducted when o2 was created (logic in creator service)
// But here we set balance manually to 1000. Let's assume it was 1200 before.
// Current balance 1000. Refund 200 -> Expect 1200.
err := Super.RejectWithdrawal(ctx, admin.ID, o2.ID, "Invalid account")
So(err, ShouldBeNil)
oReload, _ := models.OrderQuery.WithContext(ctx).Where(models.OrderQuery.ID.Eq(o2.ID)).First()
So(oReload.Status, ShouldEqual, consts.OrderStatusFailed)
uReload, _ := models.UserQuery.WithContext(ctx).Where(models.UserQuery.ID.Eq(u.ID)).First()
So(uReload.Balance, ShouldEqual, 1200)
// Check Ledger
l, _ := models.TenantLedgerQuery.WithContext(ctx).Where(models.TenantLedgerQuery.OrderID.Eq(o2.ID)).First()
So(l, ShouldNotBeNil)
So(l.Type, ShouldEqual, consts.TenantLedgerTypeAdjustment)
So(l.OperatorUserID, ShouldEqual, admin.ID)
})
})
}
func (s *SuperTestSuite) Test_TenantHealth() {
Convey("TenantHealth", s.T(), func() {
ctx := s.T().Context()
database.Truncate(
ctx,
s.DB,
models.TableNameUser,
models.TableNameTenant,
models.TableNameTenantUser,
models.TableNameContent,
models.TableNameOrder,
)
owner1 := &models.User{Username: "health_owner_1"}
owner2 := &models.User{Username: "health_owner_2"}
models.UserQuery.WithContext(ctx).Create(owner1, owner2)
tenant1 := &models.Tenant{
UserID: owner1.ID,
Name: "Health Tenant 1",
Code: "health1",
Status: consts.TenantStatusVerified,
}
tenant2 := &models.Tenant{
UserID: owner2.ID,
Name: "Health Tenant 2",
Code: "health2",
Status: consts.TenantStatusVerified,
}
models.TenantQuery.WithContext(ctx).Create(tenant1, tenant2)
models.TenantUserQuery.WithContext(ctx).Create(
&models.TenantUser{TenantID: tenant1.ID, UserID: owner1.ID},
&models.TenantUser{TenantID: tenant2.ID, UserID: owner2.ID},
)
models.ContentQuery.WithContext(ctx).Create(
&models.Content{
TenantID: tenant1.ID,
UserID: owner1.ID,
Title: "Content H1",
Status: consts.ContentStatusPublished,
},
&models.Content{
TenantID: tenant2.ID,
UserID: owner2.ID,
Title: "Content H2",
Status: consts.ContentStatusPublished,
},
)
now := time.Now()
models.OrderQuery.WithContext(ctx).Create(
&models.Order{
TenantID: tenant1.ID,
UserID: owner1.ID,
Type: consts.OrderTypeContentPurchase,
Status: consts.OrderStatusPaid,
AmountPaid: 1000,
PaidAt: now,
},
&models.Order{
TenantID: tenant2.ID,
UserID: owner2.ID,
Type: consts.OrderTypeContentPurchase,
Status: consts.OrderStatusPaid,
AmountPaid: 1000,
PaidAt: now,
},
&models.Order{
TenantID: tenant2.ID,
UserID: owner2.ID,
Type: consts.OrderTypeContentPurchase,
Status: consts.OrderStatusRefunded,
AmountPaid: 1000,
UpdatedAt: now,
},
)
filter := &super_dto.TenantListFilter{
Pagination: requests.Pagination{Page: 1, Limit: 10},
}
res, err := Super.TenantHealth(ctx, filter)
So(err, ShouldBeNil)
So(res.Total, ShouldEqual, 2)
items := res.Items.([]super_dto.TenantHealthItem)
itemMap := make(map[int64]super_dto.TenantHealthItem, len(items))
for _, item := range items {
itemMap[item.TenantID] = item
}
So(itemMap[tenant1.ID].PaidOrders, ShouldEqual, 1)
So(itemMap[tenant1.ID].RefundOrders, ShouldEqual, 0)
So(itemMap[tenant1.ID].HealthLevel, ShouldEqual, "healthy")
So(itemMap[tenant2.ID].PaidOrders, ShouldEqual, 1)
So(itemMap[tenant2.ID].RefundOrders, ShouldEqual, 1)
So(itemMap[tenant2.ID].HealthLevel, ShouldEqual, "risk")
})
}
func (s *SuperTestSuite) Test_ContentReview() {
Convey("ContentReview", s.T(), func() {
ctx := s.T().Context()
database.Truncate(ctx, s.DB, models.TableNameUser, models.TableNameTenant, models.TableNameContent)
admin := &models.User{Username: "review_admin"}
owner := &models.User{Username: "review_owner"}
models.UserQuery.WithContext(ctx).Create(admin, owner)
tenant := &models.Tenant{
UserID: owner.ID,
Name: "Review Tenant",
Code: "review",
Status: consts.TenantStatusVerified,
}
models.TenantQuery.WithContext(ctx).Create(tenant)
content := &models.Content{
TenantID: tenant.ID,
UserID: owner.ID,
Title: "Review Content",
Status: consts.ContentStatusReviewing,
}
models.ContentQuery.WithContext(ctx).Create(content)
err := Super.ReviewContent(ctx, admin.ID, content.ID, &super_dto.SuperContentReviewForm{
Action: "approve",
})
So(err, ShouldBeNil)
reloaded, _ := models.ContentQuery.WithContext(ctx).Where(models.ContentQuery.ID.Eq(content.ID)).First()
So(reloaded.Status, ShouldEqual, consts.ContentStatusPublished)
So(reloaded.PublishedAt.IsZero(), ShouldBeFalse)
content2 := &models.Content{
TenantID: tenant.ID,
UserID: owner.ID,
Title: "Review Content 2",
Status: consts.ContentStatusReviewing,
}
models.ContentQuery.WithContext(ctx).Create(content2)
err = Super.ReviewContent(ctx, admin.ID, content2.ID, &super_dto.SuperContentReviewForm{
Action: "reject",
Reason: "Policy violation",
})
So(err, ShouldBeNil)
reloaded2, _ := models.ContentQuery.WithContext(ctx).Where(models.ContentQuery.ID.Eq(content2.ID)).First()
So(reloaded2.Status, ShouldEqual, consts.ContentStatusBlocked)
})
}