Files
exchange_go/services/binanceservice/integration_test_optimized.go

587 lines
18 KiB
Go
Raw Normal View History

2025-08-27 14:54:03 +08:00
package binanceservice
import (
"context"
"fmt"
"sync"
"testing"
"time"
"github.com/go-admin-team/go-admin-core/logger"
"github.com/go-redis/redis/v8"
"github.com/shopspring/decimal"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// OptimizedSystemTestSuite 优化系统测试套件
type OptimizedSystemTestSuite struct {
suite.Suite
db *gorm.DB
redisClient *redis.Client
config *OptimizedConfig
metricsCollector *MetricsCollector
errorHandler *ErrorHandler
lockManager *LockManager
txManager *TransactionManager
reverseService *ReverseServiceOptimized
}
// SetupSuite 设置测试套件
func (suite *OptimizedSystemTestSuite) SetupSuite() {
// 初始化数据库连接(测试环境)
dsn := "root:password@tcp(localhost:3306)/test_exchange?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
suite.T().Skip("跳过集成测试:无法连接数据库")
return
}
suite.db = db
// 初始化Redis连接测试环境
suite.redisClient = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 1, // 使用测试数据库
})
// 测试Redis连接
ctx := context.Background()
if err := suite.redisClient.Ping(ctx).Err(); err != nil {
suite.T().Skip("跳过集成测试无法连接Redis")
return
}
// 初始化配置
suite.config = GetDefaultOptimizedConfig()
suite.config.MonitorConfig.Enabled = true
// 初始化组件
suite.metricsCollector = NewMetricsCollector(suite.config)
suite.errorHandler = NewErrorHandler(suite.config, suite.metricsCollector)
suite.lockManager = NewLockManager(suite.config, suite.redisClient, suite.metricsCollector)
suite.txManager = NewTransactionManager(suite.db, suite.metricsCollector, suite.config)
// 启动指标收集
suite.metricsCollector.Start()
// 创建测试表
suite.createTestTables()
}
// TearDownSuite 清理测试套件
func (suite *OptimizedSystemTestSuite) TearDownSuite() {
if suite.metricsCollector != nil {
suite.metricsCollector.Stop()
}
if suite.redisClient != nil {
suite.redisClient.Close()
}
}
// SetupTest 设置每个测试
func (suite *OptimizedSystemTestSuite) SetupTest() {
// 清理Redis测试数据
ctx := context.Background()
suite.redisClient.FlushDB(ctx)
// 清理数据库测试数据
suite.db.Exec("DELETE FROM test_orders")
suite.db.Exec("DELETE FROM test_positions")
}
// createTestTables 创建测试表
func (suite *OptimizedSystemTestSuite) createTestTables() {
// 创建测试订单表
suite.db.Exec(`
CREATE TABLE IF NOT EXISTS test_orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_id VARCHAR(100) UNIQUE NOT NULL,
user_id VARCHAR(50) NOT NULL,
symbol VARCHAR(20) NOT NULL,
side VARCHAR(10) NOT NULL,
quantity DECIMAL(20,8) NOT NULL,
price DECIMAL(20,8),
status VARCHAR(20) NOT NULL,
version INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user_symbol (user_id, symbol),
INDEX idx_status (status),
INDEX idx_version (version)
)
`)
// 创建测试持仓表
suite.db.Exec(`
CREATE TABLE IF NOT EXISTS test_positions (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id VARCHAR(50) NOT NULL,
symbol VARCHAR(20) NOT NULL,
side VARCHAR(10) NOT NULL,
quantity DECIMAL(20,8) NOT NULL,
avg_price DECIMAL(20,8),
status VARCHAR(20) NOT NULL,
version INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_user_symbol_side (user_id, symbol, side),
INDEX idx_version (version)
)
`)
}
// TestLockManager 测试锁管理器
func (suite *OptimizedSystemTestSuite) TestLockManager() {
ctx := context.Background()
// 测试分布式锁
suite.Run("DistributedLock", func() {
config := LockManagerConfig{
Type: LockTypeDistributed,
Scope: ScopeOrder,
Key: "test_order_123",
Expiration: 10 * time.Second,
Timeout: 5 * time.Second,
RetryDelay: 100 * time.Millisecond,
}
// 获取锁
lock, err := suite.lockManager.AcquireLock(ctx, config)
assert.NoError(suite.T(), err)
assert.NotNil(suite.T(), lock)
assert.True(suite.T(), lock.IsLocked())
// 尝试获取同一个锁(应该失败)
config.Timeout = 1 * time.Second
_, err = suite.lockManager.AcquireLock(ctx, config)
assert.Error(suite.T(), err)
// 释放锁
err = suite.lockManager.ReleaseLock(ctx, lock)
assert.NoError(suite.T(), err)
assert.False(suite.T(), lock.IsLocked())
})
// 测试本地锁
suite.Run("LocalLock", func() {
config := LockManagerConfig{
Type: LockTypeLocal,
Scope: ScopeUser,
Key: "test_user_456",
Expiration: 10 * time.Second,
Timeout: 5 * time.Second,
RetryDelay: 100 * time.Millisecond,
}
lock, err := suite.lockManager.AcquireLock(ctx, config)
assert.NoError(suite.T(), err)
assert.NotNil(suite.T(), lock)
assert.True(suite.T(), lock.IsLocked())
err = suite.lockManager.ReleaseLock(ctx, lock)
assert.NoError(suite.T(), err)
})
// 测试并发锁
suite.Run("ConcurrentLocks", func() {
var wg sync.WaitGroup
successCount := 0
var mu sync.Mutex
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
config := LockManagerConfig{
Type: LockTypeDistributed,
Scope: ScopeGlobal,
Key: "concurrent_test",
Expiration: 2 * time.Second,
Timeout: 1 * time.Second,
RetryDelay: 50 * time.Millisecond,
}
lock, err := suite.lockManager.AcquireLock(ctx, config)
if err == nil {
mu.Lock()
successCount++
mu.Unlock()
time.Sleep(100 * time.Millisecond)
suite.lockManager.ReleaseLock(ctx, lock)
}
}(i)
}
wg.Wait()
assert.Equal(suite.T(), 1, successCount, "只有一个goroutine应该获得锁")
})
}
// TestTransactionManager 测试事务管理器
func (suite *OptimizedSystemTestSuite) TestTransactionManager() {
ctx := context.Background()
// 测试基本事务
suite.Run("BasicTransaction", func() {
config := GetDefaultTransactionConfig()
result := suite.txManager.WithTransaction(ctx, config, func(txCtx *TransactionContext) error {
// 插入测试数据
return txCtx.Tx.Exec(`
INSERT INTO test_orders (order_id, user_id, symbol, side, quantity, status)
VALUES (?, ?, ?, ?, ?, ?)
`, "order_001", "user_001", "BTCUSDT", "BUY", 1.0, "NEW").Error
})
assert.True(suite.T(), result.Success)
assert.NoError(suite.T(), result.Error)
// 验证数据已插入
var count int64
suite.db.Raw("SELECT COUNT(*) FROM test_orders WHERE order_id = ?", "order_001").Scan(&count)
assert.Equal(suite.T(), int64(1), count)
})
// 测试事务回滚
suite.Run("TransactionRollback", func() {
config := GetDefaultTransactionConfig()
result := suite.txManager.WithTransaction(ctx, config, func(txCtx *TransactionContext) error {
// 插入数据
if err := txCtx.Tx.Exec(`
INSERT INTO test_orders (order_id, user_id, symbol, side, quantity, status)
VALUES (?, ?, ?, ?, ?, ?)
`, "order_002", "user_002", "ETHUSDT", "SELL", 2.0, "NEW").Error; err != nil {
return err
}
// 故意返回错误以触发回滚
return fmt.Errorf("测试回滚")
})
assert.False(suite.T(), result.Success)
assert.Error(suite.T(), result.Error)
// 验证数据未插入
var count int64
suite.db.Raw("SELECT COUNT(*) FROM test_orders WHERE order_id = ?", "order_002").Scan(&count)
assert.Equal(suite.T(), int64(0), count)
})
// 测试乐观锁
suite.Run("OptimisticLock", func() {
// 先插入测试数据
suite.db.Exec(`
INSERT INTO test_positions (user_id, symbol, side, quantity, avg_price, status, version)
VALUES (?, ?, ?, ?, ?, ?, ?)
`, "user_003", "BTCUSDT", "LONG", 1.0, 50000.0, "OPEN", 0)
// 测试正常更新
updates := map[string]interface{}{
"quantity": 2.0,
}
err := suite.txManager.UpdateWithOptimisticLock(ctx, nil, updates,
"user_id = ? AND symbol = ? AND side = ? AND version = ?",
"user_003", "BTCUSDT", "LONG", 0)
assert.NoError(suite.T(), err)
// 测试版本冲突
updates["quantity"] = 3.0
err = suite.txManager.UpdateWithOptimisticLock(ctx, nil, updates,
"user_id = ? AND symbol = ? AND side = ? AND version = ?",
"user_003", "BTCUSDT", "LONG", 0) // 使用旧版本号
assert.Error(suite.T(), err)
assert.Contains(suite.T(), err.Error(), "乐观锁冲突")
})
}
// TestErrorHandler 测试错误处理器
func (suite *OptimizedSystemTestSuite) TestErrorHandler() {
ctx := context.Background()
// 测试错误分类
suite.Run("ErrorClassification", func() {
testCases := []struct {
error error
expected ErrorType
}{
{fmt.Errorf("connection refused"), ErrorTypeNetwork},
{fmt.Errorf("database connection failed"), ErrorTypeDatabase},
{fmt.Errorf("context deadline exceeded"), ErrorTypeTimeout},
{fmt.Errorf("too many requests"), ErrorTypeRateLimit},
{fmt.Errorf("unauthorized access"), ErrorTypeAuth},
{fmt.Errorf("invalid parameter"), ErrorTypeValidation},
{fmt.Errorf("insufficient balance"), ErrorTypeBusiness},
}
for _, tc := range testCases {
errorInfo := suite.errorHandler.ClassifyError(tc.error, "test")
assert.Equal(suite.T(), tc.expected, errorInfo.Type)
}
})
// 测试重试机制
suite.Run("RetryMechanism", func() {
attempts := 0
config := RetryConfig{
MaxRetries: 3,
RetryDelay: 10 * time.Millisecond,
MaxRetryDelay: 100 * time.Millisecond,
BackoffFactor: 2.0,
ApiRetryCount: 3,
DbRetryCount: 3,
}
// 测试重试成功
err := suite.errorHandler.RetryWithBackoff(ctx, func() error {
attempts++
if attempts < 3 {
return fmt.Errorf("temporary error")
}
return nil
}, config, "test_retry")
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), 3, attempts)
// 测试重试失败
attempts = 0
err = suite.errorHandler.RetryWithBackoff(ctx, func() error {
attempts++
return fmt.Errorf("persistent error")
}, config, "test_retry_fail")
assert.Error(suite.T(), err)
assert.Equal(suite.T(), 3, attempts)
})
}
// TestMetricsCollector 测试指标收集器
func (suite *OptimizedSystemTestSuite) TestMetricsCollector() {
// 测试订单指标
suite.Run("OrderMetrics", func() {
// 记录订单处理
suite.metricsCollector.RecordOrderProcessed(true, 100*time.Millisecond)
suite.metricsCollector.RecordOrderProcessed(false, 200*time.Millisecond)
suite.metricsCollector.RecordOrderCanceled()
suite.metricsCollector.RecordReverseOrder(true)
metrics := suite.metricsCollector.GetMetrics()
orderMetrics := metrics["order_metrics"].(*OrderMetrics)
assert.Equal(suite.T(), int64(2), orderMetrics.TotalProcessed)
assert.Equal(suite.T(), int64(1), orderMetrics.SuccessfulOrders)
assert.Equal(suite.T(), int64(1), orderMetrics.FailedOrders)
assert.Equal(suite.T(), int64(1), orderMetrics.CanceledOrders)
assert.Equal(suite.T(), int64(1), orderMetrics.ReverseOrdersCreated)
})
// 测试持仓指标
suite.Run("PositionMetrics", func() {
difference := decimal.NewFromFloat(0.1)
suite.metricsCollector.RecordPositionSync(true, difference)
suite.metricsCollector.RecordPositionSync(false, decimal.Zero)
suite.metricsCollector.RecordClosePosition(false)
suite.metricsCollector.RecordClosePosition(true)
metrics := suite.metricsCollector.GetMetrics()
positionMetrics := metrics["position_metrics"].(*PositionMetrics)
assert.Equal(suite.T(), int64(2), positionMetrics.SyncAttempts)
assert.Equal(suite.T(), int64(1), positionMetrics.SyncSuccesses)
assert.Equal(suite.T(), int64(1), positionMetrics.SyncFailures)
assert.Equal(suite.T(), int64(2), positionMetrics.ClosePositions)
assert.Equal(suite.T(), int64(1), positionMetrics.ForceClosePositions)
})
// 测试性能指标
suite.Run("PerformanceMetrics", func() {
suite.metricsCollector.RecordLockOperation(true, 50*time.Millisecond)
suite.metricsCollector.RecordDbOperation(false, 30*time.Millisecond, nil)
suite.metricsCollector.RecordApiCall(100*time.Millisecond, nil, 1)
suite.metricsCollector.RecordConcurrency(5)
metrics := suite.metricsCollector.GetMetrics()
perfMetrics := metrics["performance_metrics"].(*PerformanceMetrics)
assert.Equal(suite.T(), int64(1), perfMetrics.LockAcquisitions)
assert.Equal(suite.T(), int64(1), perfMetrics.DbQueries)
assert.Equal(suite.T(), int64(1), perfMetrics.ApiCalls)
assert.Equal(suite.T(), int64(5), perfMetrics.ConcurrentRequests)
})
}
// TestConcurrentOperations 测试并发操作
func (suite *OptimizedSystemTestSuite) TestConcurrentOperations() {
ctx := context.Background()
suite.Run("ConcurrentOrderProcessing", func() {
var wg sync.WaitGroup
successCount := 0
var mu sync.Mutex
// 并发处理订单
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
// 使用锁保护订单处理
lockConfig := GetOrderLockConfig(fmt.Sprintf("order_%d", id))
err := suite.lockManager.WithLock(ctx, lockConfig, func() error {
// 模拟订单处理
config := GetDefaultTransactionConfig()
result := suite.txManager.WithTransaction(ctx, config, func(txCtx *TransactionContext) error {
return txCtx.Tx.Exec(`
INSERT INTO test_orders (order_id, user_id, symbol, side, quantity, status)
VALUES (?, ?, ?, ?, ?, ?)
`, fmt.Sprintf("concurrent_order_%d", id), fmt.Sprintf("user_%d", id),
"BTCUSDT", "BUY", 1.0, "NEW").Error
})
if result.Success {
mu.Lock()
successCount++
mu.Unlock()
}
return result.Error
})
if err != nil {
logger.Errorf("并发订单处理失败 [%d]: %v", id, err)
}
}(i)
}
wg.Wait()
assert.Equal(suite.T(), 10, successCount, "所有并发订单都应该成功处理")
// 验证数据库中的记录数
var count int64
suite.db.Raw("SELECT COUNT(*) FROM test_orders WHERE order_id LIKE 'concurrent_order_%'").Scan(&count)
assert.Equal(suite.T(), int64(10), count)
})
}
// TestSystemIntegration 测试系统集成
func (suite *OptimizedSystemTestSuite) TestSystemIntegration() {
ctx := context.Background()
suite.Run("CompleteOrderFlow", func() {
userID := "integration_user"
symbol := "BTCUSDT"
orderID := "integration_order_001"
// 步骤1创建订单使用锁和事务
err := WithOrderLock(ctx, orderID, func() error {
return WithDefaultTransaction(ctx, func(txCtx *TransactionContext) error {
return txCtx.Tx.Exec(`
INSERT INTO test_orders (order_id, user_id, symbol, side, quantity, price, status)
VALUES (?, ?, ?, ?, ?, ?, ?)
`, orderID, userID, symbol, "BUY", 1.0, 50000.0, "NEW").Error
})
})
assert.NoError(suite.T(), err)
// 步骤2更新订单状态使用乐观锁
updates := map[string]interface{}{
"status": "FILLED",
}
err = UpdateWithOptimisticLockGlobal(ctx, nil, updates,
"order_id = ? AND version = ?", orderID, 0)
assert.NoError(suite.T(), err)
// 步骤3创建持仓使用锁和事务
err = WithPositionLock(ctx, userID, symbol, func() error {
return WithDefaultTransaction(ctx, func(txCtx *TransactionContext) error {
return txCtx.Tx.Exec(`
INSERT INTO test_positions (user_id, symbol, side, quantity, avg_price, status)
VALUES (?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
quantity = quantity + VALUES(quantity),
avg_price = (avg_price * quantity + VALUES(avg_price) * VALUES(quantity)) / (quantity + VALUES(quantity)),
version = version + 1
`, userID, symbol, "LONG", 1.0, 50000.0, "OPEN").Error
})
})
assert.NoError(suite.T(), err)
// 验证最终状态
var orderStatus string
suite.db.Raw("SELECT status FROM test_orders WHERE order_id = ?", orderID).Scan(&orderStatus)
assert.Equal(suite.T(), "FILLED", orderStatus)
var positionQuantity float64
suite.db.Raw("SELECT quantity FROM test_positions WHERE user_id = ? AND symbol = ?",
userID, symbol).Scan(&positionQuantity)
assert.Equal(suite.T(), 1.0, positionQuantity)
})
}
// TestPerformance 测试性能
func (suite *OptimizedSystemTestSuite) TestPerformance() {
ctx := context.Background()
suite.Run("LockPerformance", func() {
startTime := time.Now()
iterations := 1000
for i := 0; i < iterations; i++ {
lockConfig := GetOrderLockConfig(fmt.Sprintf("perf_order_%d", i))
lock, err := suite.lockManager.AcquireLock(ctx, lockConfig)
assert.NoError(suite.T(), err)
suite.lockManager.ReleaseLock(ctx, lock)
}
duration := time.Since(startTime)
avgDuration := duration / time.Duration(iterations)
logger.Infof("锁性能测试: %d 次操作耗时 %v平均每次 %v", iterations, duration, avgDuration)
// 平均每次操作应该在合理范围内
assert.Less(suite.T(), avgDuration, 10*time.Millisecond)
})
suite.Run("TransactionPerformance", func() {
startTime := time.Now()
iterations := 100
for i := 0; i < iterations; i++ {
config := GetDefaultTransactionConfig()
result := suite.txManager.WithTransaction(ctx, config, func(txCtx *TransactionContext) error {
return txCtx.Tx.Exec(`
INSERT INTO test_orders (order_id, user_id, symbol, side, quantity, status)
VALUES (?, ?, ?, ?, ?, ?)
`, fmt.Sprintf("perf_order_%d", i), "perf_user", "BTCUSDT", "BUY", 1.0, "NEW").Error
})
assert.True(suite.T(), result.Success)
}
duration := time.Since(startTime)
avgDuration := duration / time.Duration(iterations)
logger.Infof("事务性能测试: %d 次操作耗时 %v平均每次 %v", iterations, duration, avgDuration)
// 平均每次事务应该在合理范围内
assert.Less(suite.T(), avgDuration, 100*time.Millisecond)
})
}
// TestSuite 运行测试套件
func TestOptimizedSystemSuite(t *testing.T) {
suite.Run(t, new(OptimizedSystemTestSuite))
}
// BenchmarkLockManager 锁管理器基准测试
func BenchmarkLockManager(b *testing.B) {
// 这里可以添加基准测试
b.Skip("基准测试需要Redis连接")
}
// BenchmarkTransactionManager 事务管理器基准测试
func BenchmarkTransactionManager(b *testing.B) {
// 这里可以添加基准测试
b.Skip("基准测试需要数据库连接")
}