Files
exchange_go/services/binanceservice/integration_test_optimized.go
2025-08-27 14:54:03 +08:00

587 lines
18 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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("基准测试需要数据库连接")
}