diff --git a/data/types/common/common_suite_test.go b/data/types/common/common_suite_test.go new file mode 100644 index 000000000..5f35361ef --- /dev/null +++ b/data/types/common/common_suite_test.go @@ -0,0 +1,11 @@ +package common_test + +import ( + "testing" + + "github.com/tidepool-org/platform/test" +) + +func TestSuite(t *testing.T) { + test.Test(t) +} diff --git a/data/types/common/day.go b/data/types/common/day.go new file mode 100644 index 000000000..186164d88 --- /dev/null +++ b/data/types/common/day.go @@ -0,0 +1,57 @@ +package common + +const ( + DaySunday = "sunday" + DayMonday = "monday" + DayTuesday = "tuesday" + DayWednesday = "wednesday" + DayThursday = "thursday" + DayFriday = "friday" + DaySaturday = "saturday" +) + +func DaysOfWeek() []string { + return []string{ + DaySunday, + DayMonday, + DayTuesday, + DayWednesday, + DayThursday, + DayFriday, + DaySaturday, + } +} + +type DaysOfWeekByDayIndex []string + +func (d DaysOfWeekByDayIndex) Len() int { + return len(d) +} +func (d DaysOfWeekByDayIndex) Swap(i int, j int) { + d[i], d[j] = d[j], d[i] +} + +func (d DaysOfWeekByDayIndex) Less(i int, j int) bool { + return DayIndex(d[i]) < DayIndex(d[j]) +} + +func DayIndex(day string) int { + switch day { + case DaySunday: + return 1 + case DayMonday: + return 2 + case DayTuesday: + return 3 + case DayWednesday: + return 4 + case DayThursday: + return 5 + case DayFriday: + return 6 + case DaySaturday: + return 7 + default: + return 0 + } +} diff --git a/data/types/common/day_test.go b/data/types/common/day_test.go new file mode 100644 index 000000000..c12e1a939 --- /dev/null +++ b/data/types/common/day_test.go @@ -0,0 +1,76 @@ +package common_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/tidepool-org/platform/data/types/common" +) + +var _ = Describe("Day", func() { + + It("DaySunday is expected", func() { + Expect(common.DaySunday).To(Equal("sunday")) + }) + + It("DayMonday is expected", func() { + Expect(common.DayMonday).To(Equal("monday")) + }) + + It("DayTuesday is expected", func() { + Expect(common.DayTuesday).To(Equal("tuesday")) + }) + + It("DayWednesday is expected", func() { + Expect(common.DayWednesday).To(Equal("wednesday")) + }) + + It("DayThursday is expected", func() { + Expect(common.DayThursday).To(Equal("thursday")) + }) + + It("DayFriday is expected", func() { + Expect(common.DayFriday).To(Equal("friday")) + }) + + It("DaySaturday is expected", func() { + Expect(common.DaySaturday).To(Equal("saturday")) + }) + + It("DaysOfWeek returns expected", func() { + Expect(common.DaysOfWeek()).To(Equal([]string{"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"})) + Expect(common.DaysOfWeek()).To(Equal([]string{ + common.DaySunday, + common.DayMonday, + common.DayTuesday, + common.DayWednesday, + common.DayThursday, + common.DayFriday, + common.DaySaturday, + })) + }) + + Context("DayIndex", func() { + DescribeTable("return the expected index when the day", + func(day string, expectedIndex int) { + Expect(common.DayIndex(day)).To(Equal(expectedIndex)) + }, + Entry("is an empty string", "", 0), + Entry("is sunday", "sunday", 1), + Entry("is constant sunday", common.DaySunday, 1), + Entry("is monday", "monday", 2), + Entry("is constant monday", common.DayMonday, 2), + Entry("is tuesday", "tuesday", 3), + Entry("is constant tuesday", common.DayTuesday, 3), + Entry("is wednesday", "wednesday", 4), + Entry("isconstant wednesday", common.DayWednesday, 4), + Entry("is thursday", "thursday", 5), + Entry("is constant thursday", common.DayThursday, 5), + Entry("is friday", "friday", 6), + Entry("is constant friday", common.DayFriday, 6), + Entry("is saturday", "saturday", 7), + Entry("is constant saturday", common.DaySaturday, 7), + Entry("is an invalid string", "invalid", 0), + ) + }) +}) diff --git a/data/types/settings/cgm/scheduled_alert.go b/data/types/settings/cgm/scheduled_alert.go index 34178c7d1..9a447aff3 100644 --- a/data/types/settings/cgm/scheduled_alert.go +++ b/data/types/settings/cgm/scheduled_alert.go @@ -3,6 +3,7 @@ package cgm import ( "strconv" + "github.com/tidepool-org/platform/data/types/common" "github.com/tidepool-org/platform/structure" structureValidator "github.com/tidepool-org/platform/structure/validator" ) @@ -12,14 +13,6 @@ const ( ScheduledAlertNameLengthMaximum = 100 - ScheduledAlertDaysSunday = "sunday" - ScheduledAlertDaysMonday = "monday" - ScheduledAlertDaysTuesday = "tuesday" - ScheduledAlertDaysWednesday = "wednesday" - ScheduledAlertDaysThursday = "thursday" - ScheduledAlertDaysFriday = "friday" - ScheduledAlertDaysSaturday = "saturday" - ScheduledAlertStartMaximum = 86400000 ScheduledAlertStartMinimum = 0 @@ -27,18 +20,6 @@ const ( ScheduledAlertEndMinimum = 0 ) -func ScheduledAlertDays() []string { - return []string{ - ScheduledAlertDaysSunday, - ScheduledAlertDaysMonday, - ScheduledAlertDaysTuesday, - ScheduledAlertDaysWednesday, - ScheduledAlertDaysThursday, - ScheduledAlertDaysFriday, - ScheduledAlertDaysSaturday, - } -} - type ScheduledAlerts []*ScheduledAlert func ParseScheduledAlerts(parser structure.ArrayParser) *ScheduledAlerts { @@ -107,7 +88,7 @@ func (s *ScheduledAlert) Parse(parser structure.ObjectParser) { func (s *ScheduledAlert) Validate(validator structure.Validator) { validator.String("name", s.Name).NotEmpty().LengthLessThanOrEqualTo(ScheduledAlertNameLengthMaximum) - validator.StringArray("days", s.Days).Exists().EachOneOf(ScheduledAlertDays()...).EachUnique() + validator.StringArray("days", s.Days).Exists().EachOneOf(common.DaysOfWeek()...).EachUnique() validator.Int("start", s.Start).Exists().InRange(ScheduledAlertStartMinimum, ScheduledAlertStartMaximum) validator.Int("end", s.End).Exists().InRange(ScheduledAlertEndMinimum, ScheduledAlertEndMaximum) if alertsValidator := validator.WithReference("alerts"); s.Alerts != nil { diff --git a/data/types/settings/cgm/scheduled_alert_test.go b/data/types/settings/cgm/scheduled_alert_test.go index 606c6975e..99da23ed7 100644 --- a/data/types/settings/cgm/scheduled_alert_test.go +++ b/data/types/settings/cgm/scheduled_alert_test.go @@ -5,6 +5,8 @@ import ( . "github.com/onsi/gomega" dataTypesSettingsCgm "github.com/tidepool-org/platform/data/types/settings/cgm" + + dataTypesCommon "github.com/tidepool-org/platform/data/types/common" dataTypesSettingsCgmTest "github.com/tidepool-org/platform/data/types/settings/cgm/test" errorsTest "github.com/tidepool-org/platform/errors/test" "github.com/tidepool-org/platform/pointer" @@ -21,34 +23,6 @@ var _ = Describe("ScheduledAlert", func() { Expect(dataTypesSettingsCgm.ScheduledAlertNameLengthMaximum).To(Equal(100)) }) - It("ScheduledAlertDaysSunday is expected", func() { - Expect(dataTypesSettingsCgm.ScheduledAlertDaysSunday).To(Equal("sunday")) - }) - - It("ScheduledAlertDaysMonday is expected", func() { - Expect(dataTypesSettingsCgm.ScheduledAlertDaysMonday).To(Equal("monday")) - }) - - It("ScheduledAlertDaysTuesday is expected", func() { - Expect(dataTypesSettingsCgm.ScheduledAlertDaysTuesday).To(Equal("tuesday")) - }) - - It("ScheduledAlertDaysWednesday is expected", func() { - Expect(dataTypesSettingsCgm.ScheduledAlertDaysWednesday).To(Equal("wednesday")) - }) - - It("ScheduledAlertDaysThursday is expected", func() { - Expect(dataTypesSettingsCgm.ScheduledAlertDaysThursday).To(Equal("thursday")) - }) - - It("ScheduledAlertDaysFriday is expected", func() { - Expect(dataTypesSettingsCgm.ScheduledAlertDaysFriday).To(Equal("friday")) - }) - - It("ScheduledAlertDaysSaturday is expected", func() { - Expect(dataTypesSettingsCgm.ScheduledAlertDaysSaturday).To(Equal("saturday")) - }) - It("ScheduledAlertStartMaximum is expected", func() { Expect(dataTypesSettingsCgm.ScheduledAlertStartMaximum).To(Equal(86400000)) }) @@ -65,10 +39,6 @@ var _ = Describe("ScheduledAlert", func() { Expect(dataTypesSettingsCgm.ScheduledAlertEndMinimum).To(Equal(0)) }) - It("ScheduledAlertDays returns expected", func() { - Expect(dataTypesSettingsCgm.ScheduledAlertDays()).To(Equal([]string{"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"})) - }) - Context("ParseScheduledAlerts", func() { // TODO }) @@ -204,20 +174,20 @@ var _ = Describe("ScheduledAlert", func() { ), Entry("days contains invalid", func(datum *dataTypesSettingsCgm.ScheduledAlert) { - datum.Days = pointer.FromStringArray(append([]string{"invalid"}, test.RandomStringArrayFromRangeAndArrayWithoutDuplicates(0, len(dataTypesSettingsCgm.ScheduledAlertDays())-1, dataTypesSettingsCgm.ScheduledAlertDays())...)) + datum.Days = pointer.FromStringArray(append([]string{"invalid"}, test.RandomStringArrayFromRangeAndArrayWithoutDuplicates(0, len(dataTypesCommon.DaysOfWeek())-1, dataTypesCommon.DaysOfWeek())...)) }, errorsTest.WithPointerSource(structureValidator.ErrorValueStringNotOneOf("invalid", []string{"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"}), "/days/0"), ), Entry("days contains duplicate", func(datum *dataTypesSettingsCgm.ScheduledAlert) { - duplicate := test.RandomStringFromArray(dataTypesSettingsCgm.ScheduledAlertDays()) + duplicate := test.RandomStringFromArray(dataTypesCommon.DaysOfWeek()) datum.Days = pointer.FromStringArray([]string{duplicate, duplicate}) }, errorsTest.WithPointerSource(structureValidator.ErrorValueDuplicate(), "/days/1"), ), Entry("days valid", func(datum *dataTypesSettingsCgm.ScheduledAlert) { - datum.Days = pointer.FromStringArray(test.RandomStringArrayFromRangeAndArrayWithoutDuplicates(1, len(dataTypesSettingsCgm.ScheduledAlertDays()), dataTypesSettingsCgm.ScheduledAlertDays())) + datum.Days = pointer.FromStringArray(test.RandomStringArrayFromRangeAndArrayWithoutDuplicates(1, len(dataTypesCommon.DaysOfWeek()), dataTypesCommon.DaysOfWeek())) }, ), Entry("start missing", diff --git a/data/types/settings/cgm/test/scheduled_alert.go b/data/types/settings/cgm/test/scheduled_alert.go index 2ba30b036..11c3c255d 100644 --- a/data/types/settings/cgm/test/scheduled_alert.go +++ b/data/types/settings/cgm/test/scheduled_alert.go @@ -1,6 +1,7 @@ package test import ( + "github.com/tidepool-org/platform/data/types/common" dataTypesSettingsCgm "github.com/tidepool-org/platform/data/types/settings/cgm" "github.com/tidepool-org/platform/pointer" "github.com/tidepool-org/platform/test" @@ -39,7 +40,7 @@ func NewArrayFromScheduledAlerts(datum *dataTypesSettingsCgm.ScheduledAlerts, ob func RandomScheduledAlert() *dataTypesSettingsCgm.ScheduledAlert { datum := dataTypesSettingsCgm.NewScheduledAlert() datum.Name = pointer.FromString(test.RandomStringFromRange(1, dataTypesSettingsCgm.ScheduledAlertNameLengthMaximum)) - datum.Days = pointer.FromStringArray(test.RandomStringArrayFromRangeAndArrayWithoutDuplicates(1, len(dataTypesSettingsCgm.ScheduledAlertDays()), dataTypesSettingsCgm.ScheduledAlertDays())) + datum.Days = pointer.FromStringArray(test.RandomStringArrayFromRangeAndArrayWithoutDuplicates(1, len(common.DaysOfWeek()), common.DaysOfWeek())) datum.Start = pointer.FromInt(test.RandomIntFromRange(dataTypesSettingsCgm.ScheduledAlertStartMinimum, dataTypesSettingsCgm.ScheduledAlertStartMaximum)) datum.End = pointer.FromInt(test.RandomIntFromRange(dataTypesSettingsCgm.ScheduledAlertEndMinimum, dataTypesSettingsCgm.ScheduledAlertEndMaximum)) datum.Alerts = RandomAlerts() diff --git a/data/types/settings/pump/pump.go b/data/types/settings/pump/pump.go index f5e1396fa..9afccb8df 100644 --- a/data/types/settings/pump/pump.go +++ b/data/types/settings/pump/pump.go @@ -58,6 +58,7 @@ type Pump struct { OverridePresets *OverridePresetMap `json:"overridePresets,omitempty" bson:"overridePresets,omitempty"` ScheduleTimeZoneOffset *int `json:"scheduleTimeZoneOffset,omitempty" bson:"scheduleTimeZoneOffset,omitempty"` SerialNumber *string `json:"serialNumber,omitempty" bson:"serialNumber,omitempty"` + SleepSchedules *SleepScheduleMap `json:"sleepSchedules,omitempty" bson:"sleepSchedules,omitempty"` SoftwareVersion *string `json:"softwareVersion,omitempty" bson:"softwareVersion,omitempty"` Units *Units `json:"units,omitempty" bson:"units,omitempty"` // TODO: Move into appropriate structs } @@ -101,6 +102,7 @@ func (p *Pump) Parse(parser structure.ObjectParser) { p.Name = parser.String("name") p.OverridePresets = ParseOverridePresetMap(parser.WithReferenceObjectParser("overridePresets")) p.ScheduleTimeZoneOffset = parser.Int("scheduleTimeZoneOffset") + p.SleepSchedules = ParseSleepScheduleMap(parser.WithReferenceObjectParser("sleepSchedules")) p.SerialNumber = parser.String("serialNumber") p.SoftwareVersion = parser.String("softwareVersion") p.Units = ParseUnits(parser.WithReferenceObjectParser("units")) @@ -194,6 +196,9 @@ func (p *Pump) Validate(validator structure.Validator) { if p.OverridePresets != nil { p.OverridePresets.Validate(validator.WithReference("overridePresets"), unitsBloodGlucose) } + if p.SleepSchedules != nil { + p.SleepSchedules.Validate(validator.WithReference("sleepSchedules")) + } validator.Int("scheduleTimeZoneOffset", p.ScheduleTimeZoneOffset).InRange(ScheduleTimeZoneOffsetMinimum, ScheduleTimeZoneOffsetMaximum) validator.String("serialNumber", p.SerialNumber).NotEmpty().LengthLessThanOrEqualTo(SerialNumberLengthMaximum) validator.String("softwareVersion", p.SoftwareVersion).NotEmpty().LengthLessThanOrEqualTo(SoftwareVersionLengthMaximum) @@ -270,6 +275,9 @@ func (p *Pump) Normalize(normalizer data.Normalizer) { if p.OverridePresets != nil { p.OverridePresets.Normalize(normalizer.WithReference("overridePresets"), unitsBloodGlucose) } + if p.SleepSchedules != nil { + p.SleepSchedules.Normalize(normalizer.WithReference("sleepSchedules")) + } if p.Units != nil { p.Units.Normalize(normalizer.WithReference("units")) } diff --git a/data/types/settings/pump/pump_test.go b/data/types/settings/pump/pump_test.go index 077a2b031..64a65fd75 100644 --- a/data/types/settings/pump/pump_test.go +++ b/data/types/settings/pump/pump_test.go @@ -59,6 +59,7 @@ var _ = Describe("Pump", func() { Expect(datum.Name).To(BeNil()) Expect(datum.OverridePresets).To(BeNil()) Expect(datum.ScheduleTimeZoneOffset).To(BeNil()) + Expect(datum.SleepSchedules).To(BeNil()) Expect(datum.SerialNumber).To(BeNil()) Expect(datum.SoftwareVersion).To(BeNil()) Expect(datum.Units).To(BeNil()) @@ -652,6 +653,35 @@ var _ = Describe("Pump", func() { }, errorsTest.WithPointerSourceAndMeta(structureValidator.ErrorLengthNotLessThanOrEqualTo(101, 100), "/serialNumber", pumpTest.NewMeta()), ), + Entry("sleep schedules missing", + pointer.FromString("mmol/L"), + func(datum *pump.Pump, unitsBloodGlucose *string) { + datum.SleepSchedules = nil + }, + ), + Entry("sleep schedules empty", + pointer.FromString("mmol/L"), + func(datum *pump.Pump, unitsBloodGlucose *string) { + datum.SleepSchedules = pump.NewSleepScheduleMap() + }, + ), + Entry("sleep schedules valid", + pointer.FromString("mmol/L"), + func(datum *pump.Pump, unitsBloodGlucose *string) { + datum.SleepSchedules = pumpTest.RandomSleepSchedules(3) + }, + ), + Entry("sleep schedules invalid", + pointer.FromString("mmol/L"), + func(datum *pump.Pump, unitsBloodGlucose *string) { + datum.SleepSchedules = pumpTest.RandomSleepSchedules(2) + (*datum.SleepSchedules)[pumpTest.SleepScheduleName(0)].End = pointer.FromInt(pump.SleepSchedulesMidnightOffsetMaximum + 1) + }, + errorsTest.WithPointerSourceAndMeta(structureValidator.ErrorValueNotInRange( + pump.SleepSchedulesMidnightOffsetMaximum+1, 0, + pump.SleepSchedulesMidnightOffsetMaximum), + fmt.Sprintf("/sleepSchedules/%s/end", pumpTest.SleepScheduleName(0)), pumpTest.NewMeta()), + ), Entry("software version missing", pointer.FromString("mmol/L"), func(datum *pump.Pump, units *string) { datum.SoftwareVersion = nil }, diff --git a/data/types/settings/pump/sleep_schedule.go b/data/types/settings/pump/sleep_schedule.go new file mode 100644 index 000000000..0da251b14 --- /dev/null +++ b/data/types/settings/pump/sleep_schedule.go @@ -0,0 +1,109 @@ +package pump + +import ( + "sort" + + "github.com/tidepool-org/platform/data" + "github.com/tidepool-org/platform/data/types/common" + "github.com/tidepool-org/platform/structure" + structureValidator "github.com/tidepool-org/platform/structure/validator" +) + +const ( + SleepSchedulesMidnightOffsetMaximum = 86400 + SleepSchedulesMidnightOffsetMinimum = 0 +) + +type SleepScheduleMap map[string]*SleepSchedule + +func ParseSleepScheduleMap(parser structure.ObjectParser) *SleepScheduleMap { + if !parser.Exists() { + return nil + } + datum := NewSleepScheduleMap() + parser.Parse(datum) + return datum +} + +func NewSleepScheduleMap() *SleepScheduleMap { + return &SleepScheduleMap{} +} + +func (s *SleepScheduleMap) Parse(parser structure.ObjectParser) { + for _, reference := range parser.References() { + s.Set(reference, ParseSleepSchedule(parser.WithReferenceObjectParser(reference))) + } +} + +func (s *SleepScheduleMap) Validate(validator structure.Validator) { + for _, name := range s.sortedNames() { + datumValidator := validator.WithReference(name) + if datum := s.Get(name); datum != nil { + datum.Validate(datumValidator) + } else { + datumValidator.ReportError(structureValidator.ErrorValueNotExists()) + } + } +} + +func (s *SleepScheduleMap) Normalize(normalizer data.Normalizer) {} + +func (s *SleepScheduleMap) Get(name string) *SleepSchedule { + if datum, exists := (*s)[name]; exists { + return datum + } + return nil +} + +func (s *SleepScheduleMap) Set(name string, datum *SleepSchedule) { + (*s)[name] = datum +} + +func (s *SleepScheduleMap) sortedNames() []string { + names := []string{} + for name := range *s { + names = append(names, name) + } + sort.Strings(names) + return names +} + +type SleepSchedule struct { + Enabled *bool `json:"enabled,omitempty" bson:"enabled,omitempty"` + Days *[]string `json:"days,omitempty" bson:"days,omitempty"` + Start *int `json:"start,omitempty" bson:"start,omitempty"` + End *int `json:"end,omitempty" bson:"end,omitempty"` +} + +func ParseSleepSchedule(parser structure.ObjectParser) *SleepSchedule { + if !parser.Exists() { + return nil + } + datum := NewSleepSchedule() + parser.Parse(datum) + return datum +} + +func NewSleepSchedule() *SleepSchedule { + return &SleepSchedule{} +} + +func (s *SleepSchedule) Parse(parser structure.ObjectParser) { + s.Enabled = parser.Bool("enabled") + s.Days = parser.StringArray("days") + s.Start = parser.Int("start") + s.End = parser.Int("end") +} + +func (s *SleepSchedule) Validate(validator structure.Validator) { + validator.Bool("enabled", s.Enabled).Exists() + if s.Enabled != nil { + if *s.Enabled { + validator.StringArray("days", s.Days).Exists().EachOneOf(common.DaysOfWeek()...).EachUnique() + validator.Int("start", s.Start).Exists().InRange(SleepSchedulesMidnightOffsetMinimum, SleepSchedulesMidnightOffsetMaximum) + validator.Int("end", s.End).Exists().InRange(SleepSchedulesMidnightOffsetMinimum, SleepSchedulesMidnightOffsetMaximum) + } + } +} + +func (s *SleepSchedule) Normalize(normalizer data.Normalizer) {} diff --git a/data/types/settings/pump/sleep_schedule_test.go b/data/types/settings/pump/sleep_schedule_test.go new file mode 100644 index 000000000..3c0f2d7b3 --- /dev/null +++ b/data/types/settings/pump/sleep_schedule_test.go @@ -0,0 +1,217 @@ +package pump_test + +import ( + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + dataTypesCommon "github.com/tidepool-org/platform/data/types/common" + dataTypesSettingsPump "github.com/tidepool-org/platform/data/types/settings/pump" + dataTypesSettingsPumpTest "github.com/tidepool-org/platform/data/types/settings/pump/test" + errorsTest "github.com/tidepool-org/platform/errors/test" + "github.com/tidepool-org/platform/pointer" + structureValidator "github.com/tidepool-org/platform/structure/validator" + "github.com/tidepool-org/platform/test" +) + +var _ = Describe("SleepSchedule", func() { + + Context("NewSleepSchedules", func() { + It("returns successfully with default values", func() { + datum := dataTypesSettingsPump.NewSleepScheduleMap() + Expect(datum).ToNot(BeNil()) + Expect(*datum).To(BeEmpty()) + }) + }) + + Context("SleepSchedules", func() { + + Context("Validate", func() { + DescribeTable("validates the datum", + func(mutator func(datum *dataTypesSettingsPump.SleepScheduleMap), expectedErrors ...error) { + datum := dataTypesSettingsPumpTest.RandomSleepSchedules(3) + mutator(datum) + errorsTest.ExpectEqual(structureValidator.New().Validate(datum), expectedErrors...) + }, + Entry("succeeds", + func(datum *dataTypesSettingsPump.SleepScheduleMap) {}, + ), + Entry("empty", + func(datum *dataTypesSettingsPump.SleepScheduleMap) { + *datum = *dataTypesSettingsPump.NewSleepScheduleMap() + }, + ), + Entry("has one", + func(datum *dataTypesSettingsPump.SleepScheduleMap) { + *datum = *dataTypesSettingsPumpTest.RandomSleepSchedules(1) + }, + ), + Entry("has many", + func(datum *dataTypesSettingsPump.SleepScheduleMap) { + *datum = *dataTypesSettingsPumpTest.RandomSleepSchedules(19) + }, + ), + Entry("entry missing", + func(datum *dataTypesSettingsPump.SleepScheduleMap) { + (*datum)[dataTypesSettingsPumpTest.SleepScheduleName(0)] = nil + }, + errorsTest.WithPointerSource(structureValidator.ErrorValueNotExists(), fmt.Sprintf("/%s", dataTypesSettingsPumpTest.SleepScheduleName(0))), + ), + Entry("multiple errors", + func(datum *dataTypesSettingsPump.SleepScheduleMap) { + *datum = *dataTypesSettingsPumpTest.RandomSleepSchedules(3) + (*datum)[dataTypesSettingsPumpTest.SleepScheduleName(1)] = nil + }, + errorsTest.WithPointerSource(structureValidator.ErrorValueNotExists(), fmt.Sprintf("/%s", dataTypesSettingsPumpTest.SleepScheduleName(1))), + ), + ) + }) + }) + + Context("NewSleepSchedule", func() { + It("returns successfully with default values", func() { + datum := dataTypesSettingsPump.NewSleepSchedule() + Expect(datum).ToNot(BeNil()) + Expect(datum.Enabled).To(BeNil()) + Expect(datum.Days).To(BeNil()) + Expect(datum.Start).To(BeNil()) + Expect(datum.End).To(BeNil()) + }) + }) + + Context("SleepSchedule", func() { + DescribeTable("serializes the datum as expected", + func(mutator func(datum *dataTypesSettingsPump.SleepSchedule)) { + datum := dataTypesSettingsPumpTest.RandomSleepSchedule() + mutator(datum) + test.ExpectSerializedObjectBSON(datum, dataTypesSettingsPumpTest.NewObjectFromSleepSchedule(datum, test.ObjectFormatBSON)) + test.ExpectSerializedObjectJSON(datum, dataTypesSettingsPumpTest.NewObjectFromSleepSchedule(datum, test.ObjectFormatJSON)) + }, + Entry("succeeds", + func(datum *dataTypesSettingsPump.SleepSchedule) {}, + ), + Entry("empty", + func(datum *dataTypesSettingsPump.SleepSchedule) { *datum = dataTypesSettingsPump.SleepSchedule{} }, + ), + ) + + Context("Validate", func() { + DescribeTable("validates the datum", + func(mutator func(datum *dataTypesSettingsPump.SleepSchedule), expectedErrors ...error) { + datum := dataTypesSettingsPumpTest.RandomSleepSchedule() + mutator(datum) + errorsTest.ExpectEqual(structureValidator.New().Validate(datum), expectedErrors...) + }, + Entry("succeeds", + func(datum *dataTypesSettingsPump.SleepSchedule) {}, + ), + Entry("enabled empty", + func(datum *dataTypesSettingsPump.SleepSchedule) { datum.Enabled = nil }, + errorsTest.WithPointerSource(structureValidator.ErrorValueNotExists(), "/enabled"), + ), + + Entry("days missing", + func(datum *dataTypesSettingsPump.SleepSchedule) { + datum.Days = nil + }, + errorsTest.WithPointerSource(structureValidator.ErrorValueNotExists(), "/days"), + ), + Entry("days contains invalid", + func(datum *dataTypesSettingsPump.SleepSchedule) { + datum.Days = pointer.FromStringArray(append([]string{"invalid"}, test.RandomStringArrayFromRangeAndArrayWithoutDuplicates(0, len(dataTypesCommon.DaysOfWeek())-1, dataTypesCommon.DaysOfWeek())...)) + }, + errorsTest.WithPointerSource(structureValidator.ErrorValueStringNotOneOf("invalid", []string{"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"}), "/days/0"), + ), + Entry("days contains duplicate", + func(datum *dataTypesSettingsPump.SleepSchedule) { + duplicate := test.RandomStringFromArray(dataTypesCommon.DaysOfWeek()) + datum.Days = pointer.FromStringArray([]string{duplicate, duplicate}) + }, + errorsTest.WithPointerSource(structureValidator.ErrorValueDuplicate(), "/days/1"), + ), + Entry("days valid", + func(datum *dataTypesSettingsPump.SleepSchedule) { + datum.Days = pointer.FromStringArray(test.RandomStringArrayFromRangeAndArrayWithoutDuplicates(1, len(dataTypesCommon.DaysOfWeek()), dataTypesCommon.DaysOfWeek())) + }, + ), + Entry("start missing", + func(datum *dataTypesSettingsPump.SleepSchedule) { + datum.Start = nil + }, + errorsTest.WithPointerSource(structureValidator.ErrorValueNotExists(), "/start"), + ), + Entry("start out of range (lower)", + func(datum *dataTypesSettingsPump.SleepSchedule) { + datum.Start = pointer.FromInt(-1) + }, + errorsTest.WithPointerSource(structureValidator.ErrorValueNotInRange(-1, + dataTypesSettingsPump.SleepSchedulesMidnightOffsetMinimum, + dataTypesSettingsPump.SleepSchedulesMidnightOffsetMaximum), "/start"), + ), + Entry("start in range (lower)", + func(datum *dataTypesSettingsPump.SleepSchedule) { + datum.Start = pointer.FromInt(dataTypesSettingsPump.SleepSchedulesMidnightOffsetMinimum) + }, + ), + Entry("start in range (upper)", + func(datum *dataTypesSettingsPump.SleepSchedule) { + datum.Start = pointer.FromInt(dataTypesSettingsPump.SleepSchedulesMidnightOffsetMaximum) + }, + ), + Entry("start out of range (upper)", + func(datum *dataTypesSettingsPump.SleepSchedule) { + datum.Start = pointer.FromInt(dataTypesSettingsPump.SleepSchedulesMidnightOffsetMaximum + 1) + }, + errorsTest.WithPointerSource(structureValidator.ErrorValueNotInRange( + dataTypesSettingsPump.SleepSchedulesMidnightOffsetMaximum+1, + dataTypesSettingsPump.SleepSchedulesMidnightOffsetMinimum, + dataTypesSettingsPump.SleepSchedulesMidnightOffsetMaximum), "/start"), + ), + Entry("end missing", + func(datum *dataTypesSettingsPump.SleepSchedule) { + datum.End = nil + }, + errorsTest.WithPointerSource(structureValidator.ErrorValueNotExists(), "/end"), + ), + Entry("end out of range (lower)", + func(datum *dataTypesSettingsPump.SleepSchedule) { + datum.End = pointer.FromInt(-1) + }, + errorsTest.WithPointerSource(structureValidator.ErrorValueNotInRange(-1, + dataTypesSettingsPump.SleepSchedulesMidnightOffsetMinimum, + dataTypesSettingsPump.SleepSchedulesMidnightOffsetMaximum), "/end"), + ), + Entry("end in range (lower)", + func(datum *dataTypesSettingsPump.SleepSchedule) { + datum.End = pointer.FromInt(dataTypesSettingsPump.SleepSchedulesMidnightOffsetMinimum) + }, + ), + Entry("end in range (upper)", + func(datum *dataTypesSettingsPump.SleepSchedule) { + datum.End = pointer.FromInt(dataTypesSettingsPump.SleepSchedulesMidnightOffsetMaximum) + }, + ), + Entry("end out of range (upper)", + func(datum *dataTypesSettingsPump.SleepSchedule) { + datum.End = pointer.FromInt(dataTypesSettingsPump.SleepSchedulesMidnightOffsetMaximum + 1) + }, + errorsTest.WithPointerSource(structureValidator.ErrorValueNotInRange( + dataTypesSettingsPump.SleepSchedulesMidnightOffsetMaximum+1, + dataTypesSettingsPump.SleepSchedulesMidnightOffsetMinimum, + dataTypesSettingsPump.SleepSchedulesMidnightOffsetMaximum), "/end"), + ), + Entry("multiple errors", + func(datum *dataTypesSettingsPump.SleepSchedule) { + datum.Days = nil + datum.Start = nil + datum.End = nil + }, + errorsTest.WithPointerSource(structureValidator.ErrorValueNotExists(), "/days"), + errorsTest.WithPointerSource(structureValidator.ErrorValueNotExists(), "/start"), + errorsTest.WithPointerSource(structureValidator.ErrorValueNotExists(), "/end"), + ), + ) + }) + }) +}) diff --git a/data/types/settings/pump/test/sleep_schedule.go b/data/types/settings/pump/test/sleep_schedule.go new file mode 100644 index 000000000..bc8c54799 --- /dev/null +++ b/data/types/settings/pump/test/sleep_schedule.go @@ -0,0 +1,87 @@ +package test + +import ( + "fmt" + + dataTypesCommon "github.com/tidepool-org/platform/data/types/common" + dataTypesSettingsPump "github.com/tidepool-org/platform/data/types/settings/pump" + "github.com/tidepool-org/platform/pointer" + "github.com/tidepool-org/platform/test" +) + +func SleepScheduleName(index int) string { + return fmt.Sprintf("schedule-%d", index) +} + +func RandomSleepSchedules(count int) *dataTypesSettingsPump.SleepScheduleMap { + datum := dataTypesSettingsPump.NewSleepScheduleMap() + for i := 0; i < count; i++ { + (*datum)[SleepScheduleName(i)] = RandomSleepSchedule() + } + return datum +} + +func CloneSleepSchedules(datum *dataTypesSettingsPump.SleepScheduleMap) *dataTypesSettingsPump.SleepScheduleMap { + if datum == nil { + return nil + } + clone := make(dataTypesSettingsPump.SleepScheduleMap, len(*datum)) + for index, d := range *datum { + clone[index] = CloneSleepSchedule(d) + } + return &clone +} + +func NewArrayFromSleepSchedules(datum *dataTypesSettingsPump.SleepScheduleMap, objectFormat test.ObjectFormat) []interface{} { + if datum == nil { + return nil + } + array := []interface{}{} + for _, d := range *datum { + array = append(array, NewObjectFromSleepSchedule(d, objectFormat)) + } + return array +} + +func RandomSleepSchedule() *dataTypesSettingsPump.SleepSchedule { + datum := dataTypesSettingsPump.NewSleepSchedule() + // enabled by default, if not enabled days, start and end not required + datum.Enabled = pointer.FromBool(true) + datum.Days = pointer.FromStringArray(test.RandomStringArrayFromRangeAndArrayWithoutDuplicates(1, len(dataTypesCommon.DaysOfWeek()), dataTypesCommon.DaysOfWeek())) + datum.Start = pointer.FromInt(test.RandomIntFromRange(dataTypesSettingsPump.SleepSchedulesMidnightOffsetMinimum, dataTypesSettingsPump.SleepSchedulesMidnightOffsetMaximum)) + datum.End = pointer.FromInt(test.RandomIntFromRange(dataTypesSettingsPump.SleepSchedulesMidnightOffsetMinimum, dataTypesSettingsPump.SleepSchedulesMidnightOffsetMaximum)) + return datum +} + +func CloneSleepSchedule(datum *dataTypesSettingsPump.SleepSchedule) *dataTypesSettingsPump.SleepSchedule { + if datum == nil { + return nil + } + clone := dataTypesSettingsPump.NewSleepSchedule() + clone.Enabled = pointer.CloneBool(datum.Enabled) + clone.Days = pointer.CloneStringArray(datum.Days) + clone.Start = pointer.CloneInt(datum.Start) + clone.End = pointer.CloneInt(datum.End) + return clone +} + +func NewObjectFromSleepSchedule(datum *dataTypesSettingsPump.SleepSchedule, objectFormat test.ObjectFormat) map[string]interface{} { + if datum == nil { + return nil + } + object := map[string]interface{}{} + if datum.Enabled != nil { + object["enabled"] = test.NewObjectFromBool(*datum.Enabled, objectFormat) + } + if datum.Days != nil { + object["days"] = test.NewObjectFromStringArray(*datum.Days, objectFormat) + } + if datum.Start != nil { + object["start"] = test.NewObjectFromInt(*datum.Start, objectFormat) + } + if datum.End != nil { + object["end"] = test.NewObjectFromInt(*datum.End, objectFormat) + } + + return object +} diff --git a/dexcom/fetch/translate.go b/dexcom/fetch/translate.go index d2f403382..3f4fbaf6d 100644 --- a/dexcom/fetch/translate.go +++ b/dexcom/fetch/translate.go @@ -8,6 +8,7 @@ import ( dataBloodGlucose "github.com/tidepool-org/platform/data/blood/glucose" dataTypes "github.com/tidepool-org/platform/data/types" dataTypesActivityPhysical "github.com/tidepool-org/platform/data/types/activity/physical" + "github.com/tidepool-org/platform/data/types/common" dataTypesAlert "github.com/tidepool-org/platform/data/types/alert" dataTypesBloodGlucoseContinuous "github.com/tidepool-org/platform/data/types/blood/glucose/continuous" @@ -205,19 +206,19 @@ func translateAlertScheduleSettingsDaysOfWeekToScheduledAlertDays(daysOfWeek *[] func translateAlertScheduleSettingsDayOfWeekToScheduledAlertDay(dayOfWeek string) string { switch dayOfWeek { case dexcom.AlertScheduleSettingsDaySunday: - return dataTypesSettingsCgm.ScheduledAlertDaysSunday + return common.DaySunday case dexcom.AlertScheduleSettingsDayMonday: - return dataTypesSettingsCgm.ScheduledAlertDaysMonday + return common.DayMonday case dexcom.AlertScheduleSettingsDayTuesday: - return dataTypesSettingsCgm.ScheduledAlertDaysTuesday + return common.DayTuesday case dexcom.AlertScheduleSettingsDayWednesday: - return dataTypesSettingsCgm.ScheduledAlertDaysWednesday + return common.DayWednesday case dexcom.AlertScheduleSettingsDayThursday: - return dataTypesSettingsCgm.ScheduledAlertDaysThursday + return common.DayThursday case dexcom.AlertScheduleSettingsDayFriday: - return dataTypesSettingsCgm.ScheduledAlertDaysFriday + return common.DayFriday case dexcom.AlertScheduleSettingsDaySaturday: - return dataTypesSettingsCgm.ScheduledAlertDaysSaturday + return common.DaySaturday } return "" }