turash/bugulma/backend/internal/service/transportation_service_test.go

205 lines
8.7 KiB
Go

package service_test
import (
"testing"
"bugulma/backend/internal/domain"
"bugulma/backend/internal/geospatial"
"bugulma/backend/internal/service"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
type TransportationServiceTestSuite struct {
suite.Suite
geoCalc geospatial.Calculator
svc *service.TransportationService
}
func (suite *TransportationServiceTestSuite) SetupTest() {
suite.geoCalc = geospatial.NewCalculatorWithDefaults()
suite.svc = service.NewTransportationService(suite.geoCalc)
}
func TestTransportationService(t *testing.T) {
suite.Run(t, new(TransportationServiceTestSuite))
}
func (suite *TransportationServiceTestSuite) TestNewTransportationService() {
assert.NotNil(suite.T(), suite.svc)
}
func (suite *TransportationServiceTestSuite) TestCalculateTransportCost_Truck() {
cost, err := suite.svc.CalculateTransportCost(52.5200, 13.4050, 53.5511, 9.9937, domain.TransportModeTruck, 15.0)
assert.NoError(suite.T(), err)
assert.NotNil(suite.T(), cost)
// Verify cost calculation (Berlin to Hamburg, ~290km, truck cost ~€0.12/km)
assert.Greater(suite.T(), cost.CostEur, 30.0) // Should be around €34.80
// Haversine distance for Berlin->Hamburg can be ~255km; use a lower bound
assert.Greater(suite.T(), cost.StraightDistanceKm, 250.0)
assert.Greater(suite.T(), cost.RoadDistanceKm, cost.StraightDistanceKm) // Road distance > straight distance
assert.Greater(suite.T(), cost.TimeHours, 4.0) // Should take several hours
assert.Equal(suite.T(), domain.TransportModeTruck, cost.TransportMode)
assert.Equal(suite.T(), 1.0, cost.EnvironmentalFactor) // Truck baseline
assert.Greater(suite.T(), cost.CapacityUtilization, 50.0) // 15/25 = 60% utilization
}
func (suite *TransportationServiceTestSuite) TestCalculateTransportCost_Rail() {
cost, err := suite.svc.CalculateTransportCost(52.5200, 13.4050, 53.5511, 9.9937, domain.TransportModeRail, 50.0)
assert.NoError(suite.T(), err)
assert.NotNil(suite.T(), cost)
// Rail should be cheaper than truck
assert.Greater(suite.T(), cost.CostEur, 20.0) // Should be around €23.20 (rail €0.08/km)
assert.Equal(suite.T(), domain.TransportModeRail, cost.TransportMode)
assert.Equal(suite.T(), 0.7, cost.EnvironmentalFactor) // Rail better for environment
}
func (suite *TransportationServiceTestSuite) TestCalculateTransportCost_Pipeline() {
cost, err := suite.svc.CalculateTransportCost(52.5200, 13.4050, 53.5511, 9.9937, domain.TransportModePipe, 500.0)
assert.NoError(suite.T(), err)
assert.NotNil(suite.T(), cost)
// Pipeline should be cheapest
assert.Greater(suite.T(), cost.CostEur, 5.0) // Should be around €5.80 (pipeline €0.02/km)
assert.Equal(suite.T(), domain.TransportModePipe, cost.TransportMode)
assert.Equal(suite.T(), 0.5, cost.EnvironmentalFactor) // Pipeline best for environment
assert.Greater(suite.T(), cost.CapacityUtilization, 0.0) // Some modes (like pipeline) may have very low utilization at small volumes.
}
func (suite *TransportationServiceTestSuite) TestCalculateTransportCost_VolumeExceedsCapacity() {
// Try to transport 150 tons with truck (max 25 tons)
_, err := suite.svc.CalculateTransportCost(52.5200, 13.4050, 53.5511, 9.9937, domain.TransportModeTruck, 150.0)
assert.Error(suite.T(), err)
assert.Contains(suite.T(), err.Error(), "transport volume exceeds capacity")
}
func (suite *TransportationServiceTestSuite) TestFindOptimalTransportRoutes() {
options, err := suite.svc.FindOptimalTransportRoutes(52.5200, 13.4050, 53.5511, 9.9937, 20.0)
assert.NoError(suite.T(), err)
assert.Len(suite.T(), options, 3) // Should return all 3 transport modes
// Verify all transport modes are present
modeCount := make(map[domain.TransportMode]bool)
for _, option := range options {
modeCount[option.TransportMode] = true
}
assert.True(suite.T(), modeCount[domain.TransportModeTruck])
assert.True(suite.T(), modeCount[domain.TransportModeRail])
assert.True(suite.T(), modeCount[domain.TransportModePipe])
// Verify they're sorted by overall score (highest first)
for i := 0; i < len(options)-1; i++ {
assert.GreaterOrEqual(suite.T(), options[i].OverallScore, options[i+1].OverallScore)
}
// Pipeline should generally have the highest score due to cost and environment
// (though this depends on the exact scoring algorithm)
found := false
for _, option := range options {
if option.TransportMode == domain.TransportModePipe {
found = true
break
}
}
assert.True(suite.T(), found)
}
func (suite *TransportationServiceTestSuite) TestFindOptimalTransportRoutes_LargeVolume() {
// Test with large volume that excludes some transport modes
options, err := suite.svc.FindOptimalTransportRoutes(52.5200, 13.4050, 53.5511, 9.9937, 150.0)
assert.NoError(suite.T(), err)
// Truck should be excluded (25 ton capacity), others should remain
for _, option := range options {
assert.NotEqual(suite.T(), domain.TransportModeTruck, option.TransportMode)
}
assert.Len(suite.T(), options, 2) // Rail and Pipeline only
}
func (suite *TransportationServiceTestSuite) TestGetTransportProfile_Truck() {
profile, err := suite.svc.GetTransportProfile(domain.TransportModeTruck)
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), 0.12, profile.CostPerKm)
assert.Equal(suite.T(), 60.0, profile.SpeedKmH)
assert.Equal(suite.T(), 25.0, profile.MaxCapacity)
assert.Equal(suite.T(), 1.0, profile.EnvironmentalFactor) // Pipeline best for environment
}
func (suite *TransportationServiceTestSuite) TestGetTransportProfile_Rail() {
profile, err := suite.svc.GetTransportProfile(domain.TransportModeRail)
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), 0.08, profile.CostPerKm)
assert.Equal(suite.T(), 40.0, profile.SpeedKmH)
assert.Equal(suite.T(), 200.0, profile.MaxCapacity)
assert.Equal(suite.T(), 0.7, profile.EnvironmentalFactor) // Rail better for environment
}
func (suite *TransportationServiceTestSuite) TestGetTransportProfile_Pipeline() {
profile, err := suite.svc.GetTransportProfile(domain.TransportModePipe)
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), 0.05, profile.CostPerKm)
assert.Equal(suite.T(), 100.0, profile.SpeedKmH)
assert.Equal(suite.T(), 1000.0, profile.MaxCapacity)
assert.Equal(suite.T(), 0.5, profile.EnvironmentalFactor)
}
func (suite *TransportationServiceTestSuite) TestGetTransportProfile_InvalidMode() {
_, err := suite.svc.GetTransportProfile(domain.TransportMode("invalid"))
assert.Error(suite.T(), err)
assert.Contains(suite.T(), err.Error(), "invalid transport mode")
}
func (suite *TransportationServiceTestSuite) TestTransportCost_SameLocation() {
// Test transport cost for same location (should still have some cost)
cost, err := suite.svc.CalculateTransportCost(52.5200, 13.4050, 52.5200, 13.4050, domain.TransportModeTruck, 10.0)
assert.NoError(suite.T(), err)
assert.NotNil(suite.T(), cost)
// Distance should be very small
assert.Less(suite.T(), cost.StraightDistanceKm, 0.1)
assert.GreaterOrEqual(suite.T(), cost.CostEur, 0.0) // Should still have some cost
}
func (suite *TransportationServiceTestSuite) TestTransportOptions_CompleteData() {
options, err := suite.svc.FindOptimalTransportRoutes(50.1109, 8.6821, 52.5200, 13.4050, 10.0) // Frankfurt to Berlin
assert.NoError(suite.T(), err)
assert.NotEmpty(suite.T(), options)
// Verify all required fields are populated
for _, option := range options {
assert.NotEqual(suite.T(), domain.TransportMode(""), option.TransportMode)
assert.Greater(suite.T(), option.DistanceKm, 500.0) // Frankfurt to Berlin is ~550km
assert.Greater(suite.T(), option.CostEur, 10.0) // Should have meaningful cost
assert.Greater(suite.T(), option.TimeHours, 5.0) // Should take several hours
assert.GreaterOrEqual(suite.T(), option.EnvironmentalScore, 5.0) // Should have environmental score
// Some modes (like pipeline) may have very low utilization at small volumes.
// Ensure there is at least a non-zero utilization percentage.
assert.Greater(suite.T(), option.CapacityUtilization, 0.0)
assert.NotEqual(suite.T(), 0.0, option.OverallScore) // Should have overall score
}
}
func (suite *TransportationServiceTestSuite) TestEnvironmentalFactor_Impact() {
// Test that environmental factor affects scoring
options, err := suite.svc.FindOptimalTransportRoutes(52.5200, 13.4050, 53.5511, 9.9937, 10.0)
assert.NoError(suite.T(), err)
// Find pipeline option (should have highest environmental score)
var pipelineOption *domain.TransportOption
for _, option := range options {
if option.TransportMode == domain.TransportModePipe {
pipelineOption = option
break
}
}
assert.NotNil(suite.T(), pipelineOption)
// Environmental score is computed as 10.0 / factor. For pipeline with 0.5,
// the expected score is 20.0.
assert.Equal(suite.T(), 20.0, pipelineOption.EnvironmentalScore)
}