package service_test import ( "context" "testing" "bugulma/backend/internal/testutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "gorm.io/gorm" "bugulma/backend/internal/domain" "bugulma/backend/internal/repository" "bugulma/backend/internal/service" ) type ResourceFlowServiceTestSuite struct { suite.Suite db *gorm.DB repo domain.ResourceFlowRepository orgRepo domain.OrganizationRepository siteRepo domain.SiteRepository svc *service.ResourceFlowService } func (suite *ResourceFlowServiceTestSuite) SetupTest() { // Setup PostgreSQL test database using pgtestdb suite.db = testutils.SetupTestDB(suite.T()) suite.repo = repository.NewResourceFlowRepository(suite.db) suite.orgRepo = repository.NewOrganizationRepository(suite.db) suite.siteRepo = repository.NewSiteRepository(suite.db) suite.svc = service.NewResourceFlowService(suite.repo, nil) } func (suite *ResourceFlowServiceTestSuite) TearDownTest() { // pgtestdb automatically cleans up the database after each test } func (suite *ResourceFlowServiceTestSuite) TestCreate() { // Create organization and site first (required for foreign key constraints) org := &domain.Organization{ID: "org-1", Name: "Test Organization"} site := &domain.Site{ID: "site-1", Name: "Test Site", Latitude: 52.52, Longitude: 13.405, OwnerOrganizationID: "org-1"} suite.Require().NoError(suite.orgRepo.Create(context.Background(), org)) suite.Require().NoError(suite.siteRepo.Create(context.Background(), site)) req := service.CreateResourceFlowRequest{ OrganizationID: "org-1", SiteID: "site-1", Direction: domain.DirectionOutput, Type: domain.TypeHeat, Quantity: domain.Quantity{ Amount: 100, Unit: "kWh", }, PrecisionLevel: domain.PrecisionEstimated, SourceType: domain.SourceDeclared, } rf, err := suite.svc.Create(context.Background(), req) assert.NoError(suite.T(), err) assert.NotEmpty(suite.T(), rf.ID) assert.Equal(suite.T(), "org-1", rf.OrganizationID) assert.Equal(suite.T(), domain.TypeHeat, rf.Type) } func (suite *ResourceFlowServiceTestSuite) TestGetByID() { // Create organization and site first org := &domain.Organization{ID: "org-1", Name: "Test Organization"} site := &domain.Site{ID: "site-1", Name: "Test Site", Latitude: 52.52, Longitude: 13.405, OwnerOrganizationID: "org-1"} suite.Require().NoError(suite.orgRepo.Create(context.Background(), org)) suite.Require().NoError(suite.siteRepo.Create(context.Background(), site)) req := service.CreateResourceFlowRequest{ OrganizationID: "org-1", SiteID: "site-1", Direction: domain.DirectionOutput, Type: domain.TypeHeat, Quantity: domain.Quantity{ Amount: 100, Unit: "kWh", }, PrecisionLevel: domain.PrecisionEstimated, SourceType: domain.SourceDeclared, } created, err := suite.svc.Create(context.Background(), req) suite.Require().NoError(err) found, err := suite.svc.GetByID(context.Background(), created.ID) assert.NoError(suite.T(), err) assert.Equal(suite.T(), created.ID, found.ID) assert.Equal(suite.T(), domain.TypeHeat, found.Type) } func (suite *ResourceFlowServiceTestSuite) TestGetBySiteID() { // Create organization and sites first org := &domain.Organization{ID: "org-1", Name: "Test Organization"} site1 := &domain.Site{ID: "site-1", Name: "Test Site 1", Latitude: 52.52, Longitude: 13.405, OwnerOrganizationID: "org-1"} site2 := &domain.Site{ID: "site-2", Name: "Test Site 2", Latitude: 52.53, Longitude: 13.406, OwnerOrganizationID: "org-1"} suite.Require().NoError(suite.orgRepo.Create(context.Background(), org)) suite.Require().NoError(suite.siteRepo.Create(context.Background(), site1)) suite.Require().NoError(suite.siteRepo.Create(context.Background(), site2)) req1 := service.CreateResourceFlowRequest{ OrganizationID: "org-1", SiteID: "site-1", Direction: domain.DirectionOutput, Type: domain.TypeHeat, Quantity: domain.Quantity{ Amount: 100, Unit: "kWh", }, PrecisionLevel: domain.PrecisionEstimated, SourceType: domain.SourceDeclared, } req2 := service.CreateResourceFlowRequest{ OrganizationID: "org-1", SiteID: "site-2", Direction: domain.DirectionOutput, Type: domain.TypeWater, Quantity: domain.Quantity{ Amount: 50, Unit: "liters", }, PrecisionLevel: domain.PrecisionEstimated, SourceType: domain.SourceDeclared, } _, err := suite.svc.Create(context.Background(), req1) suite.Require().NoError(err) _, err = suite.svc.Create(context.Background(), req2) suite.Require().NoError(err) flows, err := suite.svc.GetBySiteID(context.Background(), "site-1") assert.NoError(suite.T(), err) assert.Len(suite.T(), flows, 1) assert.Equal(suite.T(), domain.TypeHeat, flows[0].Type) } func (suite *ResourceFlowServiceTestSuite) TestGetByOrganizationID() { // Create organizations and site first org1 := &domain.Organization{ID: "org-1", Name: "Test Organization 1"} org2 := &domain.Organization{ID: "org-2", Name: "Test Organization 2"} site := &domain.Site{ID: "site-1", Name: "Test Site", Latitude: 52.52, Longitude: 13.405, OwnerOrganizationID: "org-1"} suite.Require().NoError(suite.orgRepo.Create(context.Background(), org1)) suite.Require().NoError(suite.orgRepo.Create(context.Background(), org2)) suite.Require().NoError(suite.siteRepo.Create(context.Background(), site)) req1 := service.CreateResourceFlowRequest{ OrganizationID: "org-1", SiteID: "site-1", Direction: domain.DirectionOutput, Type: domain.TypeHeat, Quantity: domain.Quantity{ Amount: 100, Unit: "kWh", }, PrecisionLevel: domain.PrecisionEstimated, SourceType: domain.SourceDeclared, } req2 := service.CreateResourceFlowRequest{ OrganizationID: "org-2", SiteID: "site-1", Direction: domain.DirectionOutput, Type: domain.TypeWater, Quantity: domain.Quantity{ Amount: 50, Unit: "liters", }, PrecisionLevel: domain.PrecisionEstimated, SourceType: domain.SourceDeclared, } _, err := suite.svc.Create(context.Background(), req1) suite.Require().NoError(err) _, err = suite.svc.Create(context.Background(), req2) suite.Require().NoError(err) flows, err := suite.svc.GetByOrganizationID(context.Background(), "org-1") assert.NoError(suite.T(), err) assert.Len(suite.T(), flows, 1) assert.Equal(suite.T(), domain.TypeHeat, flows[0].Type) } func (suite *ResourceFlowServiceTestSuite) TestGetByTypeAndDirection() { // Create organization and site first org := &domain.Organization{ID: "org-1", Name: "Test Organization"} site := &domain.Site{ID: "site-1", Name: "Test Site", Latitude: 52.52, Longitude: 13.405, OwnerOrganizationID: "org-1"} suite.Require().NoError(suite.orgRepo.Create(context.Background(), org)) suite.Require().NoError(suite.siteRepo.Create(context.Background(), site)) req1 := service.CreateResourceFlowRequest{ OrganizationID: "org-1", SiteID: "site-1", Direction: domain.DirectionOutput, Type: domain.TypeHeat, Quantity: domain.Quantity{ Amount: 100, Unit: "kWh", }, PrecisionLevel: domain.PrecisionEstimated, SourceType: domain.SourceDeclared, } req2 := service.CreateResourceFlowRequest{ OrganizationID: "org-1", SiteID: "site-1", Direction: domain.DirectionInput, Type: domain.TypeHeat, Quantity: domain.Quantity{ Amount: 50, Unit: "kWh", }, PrecisionLevel: domain.PrecisionEstimated, SourceType: domain.SourceDeclared, } _, err := suite.svc.Create(context.Background(), req1) suite.Require().NoError(err) _, err = suite.svc.Create(context.Background(), req2) suite.Require().NoError(err) flows, err := suite.svc.GetByTypeAndDirection(context.Background(), domain.TypeHeat, domain.DirectionOutput) assert.NoError(suite.T(), err) assert.Len(suite.T(), flows, 1) assert.Equal(suite.T(), domain.DirectionOutput, flows[0].Direction) } func (suite *ResourceFlowServiceTestSuite) TestUpdate() { // Create organization and site first org := &domain.Organization{ID: "org-1", Name: "Test Organization"} site := &domain.Site{ID: "site-1", Name: "Test Site", Latitude: 52.52, Longitude: 13.405, OwnerOrganizationID: "org-1"} suite.Require().NoError(suite.orgRepo.Create(context.Background(), org)) suite.Require().NoError(suite.siteRepo.Create(context.Background(), site)) req := service.CreateResourceFlowRequest{ OrganizationID: "org-1", SiteID: "site-1", Direction: domain.DirectionOutput, Type: domain.TypeHeat, Quantity: domain.Quantity{ Amount: 100, Unit: "kWh", }, PrecisionLevel: domain.PrecisionEstimated, SourceType: domain.SourceDeclared, } rf, err := suite.svc.Create(context.Background(), req) suite.Require().NoError(err) rf.Type = domain.TypeWater err = suite.svc.Update(context.Background(), rf) assert.NoError(suite.T(), err) found, err := suite.svc.GetByID(context.Background(), rf.ID) assert.NoError(suite.T(), err) assert.Equal(suite.T(), domain.TypeWater, found.Type) } func (suite *ResourceFlowServiceTestSuite) TestDelete() { // Create organization and site first org := &domain.Organization{ID: "org-1", Name: "Test Organization"} site := &domain.Site{ID: "site-1", Name: "Test Site", Latitude: 52.52, Longitude: 13.405, OwnerOrganizationID: "org-1"} suite.Require().NoError(suite.orgRepo.Create(context.Background(), org)) suite.Require().NoError(suite.siteRepo.Create(context.Background(), site)) req := service.CreateResourceFlowRequest{ OrganizationID: "org-1", SiteID: "site-1", Direction: domain.DirectionOutput, Type: domain.TypeHeat, Quantity: domain.Quantity{ Amount: 100, Unit: "kWh", }, PrecisionLevel: domain.PrecisionEstimated, SourceType: domain.SourceDeclared, } rf, err := suite.svc.Create(context.Background(), req) suite.Require().NoError(err) err = suite.svc.Delete(context.Background(), rf.ID) assert.NoError(suite.T(), err) _, err = suite.svc.GetByID(context.Background(), rf.ID) assert.Error(suite.T(), err) } func TestResourceFlowServiceTestSuite(t *testing.T) { suite.Run(t, new(ResourceFlowServiceTestSuite)) }