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

261 lines
7.2 KiB
Go

package service_test
import (
"context"
"testing"
"bugulma/backend/internal/domain"
"bugulma/backend/internal/geospatial"
"bugulma/backend/internal/repository"
"bugulma/backend/internal/service"
"bugulma/backend/internal/testutils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"gorm.io/datatypes"
"gorm.io/gorm"
)
type SpatialResourceMatcherTestSuite struct {
suite.Suite
db *gorm.DB
siteRepo domain.SiteRepository
flowRepo domain.ResourceFlowRepository
geoSvc *service.GeospatialService
transportSvc *service.TransportationService
geoCalc geospatial.Calculator
matcher *service.SpatialResourceMatcher
}
func (suite *SpatialResourceMatcherTestSuite) SetupTest() {
suite.db = testutils.SetupTestDB(suite.T())
suite.siteRepo = repository.NewSiteRepository(suite.db)
suite.flowRepo = repository.NewResourceFlowRepository(suite.db)
suite.geoSvc = service.NewGeospatialService(suite.db, nil) // We'll test without geo features for basic functionality
suite.geoCalc = geospatial.NewCalculatorWithDefaults()
suite.transportSvc = service.NewTransportationService(suite.geoCalc)
suite.matcher = service.NewSpatialResourceMatcher(
nil, // geoRepo - not needed for basic matching tests
suite.siteRepo,
suite.flowRepo,
suite.geoSvc,
suite.transportSvc,
suite.geoCalc,
)
// Create test organization
org := &domain.Organization{ID: "org-test", Name: "Test Organization"}
err := repository.NewOrganizationRepository(suite.db).Create(context.Background(), org)
suite.Require().NoError(err)
}
func TestSpatialResourceMatcher(t *testing.T) {
suite.Run(t, new(SpatialResourceMatcherTestSuite))
}
func (suite *SpatialResourceMatcherTestSuite) TestNewSpatialResourceMatcher() {
assert.NotNil(suite.T(), suite.matcher)
}
func (suite *SpatialResourceMatcherTestSuite) TestSiteProvidesResource_PlatformDefault() {
// Create test site
site := &domain.Site{
ID: "site-test",
Name: "Test Site",
Latitude: 52.5200,
Longitude: 13.4050,
SiteType: domain.SiteTypeIndustrial,
OwnerOrganizationID: "org-test",
}
err := suite.siteRepo.Create(context.Background(), site)
suite.Require().NoError(err)
// Test that site creation worked
assert.NotNil(suite.T(), site)
}
func (suite *SpatialResourceMatcherTestSuite) setupTestSitesAndFlows() []*domain.Site {
// Create test sites
sites := []*domain.Site{
{
ID: "site-provider-1",
Name: "Energy Provider Site",
Latitude: 52.5200,
Longitude: 13.4050,
SiteType: domain.SiteTypeIndustrial,
OwnerOrganizationID: "org-test",
},
{
ID: "site-provider-2",
Name: "Waste Provider Site",
Latitude: 52.5300,
Longitude: 13.4150,
SiteType: domain.SiteTypeIndustrial,
OwnerOrganizationID: "org-test",
},
{
ID: "site-consumer",
Name: "Manufacturing Site",
Latitude: 52.5400,
Longitude: 13.4250,
SiteType: domain.SiteTypeIndustrial,
OwnerOrganizationID: "org-test",
},
}
for _, site := range sites {
err := suite.siteRepo.Create(context.Background(), site)
suite.Require().NoError(err)
}
// Create test resource flows
flows := []*domain.ResourceFlow{
{
ID: "flow-energy-output",
OrganizationID: "org-test",
SiteID: "site-provider-1",
Direction: domain.DirectionOutput,
Type: domain.TypeHeat,
Quantity: datatypes.JSON(`{"amount": 1000, "unit": "kWh"}`),
EconomicData: datatypes.JSON(`{"cost_out": 0.15}`),
},
{
ID: "flow-waste-output",
OrganizationID: "org-test",
SiteID: "site-provider-2",
Direction: domain.DirectionOutput,
Type: domain.TypeBiowaste,
Quantity: datatypes.JSON(`{"amount": 500, "unit": "kg"}`),
EconomicData: datatypes.JSON(`{"cost_out": 0.05}`),
},
{
ID: "flow-energy-input",
OrganizationID: "org-test",
SiteID: "site-consumer",
Direction: domain.DirectionInput,
Type: domain.TypeHeat,
Quantity: datatypes.JSON(`{"amount": 800, "unit": "kWh"}`),
EconomicData: datatypes.JSON(`{"cost_in": 0.20}`),
},
}
for _, flow := range flows {
err := suite.flowRepo.Create(context.Background(), flow)
suite.Require().NoError(err)
}
return sites
}
func (suite *SpatialResourceMatcherTestSuite) TestFindNearbyResourceProviders_Energy() {
suite.setupTestSitesAndFlows()
results, err := suite.matcher.FindNearbyResourceProviders(
context.Background(),
domain.TypeHeat,
52.5400, 13.4250, // Consumer location
10.0, // 10km radius
domain.TransportModeTruck,
)
assert.NoError(suite.T(), err)
assert.Len(suite.T(), results, 1) // Should find one energy provider
result := results[0]
assert.Equal(suite.T(), "flow-energy-output", result.ResourceFlow.ID)
assert.Equal(suite.T(), "site-provider-1", result.ProviderSite.ID)
assert.NotNil(suite.T(), result.SpatialMetrics)
assert.Greater(suite.T(), result.SpatialMetrics.StraightLineDistance, 0.0)
assert.Greater(suite.T(), result.MatchScore, 0.0)
}
func (suite *SpatialResourceMatcherTestSuite) TestFindNearbyResourceProviders_Waste() {
suite.setupTestSitesAndFlows()
results, err := suite.matcher.FindNearbyResourceProviders(
context.Background(),
domain.TypeBiowaste,
52.5400, 13.4250,
10.0,
domain.TransportModeTruck,
)
assert.NoError(suite.T(), err)
assert.Len(suite.T(), results, 1)
result := results[0]
assert.Equal(suite.T(), "flow-waste-output", result.ResourceFlow.ID)
assert.Equal(suite.T(), "site-provider-2", result.ProviderSite.ID)
}
func (suite *SpatialResourceMatcherTestSuite) TestFindNearbyResourceProviders_NoMatches() {
suite.setupTestSitesAndFlows()
// Search for chemical resources (none exist)
results, err := suite.matcher.FindNearbyResourceProviders(
context.Background(),
domain.TypeMaterials,
52.5400, 13.4250,
10.0,
domain.TransportModeTruck,
)
assert.NoError(suite.T(), err)
assert.Len(suite.T(), results, 0)
}
func (suite *SpatialResourceMatcherTestSuite) TestFindNearbyResourceProviders_OutOfRadius() {
suite.setupTestSitesAndFlows()
// Search with very small radius
results, err := suite.matcher.FindNearbyResourceProviders(
context.Background(),
domain.TypeHeat,
52.5400, 13.4250,
0.1, // Very small radius
domain.TransportModeTruck,
)
assert.NoError(suite.T(), err)
assert.Len(suite.T(), results, 0) // No providers within 0.1km
}
func (suite *SpatialResourceMatcherTestSuite) TestSpatialMatchResult_JSONSerialization() {
flow := &domain.ResourceFlow{
ID: "test-flow",
Type: domain.TypeHeat,
}
providerSite := &domain.Site{
ID: "test-site",
Name: "Test Provider",
Latitude: 52.5200,
Longitude: 13.4050,
}
metrics := &service.SpatialMetrics{
StraightLineDistance: 5.5,
TransportCost: 42.5,
EnvironmentalScore: 7.8,
}
result := &service.SpatialMatchResult{
ResourceFlow: flow,
ProviderSite: providerSite,
SpatialMetrics: metrics,
MatchScore: 8.2,
}
// This would test JSON serialization if we had JSON tags
// For now, just verify the struct is properly constructed
assert.NotNil(suite.T(), result.ResourceFlow)
assert.NotNil(suite.T(), result.ProviderSite)
assert.NotNil(suite.T(), result.SpatialMetrics)
assert.Equal(suite.T(), 8.2, result.MatchScore)
}