Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions api/src/Feature.FormTemplates/ListAssignedTemplates/Endpoint.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Authorization.Policies.Requirements;
using Feature.FormTemplates.Specifications;
using Microsoft.AspNetCore.Authorization;
using Vote.Monitor.Core.Models;
using Vote.Monitor.Core.Services.Security;
using Vote.Monitor.Domain.Entities.ElectionRoundFormTemplateAggregate;

Expand All @@ -10,33 +9,31 @@ namespace Feature.FormTemplates.ListAssignedTemplates;
public class Endpoint(IReadRepository<ElectionRoundFormTemplate> repository,
ICurrentUserRoleProvider userRoleProvider,
IAuthorizationService authorizationService)
: Endpoint<Request, Results<Ok<PagedResponse<FormTemplateSlimModel>>, NotFound>>
: Endpoint<Request, Results<Ok<List<FormTemplateSlimModel>>, NotFound>>
{
public override void Configure()
{
Get("/api/form-templates/election-rounds/{electionRoundId}:available");
Policies(PolicyNames.AdminsOnly);
}

public override async Task<Results<Ok<PagedResponse<FormTemplateSlimModel>>, NotFound>> ExecuteAsync(Request req, CancellationToken ct)
public override async Task<Results<Ok<List<FormTemplateSlimModel>>, NotFound>> ExecuteAsync(Request req, CancellationToken ct)
{
var isNgoAdmin = userRoleProvider.IsNgoAdmin();

if (isNgoAdmin)
{
var result = await authorizationService.AuthorizeAsync(User, new NgoAdminRequirement());

if (!result.Succeeded)
{
return TypedResults.NotFound();
}
}

var specification = new ListAssignedFormTemplateSpecification(req);
var assignedFormTemplates = await repository.ListAsync(specification, ct);
var assignedFormTemplatesCount = await repository.CountAsync(specification, ct);

return TypedResults.Ok(new PagedResponse<FormTemplateSlimModel>(assignedFormTemplates,
assignedFormTemplatesCount, req.PageNumber, req.PageSize));

return TypedResults.Ok(assignedFormTemplates);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using Vote.Monitor.Core.Models;
namespace Feature.FormTemplates.ListAssignedTemplates;

namespace Feature.FormTemplates.ListAssignedTemplates;

public class Request : BaseSortPaginatedRequest
public class Request
{
public Guid ElectionRoundId { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,5 @@ public class Validator : Validator<Request>
public Validator()
{
RuleFor(x => x.ElectionRoundId).NotEmpty();
RuleFor(x => x.PageNumber)
.GreaterThanOrEqualTo(1);

RuleFor(x => x.PageSize).InclusiveBetween(1, 100);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System.Xml;
using Feature.FormTemplates.ListAssignedTemplates;
using Feature.FormTemplates.ListAssignedTemplates;
using Vote.Monitor.Domain.Entities.ElectionRoundFormTemplateAggregate;
using Vote.Monitor.Domain.Specifications;

namespace Feature.FormTemplates.Specifications;

Expand All @@ -12,8 +10,7 @@ public ListAssignedFormTemplateSpecification(Request request)
Query
.Where(x => x.ElectionRoundId == request.ElectionRoundId)
.Include(x => x.FormTemplate)
.ApplyDefaultOrdering(request)
.Paginate(request);
.OrderBy(x => x.FormTemplate.Code);

Query
.Select(x => new FormTemplateSlimModel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Feature.FormTemplates.ListAssignedTemplates;
using Feature.FormTemplates.Specifications;
using Microsoft.AspNetCore.Authorization;
using Vote.Monitor.Core.Models;
using Vote.Monitor.Core.Services.Security;
using Vote.Monitor.Domain.Entities.ElectionRoundFormTemplateAggregate;

Expand Down Expand Up @@ -63,7 +62,7 @@ public async Task Should_Return_NotFound_When_NgoAdmin_is_True_and_Authorization

// Assert
result.Should()
.BeOfType<Results<Ok<PagedResponse<FormTemplateSlimModel>>, NotFound>>()
.BeOfType<Results<Ok<List<FormTemplateSlimModel>>, NotFound>>()
.Which.Result.Should()
.BeOfType<NotFound>();
}
Expand All @@ -90,11 +89,14 @@ public async Task Should_Return_Ok_With_Empty_List_When_No_Assigned_Templates()

// Assert
result.Should()
.BeOfType<Results<Ok<PagedResponse<FormTemplateSlimModel>>, NotFound>>();
.BeOfType<Results<Ok<List<FormTemplateSlimModel>>, NotFound>>()
.Which.Result.Should()
.BeOfType<Ok<List<FormTemplateSlimModel>>>()
.Which.Value.Should().BeEmpty();
}

[Fact]
public async Task Should_Return_Ok_With_Paginated_List_When_Assigned_Templates_Exist()
public async Task Should_Return_Ok_With_List_When_Assigned_Templates_Exist()
{
// Arrange
_userRoleProvider.IsNgoAdmin().Returns(true);
Expand All @@ -105,36 +107,21 @@ public async Task Should_Return_Ok_With_Paginated_List_When_Assigned_Templates_E
Arg.Any<IEnumerable<IAuthorizationRequirement>>())
.Returns(AuthorizationResult.Success());

var numberOfFormTemplates = 3;
var totalCount = 154;
var pageSize = 100;

var formTemplates = new FormTemplateSlimModelFaker().Generate(numberOfFormTemplates);
var formTemplates = new FormTemplateSlimModelFaker().Generate(3);

_electionRoundFormTemplateRepository
.ListAsync(Arg.Any<ListAssignedFormTemplateSpecification>())
.Returns(formTemplates);

_electionRoundFormTemplateRepository
.CountAsync(Arg.Any<ListAssignedFormTemplateSpecification>())
.Returns(totalCount);

// Act
var request = new Request { PageSize = pageSize, PageNumber = numberOfFormTemplates };
var request = new Request();
var result = await _endpoint.ExecuteAsync(request, default);

// Assert
result
.Should().BeOfType<Results<Ok<PagedResponse<FormTemplateSlimModel>>, NotFound>>()
.Should().BeOfType<Results<Ok<List<FormTemplateSlimModel>>, NotFound>>()
.Which
.Result.Should().BeOfType<Ok<PagedResponse<FormTemplateSlimModel>>>()
.Which.Value.Should().NotBeNull();

var pagedResult = (result.Result as Ok<PagedResponse<FormTemplateSlimModel>>)!.Value!;

pagedResult.PageSize.Should().Be(pageSize);
pagedResult.CurrentPage.Should().Be(numberOfFormTemplates);
pagedResult.TotalCount.Should().Be(totalCount);
pagedResult.Items.Should().BeEquivalentTo(formTemplates);
.Result.Should().BeOfType<Ok<List<FormTemplateSlimModel>>>()
.Which.Value.Should().BeEquivalentTo(formTemplates);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
using Feature.FormTemplates.ListAssignedTemplates;
using Feature.FormTemplates.Specifications;
using Vote.Monitor.Core.Models;
using Vote.Monitor.Domain.Entities.ElectionRoundFormTemplateAggregate;
using Vote.Monitor.Domain.Entities.FormAggregate;

namespace Feature.FormTemplates.UnitTests.Specifications;

public class ListAssignedTemplatesSpecificationTests
{
private readonly FormStatus DefaultStatus = FormStatus.Drafted;

[Fact]
public void ListAssignedTemplatesSpecification_Should_Filter_By_ElectionRoundId_And_Return_Correct_FormTemplates()
{
Expand All @@ -18,130 +14,47 @@ public void ListAssignedTemplatesSpecification_Should_Filter_By_ElectionRoundId_
var formTemplateId = Guid.NewGuid();
var request = new Request
{
ElectionRoundId = electionRoundId,
PageNumber = 1,
PageSize = 10
ElectionRoundId = electionRoundId
};

var matchingTemplate = new ElectionRoundFormTemplateAggregateFaker(electionRoundId, formTemplateId).Generate();
var secondMatchingTemaplate = new ElectionRoundFormTemplateAggregateFaker(electionRoundId, formTemplateId).Generate();
var secondMatchingTemplate = new ElectionRoundFormTemplateAggregateFaker(electionRoundId, formTemplateId).Generate();
var nonMatchingTemplate = new ElectionRoundFormTemplateAggregateFaker(Guid.NewGuid(), Guid.NewGuid()).Generate();

var testCollection = new List<ElectionRoundFormTemplate>
{
matchingTemplate,
secondMatchingTemaplate,
secondMatchingTemplate,
nonMatchingTemplate
}.AsQueryable();

var spec = new ListAssignedFormTemplateSpecification(request);

// Act
var result = spec.Evaluate(testCollection).ToList();

// Assert
var expectedIds = new List<Guid> {matchingTemplate.FormTemplate.Id, secondMatchingTemaplate.FormTemplate.Id };

result.Should().HaveCount(2);
result.Select(x => x.Id).Should().BeEquivalentTo(expectedIds);
}

[Theory]
[MemberData(nameof(TestData.EmptyStringsTestCases), MemberType = typeof(TestData))]
public void ListAssignedTemplatesSpecification_AppliesDefaultSorting_WhenNoSortColumnSet(string columnName)
{
// Arrange
var electionRoundId = Guid.NewGuid();
var formTemplateId = Guid.NewGuid();
var template1 = new ElectionRoundFormTemplateAggregateFaker(electionRoundId, formTemplateId).Generate();
var template2 = new ElectionRoundFormTemplateAggregateFaker(electionRoundId, formTemplateId).Generate();


var testCollection = Enumerable
.Range(1, 100)
.Select(idx => new ElectionRoundFormTemplateAggregateFaker(electionRoundId, formTemplateId).Generate())
.Union(new[] { template1, template2 })
.ToList();

var request = new Request
{
ElectionRoundId = electionRoundId,
SortColumnName = columnName,
PageSize = 100,
PageNumber = 2
};

var spec = new ListAssignedFormTemplateSpecification(request);

// Act
var result = spec.Evaluate(testCollection).ToList();

// Assert
var expectedIds = new List<Guid> { matchingTemplate.FormTemplate.Id, secondMatchingTemplate.FormTemplate.Id };

result.Should().HaveCount(2);
result.Should().BeInAscendingOrder(x => x.LastModifiedOn);
result.Select(x => x.Id).Should().BeEquivalentTo(expectedIds);
}

[Theory]
[MemberData(nameof(NameSortingTestCases))]
public void ListAssignedFormTemplatesSpecification_AppliesSortingCorrectly(string columnName, SortOrder? sortOrder)
{
// Arrange
var electionRoundId = Guid.NewGuid();
var formTemplateId = Guid.NewGuid();


var testCollection = Enumerable
.Range(1, 100)
.Select(idx => new ElectionRoundFormTemplateAggregateFaker(electionRoundId, formTemplateId).Generate())
.ToList();

var request = new Request
{
ElectionRoundId = electionRoundId,
SortColumnName = columnName,
SortOrder = sortOrder,
PageSize = 100,
PageNumber = 1
};

var spec = new ListAssignedFormTemplateSpecification(request);

// Act
var result = spec.Evaluate(testCollection).ToList();

// Assert
result.Should().HaveCount(100);

var expectedCodes = (sortOrder == SortOrder.Desc)
? testCollection.OrderByDescending(x => x.FormTemplate.Code).Select(x => x.FormTemplate.Code).ToList()
: testCollection.OrderBy(x => x.FormTemplate.Code).Select(x => x.FormTemplate.Code).ToList(); // Default is ascending

var actualCodes = result.Select(x => x.Code).ToList();

actualCodes.Should().BeEquivalentTo(expectedCodes);
}

[Fact]
public void ListAssignedTemplatesSpecification_PaginatesCorrectly()
public void ListAssignedTemplatesSpecification_Should_Return_All_Matching_Templates_Ordered_By_Code()
{
// Arrange
var electionRoundId = Guid.NewGuid();
var formTemplateId = Guid.NewGuid();
var template1 = new ElectionRoundFormTemplateAggregateFaker(electionRoundId, formTemplateId).Generate();
var template2 = new ElectionRoundFormTemplateAggregateFaker(electionRoundId, formTemplateId).Generate();


var testCollection = Enumerable
.Range(1, 100)
.Select(idx => new ElectionRoundFormTemplateAggregateFaker(electionRoundId, formTemplateId).Generate())
.Union(new[] { template1, template2 })
.Range(1, 25)
.Select(_ => new ElectionRoundFormTemplateAggregateFaker(electionRoundId, formTemplateId).Generate())
.ToList();

var request = new Request
{
ElectionRoundId = electionRoundId,
PageSize = 100,
PageNumber = 2
ElectionRoundId = electionRoundId
};

var spec = new ListAssignedFormTemplateSpecification(request);
Expand All @@ -150,17 +63,7 @@ public void ListAssignedTemplatesSpecification_PaginatesCorrectly()
var result = spec.Evaluate(testCollection).ToList();

// Assert
result.Should().HaveCount(2);
result.Should().HaveCount(25);
result.Should().BeInAscendingOrder(x => x.Code);
}



public static IEnumerable<object[]> NameSortingTestCases =>
new List<object[]>
{
new object[] { "code", null },
new object[] { "Code", null },
new object[] { "code", SortOrder.Asc },
new object[] { "Code", SortOrder.Asc }
};
}
Loading
Loading