From 015aeba0c2e88f996b6d966a72a1684e3ced991d Mon Sep 17 00:00:00 2001 From: johan-autohome Date: Fri, 3 Jan 2025 19:11:50 +0100 Subject: [PATCH] Isrolling fixes and add tests --- internal/chore/handler.go | 7 ++- internal/chore/scheduler.go | 6 +- internal/chore/scheduler_test.go | 99 ++++++++++++++++++++++++++++---- 3 files changed, 97 insertions(+), 15 deletions(-) diff --git a/internal/chore/handler.go b/internal/chore/handler.go index fe69fa4..238a813 100644 --- a/internal/chore/handler.go +++ b/internal/chore/handler.go @@ -195,14 +195,17 @@ func (h *Handler) createChore(c *gin.Context) { } circleUsers, err := h.circleRepo.GetCircleUsers(c, currentUser.CircleID) + if err != nil { + log.Print(err) + c.JSON(500, gin.H{"error": "Error getting circle users"}) + return + } for _, assignee := range choreReq.Assignees { userFound := false for _, circleUser := range circleUsers { if assignee.UserID == circleUser.UserID { userFound = true - break - } } if !userFound { diff --git a/internal/chore/scheduler.go b/internal/chore/scheduler.go index 4098fe7..4324610 100644 --- a/internal/chore/scheduler.go +++ b/internal/chore/scheduler.go @@ -24,14 +24,14 @@ func scheduleNextDueDate(chore *chModel.Chore, completedDate time.Time) (*time.T return nil, nil } - if chore.NextDueDate != nil { + if chore.NextDueDate != nil && !chore.IsRolling { // no due date set, use the current date - baseDate = chore.NextDueDate.UTC() } else { baseDate = completedDate.UTC() } - if chore.FrequencyType == "day_of_the_month" || chore.FrequencyType == "days_of_the_week" || chore.FrequencyType == "interval" { + + if !chore.IsRolling && (chore.FrequencyType == "day_of_the_month" || chore.FrequencyType == "days_of_the_week" || chore.FrequencyType == "interval") { // time in frequency metadata stored as RFC3339 format like `2024-07-07T13:27:00-04:00` // parse it to time.Time: t, err := time.Parse(time.RFC3339, frequencyMetadata.Time) diff --git a/internal/chore/scheduler_test.go b/internal/chore/scheduler_test.go index 21d064a..087fca6 100644 --- a/internal/chore/scheduler_test.go +++ b/internal/chore/scheduler_test.go @@ -2,6 +2,7 @@ package chore import ( "encoding/json" + "fmt" "testing" "time" @@ -11,10 +12,13 @@ import ( func TestScheduleNextDueDateBasic(t *testing.T) { choreTime := time.Now() freqencyMetadataBytes := `{"time":"2024-07-07T14:30:00-04:00"}` + intervalFreqencyMetadataBytes := `{"time":"2024-07-07T14:30:00-04:00", "unit": "days"}` testTable := []struct { - chore *chModel.Chore - expected time.Time + Name string + chore *chModel.Chore + completedAt time.Time + expected time.Time }{ { chore: &chModel.Chore{ @@ -22,7 +26,28 @@ func TestScheduleNextDueDateBasic(t *testing.T) { NextDueDate: &choreTime, FrequencyMetadata: &freqencyMetadataBytes, }, - expected: choreTime.AddDate(0, 0, 1), + completedAt: choreTime, + expected: choreTime.AddDate(0, 0, 1), + }, + { // Completed 1 day late + chore: &chModel.Chore{ + FrequencyType: chModel.FrequancyTypeDaily, + NextDueDate: &choreTime, + FrequencyMetadata: &freqencyMetadataBytes, + }, + completedAt: choreTime.AddDate(0, 0, 1), + expected: choreTime.AddDate(0, 0, 1), + }, + { + Name: "Rolling completed 1 day late", + chore: &chModel.Chore{ + FrequencyType: chModel.FrequancyTypeDaily, + NextDueDate: &choreTime, + FrequencyMetadata: &freqencyMetadataBytes, + IsRolling: true, + }, + completedAt: choreTime.AddDate(0, 0, 1), + expected: choreTime.AddDate(0, 0, 1+1), }, { chore: &chModel.Chore{ @@ -30,7 +55,8 @@ func TestScheduleNextDueDateBasic(t *testing.T) { NextDueDate: &choreTime, FrequencyMetadata: &freqencyMetadataBytes, }, - expected: choreTime.AddDate(0, 0, 7), + completedAt: choreTime, + expected: choreTime.AddDate(0, 0, 7), }, { chore: &chModel.Chore{ @@ -38,7 +64,8 @@ func TestScheduleNextDueDateBasic(t *testing.T) { NextDueDate: &choreTime, FrequencyMetadata: &freqencyMetadataBytes, }, - expected: choreTime.AddDate(0, 1, 0), + completedAt: choreTime, + expected: choreTime.AddDate(0, 1, 0), }, { chore: &chModel.Chore{ @@ -46,19 +73,71 @@ func TestScheduleNextDueDateBasic(t *testing.T) { NextDueDate: &choreTime, FrequencyMetadata: &freqencyMetadataBytes, }, - expected: choreTime.AddDate(1, 0, 0), + completedAt: choreTime, + expected: choreTime.AddDate(1, 0, 0), + }, + { + Name: "14 days interval Rolling Completed in time", + chore: &chModel.Chore{ + FrequencyType: chModel.FrequancyTypeIntervel, + NextDueDate: &choreTime, + FrequencyMetadata: &intervalFreqencyMetadataBytes, + Frequency: 14, + IsRolling: true, + }, + completedAt: choreTime, + expected: choreTime.AddDate(0, 0, 14), + }, + { + Name: "14 days interval Rolling Completed late", + chore: &chModel.Chore{ + FrequencyType: chModel.FrequancyTypeIntervel, + NextDueDate: &choreTime, + FrequencyMetadata: &intervalFreqencyMetadataBytes, + Frequency: 14, + IsRolling: true, + }, + completedAt: choreTime.AddDate(0, 0, 1), + expected: choreTime.AddDate(0, 0, 14+1), + }, + { + Name: "14 days interval Completed in time", + chore: &chModel.Chore{ + FrequencyType: chModel.FrequancyTypeIntervel, + NextDueDate: &choreTime, + FrequencyMetadata: &intervalFreqencyMetadataBytes, + Frequency: 14, + IsRolling: false, + }, + completedAt: choreTime, + expected: choreTime.AddDate(0, 0, 14).UTC().Truncate(time.Hour).Add(30 * time.Minute), // Same Minute as Metadata time? + }, + { + Name: "14 days interval Completed late", + chore: &chModel.Chore{ + FrequencyType: chModel.FrequancyTypeIntervel, + NextDueDate: &choreTime, + FrequencyMetadata: &intervalFreqencyMetadataBytes, + Frequency: 14, + IsRolling: false, + }, + completedAt: choreTime.AddDate(0, 0, 1), + expected: choreTime.AddDate(0, 0, 14).UTC().Truncate(time.Hour).Add(30 * time.Minute), // Same Minute as Metadata time? }, // } - for _, tt := range testTable { - t.Run(string(tt.chore.FrequencyType), func(t *testing.T) { + for i, tt := range testTable { + t.Run(fmt.Sprintf("%s %s %d", tt.chore.FrequencyType, tt.Name, i), func(t *testing.T) { - actual, err := scheduleNextDueDate(tt.chore, choreTime) + actual, err := scheduleNextDueDate(tt.chore, tt.completedAt) if err != nil { t.Errorf("Error: %v", err) + t.FailNow() } - if actual != nil && actual.UTC().Format(time.RFC3339) != tt.expected.UTC().Format(time.RFC3339) { + if actual == nil { + t.Errorf("Expected: %v, Error: Actual missing", tt.expected) + } else if actual.UTC().Format(time.RFC3339) != tt.expected.UTC().Format(time.RFC3339) { t.Errorf("Expected: %v, Actual: %v", tt.expected, actual) } })