Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSP-1392: POST Permissions Endpoint #25

Merged
merged 17 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
67fa2f6
CSP-1392: POST Permissions endpoint and relevant unit test coverage.
shaun-downey-education Jun 6, 2024
072aded
CSP-1392: Remove duplicate file.
shaun-downey-education Jun 6, 2024
37d71f4
CSP-1392: Fixed PermissionDeletedAction spelling mistake.
shaun-downey-education Jun 7, 2024
d790260
CSP-1392: Code review suggestions.
shaun-downey-education Jun 13, 2024
3e7bdb2
CSP-1392: Code review changes.
shaun-downey-education Jun 14, 2024
e22b041
CSP-1392: Code review updates.
shaun-downey-education Jun 14, 2024
ff94ac8
CSP-1392: Reinstate missing unit tests.
shaun-downey-education Jun 14, 2024
69200c6
CSP-1392: Sonarcloud code suggestions.
shaun-downey-education Jun 14, 2024
f1105d6
CSP-1392: Remove unused cancellation token.
shaun-downey-education Jun 14, 2024
1facead
CSP-1392: Additional unit test coverrage for the post permissions con…
shaun-downey-education Jun 17, 2024
cbfb494
CSP-1392: Replace duplicate validator namespace.
shaun-downey-education Jun 17, 2024
a099c79
CSP-1392: Additional unit test coverage for AccountProviderLegalEntit…
shaun-downey-education Jun 17, 2024
0c58aca
CSP-1392: Remove entity type configuration for PermissionsAudit
shaun-downey-education Jun 18, 2024
47fe34d
CSP-1392: Remove empty folder.
shaun-downey-education Jun 18, 2024
49c6b1b
CSP-1392: PostPermissionsHandler - No need to convert selected permis…
shaun-downey-education Jun 18, 2024
36576df
CSP-1392: Merged main.
shaun-downey-education Jun 19, 2024
3113bb7
CSP-1392: Removed empty folders.
shaun-downey-education Jun 19, 2024
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
shaun-downey-education marked this conversation as resolved.
Show resolved Hide resolved
shaun-downey-education marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using AutoFixture.NUnit3;
using FluentAssertions;
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Moq;
using SFA.DAS.PR.Api.Controllers;
using SFA.DAS.PR.Application.Mediatr.Responses;
using SFA.DAS.PR.Application.Permissions.Commands.PostPermissions;
using SFA.DAS.Testing.AutoFixture;

namespace SFA.DAS.PR.Api.UnitTests.Controllers.Permissions;

public class PermissionsControllerPostTests
{
[Test]
[MoqAutoData]
public async Task PostPermission_InvokesQueryHandler(
[Frozen] Mock<IMediator> mediatorMock,
[Greedy] PermissionsController sut,
PostPermissionsCommand command,
CancellationToken cancellationToken
)
{
await sut.PostPermission(command, cancellationToken);

mediatorMock.Verify(m =>
m.Send(It.Is<PostPermissionsCommand>(q =>
q.AccountLegalEntityId == command.AccountLegalEntityId &&
q.Ukprn == command.Ukprn &&
q.Operations == command.Operations &&
q.UserRef == command.UserRef
),
cancellationToken)
);
}

[Test, MoqAutoData]
public async Task PostPermission_HandlerReturnsData_ReturnsOkResponse(
[Frozen] Mock<IMediator> mediatorMock,
[Greedy] PermissionsController sut,
PostPermissionsCommand command,
PostPermissionsCommandResult postPermissionsCommandResult,
CancellationToken cancellationToken
)
{
var response = new ValidatedResponse<PostPermissionsCommandResult>(postPermissionsCommandResult);

mediatorMock.Setup(m => m.Send(
It.Is<PostPermissionsCommand>(q =>
q.AccountLegalEntityId == command.AccountLegalEntityId &&
q.Ukprn == command.Ukprn &&
q.Operations == command.Operations &&
q.UserRef == command.UserRef
),
cancellationToken)
).ReturnsAsync(response);

var result = await sut.PostPermission(command, cancellationToken);
result.As<OkObjectResult>().Should().NotBeNull();
result.As<OkObjectResult>().Value.Should().Be(postPermissionsCommandResult);
}
}
11 changes: 11 additions & 0 deletions src/SFA.DAS.PR.Api/Controllers/PermissionsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using SFA.DAS.PR.Api.Common;
using SFA.DAS.PR.Api.SwaggerExamples;
using SFA.DAS.PR.Application.Mediatr.Responses;
using SFA.DAS.PR.Application.Permissions.Commands.PostPermissions;
using SFA.DAS.PR.Application.Permissions.Queries.GetHasPermissions;
using SFA.DAS.PR.Application.Permissions.Queries.GetPermissions;
using SFA.DAS.PR.Application.Permissions.Queries.HasRelationshipWithPermission;
Expand Down Expand Up @@ -53,4 +54,14 @@ public async Task<IActionResult> HasPermission([FromQuery] GetHasPermissionsQuer
ValidatedResponse<bool> result = await _mediator.Send(query, cancellationToken);
return GetResponse(result);
}

[HttpPost]
[Authorize(Policy = Policies.Management)]
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(List<ValidationError>), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> PostPermission([FromBody] PostPermissionsCommand command, CancellationToken cancellationToken)
{
ValidatedResponse<PostPermissionsCommandResult> result = await _mediator.Send(command, cancellationToken);
return GetResponse(result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ CancellationToken cancellationToken

ValidatedResponse<GetEmployerRelationshipsQueryResult> result = await sut.Handle(query, cancellationToken);

Assert.That(result.Result.AccountLegalEntities, !Is.Empty);
Assert.That(result.Result!.AccountLegalEntities, !Is.Empty);
}

[Test]
Expand All @@ -46,6 +46,6 @@ CancellationToken cancellationToken

ValidatedResponse<GetEmployerRelationshipsQueryResult> result = await sut.Handle(query, cancellationToken);

Assert.That(result.Result.AccountLegalEntities, Is.Empty);
Assert.That(result.Result!.AccountLegalEntities, Is.Empty);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
using AutoFixture.NUnit3;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Moq;
using SFA.DAS.PR.Application.Mediatr.Responses;
using SFA.DAS.PR.Application.Permissions.Commands.PostPermissions;
using SFA.DAS.PR.Data;
using SFA.DAS.PR.Data.Repositories;
using SFA.DAS.PR.Data.UnitTests.InMemoryDatabases;
using SFA.DAS.PR.Data.UnitTests.Setup;
using SFA.DAS.PR.Domain.Entities;
using SFA.DAS.PR.Domain.Interfaces;
using SFA.DAS.Testing.AutoFixture;

namespace SFA.DAS.PR.Application.UnitTests.Permissions.Commands.PostPermissions;

public class PostPermissionsCommandHandlerTests
{
private readonly CancellationToken cancellationToken = CancellationToken.None;

[Test]
[RecursiveMoqAutoData]
public async Task Handle_PostPermissions_Null_Entities_Returns_PostPermissionsCommandResult(
[Frozen]Mock<IAccountProviderLegalEntitiesReadRepository> accountProviderLegalEntitiesReadRepository,
[Frozen]Mock<IAccountLegalEntityReadRepository> accountLegalEntityReadRepository,
PostPermissionsCommandHandler sut,
PostPermissionsCommand command,
CancellationToken cancellationToken
)
{
accountProviderLegalEntitiesReadRepository.Setup(a =>
a.GetAccountProviderLegalEntity(
command.Ukprn,
command.AccountLegalEntityId,
It.IsAny<CancellationToken>()
)
).ReturnsAsync((AccountProviderLegalEntity?)null);

accountLegalEntityReadRepository.Setup(a =>
a.GetAccountLegalEntity(command.AccountLegalEntityId, cancellationToken)
).ReturnsAsync((AccountLegalEntity?)null);

ValidatedResponse<PostPermissionsCommandResult> result = await sut.Handle(command, cancellationToken);
result.Result.Should().NotBeNull();
result.Result.Should().BeOfType<PostPermissionsCommandResult>();
}

[Test]
[RecursiveMoqAutoData]
public async Task Handle_PostPermissions_Indentical_Permissions_Returns_PostPermissionsCommandResult(
[Frozen] Mock<IAccountProviderLegalEntitiesReadRepository> accountProviderLegalEntitiesReadRepository,
[Frozen] Mock<IAccountLegalEntityReadRepository> accountLegalEntityReadRepository,
PostPermissionsCommandHandler sut,
AccountProviderLegalEntity accountProviderLegalEntity,
AccountLegalEntity accountLegalEntity,
PostPermissionsCommand command,
CancellationToken cancellationToken
)
{
accountProviderLegalEntitiesReadRepository.Setup(a =>
a.GetAccountProviderLegalEntity(
command.Ukprn,
command.AccountLegalEntityId,
It.IsAny<CancellationToken>()
)
).ReturnsAsync(accountProviderLegalEntity);

accountLegalEntityReadRepository.Setup(a =>
a.GetAccountLegalEntity(command.AccountLegalEntityId, cancellationToken)
).ReturnsAsync(accountLegalEntity);

ValidatedResponse<PostPermissionsCommandResult> result = await sut.Handle(command, cancellationToken);

result.Result.Should().NotBeNull();
result.Result.Should().BeOfType<PostPermissionsCommandResult>();
}

[Test]
[RecursiveMoqAutoData]
public async Task Handle_PostPermissions_Remove_Permissions_Returns_PostPermissionsCommandResult(
[Frozen] Mock<IAccountProviderLegalEntitiesReadRepository> accountProviderLegalEntitiesReadRepository,
[Frozen] Mock<IAccountLegalEntityReadRepository> accountLegalEntityReadRepository,
AccountProviderLegalEntity accountProviderLegalEntity,
AccountLegalEntity accountLegalEntity,
PostPermissionsCommand command
)
{
command.Operations = new() { Operation.CreateCohort };

accountProviderLegalEntitiesReadRepository.Setup(a =>
a.GetAccountProviderLegalEntity(
command.Ukprn,
command.AccountLegalEntityId,
It.IsAny<CancellationToken>()
)
).ReturnsAsync(accountProviderLegalEntity);

accountLegalEntityReadRepository.Setup(a =>
a.GetAccountLegalEntity(command.AccountLegalEntityId, cancellationToken)
).ReturnsAsync(accountLegalEntity);

ValidatedResponse<PostPermissionsCommandResult> result = null!;

int permissionCount = 0;

PermissionsAudit? audit = null;

using (var context = InMemoryProviderRelationshipsDataContext.CreateInMemoryContext(
$"{nameof(InMemoryProviderRelationshipsDataContext)}_{nameof(Handle_PostPermissions_Remove_Permissions_Returns_PostPermissionsCommandResult)}")
)
{
await context.AccountProviderLegalEntities.AddAsync(accountProviderLegalEntity, cancellationToken);
await context.SaveChangesAsync(cancellationToken);

PostPermissionsCommandHandler sut = CreatePostPermissionsCommandHandler(
accountProviderLegalEntitiesReadRepository.Object,
accountLegalEntityReadRepository.Object,
context
);

result = await sut.Handle(command, cancellationToken);

permissionCount = await context.Permissions.CountAsync(a => a.AccountProviderLegalEntityId == accountProviderLegalEntity.Id);
audit = await context.PermissionsAudit.FirstOrDefaultAsync(a => a.AccountLegalEntityId == command.AccountLegalEntityId && a.Ukprn == command.Ukprn!.Value);
}

result.Result.Should().NotBeNull();
result.Result.Should().BeOfType<PostPermissionsCommandResult>();

Assert.Multiple(() =>
{
Assert.That(audit, Is.Not.Null, "Audit must have been recorded.");
Assert.That(audit?.Action, Is.EqualTo("Updated"), "Audit action must equal updated.");
Assert.That(command.Operations.Count, Is.EqualTo(permissionCount), "Permissions after removal should be equal to the passed permissions count.");
});
}

[Test]
[RecursiveMoqAutoData]
public async Task Handle_PostPermissions_Add_Permissions_Returns_PostPermissionsCommandResult(
[Frozen] Mock<IAccountProviderLegalEntitiesReadRepository> accountProviderLegalEntitiesReadRepository,
[Frozen] Mock<IAccountLegalEntityReadRepository> accountLegalEntityReadRepository,
AccountProviderLegalEntity accountProviderLegalEntity,
AccountLegalEntity accountLegalEntity,
PostPermissionsCommand command
)
{
command.Operations = new() { Operation.CreateCohort, Operation.RecruitmentRequiresReview };

accountProviderLegalEntity.Permissions = new();

accountProviderLegalEntitiesReadRepository.Setup(a =>
a.GetAccountProviderLegalEntity(
command.Ukprn,
command.AccountLegalEntityId,
It.IsAny<CancellationToken>()
)
).ReturnsAsync(accountProviderLegalEntity);

accountLegalEntityReadRepository.Setup(a =>
a.GetAccountLegalEntity(command.AccountLegalEntityId, cancellationToken)
).ReturnsAsync(accountLegalEntity);

ValidatedResponse<PostPermissionsCommandResult> result = null!;

int permissionCount = 0;

PermissionsAudit? audit = null;

using (var context = InMemoryProviderRelationshipsDataContext.CreateInMemoryContext(
$"{nameof(InMemoryProviderRelationshipsDataContext)}_{nameof(Handle_PostPermissions_Add_Permissions_Returns_PostPermissionsCommandResult)}")
)
{
await context.AccountProviderLegalEntities.AddAsync(accountProviderLegalEntity, cancellationToken);
await context.SaveChangesAsync(cancellationToken);

PostPermissionsCommandHandler sut = CreatePostPermissionsCommandHandler(
accountProviderLegalEntitiesReadRepository.Object,
accountLegalEntityReadRepository.Object,
context
);

result = await sut.Handle(command, cancellationToken);

permissionCount = await context.Permissions.CountAsync(a => a.AccountProviderLegalEntityId == accountProviderLegalEntity.Id);
audit = await context.PermissionsAudit.FirstOrDefaultAsync(a => a.AccountLegalEntityId == command.AccountLegalEntityId && a.Ukprn == command.Ukprn!.Value);
}

result.Result.Should().NotBeNull();
result.Result.Should().BeOfType<PostPermissionsCommandResult>();

Assert.Multiple(() =>
{
Assert.That(audit, Is.Not.Null, "Audit must have been recorded.");
Assert.That(audit?.Action, Is.EqualTo("Updated"), "Audit action must equal updated.");
Assert.That(command.Operations.Count, Is.EqualTo(permissionCount), "Added permissions should be equal to the passed permissions count.");
});
}

[Test]
[RecursiveMoqAutoData]
public async Task Handle_PostPermissions_Create_Permissions_Returns_PostPermissionsCommandResult(
[Frozen] Mock<IAccountProviderLegalEntitiesReadRepository> accountProviderLegalEntitiesReadRepository,
[Frozen] Mock<IAccountLegalEntityReadRepository> accountLegalEntityReadRepository,
PostPermissionsCommand command
)
{
AccountLegalEntity accountLegalEntity = AccountLegalEntityTestData.CreateAccountLegalEntity();
command.AccountLegalEntityId = accountLegalEntity.Id;

accountProviderLegalEntitiesReadRepository.Setup(a =>
a.GetAccountProviderLegalEntity(
command.Ukprn,
command.AccountLegalEntityId,
It.IsAny<CancellationToken>()
)
).ReturnsAsync((AccountProviderLegalEntity?)null);

accountLegalEntityReadRepository.Setup(a =>
a.GetAccountLegalEntity(command.AccountLegalEntityId, cancellationToken)
).ReturnsAsync(accountLegalEntity);

ValidatedResponse<PostPermissionsCommandResult> result = null!;

int permissionCount = 0;

PermissionsAudit? audit = null;

using (var context = InMemoryProviderRelationshipsDataContext.CreateInMemoryContext(
$"{nameof(InMemoryProviderRelationshipsDataContext)}_{nameof(Handle_PostPermissions_Create_Permissions_Returns_PostPermissionsCommandResult)}")
)
{
await context.AccountLegalEntities.AddAsync(accountLegalEntity, cancellationToken);
await context.SaveChangesAsync(cancellationToken);

PostPermissionsCommandHandler sut = CreatePostPermissionsCommandHandler(
accountProviderLegalEntitiesReadRepository.Object,
accountLegalEntityReadRepository.Object,
context
);

result = await sut.Handle(command, cancellationToken);

AccountProviderLegalEntity? legalEntity = await context.AccountProviderLegalEntities.FirstOrDefaultAsync(a => a.AccountLegalEntityId == command.AccountLegalEntityId);

permissionCount = await context.Permissions.CountAsync(a => a.AccountProviderLegalEntityId == legalEntity!.Id);
audit = await context.PermissionsAudit.FirstOrDefaultAsync(a => a.AccountLegalEntityId == command.AccountLegalEntityId && a.Ukprn == command.Ukprn!.Value);
}

result.Result.Should().NotBeNull();
result.Result.Should().BeOfType<PostPermissionsCommandResult>();

Assert.Multiple(() =>
{
Assert.That(audit, Is.Not.Null, "Audit must have been recorded.");
Assert.That(audit?.Action, Is.EqualTo("Created"), "Audit action must equal created.");
Assert.That(command.Operations.Count, Is.EqualTo(permissionCount), "Added permissions should be equal to the passed permissions count.");
});
}

private PostPermissionsCommandHandler CreatePostPermissionsCommandHandler(
IAccountProviderLegalEntitiesReadRepository accountProviderLegalEntitiesReadRepository,
IAccountLegalEntityReadRepository accountLegalEntityReadRepository,
ProviderRelationshipsDataContext context
)
{
AccountProviderWriteRepository accountProviderWriteRepository = new(context);
AccountProviderLegalEntitiesWriteRepository accountProviderLegalEntitiesWriteRepository = new(context);
PermissionsWriteRepository permissionsWriteRepository = new(context);
PermissionsAuditWriteRepository permissionsAuditWriteRepository = new(context);

return new(
accountProviderLegalEntitiesReadRepository,
accountLegalEntityReadRepository,
accountProviderWriteRepository,
accountProviderLegalEntitiesWriteRepository,
permissionsWriteRepository,
permissionsAuditWriteRepository,
context
);
}
}
Loading