turash/models/match/match_test.go
Damir Mukimov 4a2fda96cd
Initial commit: Repository setup with .gitignore, golangci-lint v2.6.0, and code quality checks
- Initialize git repository
- Add comprehensive .gitignore for Go projects
- Install golangci-lint v2.6.0 (latest v2) globally
- Configure .golangci.yml with appropriate linters and formatters
- Fix all formatting issues (gofmt)
- Fix all errcheck issues (unchecked errors)
- Adjust complexity threshold for validation functions
- All checks passing: build, test, vet, lint
2025-11-01 07:36:22 +01:00

321 lines
8.6 KiB
Go

package match
import (
"testing"
"github.com/damirmukimov/city_resource_graph/models/transport"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCalculateMatchEconomics(t *testing.T) {
params := MatchEconomicsParams{
SourceResource: ResourceFlowSummary{
ID: "source_001",
Type: "waste_heat",
Direction: "output",
Quantity: 1000,
Unit: "MWh",
CostPerUnit: 20, // €20/MWh disposal cost avoided
},
TargetResource: ResourceFlowSummary{
ID: "target_001",
Type: "process_heat",
Direction: "input",
Quantity: 1000,
Unit: "MWh",
CostPerUnit: 50, // €50/MWh value to target
},
DistanceKm: 5.0,
InitialInvestment: 25000, // €25k setup cost
SymbiosisType: transport.SymbiosisEnergyCascading,
Complexity: "medium",
RiskLevel: "low",
AnnualQuantity: 8000, // 8000 MWh/year
UnitValue: 35, // €35/MWh exchange value
CO2ReductionFactor: 0.0005, // 0.0005 tonnes CO2/MWh
}
assumptions := DefaultCalculationAssumptions()
result, err := CalculateMatchEconomics(params, assumptions)
require.NoError(t, err)
require.NotNil(t, result)
// Validate basic structure
assert.Equal(t, "match_source_001_target_001", result.MatchID)
assert.Equal(t, params.SourceResource, result.SourceResource)
assert.Equal(t, params.TargetResource, result.TargetResource)
// Validate calculations are reasonable
assert.Greater(t, result.Calculations.AnnualSavings, 0.0)
assert.Greater(t, result.Calculations.NPV10Years, -params.InitialInvestment)
assert.LessOrEqual(t, result.Calculations.PaybackPeriodYears, 10.0)
assert.GreaterOrEqual(t, result.Calculations.IRRPercent, -100.0)
assert.LessOrEqual(t, result.Calculations.IRRPercent, 3000.0) // Allow higher IRR for good investments
assert.Greater(t, result.Calculations.CO2ReductionTonnes, 0.0)
// Validate transportation costs
assert.Greater(t, result.Calculations.TransportationCosts.AnnualCost, 0.0)
assert.Equal(t, 5.0, result.Calculations.TransportationCosts.DistanceKm)
assert.Equal(t, "heat_pipe", result.Calculations.TransportationCosts.Method)
// Validate implementation complexity
assert.Contains(t, []string{"low", "medium", "high"}, result.Calculations.ImplementationComplexity)
// Validate regulatory requirements
assert.Contains(t, result.Calculations.RegulatoryRequirements, "energy_distribution_license")
}
func TestCalculateAnnualSavings(t *testing.T) {
params := MatchEconomicsParams{
SourceResource: ResourceFlowSummary{
CostPerUnit: 20, // €20/unit disposal cost
},
TargetResource: ResourceFlowSummary{
CostPerUnit: 50, // €50/unit value
},
AnnualQuantity: 1000, // 1000 units/year
}
savings := calculateAnnualSavings(params)
expected := float64((20 + 50) * 1000) // €70,000
assert.Equal(t, expected, savings)
}
func TestCalculatePaybackPeriod(t *testing.T) {
tests := []struct {
name string
initialInvestment float64
annualNetCashFlow float64
expected float64
}{
{
name: "normal payback",
initialInvestment: 25000,
annualNetCashFlow: 12500,
expected: 2.0,
},
{
name: "never pays back",
initialInvestment: 25000,
annualNetCashFlow: 0,
expected: 999,
},
{
name: "pays back in first year",
initialInvestment: 5000,
annualNetCashFlow: 10000,
expected: 0.5,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := calculatePaybackPeriod(tt.initialInvestment, tt.annualNetCashFlow)
assert.Equal(t, tt.expected, result)
})
}
}
func TestAssessImplementationComplexity(t *testing.T) {
tests := []struct {
name string
params MatchEconomicsParams
expected string
}{
{
name: "low complexity",
params: MatchEconomicsParams{
DistanceKm: 1.0,
InitialInvestment: 10000,
SymbiosisType: transport.SymbiosisDataSharing,
},
expected: "low",
},
{
name: "medium complexity",
params: MatchEconomicsParams{
DistanceKm: 10.0,
InitialInvestment: 30000,
SymbiosisType: transport.SymbiosisEnergyCascading,
},
expected: "medium",
},
{
name: "high complexity",
params: MatchEconomicsParams{
DistanceKm: 50.0,
InitialInvestment: 100000,
SymbiosisType: transport.SymbiosisWasteToResource,
},
expected: "high",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := assessImplementationComplexity(tt.params)
assert.Equal(t, tt.expected, result)
})
}
}
func TestIdentifyRegulatoryRequirements(t *testing.T) {
tests := []struct {
name string
params MatchEconomicsParams
expected []string
}{
{
name: "waste handling requires permits",
params: MatchEconomicsParams{
SymbiosisType: transport.SymbiosisWasteToResource,
},
expected: []string{"waste_disposal_permit", "environmental_impact_assessment"},
},
{
name: "energy transfer requires license",
params: MatchEconomicsParams{
SymbiosisType: transport.SymbiosisEnergyCascading,
},
expected: []string{"energy_distribution_license"},
},
{
name: "long distance requires transport license",
params: MatchEconomicsParams{
DistanceKm: 60.0,
SymbiosisType: transport.SymbiosisDataSharing,
},
expected: []string{"transport_license"},
},
{
name: "high value requires insurance",
params: MatchEconomicsParams{
UnitValue: 100,
AnnualQuantity: 2000, // €200k annual value
SymbiosisType: transport.SymbiosisDataSharing,
},
expected: []string{"liability_insurance"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := identifyRegulatoryRequirements(tt.params)
for _, expectedReq := range tt.expected {
assert.Contains(t, result, expectedReq)
}
})
}
}
func TestDetermineTransportMethod(t *testing.T) {
tests := []struct {
symbiosisType transport.SymbiosisType
expected string
}{
{transport.SymbiosisWasteToResource, "truck"},
{transport.SymbiosisEnergyCascading, "heat_pipe"},
{transport.SymbiosisUtilitySharing, "pipeline"},
{transport.SymbiosisDataSharing, "network"},
{transport.SymbiosisKnowledgeSharing, "truck"}, // default
}
for _, tt := range tests {
result := determineTransportMethod(tt.symbiosisType)
assert.Equal(t, tt.expected, result)
}
}
func TestFeasibilityToScore(t *testing.T) {
tests := []struct {
feasibility string
expected float64
}{
{"high", 0.9},
{"medium", 0.6},
{"low", 0.3},
{"unknown", 0.5},
}
for _, tt := range tests {
result := feasibilityToScore(tt.feasibility)
assert.Equal(t, tt.expected, result)
}
}
func TestValidateEconomicCalculation(t *testing.T) {
validCalc := &EconomicCalculation{
MatchID: "test_match",
Calculations: MatchCalculations{
AnnualSavings: 50000,
PaybackPeriodYears: 2.0,
IRRPercent: 15.0,
},
}
err := ValidateEconomicCalculation(validCalc)
assert.NoError(t, err)
// Test invalid cases
invalidCalc := &EconomicCalculation{
Calculations: MatchCalculations{
AnnualSavings: -1000, // Negative savings
},
}
err = ValidateEconomicCalculation(invalidCalc)
assert.Error(t, err)
emptyIDCalc := &EconomicCalculation{
Calculations: MatchCalculations{
AnnualSavings: 50000,
},
}
err = ValidateEconomicCalculation(emptyIDCalc)
assert.Error(t, err)
}
func TestCalculateMatchNPV(t *testing.T) {
params := MatchEconomicsParams{
InitialInvestment: 100000, // High investment
}
assumptions := DefaultCalculationAssumptions()
npv := calculateMatchNPV(params, assumptions, 5000, 3000) // Only €2k net annual benefit
// Should be negative (high investment, low returns)
assert.Less(t, npv, 0.0)
}
func TestCalculateMatchIRR(t *testing.T) {
params := MatchEconomicsParams{
InitialInvestment: 25000,
}
assumptions := DefaultCalculationAssumptions()
irr := calculateMatchIRR(params, assumptions, 12500, 2500) // €10k net annual benefit
// IRR should be reasonable (positive but not too high)
assert.Greater(t, irr, 0.0)
assert.Less(t, irr, 0.5) // Less than 50%
}
func TestGenerateMatchID(t *testing.T) {
id := generateMatchID("source_123", "target_456")
assert.Equal(t, "match_source_123_target_456", id)
}
func TestCalculateMatchEconomics_InvalidParams(t *testing.T) {
params := MatchEconomicsParams{
// Missing resource IDs
SourceResource: ResourceFlowSummary{ID: ""},
TargetResource: ResourceFlowSummary{ID: ""},
}
assumptions := DefaultCalculationAssumptions()
_, err := CalculateMatchEconomics(params, assumptions)
assert.Error(t, err)
assert.Contains(t, err.Error(), "source and target resource IDs are required")
}