/// <summary>
            /// Adjusts the time boundaries.
            /// NOTE: significantly improves the performance of the tests.
            /// </summary>
            static ScheduleTimeBounds AdjustDates(ScheduleTimeBounds dates)
            {
                // set current year for all the input dates
                var dateFromDiff = DateTime.Now.Year - dates.DateFrom.Year;

                if (dateFromDiff != 0)
                {
                    dates.DateFrom = dates.DateFrom.AddYears(dateFromDiff);
                }

                var dateToDiff = DateTime.Now.Year - dates.DateTo.Year;

                if (dateToDiff != 0)
                {
                    dates.DateTo = dates.DateTo.AddYears(dateToDiff);
                }

                var nowDiff = DateTime.Now.Year - dates.Now.Year;

                if (nowDiff != 0)
                {
                    dates.Now = dates.Now.AddYears(nowDiff);
                }

                // adjusts the time boundaries
                if (dates.DateFrom > dates.DateTo)
                {
                    (dates.DateFrom, dates.DateTo) = (dates.DateTo, dates.DateFrom); // swap
                }
                if (dates.Now < dates.DateFrom)
                {
                    (dates.Now, dates.DateFrom) = (dates.DateFrom, dates.Now); // swap
                }
                else if (dates.Now > dates.DateTo)
                {
                    (dates.Now, dates.DateTo) = (dates.DateTo, dates.Now); // swap
                }
                return(dates);
            }
        public void Build_walk_price_divided_on_dog_price_per_walk_should_equal_dogs_count_in_pack(List <DogPack> dogPacks, ScheduleTimeBounds scheduleBounds)
        {
            // Arrange
            var payload = new WeeklySchedulePayloadBuilder()
                          .With(p => p.DogPacks = dogPacks)
                          .With(p => p.DateFrom = scheduleBounds.DateFrom)
                          .With(p => p.DateTo   = scheduleBounds.DateTo)
                          .With(p => p.Now      = scheduleBounds.Now)
                          .SetupWeekDays()
                          .Build();

            // Act
            var walks = _weeklyScheduleBuilder.Build(payload).ToList();

            // Assert
            var walksWithWrongTotal = walks.Where(NotValidPrice).ToList();

            Assert.AreEqual(0, walksWithWrongTotal.Count);

            bool NotValidPrice(Walk walk) =>
            (walk.Price / PriceForDogPerWalk(walk)) != walk.DogPack.Dogs.Count;

            decimal PriceForDogPerWalk(Walk walk) =>
            PriceRate(GetDogPackType(walk.DogPack)).PricePerWalk;

            PriceRate PriceRate((DogSize size, bool isAggressive) data) =>
            payload.PriceRates.Find(pr => pr.DogSize == data.size &&
                                    pr.IsAggressive == data.isAggressive);
        }
        public void Build_walking_datetime_should_be_between_given_DateFrom_and_DateTo(List <DogPack> dogPacks, ScheduleTimeBounds scheduleBounds)
        {
            // Arrange
            var payload = new WeeklySchedulePayloadBuilder()
                          .With(p => p.DogPacks = dogPacks)
                          .With(p => p.DateFrom = scheduleBounds.DateFrom)
                          .With(p => p.DateTo   = scheduleBounds.DateTo)
                          .With(p => p.Now      = scheduleBounds.Now)
                          .SetupWeekDays()
                          .Build();

            // Act
            var walks = _weeklyScheduleBuilder.Build(payload).ToList();

            // Assert
            var outOfTimeBoundsWalks = walks.Where(w =>
                                                   w.StartDateTime <payload.DateFrom &&
                                                                    w.StartDateTime> payload.DateTo).ToList();

            Assert.AreEqual(0, outOfTimeBoundsWalks.Count);
        }
        public void Build_the_walks_with_empty_dog_packs_shouldnt_be_scheduled(List <DogPack> dogPacks, ScheduleTimeBounds scheduleBounds)
        {
            // Arrange
            var payload = new WeeklySchedulePayloadBuilder()
                          .With(p => p.DogPacks = dogPacks)
                          .With(p => p.DateFrom = scheduleBounds.DateFrom)
                          .With(p => p.DateTo   = scheduleBounds.DateTo)
                          .With(p => p.Now      = scheduleBounds.Now)
                          .SetupWeekDays()
                          .Build();

            // Act
            var walks = _weeklyScheduleBuilder.Build(payload);

            // Assert
            var allWithDogs = walks.All(w => w.DogPack?.Dogs.Any() ?? false);

            Assert.True(allWithDogs);
        }
        public void Build_walks_should_be_scheduled_at_given_days_and_time_only(List <DogPack> dogPacks, ScheduleTimeBounds scheduleBounds)
        {
            // Arrange
            var payload = new WeeklySchedulePayloadBuilder()
                          .With(p => p.DogPacks = dogPacks)
                          .With(p => p.DateFrom = scheduleBounds.DateFrom)
                          .With(p => p.DateTo   = scheduleBounds.DateTo)
                          .With(p => p.Now      = scheduleBounds.Now)
                          .SetupWeekDays()
                          .Build();

            // Act
            var walks = _weeklyScheduleBuilder.Build(payload);

            // Assert
            var isMatchWorkingHours = walks.All(w => IsMatchWorkingHours(w.StartDateTime));

            Assert.True(isMatchWorkingHours);

            bool IsMatchWorkingHours(DateTime walkingTime) =>
            payload.WorkingDays
            .FirstOrDefault(x => x.DayOfWeek == walkingTime.DayOfWeek)
            ?.WorkingHours.Any(wh => wh == walkingTime.TimeOfDay) ?? false;
        }