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