public async System.Threading.Tasks.Task SendEventByIdQueryWithCorrectEventId()
        {
            var model = new EditViewModel { EventId = 1 };
            var mediator = new Mock<IMediator>();
            mediator.Setup(x => x.SendAsync(It.IsAny<EventByEventIdQuery>())).ReturnsAsync(new Event { Campaign = new Campaign() });

            var sut = new TaskEditViewModelValidator(mediator.Object);
            await sut.Validate(model);

            mediator.Verify(x => x.SendAsync(It.Is<EventByEventIdQuery>(y => y.EventId == model.EventId)), Times.Once);
        }
        public void ReturnsCorrectErrorWhenModelsEndDateTimeIsGreaterThanParentEventStartDate()
        {
            var validator = GetValidator();

            var model = new EditViewModel
            {
                StartDateTime = eventStartDate.AddDays(1),
                EndDateTime = eventEndDate.AddDays(10)
            };

            var errors = validator.Validate(model);

            Assert.True(errors.Exists(x => x.Key.Equals("EndDateTime")));
            Assert.Equal(errors.Find(x => x.Key == "EndDateTime").Value, "End date cannot be later than the event end date " + eventEndDate.ToString("d"));
        }
        public void ReturnsCorrectErrorWhenItineraryTaskWithStartAndEndDatesNotOnSameDay()
        {
            var validator = GetValidator();

            var model = new EditViewModel
            {
                StartDateTime = new DateTimeOffset(new DateTime(1999, 12, 1)),
                EndDateTime = new DateTimeOffset(new DateTime(2000, 12, 1))
            };

            var errors = validator.Validate(model);

            Assert.True(errors.Exists(x => x.Key.Equals("EndDateTime")));
            Assert.Equal(errors.Find(x => x.Key == "EndDateTime").Value, "For itinerary events the task end date must occur on the same day as the start date. Tasks cannot span multiple days");
        }
        public List<KeyValuePair<string, string>> Validate(EditViewModel model)
        {
            var result = new List<KeyValuePair<string, string>>();

            var campaignEvent = _mediator.Send(new EventByIdQuery { EventId = model.EventId });

            var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(campaignEvent.Campaign.TimeZoneId);

            // sgordon: Date time conversion seems overly complex and may be refactored per #710
            var startDateValue = model.StartDateTime;
            var startDateTimeOffset = timeZoneInfo.GetUtcOffset(startDateValue);
            var convertedStartDateTime = new DateTimeOffset(startDateValue.Year, startDateValue.Month, startDateValue.Day, startDateValue.Hour, startDateValue.Minute, 0, startDateTimeOffset);

            var endDateValue = model.EndDateTime;
            var endDateTimeOffset = timeZoneInfo.GetUtcOffset(endDateValue);
            var convertedEndDateTime = new DateTimeOffset(endDateValue.Year, endDateValue.Month, endDateValue.Day, endDateValue.Hour, endDateValue.Minute, 0, endDateTimeOffset);

            // Rule - End date cannot be earlier than start date
            if (convertedEndDateTime < convertedStartDateTime)
            {
                result.Add(new KeyValuePair<string, string>(nameof(model.EndDateTime), "End date cannot be earlier than the start date"));
            }

            // Rule - Start date cannot be out of range of parent event
            if (convertedStartDateTime < campaignEvent.StartDateTime)
            {
                result.Add(new KeyValuePair<string, string>(nameof(model.StartDateTime), "Start date cannot be earlier than the event start date " + campaignEvent.StartDateTime.ToString("d")));
            }

            // Rule - End date cannot be out of range of parent event
            if (convertedEndDateTime > campaignEvent.EndDateTime)
            {
                result.Add(new KeyValuePair<string, string>(nameof(model.EndDateTime), "End date cannot be later than the event end date " + campaignEvent.EndDateTime.ToString("d")));
            }

            // Rule - Itinerary tasks must start and end on same calendar day
            if (campaignEvent.EventType == EventType.Itinerary)
            {
                if (convertedStartDateTime.Date != convertedEndDateTime.Date)
                {
                    result.Add(new KeyValuePair<string, string>(nameof(model.EndDateTime), "For itinerary events the task end date must occur on the same day as the start date. Tasks cannot span multiple days"));
                }
            }

            return result;
        }
        public async System.Threading.Tasks.Task InvokeGetDateTimeOffsetWithCorrectParametersForEndDate()
        {
            var now = DateTimeOffset.Now;

            var model = new EditViewModel { EventId = 1, StartDateTime = now, EndDateTime = now };
            var @event = new Event { Campaign = new Campaign { TimeZoneId = "UTC" } };

            var mediator = new Mock<IMediator>();
            mediator.Setup(x => x.SendAsync(It.IsAny<EventByEventIdQuery>())).ReturnsAsync(@event);

            var dateTimeOffsetProvider = new Mock<IConvertDateTimeOffset>();

            var sut = new TaskEditViewModelValidator(mediator.Object, dateTimeOffsetProvider.Object);
            await sut.Validate(model);

            dateTimeOffsetProvider.Verify(x => x.ConvertDateTimeOffsetTo(@event.Campaign.TimeZoneId, model.EndDateTime, model.EndDateTime.Hour,
                model.EndDateTime.Minute, 0));
        }
        public void ReturnCorrectErrorWhenEndDateTimeIsLessThanStartDateTime()
        {
            var mockMediator = new Mock<IMediator>();
            mockMediator.Setup(x => x.Send(It.IsAny<EventByIdQuery>())).Returns(new AllReady.Models.Event { Id = 1, Campaign = new Campaign { TimeZoneId = "UTC" } });

            var validator = new TaskEditViewModelValidator(mockMediator.Object);

            var model = new EditViewModel
            {
                StartDateTime = new DateTimeOffset(new DateTime(2000, 1, 1)),
                EndDateTime = new DateTimeOffset(new DateTime(1999, 1, 1))
            };

            var errors = validator.Validate(model);

            Assert.True(errors.Exists(x => x.Key.Equals("EndDateTime")));
            Assert.Equal(errors.Find(x => x.Key == "EndDateTime").Value, "End date cannot be earlier than the start date");
        }
        public async Task CreateGetAssignsCancelUrlToModel()
        {
            const string cancelUrl = "url";
            var model = new EditViewModel { EventId = 1, OrganizationId = 1 };

            var mediator = new Mock<IMediator>();
            mediator.Setup(x => x.SendAsync(It.IsAny<CreateTaskQueryAsync>())).ReturnsAsync(model);

            var urlHelper = new Mock<IUrlHelper>();
            urlHelper.Setup(x => x.Action(It.IsAny<UrlActionContext>())).Returns(cancelUrl);

            var sut = new TaskController(mediator.Object, null) { Url = urlHelper.Object };
            sut.MakeUserAnOrgAdmin(model.OrganizationId.ToString());

            var result = await sut.Create(It.IsAny<int>()) as ViewResult;
            var modelResult = result.Model as EditViewModel;

            Assert.Equal(modelResult.CancelUrl, cancelUrl);
        }
        public async Task<IActionResult> Edit(EditViewModel viewModel)
        {
            var errors = await _taskEditViewModelValidator.Validate(viewModel);
            errors.ToList().ForEach(e => ModelState.AddModelError(e.Key, e.Value));

            if (ModelState.IsValid)
            {
                if (!User.IsOrganizationAdmin(viewModel.OrganizationId))
                {
                    return Unauthorized();
                }

                var taskId = await _mediator.SendAsync(new EditTaskCommand { Task = viewModel });

                return viewModel.Id == 0 ?
                    RedirectToAction(nameof(EventController.Details), "Event", new { id = viewModel.EventId }) :
                    RedirectToAction(nameof(Details), "Task", new { id = taskId });
            }

            return View(viewModel);
        }
        public async Task CreateGetInvokesUrlActionWithTheCorrectParameters()
        {
            var model = new EditViewModel { EventId = 1, OrganizationId = 1 };

            var mediator = new Mock<IMediator>();
            mediator.Setup(x => x.SendAsync(It.IsAny<CreateTaskQueryAsync>())).ReturnsAsync(model);

            var urlHelper = new Mock<IUrlHelper>();
            urlHelper.Setup(x => x.Action(It.IsAny<UrlActionContext>())).Returns(It.IsAny<string>());

            var sut = new TaskController(mediator.Object, null) { Url = urlHelper.Object };
            sut.MakeUserAnOrgAdmin(model.OrganizationId.ToString());

            await sut.Create(It.IsAny<int>());

            urlHelper.Verify(x => x.Action(It.Is<UrlActionContext>(y =>
                y.Action == nameof(EventController.Details) &&
                y.Controller == "Event" &&
                y.Values.ToString() == $"{{ id = {model.EventId}, area = Admin }}")),
            Times.Once);
        }
        public async Task<List<KeyValuePair<string, string>>> Validate(EditViewModel viewModel)
        {
            var result = new List<KeyValuePair<string, string>>();

            var @event = await _mediator.SendAsync(new EventByEventIdQuery { EventId = viewModel.EventId });

            var convertedStartDateTime = _convertDateTimeOffsets.ConvertDateTimeOffsetTo(@event.Campaign.TimeZoneId, viewModel.StartDateTime, viewModel.StartDateTime.Hour, viewModel.StartDateTime.Minute);
            var convertedEndDateTime = _convertDateTimeOffsets.ConvertDateTimeOffsetTo(@event.Campaign.TimeZoneId, viewModel.EndDateTime, viewModel.EndDateTime.Hour, viewModel.EndDateTime.Minute);

            // Rule - End date cannot be earlier than start date
            if (convertedEndDateTime < convertedStartDateTime)
            {
                result.Add(new KeyValuePair<string, string>(nameof(viewModel.EndDateTime), "End date cannot be earlier than the start date"));
            }

            // Rule - Start date cannot be out of range of parent event
            if (convertedStartDateTime < @event.StartDateTime)
            {
                result.Add(new KeyValuePair<string, string>(nameof(viewModel.StartDateTime), "Start date cannot be earlier than the event start date " + @event.StartDateTime.ToString("d")));
            }

            // Rule - End date cannot be out of range of parent event
            if (convertedEndDateTime > @event.EndDateTime)
            {
                result.Add(new KeyValuePair<string, string>(nameof(viewModel.EndDateTime), "End date cannot be later than the event end date " + @event.EndDateTime.ToString("d")));
            }

            // Rule - Itinerary tasks must start and end on same calendar day
            if (@event.EventType == EventType.Itinerary)
            {
                if (convertedStartDateTime.Date != convertedEndDateTime.Date)
                {
                    result.Add(new KeyValuePair<string, string>(nameof(viewModel.EndDateTime), "For itinerary events the task end date must occur on the same day as the start date. Tasks cannot span multiple days"));
                }
            }

            return result;
        }
        public async Task<List<KeyValuePair<string, string>>> Validate(EditViewModel viewModel)
        {
            var result = new List<KeyValuePair<string, string>>();

            var parentEvent = await _mediator.SendAsync(new EventByEventIdQuery { EventId = viewModel.EventId });


            // Rule - End date cannot be earlier than start date
            if (viewModel.EndDateTime < viewModel.StartDateTime)
            {
                result.Add(new KeyValuePair<string, string>(nameof(viewModel.EndDateTime), "End date cannot be earlier than the start date"));
            }

            // Rule - Start date cannot be out of range of parent event
            if (viewModel.StartDateTime < parentEvent.StartDateTime)
            {
                result.Add(new KeyValuePair<string, string>(nameof(viewModel.StartDateTime), String.Format("Start date cannot be earlier than the event start date {0:g}.", parentEvent.StartDateTime)));
            }

            // Rule - End date cannot be out of range of parent event
            if (viewModel.EndDateTime > parentEvent.EndDateTime)
            {
                result.Add(new KeyValuePair<string, string>(nameof(viewModel.EndDateTime), String.Format("The end date of this task cannot be after the end date of the event {0:g}", parentEvent.EndDateTime)));
            }

            // Rule - Itinerary tasks must start and end on same calendar day
            if (parentEvent.EventType == EventType.Itinerary)
            {
                if (viewModel.StartDateTime.Date != viewModel.EndDateTime.Date)
                {
                    result.Add(new KeyValuePair<string, string>(nameof(viewModel.EndDateTime), "For itinerary events the task end date must occur on the same day as the start date. Tasks cannot span multiple days"));
                }
            }

            return result;
        }
        public async Task CreateGetSetCorrectToTitleOnViewBag()
        {
            var model = new EditViewModel { EventId = 1, OrganizationId = 1 };

            var mediator = new Mock<IMediator>();
            mediator.Setup(x => x.SendAsync(It.IsAny<CreateTaskQueryAsync>())).ReturnsAsync(model);

            var urlHelper = new Mock<IUrlHelper>();
            urlHelper.Setup(x => x.Action(It.IsAny<UrlActionContext>())).Returns(It.IsAny<string>());

            var sut = new TaskController(mediator.Object, null) { Url = urlHelper.Object };
            sut.MakeUserAnOrgAdmin(model.OrganizationId.ToString());

            await sut.Create(It.IsAny<int>());

            Assert.Equal(sut.ViewBag.Title, "Create Task");
        }
        public async Task EditPostRedirectsToCorrectAction_WhenUpdatingTask()
        {
            const int organizationId = 1;

            var model = new EditViewModel { Id = 1, OrganizationId = organizationId };

            var mediator = new Mock<IMediator>();
            mediator.Setup(x => x.SendAsync(It.IsAny<EditTaskCommandAsync>())).ReturnsAsync(1);

            var validator = new Mock<ITaskEditViewModelValidator>();
            validator.Setup(x => x.Validate(It.IsAny<EditViewModel>())).Returns(new List<KeyValuePair<string, string>>());

            var sut = new TaskController(mediator.Object, validator.Object);
            sut.MakeUserAnOrgAdmin(organizationId.ToString());

            var result = await sut.Edit(model) as RedirectToActionResult;

            var routeValues = new Dictionary<string, object> { ["id"] = model.Id };

            Assert.Equal(result.ControllerName, "Task");
            Assert.Equal(result.ActionName, nameof(TaskController.Details));
            Assert.Equal(result.RouteValues, routeValues);
        }
        public async Task EditPostSendsEditTaskCommandAsyncWithCorrectModel_WhenModelStateIsValidAndUserIsOrganizationAdmin()
        {
            const int organizationId = 1;

            var model = new EditViewModel { OrganizationId = organizationId };

            var mediator = new Mock<IMediator>();
            mediator.Setup(x => x.SendAsync(It.IsAny<EditTaskCommandAsync>())).ReturnsAsync(1);

            var validator = new Mock<ITaskEditViewModelValidator>();
            validator.Setup(x => x.Validate(It.IsAny<EditViewModel>())).Returns(new List<KeyValuePair<string, string>>());

            var sut = new TaskController(mediator.Object, validator.Object);
            sut.MakeUserAnOrgAdmin(organizationId.ToString());

            await sut.Edit(model);

            mediator.Verify(x => x.SendAsync(It.Is<EditTaskCommandAsync>(y => y.Task == model)));
        }
        public async Task EditPostReturnsCorrectViewAndViewModelWhenValidatorReturnsError()
        {
            var model = new EditViewModel();

            var validator = new Mock<ITaskEditViewModelValidator>();
            validator.Setup(x => x.Validate(It.IsAny<EditViewModel>()))
                .Returns(new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("key", "value") });

            var sut = new TaskController(Mock.Of<IMediator>(), validator.Object);
            var result = await sut.Edit(model) as ViewResult;
            var modelResult = result.ViewData.Model as EditViewModel;

            Assert.IsType<ViewResult>(result);
            Assert.Equal(modelResult, model);
        }
        public async Task EditPostInvokesValidateOnTaskSummaryModelValidatorWithCorrectParameters()
        {
            var model = new EditViewModel { EndDateTime = DateTimeOffset.Now.AddDays(-1), StartDateTime = DateTimeOffset.Now.AddDays(1), EventId = 1 };

            var validator = new Mock<ITaskEditViewModelValidator>();
            validator.Setup(x => x.Validate(model)).Returns(new List<KeyValuePair<string, string>>()).Verifiable();

            var sut = new TaskController(null, validator.Object);
            sut.AddModelStateError();

            await sut.Edit(model);

            validator.Verify();
        }
        public async Task EditGetReturnsCorrectViewModelAndView()
        {
            const int organizationId = 1;
            var editViewModel = new EditViewModel { OrganizationId = organizationId };

            var mediator = new Mock<IMediator>();
            mediator.Setup(x => x.SendAsync(It.IsAny<EditTaskQueryAsync>())).ReturnsAsync(editViewModel);

            var urlHelper = new Mock<IUrlHelper>();
            urlHelper.Setup(x => x.Action(It.IsAny<UrlActionContext>())).Returns(It.IsAny<string>());

            var sut = new TaskController(mediator.Object, null) { Url = urlHelper.Object };
            sut.MakeUserAnOrgAdmin(organizationId.ToString());

            var result = await sut.Edit(It.IsAny<int>()) as ViewResult;
            var modelResult = result.ViewData.Model as EditViewModel;

            Assert.IsType<ViewResult>(result);
            Assert.IsType<EditViewModel>(modelResult);
            Assert.Equal(modelResult, editViewModel);
        }
        public void ReturnsNoErrorForNonItineraryTaskWhenModelsDatesAreValid()
        {
            var mockMediator = new Mock<IMediator>();

            mockMediator.Setup(x => x.Send(It.IsAny<EventByIdQuery>())).Returns(new AllReady.Models.Event
            {
                Id = 1,
                Campaign = new Campaign
                {
                    TimeZoneId = "UTC",
                },
                StartDateTime = eventStartDate,
                EndDateTime = eventEndDate,
                EventType = EventType.Rally
            });

            var validator = new TaskEditViewModelValidator(mockMediator.Object);

            var model = new EditViewModel
            {
                StartDateTime = eventStartDate.AddDays(1),
                EndDateTime = eventEndDate.AddDays(-1)
            };

            var errors = validator.Validate(model);

            Assert.True(errors.Count == 0);
        }
        public void ReturnsNoErrorForItineraryTaskWhenModelsDatesAreValid()
        {
            var validator = GetValidator();

            var model = new EditViewModel
            {
                StartDateTime = eventStartDate.AddDays(1),
                EndDateTime = eventStartDate.AddDays(1).AddHours(2),
            };

            var errors = validator.Validate(model);

            Assert.True(errors.Count == 0);
        }