package financial import ( "testing" ) func TestFinancialCalculator_NewCalculator(t *testing.T) { config := DefaultConfig() calc := NewCalculator(config) if calc == nil { t.Error("NewCalculator() returned nil") } } func TestFinancialCalculator_Calculate_Basic(t *testing.T) { config := DefaultConfig() calc := NewCalculator(config) data := &ResourceFlowData{ ResourceType: "heat", DistanceKm: 10.0, AnnualVolume: 1000.0, CostIn: 100.0, CostOut: 20.0, } assumptions := &EconomicAssumptions{ DiscountRate: 0.08, ProjectLifeYears: 10, } result, err := calc.Calculate(AnalysisTypeBasic, data, assumptions) if err != nil { t.Fatalf("Calculate() error = %v", err) } analysis, ok := result.(*EconomicAnalysis) if !ok { t.Error("Calculate() returned wrong type") } if analysis.AnalysisType != AnalysisTypeBasic { t.Errorf("AnalysisType = %v, want %v", analysis.AnalysisType, AnalysisTypeBasic) } if analysis.Version != "1.0.0" { t.Errorf("Version = %v, want 1.0.0", analysis.Version) } if analysis.AnalysisDate.IsZero() { t.Error("AnalysisDate should not be zero") } } func TestFinancialCalculator_Calculate_Advanced(t *testing.T) { config := DefaultConfig() calc := NewCalculator(config) data := &ResourceFlowData{ ResourceType: "heat", DistanceKm: 10.0, AnnualVolume: 1000.0, CostIn: 100.0, CostOut: 20.0, } assumptions := &EconomicAssumptions{ DiscountRate: 0.08, ProjectLifeYears: 10, } result, err := calc.Calculate(AnalysisTypeAdvanced, data, assumptions) if err != nil { t.Fatalf("Calculate() error = %v", err) } analysis, ok := result.(*AdvancedEconomicAnalysis) if !ok { t.Error("Calculate() returned wrong type") } if analysis.AnalysisType != AnalysisTypeAdvanced { t.Errorf("AnalysisType = %v, want %v", analysis.AnalysisType, AnalysisTypeAdvanced) } if len(analysis.SensitivityScenarios) == 0 { t.Error("SensitivityScenarios should not be empty") } if analysis.RiskProfile.RiskLevel == "" { t.Error("RiskProfile should be populated") } if len(analysis.RegulatoryRequirements) == 0 { t.Error("RegulatoryRequirements should not be empty") } if analysis.CO2ReductionBreakdown.TotalTonnes <= 0 { t.Error("CO2ReductionBreakdown should have positive total") } } func TestFinancialCalculator_Calculate_Sensitivity(t *testing.T) { config := DefaultConfig() calc := NewCalculator(config) data := &ResourceFlowData{ ResourceType: "heat", DistanceKm: 10.0, AnnualVolume: 1000.0, CostIn: 100.0, CostOut: 20.0, } assumptions := &EconomicAssumptions{ DiscountRate: 0.08, ProjectLifeYears: 10, } result, err := calc.Calculate(AnalysisTypeSensitivity, data, assumptions) if err != nil { t.Fatalf("Calculate() error = %v", err) } analysis, ok := result.(*EconomicAnalysis) if !ok { t.Error("Calculate() returned wrong type") } if analysis.AnalysisType != AnalysisTypeSensitivity { t.Errorf("AnalysisType = %v, want %v", analysis.AnalysisType, AnalysisTypeSensitivity) } } func TestFinancialCalculator_Calculate_InvalidType(t *testing.T) { config := DefaultConfig() calc := NewCalculator(config) data := &ResourceFlowData{ ResourceType: "heat", DistanceKm: 10.0, AnnualVolume: 1000.0, CostIn: 100.0, CostOut: 20.0, } assumptions := &EconomicAssumptions{ DiscountRate: 0.08, ProjectLifeYears: 10, } _, err := calc.Calculate("invalid", data, assumptions) if err == nil { t.Error("Calculate() expected error for invalid analysis type") } } func TestAssessImplementationComplexity(t *testing.T) { config := DefaultConfig() calc := NewCalculator(config) tests := []struct { name string data *ResourceFlowData wantScore float64 }{ { name: "Simple heat exchange", data: &ResourceFlowData{ ResourceType: "heat", DistanceKm: 5.0, AnnualVolume: 1000.0, }, wantScore: 1.0, }, { name: "Complex long-distance", data: &ResourceFlowData{ ResourceType: "heat", DistanceKm: 200.0, AnnualVolume: 15000.0, }, wantScore: 1.5, // Base + 0.3 (distance) + 0.2 (volume) }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Access the internal method by casting fc := calc.(*FinancialCalculator) complexity := fc.assessImplementationComplexity(tt.data) if complexity.Score != tt.wantScore { t.Errorf("assessImplementationComplexity() = %v, want %v", complexity.Score, tt.wantScore) } }) } } func TestIdentifyRegulatoryRequirements(t *testing.T) { config := DefaultConfig() calc := NewCalculator(config) fc := calc.(*FinancialCalculator) requirements := fc.identifyRegulatoryRequirements("heat") if len(requirements) == 0 { t.Error("identifyRegulatoryRequirements() should return requirements for heat") } for _, req := range requirements { if req.Type == "" || req.EstimatedCost <= 0 { t.Errorf("Regulatory requirement incomplete: %+v", req) } } } func TestCalculateCO2ReductionBreakdown(t *testing.T) { config := DefaultConfig() calc := NewCalculator(config) fc := calc.(*FinancialCalculator) breakdown := fc.calculateCO2ReductionBreakdown("heat", 1000.0, 10.0) if breakdown.TotalTonnes <= 0 { t.Error("calculateCO2ReductionBreakdown() should return positive total") } if len(breakdown.Categories) == 0 { t.Error("calculateCO2ReductionBreakdown() should return categories") } for _, category := range breakdown.Categories { if category.Name == "" || category.Tonnes < 0 { t.Errorf("CO2 category invalid: %+v", category) } } } func TestGenerateMitigationStrategies(t *testing.T) { config := DefaultConfig() calc := NewCalculator(config) fc := calc.(*FinancialCalculator) tests := []struct { name string riskLevel string wantCount int }{ { name: "High risk", riskLevel: "high", wantCount: 4, // Should have multiple strategies }, { name: "Low risk", riskLevel: "low", wantCount: 2, // Should have basic strategies }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { risk := &RiskAssessment{ RiskLevel: tt.riskLevel, } strategies := fc.generateMitigationStrategies(risk) if len(strategies) != tt.wantCount { t.Errorf("generateMitigationStrategies() = %v strategies, want %v", len(strategies), tt.wantCount) } for _, strategy := range strategies { if strategy == "" { t.Error("generateMitigationStrategies() returned empty strategy") } } }) } }