2026-05-12 14:08:46 +08:00

202 lines
5.6 KiB
Go

package binancerecharge
import (
"context"
"testing"
"chatapp3-golang/internal/config"
"chatapp3-golang/internal/integration"
"chatapp3-golang/internal/model"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func TestSaveConfigKeepsExistingSecretWhenBlank(t *testing.T) {
db := newBinanceRechargeTestDB(t)
service := NewService(config.Config{}, db, &fakeBinanceJavaGateway{})
if _, err := service.SaveConfig(context.Background(), SaveConfigRequest{
SysOrigin: "LIKEI",
APIKey: "key-1",
APISecret: "secret-1",
Enabled: true,
Rates: []RateConfigRequest{
{MinAmount: "0", MaxAmount: "100", GoldPerUSD: "80000"},
},
}); err != nil {
t.Fatalf("initial save config: %v", err)
}
resp, err := service.SaveConfig(context.Background(), SaveConfigRequest{
SysOrigin: "LIKEI",
APIKey: "key-2",
Enabled: true,
Rates: []RateConfigRequest{
{MinAmount: "0", MaxAmount: "0", GoldPerUSD: "82000"},
},
})
if err != nil {
t.Fatalf("update config: %v", err)
}
if resp.APIKey != "key-2" {
t.Fatalf("APIKey = %q, want key-2", resp.APIKey)
}
if !resp.APISecretConfigured {
t.Fatalf("APISecretConfigured = false, want true")
}
var row model.BinanceRechargeConfig
if err := db.Where("sys_origin = ?", "LIKEI").First(&row).Error; err != nil {
t.Fatalf("query config: %v", err)
}
if row.APISecret != "secret-1" {
t.Fatalf("APISecret = %q, want secret-1", row.APISecret)
}
}
func TestVerifyAndRechargeIsIdempotent(t *testing.T) {
db := newBinanceRechargeTestDB(t)
java := &fakeBinanceJavaGateway{
profile: integration.UserProfile{
ID: integration.Int64Value(200),
OriginSys: "LIKEI",
},
balanceExists: true,
}
service := NewService(config.Config{}, db, java)
service.SetBinanceVerifier(&fakeBinanceVerifier{})
if _, err := service.SaveConfig(context.Background(), SaveConfigRequest{
SysOrigin: "LIKEI",
APIKey: "key",
APISecret: "secret",
Enabled: true,
Rates: []RateConfigRequest{
{MinAmount: "0", MaxAmount: "100", GoldPerUSD: "80000"},
{MinAmount: "100", MaxAmount: "0", GoldPerUSD: "82000"},
},
}); err != nil {
t.Fatalf("save config: %v", err)
}
req := VerifyRechargeRequest{
TargetUserID: 200,
OrderNo: "order-1",
TransferAmount: "12.5",
TransferType: TransferTypeBinancePay,
BillNo: "bill-1",
}
resp, err := service.VerifyAndRecharge(context.Background(), AuthUser{
UserID: 100,
SysOrigin: "LIKEI",
}, req)
if err != nil {
t.Fatalf("verify recharge: %v", err)
}
if resp.Idempotent {
t.Fatalf("first response idempotent = true, want false")
}
if resp.GoldAmount != 1_000_000 {
t.Fatalf("GoldAmount = %d, want 1000000", resp.GoldAmount)
}
if java.changeCalls != 1 {
t.Fatalf("changeCalls = %d, want 1", java.changeCalls)
}
if java.lastChange.RechargeType != rechargeTypeUSDT {
t.Fatalf("RechargeType = %q, want %q", java.lastChange.RechargeType, rechargeTypeUSDT)
}
if java.lastChange.AcceptAmount != "1000000" {
t.Fatalf("AcceptAmount = %q, want 1000000", java.lastChange.AcceptAmount)
}
if java.lastChange.OrderAmount != "12.5" {
t.Fatalf("OrderAmount = %q, want 12.5", java.lastChange.OrderAmount)
}
resp, err = service.VerifyAndRecharge(context.Background(), AuthUser{
UserID: 100,
SysOrigin: "LIKEI",
}, req)
if err != nil {
t.Fatalf("verify idempotent recharge: %v", err)
}
if !resp.Idempotent {
t.Fatalf("second response idempotent = false, want true")
}
if java.changeCalls != 1 {
t.Fatalf("changeCalls after idempotent call = %d, want 1", java.changeCalls)
}
var count int64
if err := db.Model(&model.BinanceRechargeVerifyRecord{}).Where("status = ?", verifyStatusSuccess).Count(&count).Error; err != nil {
t.Fatalf("count success records: %v", err)
}
if count != 1 {
t.Fatalf("success record count = %d, want 1", count)
}
}
func newBinanceRechargeTestDB(t *testing.T) *gorm.DB {
t.Helper()
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
if err != nil {
t.Fatalf("open sqlite: %v", err)
}
if err := db.AutoMigrate(
&model.BinanceRechargeConfig{},
&model.BinanceRechargeRate{},
&model.BinanceRechargeVerifyRecord{},
); err != nil {
t.Fatalf("auto migrate: %v", err)
}
return db
}
type fakeBinanceVerifier struct{}
func (f *fakeBinanceVerifier) Verify(_ context.Context, _ BinanceCredential, query BinanceVerifyQuery) (VerifiedTransaction, error) {
return VerifiedTransaction{
TransactionID: query.BillNo,
OrderType: query.TransferType,
Amount: query.Amount,
RawJSON: `{"transactionId":"` + query.BillNo + `"}`,
}, nil
}
type fakeBinanceJavaGateway struct {
profile integration.UserProfile
balanceExists bool
sellerExists bool
changeCalls int
lastChange integration.FreightBalanceChangeRequest
}
func (f *fakeBinanceJavaGateway) GetUserProfile(_ context.Context, userID int64) (integration.UserProfile, error) {
if int64(f.profile.ID) == 0 {
f.profile.ID = integration.Int64Value(userID)
}
return f.profile, nil
}
func (f *fakeBinanceJavaGateway) ExistsFreightBalance(context.Context, int64) (bool, error) {
return f.balanceExists, nil
}
func (f *fakeBinanceJavaGateway) ExistsFreightSeller(context.Context, int64) (bool, error) {
return f.sellerExists, nil
}
func (f *fakeBinanceJavaGateway) ChangeFreightBalance(_ context.Context, req integration.FreightBalanceChangeRequest) error {
f.changeCalls++
f.lastChange = req
return nil
}
func (f *fakeBinanceJavaGateway) AddFreightAgentReview(context.Context, integration.FreightAgentReviewRequest) error {
return nil
}
func (f *fakeBinanceJavaGateway) IncreaseFreightRechargeRecord(context.Context, int64, string) error {
return nil
}