From 1a10a4fb21c27fbf80515784538aedb93e65658a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferdinand=20M=C3=BCtsch?= Date: Mon, 19 Apr 2021 20:48:07 +0200 Subject: [PATCH] fix: prevent duplicate summaries from being counted twice (resolve #179) --- services/summary.go | 11 ++++++++++ services/summary_test.go | 46 ++++++++++++++++++++++++++++++++++++++++ version.txt | 2 +- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/services/summary.go b/services/summary.go index b3daea5..f9cf59e 100644 --- a/services/summary.go +++ b/services/summary.go @@ -3,6 +3,7 @@ package services import ( "crypto/md5" "errors" + "github.com/emvi/logbuch" "github.com/muety/wakapi/config" "github.com/muety/wakapi/models" "github.com/muety/wakapi/repositories" @@ -236,7 +237,15 @@ func (srv *SummaryService) mergeSummaries(summaries []*models.Summary) (*models. Machines: make([]*models.SummaryItem, 0), } + var processed = map[int64]bool{} + for _, s := range summaries { + hash := s.FromTime.T().UnixNano() ^ s.ToTime.T().UnixNano() + if _, found := processed[hash]; found { + logbuch.Warn("summary from %v to %v (user '%s') was attempted to be processed more often than once", s.FromTime, s.ToTime, s.UserID) + continue + } + if s.UserID != finalSummary.UserID { return nil, errors.New("users don't match") } @@ -254,6 +263,8 @@ func (srv *SummaryService) mergeSummaries(summaries []*models.Summary) (*models. finalSummary.Editors = srv.mergeSummaryItems(finalSummary.Editors, s.Editors) finalSummary.OperatingSystems = srv.mergeSummaryItems(finalSummary.OperatingSystems, s.OperatingSystems) finalSummary.Machines = srv.mergeSummaryItems(finalSummary.Machines, s.Machines) + + processed[hash] = true } finalSummary.FromTime = models.CustomTime(minTime) diff --git a/services/summary_test.go b/services/summary_test.go index 5eb7968..dabd336 100644 --- a/services/summary_test.go +++ b/services/summary_test.go @@ -291,6 +291,52 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Retrieve() { suite.HeartbeatService.AssertNumberOfCalls(suite.T(), "GetAllWithin", 2+1+1) } +func (suite *SummaryServiceTestSuite) TestSummaryService_Retrieve_DuplicateSummaries() { + sut := NewSummaryService(suite.SummaryRepository, suite.HeartbeatService, suite.AliasService) + + var ( + summaries []*models.Summary + from time.Time + to time.Time + result *models.Summary + err error + ) + + from, to = suite.TestStartTime.Add(-12*time.Hour), suite.TestStartTime.Add(12*time.Hour) + summaries = []*models.Summary{ + { + ID: uint(rand.Uint32()), + UserID: TestUserId, + FromTime: models.CustomTime(from.Add(10 * time.Minute)), + ToTime: models.CustomTime(to.Add(-10 * time.Minute)), + Projects: []*models.SummaryItem{ + { + Type: models.SummaryProject, + Key: TestProject1, + Total: 45 * time.Minute / time.Second, // hack + }, + }, + Languages: []*models.SummaryItem{}, + Editors: []*models.SummaryItem{}, + OperatingSystems: []*models.SummaryItem{}, + Machines: []*models.SummaryItem{}, + }, + } + summaries = append(summaries, &(*summaries[0])) // add same summary again -> mustn't be counted twice! + + suite.SummaryRepository.On("GetByUserWithin", suite.TestUser, from, to).Return(summaries, nil) + suite.HeartbeatService.On("GetAllWithin", from, summaries[0].FromTime.T(), suite.TestUser).Return([]*models.Heartbeat{}, nil) + suite.HeartbeatService.On("GetAllWithin", summaries[0].ToTime.T(), to, suite.TestUser).Return([]*models.Heartbeat{}, nil) + + result, err = sut.Retrieve(from, to, suite.TestUser) + + assert.Nil(suite.T(), err) + assert.NotNil(suite.T(), result) + assert.Len(suite.T(), result.Projects, 1) + assert.Equal(suite.T(), summaries[0].Projects[0].Total*time.Second, result.TotalTime()) + suite.HeartbeatService.AssertNumberOfCalls(suite.T(), "GetAllWithin", 2) +} + func (suite *SummaryServiceTestSuite) TestSummaryService_Aliased() { sut := NewSummaryService(suite.SummaryRepository, suite.HeartbeatService, suite.AliasService) diff --git a/version.txt b/version.txt index 8fe00a5..f250c8f 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.26.5 +1.26.6