fix: compute leaderboard with consistent time interval (resolve #749)

This commit is contained in:
Ferdinand Mütsch
2025-02-26 23:45:51 +01:00
parent 1bd00f7209
commit fde25948ab
22 changed files with 1641 additions and 1602 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -11,18 +11,18 @@ type SummaryServiceMock struct {
mock.Mock
}
func (m *SummaryServiceMock) Aliased(t time.Time, t2 time.Time, u *models.User, r types.SummaryRetriever, f *models.Filters, b bool) (*models.Summary, error) {
args := m.Called(t, t2, u, r, f)
func (m *SummaryServiceMock) Aliased(t time.Time, t2 time.Time, u *models.User, r types.SummaryRetriever, f *models.Filters, d *time.Duration, b bool) (*models.Summary, error) {
args := m.Called(t, t2, u, r, f, d, b)
return args.Get(0).(*models.Summary), args.Error(1)
}
func (m *SummaryServiceMock) Retrieve(t time.Time, t2 time.Time, u *models.User, f *models.Filters) (*models.Summary, error) {
args := m.Called(t, t2, u, f)
func (m *SummaryServiceMock) Retrieve(t time.Time, t2 time.Time, u *models.User, f *models.Filters, d *time.Duration) (*models.Summary, error) {
args := m.Called(t, t2, u, d, f)
return args.Get(0).(*models.Summary), args.Error(1)
}
func (m *SummaryServiceMock) Summarize(t time.Time, t2 time.Time, u *models.User, f *models.Filters) (*models.Summary, error) {
args := m.Called(t, t2, u, f)
func (m *SummaryServiceMock) Summarize(t time.Time, t2 time.Time, u *models.User, f *models.Filters, d *time.Duration) (*models.Summary, error) {
args := m.Called(t, t2, u, d, f)
return args.Get(0).(*models.Summary), args.Error(1)
}

View File

@@ -5,4 +5,4 @@ import (
"time"
)
type SummaryRetriever func(f, t time.Time, u *models.User, filters *models.Filters) (*models.Summary, error)
type SummaryRetriever func(f, t time.Time, u *models.User, filters *models.Filters, duration *time.Duration) (*models.Summary, error)

View File

@@ -51,7 +51,7 @@ func TestBadgeHandler_Get(t *testing.T) {
userServiceMock.On("GetUserById", "user1").Return(&user1, nil)
summaryServiceMock := new(mocks.SummaryServiceMock)
summaryServiceMock.On("Aliased", mock.AnythingOfType("time.Time"), mock.AnythingOfType("time.Time"), &user1, mock.Anything, mock.Anything).Return(&summary1, nil)
summaryServiceMock.On("Aliased", mock.AnythingOfType("time.Time"), mock.AnythingOfType("time.Time"), &user1, mock.AnythingOfType("types.SummaryRetriever"), mock.AnythingOfType("*models.Filters"), mock.AnythingOfType("*time.Duration"), mock.Anything).Return(&summary1, nil)
badgeHandler := NewBadgeHandler(userServiceMock, summaryServiceMock)
badgeHandler.RegisterRoutes(apiRouter)

View File

@@ -139,7 +139,7 @@ func (h *MetricsHandler) Get(w http.ResponseWriter, r *http.Request) {
func (h *MetricsHandler) getUserMetrics(user *models.User) (*mm.Metrics, error) {
var metrics mm.Metrics
summaryAllTime, err := h.summarySrvc.Aliased(time.Time{}, time.Now(), user, h.summarySrvc.Retrieve, nil, false)
summaryAllTime, err := h.summarySrvc.Aliased(time.Time{}, time.Now(), user, h.summarySrvc.Retrieve, nil, nil, false)
if err != nil {
conf.Log().Error("failed to retrieve all time summary for metric", "userID", user.ID, "error", err)
return nil, err
@@ -147,7 +147,7 @@ func (h *MetricsHandler) getUserMetrics(user *models.User) (*mm.Metrics, error)
from, to := helpers.MustResolveIntervalRawTZ("today", user.TZ())
summaryToday, err := h.summarySrvc.Aliased(from, to, user, h.summarySrvc.Retrieve, nil, false)
summaryToday, err := h.summarySrvc.Aliased(from, to, user, h.summarySrvc.Retrieve, nil, nil, false)
if err != nil {
conf.Log().Error("failed to retrieve today's summary for metric", "userID", user.ID, "error", err)
return nil, err
@@ -463,7 +463,7 @@ func (h *MetricsHandler) getAdminMetrics(user *models.User) (*mm.Metrics, error)
for i := range activeUsers {
wp.Submit(func() {
summary, err := h.summarySrvc.Aliased(from, to, activeUsers[i], h.summarySrvc.Retrieve, nil, false) // only using aliased because aliased has caching
summary, err := h.summarySrvc.Aliased(from, to, activeUsers[i], h.summarySrvc.Retrieve, nil, nil, false) // only using aliased because aliased has caching
if err != nil {
conf.Log().Error("failed to get total time for user as part of metrics", "userID", activeUsers[i].ID, "error", err)
return

View File

@@ -110,6 +110,7 @@ func (h *BadgeHandler) loadUserSummary(user *models.User, interval *models.Inter
summaryParams.User,
retrieveSummary,
filters,
nil,
summaryParams.Recompute,
)
if err != nil {

View File

@@ -80,6 +80,7 @@ func (h *AllTimeHandler) loadUserSummary(user *models.User, filters *models.Filt
summaryParams.User,
retrieveSummary,
filters,
nil,
summaryParams.Recompute,
)
if err != nil {

View File

@@ -145,7 +145,7 @@ func (h *StatsHandler) loadUserSummary(user *models.User, start, end time.Time,
Recompute: false,
}
summary, err := h.summarySrvc.Aliased(overallParams.From, overallParams.To, user, h.summarySrvc.Retrieve, filters, false)
summary, err := h.summarySrvc.Aliased(overallParams.From, overallParams.To, user, h.summarySrvc.Retrieve, filters, nil, false)
if err != nil {
return nil, err, http.StatusInternalServerError
}

View File

@@ -96,7 +96,7 @@ func (h *StatusBarHandler) loadUserSummary(user *models.User, start, end time.Ti
retrieveSummary = h.summarySrvc.Summarize
}
summary, err := h.summarySrvc.Aliased(summaryParams.From, summaryParams.To, summaryParams.User, retrieveSummary, nil, summaryParams.Recompute)
summary, err := h.summarySrvc.Aliased(summaryParams.From, summaryParams.To, summaryParams.User, retrieveSummary, nil, nil, summaryParams.Recompute)
if err != nil {
return nil, http.StatusInternalServerError, err
}

View File

@@ -139,7 +139,7 @@ func (h *SummariesHandler) loadUserSummaries(r *http.Request, user *models.User)
filters := helpers.ParseSummaryFilters(r)
for i, interval := range intervals {
summary, err := h.summarySrvc.Aliased(interval[0], interval[1], user, h.summarySrvc.Retrieve, filters, end.After(time.Now()))
summary, err := h.summarySrvc.Aliased(interval[0], interval[1], user, h.summarySrvc.Retrieve, filters, nil, end.After(time.Now()))
if err != nil {
return nil, err, http.StatusInternalServerError
}

View File

@@ -844,7 +844,7 @@ func (h *SettingsHandler) regenerateSummaries(user *models.User) error {
return err
}
if err := h.aggregationSrvc.AggregateSummaries(datastructure.New(user.ID)); err != nil {
if err := h.aggregationSrvc.AggregateSummaries(datastructure.New(user.ID)); err != nil { // involves regenerating durations as well
conf.Log().Error("failed to regenerate summaries", "error", err)
return err
}

View File

@@ -135,7 +135,7 @@ func (h *SummaryHandler) fetchSplitSummaries(params *models.SummaryParams) ([]*m
summaries := make([]*models.Summary, 0)
intervals := utils.SplitRangeByDays(params.From, params.To)
for _, interval := range intervals {
curSummary, err := h.summarySrvc.Aliased(interval[0], interval[1], params.User, h.summarySrvc.Retrieve, params.Filters, false)
curSummary, err := h.summarySrvc.Aliased(interval[0], interval[1], params.User, h.summarySrvc.Retrieve, params.Filters, nil, false)
if err != nil {
return nil, err
}

View File

@@ -29,6 +29,7 @@ func LoadUserSummaryByParams(ss services.ISummaryService, params *models.Summary
params.User,
retrieveSummary,
params.Filters,
nil,
params.Recompute,
)
if err != nil {

View File

@@ -85,7 +85,7 @@ func (s *ActivityService) getChartPastYear(user *models.User, darkTheme, hideAtt
interval := interval
wp.Submit(func() {
summary, err := s.summaryService.Retrieve(interval[0], interval[1], user, nil)
summary, err := s.summaryService.Retrieve(interval[0], interval[1], user, nil, nil)
if err != nil {
config.Log().Warn("failed to retrieve summary for activity chart", "userID", user.ID, "from", from, "to", to)
summary = models.NewEmptySummary()

View File

@@ -180,7 +180,7 @@ func (srv *AggregationService) AggregateDurations(userIds datastructure.Set[stri
func (srv *AggregationService) process(job AggregationJob) {
// process single summary interval for single user
slog.Info("regenerating actual user summaries as part of summary aggregation", "user", job.User.ID, "from", job.From, "to", job.To)
if summary, err := srv.summaryService.Summarize(job.From, job.To, job.User, nil); err != nil {
if summary, err := srv.summaryService.Summarize(job.From, job.To, job.User, nil, nil); err != nil {
config.Log().Error("failed to regenerate summary", "from", job.From, "to", job.To, "userID", job.User.ID, "error", err)
} else {
slog.Info("successfully generated summary", "from", job.From, "to", job.To, "userID", job.User.ID)

View File

@@ -61,16 +61,16 @@ func NewDurationService(durationRepository repositories.IDurationRepository, hea
return srv
}
func (srv *DurationService) Get(from, to time.Time, user *models.User, filters *models.Filters, customInterval *time.Duration, skipCache bool) (durations models.Durations, err error) {
func (srv *DurationService) Get(from, to time.Time, user *models.User, filters *models.Filters, customTimeout *time.Duration, skipCache bool) (durations models.Durations, err error) {
// note about "multi-level" durations at different intervals:
// while durations themselves store the interval (aka. heartbeats timeout) they were computed for, we currently don't support actually storing durations at different intervals
// if an interval different from the user's preference is requested, recompute durations live from heartbeats and skip cache
effectiveInterval := getEffectiveInterval(user, customInterval)
skipCache = skipCache || effectiveInterval != user.HeartbeatsTimeout()
effectiveTimeout := getEffectiveTimeout(user, customTimeout)
skipCache = skipCache || effectiveTimeout != user.HeartbeatsTimeout()
// recompute live
if skipCache {
durations, err = srv.getLive(from, to, user, effectiveInterval)
durations, err = srv.getLive(from, to, user, effectiveTimeout)
if err != nil {
return nil, err
}
@@ -92,7 +92,7 @@ func (srv *DurationService) Get(from, to time.Time, user *models.User, filters *
from = cached.Last().TimeEnd().Add(time.Second)
}
missing, err := srv.getLive(from, to, user, effectiveInterval)
missing, err := srv.getLive(from, to, user, effectiveTimeout)
if err != nil {
return nil, err
}
@@ -318,11 +318,11 @@ func (srv *DurationService) filtersToColumnMap(filters *models.Filters) map[stri
return columnMap
}
func getEffectiveInterval(user *models.User, overrideInterval *time.Duration) time.Duration {
if overrideInterval == nil {
func getEffectiveTimeout(user *models.User, overrideTimeout *time.Duration) time.Duration {
if overrideTimeout == nil {
return user.HeartbeatsTimeout()
}
return *overrideInterval
return *overrideTimeout
}
func updateDurationEntity(d *models.Duration, h *models.Heartbeat, entityDurations map[tuple.Tuple2[string, string]]time.Duration) *models.Duration {

View File

@@ -230,7 +230,8 @@ func (srv *LeaderboardService) GenerateByUser(user *models.User, interval *model
return nil, err
}
summary, err := srv.summaryService.Aliased(from, to, user, srv.summaryService.Retrieve, nil, false)
timeout := models.DefaultHeartbeatsTimeout
summary, err := srv.summaryService.Aliased(from, to, user, srv.summaryService.Retrieve, nil, &timeout, false)
if err != nil {
return nil, err
}
@@ -251,7 +252,7 @@ func (srv *LeaderboardService) GenerateAggregatedByUser(user *models.User, inter
return nil, err
}
summary, err := srv.summaryService.Aliased(from, to, user, srv.summaryService.Retrieve, nil, false)
summary, err := srv.summaryService.Aliased(from, to, user, srv.summaryService.Retrieve, nil, nil, false)
if err != nil {
return nil, err
}

View File

@@ -239,7 +239,7 @@ func (srv *MiscService) NotifyExpiringSubscription() {
}
func (srv *MiscService) countUserTotalTime(userId string) time.Duration {
result, err := srv.summaryService.Aliased(time.Time{}, time.Now(), &models.User{ID: userId}, srv.summaryService.Retrieve, nil, false)
result, err := srv.summaryService.Aliased(time.Time{}, time.Now(), &models.User{ID: userId}, srv.summaryService.Retrieve, nil, nil, false)
if err != nil {
config.Log().Error("failed to count total for user", "userID", userId, "error", err)
return 0

View File

@@ -102,7 +102,7 @@ func (srv *ReportService) SendReport(user *models.User, duration time.Duration)
end := time.Now().In(user.TZ())
start := time.Now().Add(-1 * duration)
fullSummary, err := srv.summaryService.Aliased(start, end, user, srv.summaryService.Retrieve, nil, false)
fullSummary, err := srv.summaryService.Aliased(start, end, user, srv.summaryService.Retrieve, nil, nil, false)
if err != nil {
config.Log().Error("failed to regenerate report", "userID", user.ID, "error", err)
return err
@@ -114,7 +114,7 @@ func (srv *ReportService) SendReport(user *models.User, duration time.Duration)
for i, interval := range dayIntervals {
from, to := datetime.BeginOfDay(interval[0]), interval[1]
summary, err := srv.summaryService.Aliased(from, to, user, srv.summaryService.Retrieve, nil, false)
summary, err := srv.summaryService.Aliased(from, to, user, srv.summaryService.Retrieve, nil, nil, false)
if err != nil {
config.Log().Error("failed to regenerate day summary for report", "from", from, "to", to, "userID", user.ID, "error", err)
break

View File

@@ -97,9 +97,9 @@ type IDurationService interface {
}
type ISummaryService interface {
Aliased(time.Time, time.Time, *models.User, types.SummaryRetriever, *models.Filters, bool) (*models.Summary, error)
Retrieve(time.Time, time.Time, *models.User, *models.Filters) (*models.Summary, error)
Summarize(time.Time, time.Time, *models.User, *models.Filters) (*models.Summary, error)
Aliased(time.Time, time.Time, *models.User, types.SummaryRetriever, *models.Filters, *time.Duration, bool) (*models.Summary, error)
Retrieve(time.Time, time.Time, *models.User, *models.Filters, *time.Duration) (*models.Summary, error)
Summarize(time.Time, time.Time, *models.User, *models.Filters, *time.Duration) (*models.Summary, error)
GetLatestByUser() ([]*models.TimeByUser, error)
DeleteByUser(string) error
DeleteByUserBefore(string, time.Time) error

View File

@@ -13,6 +13,7 @@ import (
"github.com/patrickmn/go-cache"
"log/slog"
"sort"
"strconv"
"strings"
"time"
)
@@ -53,9 +54,11 @@ func NewSummaryService(summaryRepo repositories.ISummaryRepository, heartbeatSer
// Public summary generation methods
// Aliased retrieves or computes a new summary based on the given SummaryRetriever and augments it with entity aliases and project labels
func (srv *SummaryService) Aliased(from, to time.Time, user *models.User, f types.SummaryRetriever, filters *models.Filters, skipCache bool) (*models.Summary, error) {
func (srv *SummaryService) Aliased(from, to time.Time, user *models.User, f types.SummaryRetriever, filters *models.Filters, customTimeout *time.Duration, skipCache bool) (*models.Summary, error) {
requestedTimeout := getEffectiveTimeout(user, customTimeout)
// Check cache (or skip for sub second-level date precision)
cacheKey := srv.getHash(from.String(), to.String(), user.ID, filters.Hash(), "--aliased")
cacheKey := srv.getHash(from.String(), to.String(), user.ID, filters.Hash(), strconv.Itoa(int(requestedTimeout)), "--aliased")
if to.Truncate(time.Second).Equal(to) && from.Truncate(time.Second).Equal(from) {
if cacheResult, ok := srv.cache.Get(cacheKey); ok && !skipCache {
return cacheResult.(*models.Summary), nil
@@ -79,7 +82,7 @@ func (srv *SummaryService) Aliased(from, to time.Time, user *models.User, f type
}
// Get actual summary
s, err := f(from, to, user, filters)
s, err := f(from, to, user, filters, customTimeout)
if err != nil {
return nil, err
}
@@ -99,13 +102,17 @@ func (srv *SummaryService) Aliased(from, to time.Time, user *models.User, f type
return summary.Sorted().InTZ(user.TZ()), nil
}
func (srv *SummaryService) Retrieve(from, to time.Time, user *models.User, filters *models.Filters) (*models.Summary, error) {
func (srv *SummaryService) Retrieve(from, to time.Time, user *models.User, filters *models.Filters, customTimeout *time.Duration) (*models.Summary, error) {
summaries := make([]*models.Summary, 0)
requestedTimeout := getEffectiveTimeout(user, customTimeout)
// Filtered summaries are not persisted currently
// Filtered summaries or summaries at alternative timeouts are not persisted currently
// Special case: if (a) filters apply to only one entity type and (b) we're only interested in the summary items of that particular entity type,
// we can still fetch the persisted summary and drop all irrelevant parts from it
if filters == nil || filters.IsEmpty() || (filters.CountDistinctTypes() == 1 && filters.SelectFilteredOnly) {
requiresFiltering := filters != nil && !filters.IsEmpty() && !(filters.CountDistinctTypes() == 1 || filters.SelectFilteredOnly)
mustRecompute := requiresFiltering || requestedTimeout != user.HeartbeatsTimeout()
if !mustRecompute {
// Get all already existing, pre-generated summaries that fall into the requested interval
result, err := srv.repository.GetByUserWithin(user, from, to)
if err == nil {
@@ -118,7 +125,7 @@ func (srv *SummaryService) Retrieve(from, to time.Time, user *models.User, filte
// Generate missing slots (especially before and after existing summaries) from durations (formerly raw heartbeats)
missingIntervals := srv.getMissingIntervals(from, to, summaries, false)
for _, interval := range missingIntervals {
if s, err := srv.Summarize(interval.Start, interval.End, user, filters); err == nil {
if s, err := srv.Summarize(interval.Start, interval.End, user, filters, customTimeout); err == nil {
if len(missingIntervals) > 2 && s.FromTime.T().Equal(s.ToTime.T()) {
// little hack here: GetAllWithin will query for >= from_date
// however, for "in-between" / intra-day missing intervals, we want strictly > from_date to prevent double-counting
@@ -146,9 +153,9 @@ func (srv *SummaryService) Retrieve(from, to time.Time, user *models.User, filte
return summary.Sorted().InTZ(user.TZ()), nil
}
func (srv *SummaryService) Summarize(from, to time.Time, user *models.User, filters *models.Filters) (*models.Summary, error) {
func (srv *SummaryService) Summarize(from, to time.Time, user *models.User, filters *models.Filters, customTimeout *time.Duration) (*models.Summary, error) {
// Initialize and fetch data
durations, err := srv.durationService.Get(from, to, user, filters, nil, false)
durations, err := srv.durationService.Get(from, to, user, filters, customTimeout, false)
if err != nil {
return nil, err
}

View File

@@ -121,7 +121,7 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Summarize() {
from, to = suite.TestStartTime.Add(-1*time.Hour), suite.TestStartTime.Add(-1*time.Minute)
suite.DurationService.On("Get", from, to, suite.TestUser, mock.Anything, mock.Anything, false).Return(filterDurations(from, to, suite.TestDurations), nil)
result, err = sut.Summarize(from, to, suite.TestUser, nil)
result, err = sut.Summarize(from, to, suite.TestUser, nil, nil)
assert.Nil(suite.T(), err)
assert.NotNil(suite.T(), result)
@@ -135,7 +135,7 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Summarize() {
from, to = suite.TestStartTime.Add(-1*time.Hour), suite.TestStartTime.Add(1*time.Second)
suite.DurationService.On("Get", from, to, suite.TestUser, mock.Anything, mock.Anything, false).Return(filterDurations(from, to, suite.TestDurations), nil)
result, err = sut.Summarize(from, to, suite.TestUser, nil)
result, err = sut.Summarize(from, to, suite.TestUser, nil, nil)
assert.Nil(suite.T(), err)
assert.NotNil(suite.T(), result)
@@ -149,7 +149,7 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Summarize() {
from, to = suite.TestStartTime, suite.TestStartTime.Add(1*time.Hour)
suite.DurationService.On("Get", from, to, suite.TestUser, mock.Anything, mock.Anything, false).Return(filterDurations(from, to, suite.TestDurations), nil)
result, err = sut.Summarize(from, to, suite.TestUser, nil)
result, err = sut.Summarize(from, to, suite.TestUser, nil, nil)
assert.Nil(suite.T(), err)
assert.NotNil(suite.T(), result)
@@ -211,7 +211,7 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Retrieve() {
suite.DurationService.On("Get", from, summaries[0].FromTime.T(), suite.TestUser, mock.Anything, mock.Anything, false).Return(models.Durations{}, nil)
suite.DurationService.On("Get", summaries[0].ToTime.T(), to, suite.TestUser, mock.Anything, mock.Anything, false).Return(models.Durations{}, nil)
result, err = sut.Retrieve(from, to, suite.TestUser, nil)
result, err = sut.Retrieve(from, to, suite.TestUser, nil, nil)
assert.Nil(suite.T(), err)
assert.NotNil(suite.T(), result)
@@ -264,7 +264,7 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Retrieve() {
suite.SummaryRepository.On("GetByUserWithin", suite.TestUser, from, to).Return(summaries, nil)
suite.DurationService.On("Get", from, summaries[0].FromTime.T(), suite.TestUser, mock.Anything, mock.Anything, false).Return(filterDurations(from, summaries[0].FromTime.T(), suite.TestDurations), nil)
result, err = sut.Retrieve(from, to, suite.TestUser, nil)
result, err = sut.Retrieve(from, to, suite.TestUser, nil, nil)
assert.Nil(suite.T(), err)
assert.NotNil(suite.T(), result)
@@ -320,7 +320,7 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Retrieve() {
suite.SummaryRepository.On("GetByUserWithin", suite.TestUser, from, to).Return(summaries, nil)
suite.DurationService.On("Get", summaries[0].ToTime.T(), summaries[1].FromTime.T(), suite.TestUser, mock.Anything, mock.Anything, false).Return(filterDurations(summaries[0].ToTime.T(), summaries[1].FromTime.T(), suite.TestDurations), nil)
result, err = sut.Retrieve(from, to, suite.TestUser, nil)
result, err = sut.Retrieve(from, to, suite.TestUser, nil, nil)
assert.Nil(suite.T(), err)
assert.NotNil(suite.T(), result)
@@ -371,7 +371,7 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Retrieve_DuplicateSumma
suite.DurationService.On("Get", from, summaries[0].FromTime.T(), suite.TestUser, mock.Anything, mock.Anything, false).Return(models.Durations{}, nil)
suite.DurationService.On("Get", summaries[0].ToTime.T(), to, suite.TestUser, mock.Anything, mock.Anything, false).Return(models.Durations{}, nil)
result, err = sut.Retrieve(from, to, suite.TestUser, nil)
result, err = sut.Retrieve(from, to, suite.TestUser, nil, nil)
assert.Nil(suite.T(), err)
assert.NotNil(suite.T(), result)
@@ -414,7 +414,7 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Aliased() {
suite.AliasService.On("GetAliasOrDefault", TestUserId, mock.Anything, mock.Anything).Return("", nil)
suite.ProjectLabelService.On("GetByUser", suite.TestUser.ID).Return(suite.TestLabels, nil).Once()
result, err = sut.Aliased(from, to, suite.TestUser, sut.Summarize, nil, false)
result, err = sut.Aliased(from, to, suite.TestUser, sut.Summarize, nil, nil, false)
assert.Nil(suite.T(), err)
assert.NotNil(suite.T(), result)
@@ -455,7 +455,7 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Aliased_ProjectLabels()
suite.AliasService.On("GetAliasOrDefault", TestUserId, mock.Anything, TestProject2).Return(TestProject1, nil)
suite.AliasService.On("GetAliasOrDefault", TestUserId, mock.Anything, mock.Anything).Return("", nil)
result, err = sut.Aliased(from, to, suite.TestUser, sut.Summarize, nil, false)
result, err = sut.Aliased(from, to, suite.TestUser, sut.Summarize, nil, nil, false)
assert.Nil(suite.T(), err)
assert.NotNil(suite.T(), result)
@@ -463,6 +463,34 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Aliased_ProjectLabels()
assert.Equal(suite.T(), 6, result.NumHeartbeats)
}
func (suite *SummaryServiceTestSuite) TestSummaryService_Aliased_CustomTimeout() {
sut := NewSummaryService(suite.SummaryRepository, suite.HeartbeatService, suite.DurationService, suite.AliasService, suite.ProjectLabelService)
var (
from time.Time
to time.Time
err error
)
from, to = suite.TestStartTime, suite.TestStartTime.Add(1*time.Hour)
testSummary := &models.Summary{
FromTime: models.CustomTime(from),
ToTime: models.CustomTime(to),
}
suite.SummaryRepository.On("GetByUserWithin", suite.TestUser, from, to).Return([]*models.Summary{testSummary}, nil)
_, err = sut.Retrieve(from, to, suite.TestUser, nil, nil)
assert.Nil(suite.T(), err)
suite.DurationService.On("Get", from, to, suite.TestUser, mock.Anything, mock.Anything, false).Return(models.Durations([]*models.Duration{}), nil)
customTimeout := 1337 * time.Minute
_, err = sut.Retrieve(from, to, suite.TestUser, nil, &customTimeout)
assert.Nil(suite.T(), err)
suite.DurationService.AssertExpectations(suite.T())
}
func (suite *SummaryServiceTestSuite) TestSummaryService_Filters() {
sut := NewSummaryService(suite.SummaryRepository, suite.HeartbeatService, suite.DurationService, suite.AliasService, suite.ProjectLabelService)
@@ -492,7 +520,7 @@ func (suite *SummaryServiceTestSuite) TestSummaryService_Filters() {
suite.TestLabels[1].Label: suite.TestLabels[1:2],
}, nil).Once()
result, _ := sut.Aliased(from, to, suite.TestUser, sut.Summarize, filters, false)
result, _ := sut.Aliased(from, to, suite.TestUser, sut.Summarize, filters, nil, false)
assert.NotNil(suite.T(), result.Branches) // project filters were applied -> include branches
assert.NotNil(suite.T(), result.Entities) // project filters were applied -> include entities