From a236d27f69b7f1a84241cd82c13fae40600a6a35 Mon Sep 17 00:00:00 2001 From: Cedrik Hoffmann Date: Sat, 29 Mar 2025 19:23:45 +0100 Subject: [PATCH 1/2] feat: update watering status after each update and create new cluster --- .../service/domain/treecluster/treecluster.go | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/internal/service/domain/treecluster/treecluster.go b/internal/service/domain/treecluster/treecluster.go index 751a70be..6576aaf3 100644 --- a/internal/service/domain/treecluster/treecluster.go +++ b/internal/service/domain/treecluster/treecluster.go @@ -121,6 +121,10 @@ func (s *TreeClusterService) Create(ctx context.Context, createTc *domain.TreeCl return nil, service.MapError(ctx, err, service.ErrorLogAll) } + if err := s.UpdateWateringStatuses(ctx); err != nil { + log.Warn("failed to update watering status after creating tree cluster", "error", err, "cluster_id", c.ID) + } + if err := s.updateTreeClusterPosition(ctx, c.ID); err != nil { log.Debug("error while update the cluster locations", "error", err, "cluster_id", c.ID) return nil, service.MapError(ctx, err, service.ErrorLogAll) @@ -177,6 +181,10 @@ func (s *TreeClusterService) Update(ctx context.Context, id int32, tcUpdate *dom } log.Info("tree cluster updated successfully", "cluster_id", id) + if err := s.UpdateWateringStatuses(ctx); err != nil { + log.Warn("failed to update watering status after updating tree cluster", "error", err, "cluster_id", id) + } + var eventTreeClusters []*domain.TreeCluster if len(trees) > 0 { eventTreeClusters = utils.Filter(utils.Map(trees, func(t *domain.Tree) *domain.TreeCluster { @@ -232,27 +240,28 @@ func (s *TreeClusterService) UpdateWateringStatuses(ctx context.Context) error { cutoffTime := time.Now().Add(-24 * time.Hour) // 1 day ago for _, cluster := range treeClusters { - // Do nothing if watering status is not »just watered« - if cluster.WateringStatus != domain.WateringStatusJustWatered { - continue - } + var wateringStatus domain.WateringStatus - if cluster.LastWatered.Before(cutoffTime) { - wateringStatus, err := s.getWateringStatusOfTreeCluster(ctx, cluster.ID) + if cluster.Trees == nil || (cluster.Trees != nil && len(cluster.Trees) == 0) { + // tree cluster has no trees + wateringStatus = domain.WateringStatusUnknown + } else if cluster.LastWatered != nil && cluster.LastWatered.Before(cutoffTime) { + wateringStatus, err = s.getWateringStatusOfTreeCluster(ctx, cluster.ID) if err != nil { log.Error("failed to get watering status of cluster", "cluster_id", cluster.ID, "error", err) return err } + } + if wateringStatus != "" { err = s.treeClusterRepo.Update(ctx, cluster.ID, func(tc *domain.TreeCluster, _ storage.TreeClusterRepository) (bool, error) { tc.WateringStatus = wateringStatus return true, nil }) - if err != nil { log.Error("failed to update watering status of tree cluster", "cluster_id", cluster.ID, "error", err) } else { - log.Debug("watering status of tree cluster is updated by scheduler", "cluster_id", cluster.ID) + log.Debug("watering status of tree cluster is updated", "cluster_id", cluster.ID) } } } From f83c9496941786fd0c26c01e681a53bcd2934245 Mon Sep 17 00:00:00 2001 From: Cedrik Hoffmann Date: Sat, 29 Mar 2025 20:08:33 +0100 Subject: [PATCH 2/2] fix: treecluster tests --- .../domain/treecluster/treecluster_test.go | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/internal/service/domain/treecluster/treecluster_test.go b/internal/service/domain/treecluster/treecluster_test.go index b287425c..2e84e6ee 100644 --- a/internal/service/domain/treecluster/treecluster_test.go +++ b/internal/service/domain/treecluster/treecluster_test.go @@ -158,6 +158,9 @@ func TestTreeClusterService_Create(t *testing.T) { mock.Anything, ).Return(expectedCluster, nil) + // UpdateWateringStatuses + clusterRepo.EXPECT().GetAll(mock.Anything, entities.TreeClusterQuery{}).Return(testClusters, int64(len(testClusters)), nil) + clusterRepo.EXPECT().GetAllLatestSensorDataByClusterID( ctx, int32(1), @@ -208,6 +211,9 @@ func TestTreeClusterService_Create(t *testing.T) { mock.Anything, ).Return(expectedCluster, nil) + // UpdateWateringStatuses + clusterRepo.EXPECT().GetAll(mock.Anything, entities.TreeClusterQuery{}).Return(testClusters, int64(len(testClusters)), nil) + clusterRepo.EXPECT().GetAllLatestSensorDataByClusterID( ctx, int32(2), @@ -297,6 +303,9 @@ func TestTreeClusterService_Create(t *testing.T) { mock.Anything, ).Return(expectedCluster, nil) + // UpdateWateringStatuses + clusterRepo.EXPECT().GetAll(mock.Anything, entities.TreeClusterQuery{}).Return(testClusters, int64(len(testClusters)), nil) + clusterRepo.EXPECT().GetAllLatestSensorDataByClusterID( ctx, int32(1), @@ -390,6 +399,9 @@ func TestTreeClusterService_Update(t *testing.T) { mock.Anything, ).Return(nil) + // UpdateWateringStatuses + clusterRepo.EXPECT().GetAll(mock.Anything, entities.TreeClusterQuery{}).Return(testClusters, int64(len(testClusters)), nil) + // when result, err := svc.Update(ctx, clusterID, updatedCluster) @@ -432,6 +444,9 @@ func TestTreeClusterService_Update(t *testing.T) { mock.Anything, ).Return(nil) + // UpdateWateringStatuses + clusterRepo.EXPECT().GetAll(mock.Anything, entities.TreeClusterQuery{}).Return(testClusters, int64(len(testClusters)), nil) + // when result, err := svc.Update(ctx, expectedCluster.ID, updatedClusterEmptyTrees) @@ -590,6 +605,9 @@ func TestTreeClusterService_EventSystem(t *testing.T) { mock.Anything, ).Return(nil) + // UpdateWateringStatuses + clusterRepo.EXPECT().GetAll(mock.Anything, entities.TreeClusterQuery{}).Return(testClusters, int64(len(testClusters)), nil) + svc := NewTreeClusterService(clusterRepo, treeRepo, regionRepo, eventManager) // when @@ -696,11 +714,13 @@ func TestTreeClusterService_UpdateWateringStatuses(t *testing.T) { staleCluster := &entities.TreeCluster{ ID: 1, LastWatered: &staleDate, // Older than 24h + Trees: testTrees, WateringStatus: entities.WateringStatusJustWatered, } recentCluster := &entities.TreeCluster{ ID: 2, LastWatered: &recentDate, + Trees: testTrees, WateringStatus: entities.WateringStatusJustWatered, } expectList := []*entities.TreeCluster{staleCluster, recentCluster} @@ -722,7 +742,7 @@ func TestTreeClusterService_UpdateWateringStatuses(t *testing.T) { clusterRepo.AssertExpectations(t) }) - t.Run("should do nothing when there are no tree cluster with correct watering status", func(t *testing.T) { + t.Run("should update watering status to unknown when tree cluster has no trees", func(t *testing.T) { // given ctx := context.Background() clusterRepo := storageMock.NewMockTreeClusterRepository(t) @@ -730,26 +750,35 @@ func TestTreeClusterService_UpdateWateringStatuses(t *testing.T) { regionRepo := storageMock.NewMockRegionRepository(t) svc := NewTreeClusterService(clusterRepo, treeRepo, regionRepo, globalEventManager) + staleDate := time.Now().Add(-34 * time.Hour) recentDate := time.Now().Add(-2 * time.Hour) - recentCluster := &entities.TreeCluster{ + + staleCluster := &entities.TreeCluster{ ID: 1, + LastWatered: &staleDate, // Older than 24h + Trees: nil, + WateringStatus: entities.WateringStatusJustWatered, + } + recentCluster := &entities.TreeCluster{ + ID: 2, LastWatered: &recentDate, + Trees: nil, WateringStatus: entities.WateringStatusJustWatered, } - - expectList := []*entities.TreeCluster{recentCluster} + expectList := []*entities.TreeCluster{staleCluster, recentCluster} // when clusterRepo.EXPECT().GetAll(mock.Anything, entities.TreeClusterQuery{}).Return(expectList, int64(len(expectList)), nil) + clusterRepo.EXPECT().Update(mock.Anything, staleCluster.ID, mock.Anything).Return(nil) + clusterRepo.EXPECT().Update(mock.Anything, recentCluster.ID, mock.Anything).Return(nil) err := svc.UpdateWateringStatuses(ctx) // then assert.NoError(t, err) clusterRepo.AssertCalled(t, "GetAll", mock.Anything, entities.TreeClusterQuery{}) - clusterRepo.AssertNotCalled(t, "GetAllLatestSensorDataByClusterID") - clusterRepo.AssertNotCalled(t, "GetBySensorIDs") - clusterRepo.AssertNotCalled(t, "Update") + clusterRepo.AssertCalled(t, "Update", mock.Anything, staleCluster.ID, mock.Anything) + clusterRepo.AssertCalled(t, "Update", mock.Anything, recentCluster.ID, mock.Anything) clusterRepo.AssertExpectations(t) }) @@ -789,6 +818,7 @@ func TestTreeClusterService_UpdateWateringStatuses(t *testing.T) { staleCluster := &entities.TreeCluster{ ID: 1, LastWatered: &staleDate, // Older than 24h + Trees: testTrees, WateringStatus: entities.WateringStatusJustWatered, } expectList := []*entities.TreeCluster{staleCluster}