Skip to content

Commit

Permalink
Merge pull request #1615 from dchristensen/issue-1100-request-filtering
Browse files Browse the repository at this point in the history
Added filtering to request list on itinerary details.
  • Loading branch information
tonysurma committed Dec 16, 2016
2 parents 2294321 + fcfa324 commit 3b14821
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 27 deletions.
Expand Up @@ -50,15 +50,15 @@ public void ControllerHasAreaAuthorizeAttributeWithCorrectPolicy()
}

[Fact]
public void DetailsHasHttpGetAttribute()
public void DetailsGet_HasHttpGetAttribute()
{
var sut = new ItineraryController(null, null);
var attribute = sut.GetAttributesOn(x => x.Details(It.IsAny<int>())).OfType<HttpGetAttribute>().SingleOrDefault();
Assert.NotNull(attribute);
}

[Fact]
public void DetailsHasRouteAttributeWithCorrectRoute()
public void DetailsGet_HasRouteAttributeWithCorrectRoute()
{
var sut = new ItineraryController(null, null);
var routeAttribute = sut.GetAttributesOn(x => x.Details(It.IsAny<int>())).OfType<RouteAttribute>().SingleOrDefault();
Expand All @@ -67,7 +67,7 @@ public void DetailsHasRouteAttributeWithCorrectRoute()
}

[Fact]
public async Task DetailsSendsEventDetailQueryWithCorrectEventId()
public async Task DetailsGet_SendsEventDetailQueryWithCorrectEventId()
{
var mockMediator = new Mock<IMediator>();
mockMediator.Setup(mock => mock.SendAsync(It.IsAny<ItineraryDetailQuery>())).ReturnsAsync(null).Verifiable();
Expand All @@ -79,7 +79,7 @@ public async Task DetailsSendsEventDetailQueryWithCorrectEventId()
}

[Fact]
public async Task DetailsReturnsHttpNotFoundResultWhenEventIsNull()
public async Task DetailsGet_ReturnsHttpNotFoundResultWhenEventIsNull()
{
var mockMediator = new Mock<IMediator>();
mockMediator.Setup(mock => mock.SendAsync(It.IsAny<ItineraryDetailQuery>())).ReturnsAsync(null).Verifiable();
Expand All @@ -89,7 +89,7 @@ public async Task DetailsReturnsHttpNotFoundResultWhenEventIsNull()
}

[Fact]
public async Task DetailsReturnsHttpUnauthorizedResultWhenUserIsNotOrgAdmin()
public async Task DetailsGet_ReturnsHttpUnauthorizedResultWhenUserIsNotOrgAdmin()
{
var mediator = new Mock<IMediator>();
mediator.Setup(x => x.SendAsync(It.IsAny<ItineraryDetailQuery>())).ReturnsAsync(new ItineraryDetailsViewModel());
Expand All @@ -101,7 +101,7 @@ public async Task DetailsReturnsHttpUnauthorizedResultWhenUserIsNotOrgAdmin()
}

[Fact]
public async Task DetailsReturnsCorrectViewAndViewModelWhenEventIsNotNullAndUserIsOrgAdmin()
public async Task DetailsGet_ReturnsCorrectViewAndViewModelWhenEventIsNotNullAndUserIsOrgAdmin()
{
const int orgId = 1;
var viewModel = new ItineraryDetailsViewModel { OrganizationId = orgId };
Expand All @@ -121,6 +121,125 @@ public async Task DetailsReturnsCorrectViewAndViewModelWhenEventIsNotNullAndUser
Assert.Equal(resultViewModel, viewModel);
}

[Fact]
public void DetailsPost_HasHttpPostAttribute()
{
var sut = new ItineraryController(null, null);
var attribute = sut.GetAttributesOn(x => x.Details(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<RequestStatus?>()))
.OfType<HttpPostAttribute>().SingleOrDefault();
Assert.NotNull(attribute);
}

[Fact]
public void DetailsPost_HasRouteAttributeWithCorrectRoute()
{
var sut = new ItineraryController(null, null);
var routeAttribute = sut.GetAttributesOn(x => x.Details(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<RequestStatus?>()))
.OfType<RouteAttribute>().SingleOrDefault();
Assert.NotNull(routeAttribute);
Assert.Equal(routeAttribute.Template, "Admin/Itinerary/Details/{id}");
}

[Fact]
public async Task DetailsPost_SendsEventDetailQueryWithCorrectEventId()
{
var mockMediator = new Mock<IMediator>();
mockMediator.Setup(mock => mock.SendAsync(It.IsAny<ItineraryDetailQuery>())).ReturnsAsync(null).Verifiable();

var sut = new ItineraryController(mockMediator.Object, null);
await sut.Details(1, null, null);

mockMediator.Verify(x => x.SendAsync(It.IsAny<ItineraryDetailQuery>()), Times.Once);
}

[Fact]
public async Task DetailsPost_ReturnsHttpNotFoundResultWhenEventIsNull()
{
var mockMediator = new Mock<IMediator>();
mockMediator.Setup(mock => mock.SendAsync(It.IsAny<ItineraryDetailQuery>())).ReturnsAsync(null).Verifiable();

var controller = new ItineraryController(mockMediator.Object, null);
Assert.IsType<NotFoundResult>(await controller.Details(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<RequestStatus?>()));
}

[Fact]
public async Task DetailsPost_ReturnsHttpUnauthorizedResultWhenUserIsNotOrgAdmin()
{
var mediator = new Mock<IMediator>();
mediator.Setup(x => x.SendAsync(It.IsAny<ItineraryDetailQuery>())).ReturnsAsync(new ItineraryDetailsViewModel());

var sut = new ItineraryController(mediator.Object, null);
sut.MakeUserNotAnOrgAdmin();

Assert.IsType<UnauthorizedResult>(await sut.Details(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<RequestStatus?>()));
}

[Fact]
public async Task DetailsPost_ReturnsCorrectViewAndViewModelWhenEventIsNotNullAndUserIsOrgAdmin()
{
const int orgId = 1;
var viewModel = new ItineraryDetailsViewModel { OrganizationId = orgId };

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

var sut = new ItineraryController(mediator.Object, null);
sut.MakeUserAnOrgAdmin(orgId.ToString());

var result = await sut.Details(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<RequestStatus?>()) as ViewResult;
Assert.Equal(result.ViewName, "Details");

var resultViewModel = result.ViewData.Model;
Assert.IsType<ItineraryDetailsViewModel>(resultViewModel);

Assert.Equal(resultViewModel, viewModel);
}

[Fact]
public async Task DetailsPost_SendsRequestListQueryWithCorrectData()
{
const int orgId = 1;
const int itineraryId = 2;
const string keywords = "search";
const RequestStatus status = RequestStatus.Assigned;
var viewModel = new ItineraryDetailsViewModel { OrganizationId = orgId, Id = 2 };
var requestList = new List<RequestListViewModel>();

var mediator = new Mock<IMediator>();
mediator.Setup(x => x.SendAsync(It.IsAny<ItineraryDetailQuery>())).ReturnsAsync(viewModel);
mediator.Setup(x => x.SendAsync(It.IsAny<RequestListItemsQuery>())).ReturnsAsync(requestList);

var sut = new ItineraryController(mediator.Object, null);
sut.MakeUserAnOrgAdmin(orgId.ToString());
await sut.Details(itineraryId, keywords, status);

mediator.Verify(x => x.SendAsync(It.Is<RequestListItemsQuery>(
y => y.Criteria.ItineraryId == itineraryId &&
y.Criteria.Keywords == keywords &&
y.Criteria.Status == status)), Times.Once);
}

[Fact]
public async Task DetailsPost_ReturnsViewModelWithCorrectRequestListData()
{
const int orgId = 1;
var fullList = new List<RequestListViewModel> {new RequestListViewModel {Id = Guid.NewGuid()}};
var filteredList = new List<RequestListViewModel> {new RequestListViewModel {Id = Guid.NewGuid()}};
var viewModel = new ItineraryDetailsViewModel { OrganizationId = orgId, Requests = fullList};

var mediator = new Mock<IMediator>();
mediator.Setup(x => x.SendAsync(It.IsAny<ItineraryDetailQuery>())).ReturnsAsync(viewModel);
mediator.Setup(x => x.SendAsync(It.IsAny<RequestListItemsQuery>())).ReturnsAsync(filteredList);

var sut = new ItineraryController(mediator.Object, null);
sut.MakeUserAnOrgAdmin(orgId.ToString());
var result = await sut.Details(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<RequestStatus?>()) as ViewResult;

var model = (ItineraryDetailsViewModel) result.ViewData.Model;

Assert.Equal(model.Requests, filteredList);
}

[Fact]
public void CreateHasHttpPostAttribute()
{
Expand Down Expand Up @@ -449,7 +568,7 @@ public async Task EditGet_ReturnsViewResult_WhenUserIsSiteAdmin()
// Assert
var viewResult = Assert.IsType<ViewResult>(await sut.Edit(1));
var model = viewResult.Model as ItineraryEditViewModel;

model.ShouldNotBeNull();
model.Name.ShouldBe("Test");
}
Expand Down Expand Up @@ -514,7 +633,7 @@ public async Task EditPost_ReturnsHttpUnauthorizedResult_WhenUserIsNotOrgAdminFo

Assert.IsType<UnauthorizedResult>(await sut.Edit(new ItineraryEditViewModel { Id = 50 }));
}

[Fact]
public async Task EditPost_CallsValidator_WithCorrectProperties()
{
Expand All @@ -532,7 +651,7 @@ public async Task EditPost_CallsValidator_WithCorrectProperties()
Id = 1,
Name = "Itinerary",
OrganizationId = 1,
EventSummary = new EventSummaryViewModel {
EventSummary = new EventSummaryViewModel {
StartDateTime = new DateTimeOffset(new DateTime(2016, 01, 01)),
EndDateTime = new DateTimeOffset(new DateTime(2016, 12, 31))
}
Expand Down Expand Up @@ -1639,4 +1758,4 @@ private List<RequestListViewModel> GetRequestsForSelectRequestHappyPathTests()
};
}
}
}
}
Expand Up @@ -51,6 +51,37 @@ public async Task<IActionResult> Details(int id)
return View("Details", itinerary);
}

[HttpPost]
[Route("Admin/Itinerary/Details/{id}")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Details(int id, string requestKeywords, RequestStatus? requestStatus)
{
var itinerary = await _mediator.SendAsync(new ItineraryDetailQuery {ItineraryId = id});
if (itinerary == null)
{
return NotFound();
}

if (!User.IsOrganizationAdmin(itinerary.OrganizationId))
{
return Unauthorized();
}

var filteredRequests = await _mediator.SendAsync(new RequestListItemsQuery
{
Criteria = new RequestSearchCriteria
{
ItineraryId = itinerary.Id,
Keywords = requestKeywords,
Status = requestStatus
}
});

itinerary.Requests = filteredRequests;

return View("Details", itinerary);
}

[HttpPost]
[Route("Admin/Itinerary/Create")]
[ValidateAntiForgeryToken]
Expand Down Expand Up @@ -141,9 +172,9 @@ public async Task<IActionResult> Edit(ItineraryEditViewModel model)

await _mediator.SendAsync(new EditItineraryCommand { Itinerary = model });

return RedirectToAction(nameof(Details), new { area = "Admin", id = model.Id });
return RedirectToAction(nameof(Details), new { area = "Admin", id = model.Id });
}

[HttpPost]
[Route("Admin/Itinerary/AddTeamMember")]
[ValidateAntiForgeryToken]
Expand Down Expand Up @@ -294,7 +325,7 @@ public async Task<IActionResult> ConfirmRemoveRequest(int itineraryId, Guid requ
public async Task<IActionResult> RemoveRequest(RequestSummaryViewModel viewModel)
{
if(!viewModel.UserIsOrgAdmin)
{
{
return Unauthorized();
}

Expand Down Expand Up @@ -388,7 +419,7 @@ public async Task<IActionResult> OptimizeRoute(int itineraryId, OptimizeRouteInp
}

var result = await _mediator.SendAsync(new OptimizeRouteCommand { ItineraryId = itineraryId });

return RedirectToAction("Details", new { id = itineraryId, startAddress = model.StartAddress, endAddress = model.EndAddress });
}

Expand Down Expand Up @@ -433,4 +464,4 @@ private async Task<int> GetOrganizationIdBy(int intinerayId)
return await _mediator.SendAsync(new OrganizationIdQuery { ItineraryId = intinerayId });
}
}
}
}
Expand Up @@ -42,6 +42,11 @@ public async Task<List<RequestListViewModel>> Handle(RequestListItemsQuery messa
results = results.Where(r => r.EventId == message.Criteria.EventId.Value);
}

if (message.Criteria.ItineraryId.HasValue)
{
results = results.Where(r => r.Itineraries.Any(i => i.ItineraryId == message.Criteria.ItineraryId.Value));
}

if (message.Criteria.Status.HasValue)
{
results = results.Where(r => r.Status == message.Criteria.Status); ;
Expand Down
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using AllReady.Models;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace AllReady.Areas.Admin.ViewModels.Itinerary
Expand Down Expand Up @@ -58,5 +59,9 @@ public class ItineraryDetailsViewModel
/// A URL to the bing maps site with parameters needed to generate a driving route
/// </summary>
public string BingMapUrl { get; set; }

public string RequestKeywords { get; set; }

public RequestStatus? RequestStatus { get; set; }
}
}
Expand Up @@ -9,6 +9,7 @@ public class RequestSearchCriteria
public bool IncludeAssigned { get; set; } = false;
public bool IncludeCanceled { get; set; } = false;
public int? EventId { get; set; }
public int? ItineraryId { get; set; }

public string Keywords { get; set; }
public RequestStatus? Status { get; set; }
Expand Down

0 comments on commit 3b14821

Please sign in to comment.