175 lines
7.1 KiB
Go
175 lines
7.1 KiB
Go
package financeorder
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/DATA-DOG/go-sqlmock"
|
|
"hyapp-admin-server/internal/config"
|
|
)
|
|
|
|
func TestLegacyCoinSellerRechargeWriterGrantUpdatesBalanceAndRunningWater(t *testing.T) {
|
|
walletDB, walletMock, cleanup := newLegacyWriterSQLMock(t)
|
|
defer cleanup()
|
|
writer := NewMySQLLegacyCoinSellerRechargeWriter(config.LegacyCoinSellerRechargeSourceConfig{
|
|
AppCode: "aslan",
|
|
SysOrigin: "ATYOU",
|
|
RequestTimeout: 5 * time.Second,
|
|
}, nil, walletDB)
|
|
grantedAt := time.Date(2026, 7, 9, 9, 30, 0, 0, time.UTC)
|
|
|
|
walletMock.ExpectBegin()
|
|
expectLegacyBalanceLock(walletMock, "ATYOU", 10001).
|
|
WillReturnRows(sqlmock.NewRows([]string{"id", "earn_points", "consumption_points"}).AddRow(int64(77), "5000", "1000"))
|
|
walletMock.ExpectQuery(`(?s)SELECT id\s+FROM user_freight_balance_running_water\s+WHERE sys_origin = \? AND remark = \?\s+LIMIT 1`).
|
|
WithArgs("ATYOU", "MIFA-001").
|
|
WillReturnError(sql.ErrNoRows)
|
|
walletMock.ExpectExec(`(?s)UPDATE user_freight_balance\s+SET earn_points = COALESCE\(earn_points, 0\) \+ \?, update_user = \?, update_time = \?\s+WHERE id = \?`).
|
|
WithArgs(int64(1200), int64(10001), grantedAt, int64(77)).
|
|
WillReturnResult(sqlmock.NewResult(0, 1))
|
|
walletMock.ExpectExec(`(?s)INSERT INTO user_freight_balance_running_water`).
|
|
WithArgs(sqlmock.AnyArg(), "ATYOU", int64(10001), int64(10001), nil, int64(1200), int64(5200), "12.34", "MIFAPAY-进货", "MIFA-001", int64(10001), int64(10001), grantedAt, grantedAt).
|
|
WillReturnResult(sqlmock.NewResult(0, 1))
|
|
walletMock.ExpectCommit()
|
|
|
|
result, err := writer.GrantCoinSellerRecharge(context.Background(), legacyRechargeGrantInput{
|
|
UserID: 10001,
|
|
ExternalOrderNo: "MIFA-001",
|
|
ProviderCode: "mifapay",
|
|
USDAmount: "12.34",
|
|
CoinAmount: 1200,
|
|
GrantedAt: grantedAt,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("legacy grant failed: %v", err)
|
|
}
|
|
if !strings.HasPrefix(result.TransactionID, "legacy-freight-purchase-") || result.BalanceAfter != 5200 {
|
|
t.Fatalf("legacy grant result mismatch: %+v", result)
|
|
}
|
|
if err := walletMock.ExpectationsWereMet(); err != nil {
|
|
t.Fatalf("wallet sql expectations: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLegacyCoinSellerRechargeWriterGrantRejectsDuplicateOrder(t *testing.T) {
|
|
walletDB, walletMock, cleanup := newLegacyWriterSQLMock(t)
|
|
defer cleanup()
|
|
writer := NewMySQLLegacyCoinSellerRechargeWriter(config.LegacyCoinSellerRechargeSourceConfig{
|
|
AppCode: "yumi",
|
|
SysOrigin: "LIKEI",
|
|
RequestTimeout: 5 * time.Second,
|
|
}, nil, walletDB)
|
|
|
|
walletMock.ExpectBegin()
|
|
expectLegacyBalanceLock(walletMock, "LIKEI", 20002).
|
|
WillReturnRows(sqlmock.NewRows([]string{"id", "earn_points", "consumption_points"}).AddRow(int64(88), "7000", "0"))
|
|
walletMock.ExpectQuery(`(?s)SELECT id\s+FROM user_freight_balance_running_water\s+WHERE sys_origin = \? AND remark = \?\s+LIMIT 1`).
|
|
WithArgs("LIKEI", "V5-001").
|
|
WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(int64(9001)))
|
|
walletMock.ExpectRollback()
|
|
|
|
_, err := writer.GrantCoinSellerRecharge(context.Background(), legacyRechargeGrantInput{
|
|
UserID: 20002,
|
|
ExternalOrderNo: "V5-001",
|
|
ProviderCode: "v5pay",
|
|
USDAmount: "10.00",
|
|
CoinAmount: 1000,
|
|
GrantedAt: time.Date(2026, 7, 9, 10, 0, 0, 0, time.UTC),
|
|
})
|
|
if !errors.Is(err, errLegacyCoinSellerOrderDuplicate) {
|
|
t.Fatalf("expected duplicate order error, got %v", err)
|
|
}
|
|
if err := walletMock.ExpectationsWereMet(); err != nil {
|
|
t.Fatalf("wallet sql expectations: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLegacyCoinSellerRechargeWriterResolveCoinSellerByAccountRequiresDealer(t *testing.T) {
|
|
appDB, appMock, cleanupApp := newLegacyWriterSQLMock(t)
|
|
defer cleanupApp()
|
|
walletDB, walletMock, cleanupWallet := newLegacyWriterSQLMock(t)
|
|
defer cleanupWallet()
|
|
writer := NewMySQLLegacyCoinSellerRechargeWriter(config.LegacyCoinSellerRechargeSourceConfig{
|
|
AppCode: "aslan",
|
|
SysOrigin: "ATYOU",
|
|
RequestTimeout: 5 * time.Second,
|
|
}, appDB, walletDB)
|
|
|
|
expectLegacyUserLookup(appMock, "ATYOU", "agent001").
|
|
WillReturnRows(sqlmock.NewRows([]string{"id", "account", "country_id"}).AddRow(int64(10001), "agent001", int64(63)))
|
|
expectLegacyCoinSellerCheck(walletMock, "ATYOU", 10001).
|
|
WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(int64(77)))
|
|
|
|
target, err := writer.ResolveCoinSeller(context.Background(), " agent001 ")
|
|
if err != nil {
|
|
t.Fatalf("resolve legacy coin seller failed: %v", err)
|
|
}
|
|
if target.UserID != 10001 || target.DisplayUserID != "agent001" || target.CountryID != 63 {
|
|
t.Fatalf("legacy coin seller target mismatch: %+v", target)
|
|
}
|
|
if err := appMock.ExpectationsWereMet(); err != nil {
|
|
t.Fatalf("app sql expectations: %v", err)
|
|
}
|
|
if err := walletMock.ExpectationsWereMet(); err != nil {
|
|
t.Fatalf("wallet sql expectations: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLegacyCoinSellerRechargeWriterResolveCoinSellerRejectsNonDealer(t *testing.T) {
|
|
appDB, appMock, cleanupApp := newLegacyWriterSQLMock(t)
|
|
defer cleanupApp()
|
|
walletDB, walletMock, cleanupWallet := newLegacyWriterSQLMock(t)
|
|
defer cleanupWallet()
|
|
writer := NewMySQLLegacyCoinSellerRechargeWriter(config.LegacyCoinSellerRechargeSourceConfig{
|
|
AppCode: "yumi",
|
|
SysOrigin: "LIKEI",
|
|
RequestTimeout: 5 * time.Second,
|
|
}, appDB, walletDB)
|
|
|
|
expectLegacyUserLookup(appMock, "LIKEI", "visitor001").
|
|
WillReturnRows(sqlmock.NewRows([]string{"id", "account", "country_id"}).AddRow(int64(20002), "visitor001", int64(86)))
|
|
expectLegacyCoinSellerCheck(walletMock, "LIKEI", 20002).
|
|
WillReturnError(sql.ErrNoRows)
|
|
|
|
_, err := writer.ResolveCoinSeller(context.Background(), "visitor001")
|
|
if !errors.Is(err, errLegacyCoinSellerNotFound) {
|
|
t.Fatalf("expected non-dealer legacy user to be rejected, got %v", err)
|
|
}
|
|
if err := appMock.ExpectationsWereMet(); err != nil {
|
|
t.Fatalf("app sql expectations: %v", err)
|
|
}
|
|
if err := walletMock.ExpectationsWereMet(); err != nil {
|
|
t.Fatalf("wallet sql expectations: %v", err)
|
|
}
|
|
}
|
|
|
|
func expectLegacyBalanceLock(mock sqlmock.Sqlmock, sysOrigin string, userID int64) *sqlmock.ExpectedQuery {
|
|
return mock.ExpectQuery(`(?s)SELECT id, CAST\(COALESCE\(earn_points, 0\) AS CHAR\), CAST\(COALESCE\(consumption_points, 0\) AS CHAR\)\s+FROM user_freight_balance\s+WHERE sys_origin = \? AND user_id = \?.*FOR UPDATE`).
|
|
WithArgs(sysOrigin, userID)
|
|
}
|
|
|
|
func expectLegacyUserLookup(mock sqlmock.Sqlmock, sysOrigin string, keyword string) *sqlmock.ExpectedQuery {
|
|
return mock.ExpectQuery(`(?s)SELECT id, account, COALESCE\(country_id, 0\)\s+FROM user_base_info\s+WHERE origin_sys = \? AND \(account = \? OR CAST\(id AS CHAR\) = \?\).*LIMIT 1`).
|
|
WithArgs(sysOrigin, keyword, keyword)
|
|
}
|
|
|
|
func expectLegacyCoinSellerCheck(mock sqlmock.Sqlmock, sysOrigin string, userID int64) *sqlmock.ExpectedQuery {
|
|
return mock.ExpectQuery(`(?s)SELECT id\s+FROM user_freight_balance\s+WHERE sys_origin = \? AND user_id = \? AND COALESCE\(is_close, 0\) = 0 AND \(COALESCE\(dealer, 0\) = 1 OR COALESCE\(super_dealer, 0\) = 1\)\s+LIMIT 1`).
|
|
WithArgs(sysOrigin, userID)
|
|
}
|
|
|
|
func newLegacyWriterSQLMock(t *testing.T) (*sql.DB, sqlmock.Sqlmock, func()) {
|
|
t.Helper()
|
|
db, mock, err := sqlmock.New()
|
|
if err != nil {
|
|
t.Fatalf("new sqlmock: %v", err)
|
|
}
|
|
return db, mock, func() {
|
|
_ = db.Close()
|
|
}
|
|
}
|