Restructured project for V2 refactor: **Structure Changes:** - Moved all V1 code to orig/ folder (preserved with git mv) - Created docs/planning/ directory - Added orig/README_V1.md explaining V1 preservation **Planning Documents:** - 00_V2_MASTER_PLAN.md: Complete architecture overview - Executive summary of critical V1 issues - High-level component architecture diagrams - 5-phase implementation roadmap - Success metrics and risk mitigation - 07_TASK_BREAKDOWN.md: Atomic task breakdown - 99+ hours of detailed tasks - Every task < 2 hours (atomic) - Clear dependencies and success criteria - Organized by implementation phase **V2 Key Improvements:** - Per-exchange parsers (factory pattern) - Multi-layer strict validation - Multi-index pool cache - Background validation pipeline - Comprehensive observability **Critical Issues Addressed:** - Zero address tokens (strict validation + cache enrichment) - Parsing accuracy (protocol-specific parsers) - No audit trail (background validation channel) - Inefficient lookups (multi-index cache) - Stats disconnection (event-driven metrics) Next Steps: 1. Review planning documents 2. Begin Phase 1: Foundation (P1-001 through P1-010) 3. Implement parsers in Phase 2 4. Build cache system in Phase 3 5. Add validation pipeline in Phase 4 6. Migrate and test in Phase 5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
587 lines
17 KiB
Go
587 lines
17 KiB
Go
package security
|
|
|
|
import (
|
|
"encoding/json"
|
|
"runtime"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/fraktal/mev-beta/internal/logger"
|
|
)
|
|
|
|
func TestNewPerformanceProfiler(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
|
|
// Test with default config
|
|
profiler := NewPerformanceProfiler(testLogger, nil)
|
|
assert.NotNil(t, profiler)
|
|
assert.NotNil(t, profiler.config)
|
|
assert.Equal(t, time.Second, profiler.config.SamplingInterval)
|
|
assert.Equal(t, 24*time.Hour, profiler.config.RetentionPeriod)
|
|
|
|
// Test with custom config
|
|
customConfig := &ProfilerConfig{
|
|
SamplingInterval: 500 * time.Millisecond,
|
|
RetentionPeriod: 12 * time.Hour,
|
|
MaxOperations: 500,
|
|
MaxMemoryUsage: 512 * 1024 * 1024,
|
|
MaxGoroutines: 500,
|
|
MaxResponseTime: 500 * time.Millisecond,
|
|
MinThroughput: 50,
|
|
EnableGCMetrics: false,
|
|
EnableCPUProfiling: false,
|
|
EnableMemProfiling: false,
|
|
ReportInterval: 30 * time.Minute,
|
|
AutoOptimize: true,
|
|
}
|
|
|
|
profiler2 := NewPerformanceProfiler(testLogger, customConfig)
|
|
assert.NotNil(t, profiler2)
|
|
assert.Equal(t, 500*time.Millisecond, profiler2.config.SamplingInterval)
|
|
assert.Equal(t, 12*time.Hour, profiler2.config.RetentionPeriod)
|
|
assert.True(t, profiler2.config.AutoOptimize)
|
|
|
|
// Cleanup
|
|
profiler.Stop()
|
|
profiler2.Stop()
|
|
}
|
|
|
|
func TestOperationTracking(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
profiler := NewPerformanceProfiler(testLogger, nil)
|
|
defer profiler.Stop()
|
|
|
|
// Test basic operation tracking
|
|
tracker := profiler.StartOperation("test_operation")
|
|
time.Sleep(10 * time.Millisecond) // Simulate work
|
|
tracker.End()
|
|
|
|
// Verify operation was recorded
|
|
profiler.mutex.RLock()
|
|
profile, exists := profiler.operations["test_operation"]
|
|
profiler.mutex.RUnlock()
|
|
|
|
assert.True(t, exists)
|
|
assert.Equal(t, "test_operation", profile.Operation)
|
|
assert.Equal(t, int64(1), profile.TotalCalls)
|
|
assert.Greater(t, profile.TotalDuration, time.Duration(0))
|
|
assert.Greater(t, profile.AverageTime, time.Duration(0))
|
|
assert.Equal(t, 0.0, profile.ErrorRate)
|
|
assert.NotEmpty(t, profile.PerformanceClass)
|
|
}
|
|
|
|
func TestOperationTrackingWithError(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
profiler := NewPerformanceProfiler(testLogger, nil)
|
|
defer profiler.Stop()
|
|
|
|
// Test operation tracking with error
|
|
tracker := profiler.StartOperation("error_operation")
|
|
time.Sleep(5 * time.Millisecond)
|
|
tracker.EndWithError(assert.AnError)
|
|
|
|
// Verify error was recorded
|
|
profiler.mutex.RLock()
|
|
profile, exists := profiler.operations["error_operation"]
|
|
profiler.mutex.RUnlock()
|
|
|
|
assert.True(t, exists)
|
|
assert.Equal(t, int64(1), profile.ErrorCount)
|
|
assert.Equal(t, 100.0, profile.ErrorRate)
|
|
assert.Equal(t, assert.AnError.Error(), profile.LastError)
|
|
assert.False(t, profile.LastErrorTime.IsZero())
|
|
}
|
|
|
|
func TestPerformanceClassification(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
profiler := NewPerformanceProfiler(testLogger, nil)
|
|
defer profiler.Stop()
|
|
|
|
testCases := []struct {
|
|
name string
|
|
sleepDuration time.Duration
|
|
expectedClass string
|
|
}{
|
|
{"excellent", 1 * time.Millisecond, "excellent"},
|
|
{"good", 20 * time.Millisecond, "good"},
|
|
{"average", 100 * time.Millisecond, "average"},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
tracker := profiler.StartOperation(tc.name)
|
|
time.Sleep(tc.sleepDuration)
|
|
tracker.End()
|
|
|
|
profiler.mutex.RLock()
|
|
profile := profiler.operations[tc.name]
|
|
profiler.mutex.RUnlock()
|
|
|
|
assert.Equal(t, tc.expectedClass, profile.PerformanceClass)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSystemMetricsCollection(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
config := &ProfilerConfig{
|
|
SamplingInterval: 100 * time.Millisecond,
|
|
RetentionPeriod: time.Hour,
|
|
}
|
|
profiler := NewPerformanceProfiler(testLogger, config)
|
|
defer profiler.Stop()
|
|
|
|
// Wait for metrics collection
|
|
time.Sleep(200 * time.Millisecond)
|
|
|
|
profiler.mutex.RLock()
|
|
metrics := profiler.metrics
|
|
resourceUsage := profiler.resourceUsage
|
|
profiler.mutex.RUnlock()
|
|
|
|
// Verify system metrics were collected
|
|
assert.NotNil(t, metrics["heap_alloc"])
|
|
assert.NotNil(t, metrics["heap_sys"])
|
|
assert.NotNil(t, metrics["goroutines"])
|
|
assert.NotNil(t, metrics["gc_cycles"])
|
|
|
|
// Verify resource usage was updated
|
|
assert.Greater(t, resourceUsage.HeapUsed, uint64(0))
|
|
assert.GreaterOrEqual(t, resourceUsage.GCCycles, uint32(0))
|
|
assert.False(t, resourceUsage.Timestamp.IsZero())
|
|
}
|
|
|
|
func TestPerformanceAlerts(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
config := &ProfilerConfig{
|
|
SamplingInterval: time.Second,
|
|
MaxResponseTime: 10 * time.Millisecond, // Very low threshold for testing
|
|
}
|
|
profiler := NewPerformanceProfiler(testLogger, config)
|
|
defer profiler.Stop()
|
|
|
|
// Trigger a slow operation to generate alert
|
|
tracker := profiler.StartOperation("slow_operation")
|
|
time.Sleep(50 * time.Millisecond) // Exceeds threshold
|
|
tracker.End()
|
|
|
|
// Check if alert was generated
|
|
profiler.mutex.RLock()
|
|
alerts := profiler.alerts
|
|
profiler.mutex.RUnlock()
|
|
|
|
assert.NotEmpty(t, alerts)
|
|
|
|
foundAlert := false
|
|
for _, alert := range alerts {
|
|
if alert.Operation == "slow_operation" && alert.Type == "response_time" {
|
|
foundAlert = true
|
|
assert.Contains(t, []string{"warning", "critical"}, alert.Severity)
|
|
assert.Greater(t, alert.Value, 10.0) // Should exceed 10ms threshold
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, foundAlert, "Expected to find response time alert for slow operation")
|
|
}
|
|
|
|
func TestReportGeneration(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
profiler := NewPerformanceProfiler(testLogger, nil)
|
|
defer profiler.Stop()
|
|
|
|
// Generate some test data
|
|
tracker1 := profiler.StartOperation("fast_op")
|
|
time.Sleep(1 * time.Millisecond)
|
|
tracker1.End()
|
|
|
|
tracker2 := profiler.StartOperation("slow_op")
|
|
time.Sleep(50 * time.Millisecond)
|
|
tracker2.End()
|
|
|
|
// Generate report
|
|
report, err := profiler.GenerateReport()
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, report)
|
|
|
|
// Verify report structure
|
|
assert.NotEmpty(t, report.ID)
|
|
assert.False(t, report.Timestamp.IsZero())
|
|
assert.NotEmpty(t, report.OverallHealth)
|
|
assert.GreaterOrEqual(t, report.HealthScore, 0.0)
|
|
assert.LessOrEqual(t, report.HealthScore, 100.0)
|
|
|
|
// Verify operations are included
|
|
assert.NotEmpty(t, report.TopOperations)
|
|
assert.NotNil(t, report.ResourceSummary)
|
|
assert.NotNil(t, report.TrendAnalysis)
|
|
assert.NotNil(t, report.OptimizationPlan)
|
|
|
|
// Verify resource summary
|
|
assert.GreaterOrEqual(t, report.ResourceSummary.MemoryEfficiency, 0.0)
|
|
assert.LessOrEqual(t, report.ResourceSummary.MemoryEfficiency, 100.0)
|
|
assert.GreaterOrEqual(t, report.ResourceSummary.CPUEfficiency, 0.0)
|
|
assert.LessOrEqual(t, report.ResourceSummary.CPUEfficiency, 100.0)
|
|
}
|
|
|
|
func TestBottleneckAnalysis(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
profiler := NewPerformanceProfiler(testLogger, nil)
|
|
defer profiler.Stop()
|
|
|
|
// Create operations with different performance characteristics
|
|
tracker1 := profiler.StartOperation("critical_op")
|
|
time.Sleep(200 * time.Millisecond) // This should be classified as poor/critical
|
|
tracker1.End()
|
|
|
|
tracker2 := profiler.StartOperation("good_op")
|
|
time.Sleep(1 * time.Millisecond) // This should be excellent
|
|
tracker2.End()
|
|
|
|
// Generate report to trigger bottleneck analysis
|
|
report, err := profiler.GenerateReport()
|
|
require.NoError(t, err)
|
|
|
|
// Should detect performance bottleneck for critical_op
|
|
assert.NotEmpty(t, report.Bottlenecks)
|
|
|
|
foundBottleneck := false
|
|
for _, bottleneck := range report.Bottlenecks {
|
|
if bottleneck.Operation == "critical_op" || bottleneck.Type == "performance" {
|
|
foundBottleneck = true
|
|
assert.Contains(t, []string{"medium", "high"}, bottleneck.Severity)
|
|
assert.Greater(t, bottleneck.Impact, 0.0)
|
|
break
|
|
}
|
|
}
|
|
|
|
// Note: May not always find bottleneck due to classification thresholds
|
|
if !foundBottleneck {
|
|
t.Log("Bottleneck not detected - this may be due to classification thresholds")
|
|
}
|
|
}
|
|
|
|
func TestImprovementSuggestions(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
profiler := NewPerformanceProfiler(testLogger, nil)
|
|
defer profiler.Stop()
|
|
|
|
// Simulate memory pressure by allocating memory
|
|
largeData := make([]byte, 100*1024*1024) // 100MB
|
|
_ = largeData
|
|
|
|
// Force GC to update memory stats
|
|
runtime.GC()
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
// Create a slow operation
|
|
tracker := profiler.StartOperation("slow_operation")
|
|
time.Sleep(300 * time.Millisecond) // Should be classified as poor/critical
|
|
tracker.End()
|
|
|
|
// Generate report
|
|
report, err := profiler.GenerateReport()
|
|
require.NoError(t, err)
|
|
|
|
// Should have improvement suggestions
|
|
assert.NotNil(t, report.Improvements)
|
|
|
|
// Look for memory or performance improvements
|
|
hasMemoryImprovement := false
|
|
hasPerformanceImprovement := false
|
|
|
|
for _, suggestion := range report.Improvements {
|
|
if suggestion.Area == "memory" {
|
|
hasMemoryImprovement = true
|
|
}
|
|
if suggestion.Area == "operation_slow_operation" {
|
|
hasPerformanceImprovement = true
|
|
}
|
|
}
|
|
|
|
// At least one type of improvement should be suggested
|
|
assert.True(t, hasMemoryImprovement || hasPerformanceImprovement,
|
|
"Expected memory or performance improvement suggestions")
|
|
}
|
|
|
|
func TestMetricsExport(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
profiler := NewPerformanceProfiler(testLogger, nil)
|
|
defer profiler.Stop()
|
|
|
|
// Wait for some metrics to be collected
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
// Test JSON export
|
|
jsonData, err := profiler.ExportMetrics("json")
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, jsonData)
|
|
|
|
// Verify it's valid JSON
|
|
var metrics map[string]*PerformanceMetric
|
|
err = json.Unmarshal(jsonData, &metrics)
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, metrics)
|
|
|
|
// Test Prometheus export
|
|
promData, err := profiler.ExportMetrics("prometheus")
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, promData)
|
|
assert.Contains(t, string(promData), "# HELP")
|
|
assert.Contains(t, string(promData), "# TYPE")
|
|
assert.Contains(t, string(promData), "mev_bot_")
|
|
|
|
// Test unsupported format
|
|
_, err = profiler.ExportMetrics("unsupported")
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "unsupported export format")
|
|
}
|
|
|
|
func TestThresholdConfiguration(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
profiler := NewPerformanceProfiler(testLogger, nil)
|
|
defer profiler.Stop()
|
|
|
|
// Verify default thresholds were set
|
|
profiler.mutex.RLock()
|
|
thresholds := profiler.thresholds
|
|
profiler.mutex.RUnlock()
|
|
|
|
assert.NotEmpty(t, thresholds)
|
|
assert.Contains(t, thresholds, "memory_usage")
|
|
assert.Contains(t, thresholds, "goroutine_count")
|
|
assert.Contains(t, thresholds, "response_time")
|
|
assert.Contains(t, thresholds, "error_rate")
|
|
|
|
// Verify threshold structure
|
|
memThreshold := thresholds["memory_usage"]
|
|
assert.Equal(t, "memory_usage", memThreshold.Metric)
|
|
assert.Greater(t, memThreshold.Warning, 0.0)
|
|
assert.Greater(t, memThreshold.Critical, memThreshold.Warning)
|
|
assert.Equal(t, "gt", memThreshold.Operator)
|
|
}
|
|
|
|
func TestResourceEfficiencyCalculation(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
profiler := NewPerformanceProfiler(testLogger, nil)
|
|
defer profiler.Stop()
|
|
|
|
// Create operations with different performance classes
|
|
tracker1 := profiler.StartOperation("excellent_op")
|
|
time.Sleep(1 * time.Millisecond)
|
|
tracker1.End()
|
|
|
|
tracker2 := profiler.StartOperation("good_op")
|
|
time.Sleep(20 * time.Millisecond)
|
|
tracker2.End()
|
|
|
|
// Calculate efficiencies
|
|
memEfficiency := profiler.calculateMemoryEfficiency()
|
|
cpuEfficiency := profiler.calculateCPUEfficiency()
|
|
gcEfficiency := profiler.calculateGCEfficiency()
|
|
throughputScore := profiler.calculateThroughputScore()
|
|
|
|
// All efficiency scores should be between 0 and 100
|
|
assert.GreaterOrEqual(t, memEfficiency, 0.0)
|
|
assert.LessOrEqual(t, memEfficiency, 100.0)
|
|
assert.GreaterOrEqual(t, cpuEfficiency, 0.0)
|
|
assert.LessOrEqual(t, cpuEfficiency, 100.0)
|
|
assert.GreaterOrEqual(t, gcEfficiency, 0.0)
|
|
assert.LessOrEqual(t, gcEfficiency, 100.0)
|
|
assert.GreaterOrEqual(t, throughputScore, 0.0)
|
|
assert.LessOrEqual(t, throughputScore, 100.0)
|
|
|
|
// CPU efficiency should be high since we have good operations
|
|
assert.Greater(t, cpuEfficiency, 50.0)
|
|
}
|
|
|
|
func TestCleanupOldData(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
config := &ProfilerConfig{
|
|
RetentionPeriod: 100 * time.Millisecond, // Very short for testing
|
|
}
|
|
profiler := NewPerformanceProfiler(testLogger, config)
|
|
defer profiler.Stop()
|
|
|
|
// Create some alerts
|
|
profiler.mutex.Lock()
|
|
oldAlert := PerformanceAlert{
|
|
ID: "old_alert",
|
|
Timestamp: time.Now().Add(-200 * time.Millisecond), // Older than retention
|
|
}
|
|
newAlert := PerformanceAlert{
|
|
ID: "new_alert",
|
|
Timestamp: time.Now(),
|
|
}
|
|
profiler.alerts = []PerformanceAlert{oldAlert, newAlert}
|
|
profiler.mutex.Unlock()
|
|
|
|
// Trigger cleanup
|
|
profiler.cleanupOldData()
|
|
|
|
// Verify old data was removed
|
|
profiler.mutex.RLock()
|
|
alerts := profiler.alerts
|
|
profiler.mutex.RUnlock()
|
|
|
|
assert.Len(t, alerts, 1)
|
|
assert.Equal(t, "new_alert", alerts[0].ID)
|
|
}
|
|
|
|
func TestOptimizationPlanGeneration(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
profiler := NewPerformanceProfiler(testLogger, nil)
|
|
defer profiler.Stop()
|
|
|
|
// Create test recommendations
|
|
recommendations := []PerformanceRecommendation{
|
|
{
|
|
Type: "immediate",
|
|
Priority: "high",
|
|
Category: "memory",
|
|
Title: "Fix Memory Leak",
|
|
ExpectedGain: 25.0,
|
|
},
|
|
{
|
|
Type: "short_term",
|
|
Priority: "medium",
|
|
Category: "algorithm",
|
|
Title: "Optimize Algorithm",
|
|
ExpectedGain: 40.0,
|
|
},
|
|
{
|
|
Type: "long_term",
|
|
Priority: "low",
|
|
Category: "architecture",
|
|
Title: "Refactor Architecture",
|
|
ExpectedGain: 15.0,
|
|
},
|
|
}
|
|
|
|
// Generate optimization plan
|
|
plan := profiler.createOptimizationPlan(recommendations)
|
|
|
|
assert.NotNil(t, plan)
|
|
assert.Equal(t, 80.0, plan.TotalGain) // 25 + 40 + 15
|
|
assert.Greater(t, plan.Timeline, time.Duration(0))
|
|
|
|
// Verify phase categorization
|
|
assert.Len(t, plan.Phase1, 1) // immediate
|
|
assert.Len(t, plan.Phase2, 1) // short_term
|
|
assert.Len(t, plan.Phase3, 1) // long_term
|
|
|
|
assert.Equal(t, "Fix Memory Leak", plan.Phase1[0].Title)
|
|
assert.Equal(t, "Optimize Algorithm", plan.Phase2[0].Title)
|
|
assert.Equal(t, "Refactor Architecture", plan.Phase3[0].Title)
|
|
}
|
|
|
|
func TestConcurrentOperationTracking(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
profiler := NewPerformanceProfiler(testLogger, nil)
|
|
defer profiler.Stop()
|
|
|
|
// Run multiple operations concurrently
|
|
numOperations := 100
|
|
done := make(chan bool, numOperations)
|
|
|
|
for i := 0; i < numOperations; i++ {
|
|
go func(id int) {
|
|
defer func() { done <- true }()
|
|
|
|
tracker := profiler.StartOperation("concurrent_op")
|
|
time.Sleep(1 * time.Millisecond)
|
|
tracker.End()
|
|
}(i)
|
|
}
|
|
|
|
// Wait for all operations to complete
|
|
for i := 0; i < numOperations; i++ {
|
|
<-done
|
|
}
|
|
|
|
// Verify all operations were tracked
|
|
profiler.mutex.RLock()
|
|
profile := profiler.operations["concurrent_op"]
|
|
profiler.mutex.RUnlock()
|
|
|
|
assert.NotNil(t, profile)
|
|
assert.Equal(t, int64(numOperations), profile.TotalCalls)
|
|
assert.Greater(t, profile.TotalDuration, time.Duration(0))
|
|
assert.Equal(t, 0.0, profile.ErrorRate) // No errors expected
|
|
}
|
|
|
|
func BenchmarkOperationTracking(b *testing.B) {
|
|
testLogger := logger.New("error", "text", "/tmp/test.log") // Reduce logging noise
|
|
profiler := NewPerformanceProfiler(testLogger, nil)
|
|
defer profiler.Stop()
|
|
|
|
b.ResetTimer()
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
tracker := profiler.StartOperation("benchmark_op")
|
|
// Simulate minimal work
|
|
runtime.Gosched()
|
|
tracker.End()
|
|
}
|
|
})
|
|
}
|
|
|
|
func BenchmarkReportGeneration(b *testing.B) {
|
|
testLogger := logger.New("error", "text", "/tmp/test.log")
|
|
profiler := NewPerformanceProfiler(testLogger, nil)
|
|
defer profiler.Stop()
|
|
|
|
// Create some sample data
|
|
for i := 0; i < 10; i++ {
|
|
tracker := profiler.StartOperation("sample_op")
|
|
time.Sleep(time.Microsecond)
|
|
tracker.End()
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := profiler.GenerateReport()
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHealthScoreCalculation(t *testing.T) {
|
|
testLogger := logger.New("info", "text", "/tmp/test.log")
|
|
profiler := NewPerformanceProfiler(testLogger, nil)
|
|
defer profiler.Stop()
|
|
|
|
// Test with clean system (should have high health score)
|
|
health, score := profiler.calculateOverallHealth()
|
|
assert.NotEmpty(t, health)
|
|
assert.GreaterOrEqual(t, score, 0.0)
|
|
assert.LessOrEqual(t, score, 100.0)
|
|
assert.Equal(t, "excellent", health) // Should be excellent with no issues
|
|
|
|
// Add some performance issues
|
|
profiler.mutex.Lock()
|
|
profiler.operations["poor_op"] = &OperationProfile{
|
|
Operation: "poor_op",
|
|
PerformanceClass: "poor",
|
|
}
|
|
profiler.operations["critical_op"] = &OperationProfile{
|
|
Operation: "critical_op",
|
|
PerformanceClass: "critical",
|
|
}
|
|
profiler.alerts = append(profiler.alerts, PerformanceAlert{
|
|
Severity: "warning",
|
|
})
|
|
profiler.alerts = append(profiler.alerts, PerformanceAlert{
|
|
Severity: "critical",
|
|
})
|
|
profiler.mutex.Unlock()
|
|
|
|
// Recalculate health
|
|
health2, score2 := profiler.calculateOverallHealth()
|
|
assert.Less(t, score2, score) // Score should be lower with issues
|
|
assert.NotEqual(t, "excellent", health2) // Should not be excellent anymore
|
|
}
|