mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
Some checks failed
CI/CD Pipeline / backend-lint (push) Failing after 1m1s
CI/CD Pipeline / backend-build (push) Has been skipped
CI/CD Pipeline / frontend-lint (push) Successful in 1m37s
CI/CD Pipeline / frontend-build (push) Failing after 35s
CI/CD Pipeline / e2e-test (push) Has been skipped
- Replace pgtestdb with Testcontainers for improved test isolation and reliability - Update test setup functions to spin up dedicated PostgreSQL containers for each test - Ensure automatic cleanup of containers after tests to prevent resource leaks - Modify documentation to reflect changes in testing methodology and benefits of using Testcontainers
272 lines
10 KiB
Go
272 lines
10 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 EnvironmentalImpactServiceTestSuite struct {
|
|
suite.Suite
|
|
db *gorm.DB
|
|
siteRepo domain.SiteRepository
|
|
geoRepo domain.GeographicalFeatureRepository
|
|
geoCalc geospatial.Calculator
|
|
geospatialSvc *service.GeospatialService
|
|
envSvc *service.EnvironmentalImpactService
|
|
}
|
|
|
|
func (suite *EnvironmentalImpactServiceTestSuite) SetupTest() {
|
|
suite.db = testutils.SetupTestDBWithTestcontainers(suite.T())
|
|
suite.siteRepo = repository.NewSiteRepository(suite.db)
|
|
suite.geoRepo = repository.NewGeographicalFeatureRepository(suite.db)
|
|
suite.geospatialSvc = service.NewGeospatialService(suite.db, suite.geoRepo)
|
|
suite.geoCalc = geospatial.NewCalculatorWithDefaults()
|
|
|
|
suite.envSvc = service.NewEnvironmentalImpactService(suite.geoRepo, suite.siteRepo, suite.geospatialSvc, suite.geoCalc)
|
|
|
|
// Create test organization
|
|
org := &domain.Organization{ID: "org-env-test", Name: "Environmental Test Organization"}
|
|
err := repository.NewOrganizationRepository(suite.db).Create(context.Background(), org)
|
|
suite.Require().NoError(err)
|
|
}
|
|
|
|
func TestEnvironmentalImpactService(t *testing.T) {
|
|
suite.Run(t, new(EnvironmentalImpactServiceTestSuite))
|
|
}
|
|
|
|
func (suite *EnvironmentalImpactServiceTestSuite) TestNewEnvironmentalImpactService() {
|
|
assert.NotNil(suite.T(), suite.envSvc)
|
|
}
|
|
|
|
func (suite *EnvironmentalImpactServiceTestSuite) setupTestSites() []*domain.Site {
|
|
sites := []*domain.Site{
|
|
{
|
|
ID: "site-high-impact",
|
|
Name: "High Impact Industrial Site",
|
|
Latitude: 52.5200,
|
|
Longitude: 13.4050,
|
|
SiteType: domain.SiteTypeIndustrial,
|
|
OwnerOrganizationID: "org-env-test",
|
|
EnvironmentalImpact: "high_impact",
|
|
},
|
|
{
|
|
ID: "site-low-impact",
|
|
Name: "Low Impact Office Site",
|
|
Latitude: 52.5300,
|
|
Longitude: 13.4150,
|
|
SiteType: domain.SiteTypeOffice,
|
|
OwnerOrganizationID: "org-env-test",
|
|
EnvironmentalImpact: "low_impact",
|
|
},
|
|
{
|
|
ID: "site-eco-friendly",
|
|
Name: "Eco-Friendly Site",
|
|
Latitude: 52.5400,
|
|
Longitude: 13.4250,
|
|
SiteType: domain.SiteTypeRetail,
|
|
OwnerOrganizationID: "org-env-test",
|
|
EnvironmentalImpact: "eco_friendly",
|
|
},
|
|
}
|
|
|
|
for _, site := range sites {
|
|
err := suite.siteRepo.Create(context.Background(), site)
|
|
suite.Require().NoError(err)
|
|
}
|
|
|
|
return sites
|
|
}
|
|
|
|
func (suite *EnvironmentalImpactServiceTestSuite) setupTestGreenSpaces() {
|
|
// Create some mock green space features
|
|
// Note: In a real scenario, these would have actual geometry data
|
|
greenSpaces := []*domain.GeographicalFeature{
|
|
{
|
|
ID: "park-central",
|
|
Name: "Central Park",
|
|
FeatureType: domain.GeographicalFeatureTypeGreenSpace,
|
|
OSMType: "way",
|
|
OSMID: "1001",
|
|
Properties: datatypes.JSON(`{"leisure": "park", "area": "large"}`),
|
|
Source: "osm",
|
|
},
|
|
{
|
|
ID: "park-small",
|
|
Name: "Small Park",
|
|
FeatureType: domain.GeographicalFeatureTypeGreenSpace,
|
|
OSMType: "way",
|
|
OSMID: "1002",
|
|
Properties: datatypes.JSON(`{"leisure": "park", "area": "small"}`),
|
|
Source: "osm",
|
|
},
|
|
}
|
|
|
|
for i, gs := range greenSpaces {
|
|
err := suite.geoRepo.Create(context.Background(), gs)
|
|
suite.Require().NoError(err)
|
|
// Ensure the DB has a geometry for each green space so radius queries
|
|
// return them during tests. Place them near 52.5200,13.4050 with small offsets
|
|
lat := 52.5200 + float64(i)*0.001
|
|
lng := 13.4050 + float64(i)*0.001
|
|
// Update raw geometry via SQL (PostGIS required in test DB template)
|
|
if err := suite.db.Exec(`UPDATE geographical_features SET geometry = ST_SetSRID(ST_MakePoint(?::double precision, ?::double precision), 4326) WHERE id = ?`, lng, lat, gs.ID).Error; err != nil {
|
|
// If updating geometry fails (e.g., PostGIS not available), continue — tests will adapt
|
|
suite.T().Logf("warning: could not set geometry for test green space %s: %v", gs.ID, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (suite *EnvironmentalImpactServiceTestSuite) TestCalculateFacilityEnvironmentalScore_HighImpact() {
|
|
sites := suite.setupTestSites()
|
|
|
|
score, err := suite.envSvc.CalculateFacilityEnvironmentalScore(context.Background(), sites[0].Latitude, sites[0].Longitude)
|
|
assert.NoError(suite.T(), err)
|
|
assert.GreaterOrEqual(suite.T(), score.OverallScore, 0.0)
|
|
assert.LessOrEqual(suite.T(), score.OverallScore, 10.0)
|
|
// High impact sites should have lower scores
|
|
assert.Less(suite.T(), score.OverallScore, 5.0)
|
|
}
|
|
|
|
func (suite *EnvironmentalImpactServiceTestSuite) TestCalculateFacilityEnvironmentalScore_LowImpact() {
|
|
sites := suite.setupTestSites()
|
|
|
|
score, err := suite.envSvc.CalculateFacilityEnvironmentalScore(context.Background(), sites[1].Latitude, sites[1].Longitude)
|
|
assert.NoError(suite.T(), err)
|
|
assert.GreaterOrEqual(suite.T(), score.OverallScore, 0.0)
|
|
assert.LessOrEqual(suite.T(), score.OverallScore, 10.0)
|
|
// Low impact sites should have higher scores
|
|
assert.Greater(suite.T(), score.OverallScore, 5.0)
|
|
}
|
|
|
|
func (suite *EnvironmentalImpactServiceTestSuite) TestCalculateFacilityEnvironmentalScore_EcoFriendly() {
|
|
sites := suite.setupTestSites()
|
|
|
|
score, err := suite.envSvc.CalculateFacilityEnvironmentalScore(context.Background(), sites[2].Latitude, sites[2].Longitude)
|
|
assert.NoError(suite.T(), err)
|
|
assert.GreaterOrEqual(suite.T(), score.OverallScore, 0.0)
|
|
assert.LessOrEqual(suite.T(), score.OverallScore, 10.0)
|
|
// Eco-friendly sites should have highest scores
|
|
assert.Greater(suite.T(), score.OverallScore, 7.0)
|
|
}
|
|
|
|
func (suite *EnvironmentalImpactServiceTestSuite) TestCalculateFacilityEnvironmentalScore_NonExistentSite() {
|
|
// For non-existent sites, we test with coordinates directly since the method takes coordinates
|
|
_, err := suite.envSvc.CalculateFacilityEnvironmentalScore(context.Background(), 0.0, 0.0)
|
|
// This should work since it doesn't depend on site existence, just coordinates
|
|
assert.NoError(suite.T(), err)
|
|
}
|
|
|
|
func (suite *EnvironmentalImpactServiceTestSuite) TestCalculateSiteEnvironmentalScore_DefaultScore() {
|
|
// Create a site without environmental impact data
|
|
site := &domain.Site{
|
|
ID: "site-no-data",
|
|
Name: "Site Without Data",
|
|
Latitude: 52.5500,
|
|
Longitude: 13.4350,
|
|
SiteType: domain.SiteTypeMixed,
|
|
OwnerOrganizationID: "org-env-test",
|
|
EnvironmentalImpact: "", // No environmental data
|
|
}
|
|
|
|
err := suite.siteRepo.Create(context.Background(), site)
|
|
suite.Require().NoError(err)
|
|
|
|
score, err := suite.envSvc.CalculateFacilityEnvironmentalScore(context.Background(), site.Latitude, site.Longitude)
|
|
assert.NoError(suite.T(), err)
|
|
assert.GreaterOrEqual(suite.T(), score.OverallScore, 0.0)
|
|
assert.LessOrEqual(suite.T(), score.OverallScore, 10.0)
|
|
// Sites without data should get a default score around 5.0
|
|
assert.InDelta(suite.T(), 5.0, score.OverallScore, 2.0)
|
|
}
|
|
|
|
func (suite *EnvironmentalImpactServiceTestSuite) TestEnvironmentalScoreCalculation_Components() {
|
|
// Test the scoring algorithm with different site configurations
|
|
testCases := []struct {
|
|
name string
|
|
environmentalImpact string
|
|
expectedMin float64
|
|
expectedMax float64
|
|
}{
|
|
{"High Impact", "high_impact", 0.0, 3.0},
|
|
{"Low Impact", "low_impact", 4.0, 7.0},
|
|
{"Eco Friendly", "eco_friendly", 7.0, 10.0},
|
|
{"Unknown", "unknown_type", 3.0, 6.0},
|
|
}
|
|
|
|
for i, tc := range testCases {
|
|
suite.T().Run(tc.name, func(t *testing.T) {
|
|
site := &domain.Site{
|
|
ID: "test-site-" + tc.name,
|
|
Name: tc.name + " Site",
|
|
Latitude: 52.5200 + float64(i)*0.01,
|
|
Longitude: 13.4050 + float64(i)*0.01,
|
|
SiteType: domain.SiteTypeIndustrial,
|
|
OwnerOrganizationID: "org-env-test",
|
|
EnvironmentalImpact: tc.environmentalImpact,
|
|
}
|
|
|
|
err := suite.siteRepo.Create(context.Background(), site)
|
|
assert.NoError(t, err)
|
|
|
|
// Quick check: our repository should find the site by ID
|
|
got, gerr := suite.siteRepo.GetByID(context.Background(), site.ID)
|
|
assert.NoError(t, gerr)
|
|
assert.Equal(t, tc.environmentalImpact, got.EnvironmentalImpact, "site EnvironmentalImpact persisted correctly")
|
|
|
|
score, err := suite.envSvc.CalculateFacilityEnvironmentalScore(context.Background(), site.Latitude, site.Longitude)
|
|
assert.NoError(t, err)
|
|
assert.GreaterOrEqual(t, score.OverallScore, tc.expectedMin, "Score should be >= %f for %s", tc.expectedMin, tc.name)
|
|
assert.LessOrEqual(t, score.OverallScore, tc.expectedMax, "Score should be <= %f for %s", tc.expectedMax, tc.name)
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *EnvironmentalImpactServiceTestSuite) TestEnvironmentalScoreCalculation_ScoreNormalization() {
|
|
// Test that scores are properly normalized to 0-10 range
|
|
sites := suite.setupTestSites()
|
|
|
|
for _, site := range sites {
|
|
score, err := suite.envSvc.CalculateFacilityEnvironmentalScore(context.Background(), site.Latitude, site.Longitude)
|
|
suite.T().Run("ScoreNormalization_"+site.ID, func(t *testing.T) {
|
|
assert.NoError(t, err)
|
|
assert.GreaterOrEqual(t, score.OverallScore, 0.0, "Score should be >= 0")
|
|
assert.LessOrEqual(t, score.OverallScore, 10.0, "Score should be <= 10")
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *EnvironmentalImpactServiceTestSuite) TestGreenSpaceStatistics_IncludesExpectedFields() {
|
|
suite.setupTestGreenSpaces()
|
|
|
|
// Test that green spaces are created and can be retrieved
|
|
greenSpaces, err := suite.geoRepo.GetGreenSpacesWithinRadius(context.Background(), 52.5200, 13.4050, 10.0)
|
|
assert.NoError(suite.T(), err)
|
|
|
|
// Should have at least 2 green spaces from our setup
|
|
assert.GreaterOrEqual(suite.T(), len(greenSpaces), 2, "Should have at least 2 green spaces")
|
|
}
|
|
|
|
func (suite *EnvironmentalImpactServiceTestSuite) TestEnvironmentalImpactService_HandlesEmptyDatabase() {
|
|
// Test that CalculateFacilityEnvironmentalScore works even with coordinates not near green spaces
|
|
score, err := suite.envSvc.CalculateFacilityEnvironmentalScore(context.Background(), 0.0, 0.0)
|
|
assert.NoError(suite.T(), err) // Should work with any coordinates
|
|
assert.NotNil(suite.T(), score)
|
|
|
|
// Test that green space queries work with empty database
|
|
greenSpaces, err := suite.geoRepo.GetGreenSpacesWithinRadius(context.Background(), 52.5200, 13.4050, 1.0)
|
|
assert.NoError(suite.T(), err)
|
|
assert.Equal(suite.T(), 0, len(greenSpaces))
|
|
}
|