mirror of
https://github.com/bitwarden/server.git
synced 2024-11-24 12:35:25 +01:00
PM-13237 password health report application add get (#5000)
* PM-13236 PasswordHealthReportApplications db * PM-13236 incorporated pr comments * PM-13236 fixed error in SQL script * PM-13236 resolve quality scan errors SQL71006, SQL7101, SQL70001 * PM-13236 fixed warnings on procedures * PM-13236 added efMigrations * PM-13236 renamed files to PasswordHealthReportApplication (singular) * PM-13236 changed file name to more appropriate naming * PM-13236 changed the file name singular * PM-13236 PasswordHealthReportApplication Entities and Repos * PM-13236 moved files under tools from core * PM-13236 Entity PasswordHealthReportApplication namespace changed to tools/entities * PM-13236 moved Repos and Interfaces to tools * PM-13236 migrated model to tools namespace * PM-13236 minor fixes to the unit tests * PM-13236 fixed script errors during build * PM-13236 Script to drop PasswordHealthReportApplications if it exists * PM-13236 fixes to database snapshot * PM-13236 updated databasesnapshots * PM-13236 Update database model changes for Mysql * PM-13236 update model changes for Sqlite * PM-13236 updated the models to remove commented code * PM-13236 added correct db snapshot for MySql * PM-13236 updated database snapshot for Postgres * PM-13236 updated database snapshot for Sqlite * PM-13236 removed unwanted directive to fix linting error * PM-13236 removed redundant script files * PM-13237 Add entity command and unit tests * PM-13237 Get query added with unit tests * PM-13237 Controller to add/get PasswordHealthReportApplication * PM-13237 Setup dependencies in the EF Service collection extensions * PM-13237 Added unit tests for ReportsController
This commit is contained in:
parent
0e23a07bbc
commit
9fb3f1d346
@ -32,6 +32,8 @@ using Bit.Core.Tools.Entities;
|
|||||||
using Bit.Core.Vault.Entities;
|
using Bit.Core.Vault.Entities;
|
||||||
using Bit.Api.Auth.Models.Request.WebAuthn;
|
using Bit.Api.Auth.Models.Request.WebAuthn;
|
||||||
using Bit.Core.Auth.Models.Data;
|
using Bit.Core.Auth.Models.Data;
|
||||||
|
using Bit.Core.Tools.ReportFeatures;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if !OSS
|
#if !OSS
|
||||||
@ -176,6 +178,7 @@ public class Startup
|
|||||||
services.AddOrganizationSubscriptionServices();
|
services.AddOrganizationSubscriptionServices();
|
||||||
services.AddCoreLocalizationServices();
|
services.AddCoreLocalizationServices();
|
||||||
services.AddBillingOperations();
|
services.AddBillingOperations();
|
||||||
|
services.AddReportingServices();
|
||||||
|
|
||||||
// Authorization Handlers
|
// Authorization Handlers
|
||||||
services.AddAuthorizationHandlers();
|
services.AddAuthorizationHandlers();
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
using Bit.Api.Tools.Models.Response;
|
using Bit.Api.Tools.Models;
|
||||||
|
using Bit.Api.Tools.Models.Response;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Tools.Entities;
|
||||||
using Bit.Core.Tools.Models.Data;
|
using Bit.Core.Tools.Models.Data;
|
||||||
|
using Bit.Core.Tools.ReportFeatures.Interfaces;
|
||||||
using Bit.Core.Tools.ReportFeatures.OrganizationReportMembers.Interfaces;
|
using Bit.Core.Tools.ReportFeatures.OrganizationReportMembers.Interfaces;
|
||||||
using Bit.Core.Tools.ReportFeatures.Requests;
|
using Bit.Core.Tools.ReportFeatures.Requests;
|
||||||
|
using Bit.Core.Tools.Requests;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
@ -15,14 +19,20 @@ public class ReportsController : Controller
|
|||||||
{
|
{
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly IMemberAccessCipherDetailsQuery _memberAccessCipherDetailsQuery;
|
private readonly IMemberAccessCipherDetailsQuery _memberAccessCipherDetailsQuery;
|
||||||
|
private readonly IAddPasswordHealthReportApplicationCommand _addPwdHealthReportAppCommand;
|
||||||
|
private readonly IGetPasswordHealthReportApplicationQuery _getPwdHealthReportAppQuery;
|
||||||
|
|
||||||
public ReportsController(
|
public ReportsController(
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
IMemberAccessCipherDetailsQuery memberAccessCipherDetailsQuery
|
IMemberAccessCipherDetailsQuery memberAccessCipherDetailsQuery,
|
||||||
|
IAddPasswordHealthReportApplicationCommand addPasswordHealthReportApplicationCommand,
|
||||||
|
IGetPasswordHealthReportApplicationQuery getPasswordHealthReportApplicationQuery
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_memberAccessCipherDetailsQuery = memberAccessCipherDetailsQuery;
|
_memberAccessCipherDetailsQuery = memberAccessCipherDetailsQuery;
|
||||||
|
_addPwdHealthReportAppCommand = addPasswordHealthReportApplicationCommand;
|
||||||
|
_getPwdHealthReportAppQuery = getPasswordHealthReportApplicationQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -83,4 +93,72 @@ public class ReportsController : Controller
|
|||||||
await _memberAccessCipherDetailsQuery.GetMemberAccessCipherDetails(request);
|
await _memberAccessCipherDetailsQuery.GetMemberAccessCipherDetails(request);
|
||||||
return memberCipherDetails;
|
return memberCipherDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the password health report applications for an organization
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="orgId">A valid Organization Id</param>
|
||||||
|
/// <returns>An Enumerable of PasswordHealthReportApplication </returns>
|
||||||
|
/// <exception cref="NotFoundException">If the user lacks access</exception>
|
||||||
|
/// <exception cref="BadRequestException">If the organization Id is not valid</exception>
|
||||||
|
[HttpGet("password-health-report-applications/{orgId}")]
|
||||||
|
public async Task<IEnumerable<PasswordHealthReportApplication>> GetPasswordHealthReportApplications(Guid orgId)
|
||||||
|
{
|
||||||
|
if (!await _currentContext.AccessReports(orgId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return await _getPwdHealthReportAppQuery.GetPasswordHealthReportApplicationAsync(orgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a new record into PasswordHealthReportApplication
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">A single instance of PasswordHealthReportApplication Model</param>
|
||||||
|
/// <returns>A single instance of PasswordHealthReportApplication</returns>
|
||||||
|
/// <exception cref="BadRequestException">If the organization Id is not valid</exception>
|
||||||
|
/// <exception cref="NotFoundException">If the user lacks access</exception>
|
||||||
|
[HttpPost("password-health-report-application")]
|
||||||
|
public async Task<PasswordHealthReportApplication> AddPasswordHealthReportApplication(
|
||||||
|
[FromBody] PasswordHealthReportApplicationModel request)
|
||||||
|
{
|
||||||
|
if (!await _currentContext.AccessReports(request.OrganizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandRequest = new AddPasswordHealthReportApplicationRequest
|
||||||
|
{
|
||||||
|
OrganizationId = request.OrganizationId,
|
||||||
|
Url = request.Url
|
||||||
|
};
|
||||||
|
|
||||||
|
return await _addPwdHealthReportAppCommand.AddPasswordHealthReportApplicationAsync(commandRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds multiple records into PasswordHealthReportApplication
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">A enumerable of PasswordHealthReportApplicationModel</param>
|
||||||
|
/// <returns>An Enumerable of PasswordHealthReportApplication</returns>
|
||||||
|
/// <exception cref="NotFoundException">If user does not have access to the OrganizationId</exception>
|
||||||
|
/// <exception cref="BadRequestException">If the organization Id is not valid</exception>
|
||||||
|
[HttpPost("password-health-report-applications")]
|
||||||
|
public async Task<IEnumerable<PasswordHealthReportApplication>> AddPasswordHealthReportApplications(
|
||||||
|
[FromBody] IEnumerable<PasswordHealthReportApplicationModel> request)
|
||||||
|
{
|
||||||
|
if (request.Any(_ => _currentContext.AccessReports(_.OrganizationId).Result == false))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandRequests = request.Select(request => new AddPasswordHealthReportApplicationRequest
|
||||||
|
{
|
||||||
|
OrganizationId = request.OrganizationId,
|
||||||
|
Url = request.Url
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
return await _addPwdHealthReportAppCommand.AddPasswordHealthReportApplicationAsync(commandRequests);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace Bit.Api.Tools.Models;
|
||||||
|
|
||||||
|
public class PasswordHealthReportApplicationModel
|
||||||
|
{
|
||||||
|
public Guid OrganizationId { get; set; }
|
||||||
|
public string Url { get; set; }
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Tools.Entities;
|
||||||
|
using Bit.Core.Tools.ReportFeatures.Interfaces;
|
||||||
|
using Bit.Core.Tools.Repositories;
|
||||||
|
using Bit.Core.Tools.Requests;
|
||||||
|
|
||||||
|
namespace Bit.Core.Tools.ReportFeatures;
|
||||||
|
|
||||||
|
public class AddPasswordHealthReportApplicationCommand : IAddPasswordHealthReportApplicationCommand
|
||||||
|
{
|
||||||
|
private IOrganizationRepository _organizationRepo;
|
||||||
|
private IPasswordHealthReportApplicationRepository _passwordHealthReportApplicationRepo;
|
||||||
|
|
||||||
|
public AddPasswordHealthReportApplicationCommand(
|
||||||
|
IOrganizationRepository organizationRepository,
|
||||||
|
IPasswordHealthReportApplicationRepository passwordHealthReportApplicationRepository)
|
||||||
|
{
|
||||||
|
_organizationRepo = organizationRepository;
|
||||||
|
_passwordHealthReportApplicationRepo = passwordHealthReportApplicationRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PasswordHealthReportApplication> AddPasswordHealthReportApplicationAsync(AddPasswordHealthReportApplicationRequest request)
|
||||||
|
{
|
||||||
|
var (req, IsValid, errorMessage) = await ValidateRequestAsync(request);
|
||||||
|
if (!IsValid)
|
||||||
|
{
|
||||||
|
throw new BadRequestException(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
var passwordHealthReportApplication = new PasswordHealthReportApplication
|
||||||
|
{
|
||||||
|
OrganizationId = request.OrganizationId,
|
||||||
|
Uri = request.Url,
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordHealthReportApplication.SetNewId();
|
||||||
|
|
||||||
|
var data = await _passwordHealthReportApplicationRepo.CreateAsync(passwordHealthReportApplication);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<PasswordHealthReportApplication>> AddPasswordHealthReportApplicationAsync(IEnumerable<AddPasswordHealthReportApplicationRequest> requests)
|
||||||
|
{
|
||||||
|
var requestsList = requests.ToList();
|
||||||
|
|
||||||
|
// create tasks to validate each request
|
||||||
|
var tasks = requestsList.Select(async request =>
|
||||||
|
{
|
||||||
|
var (req, IsValid, errorMessage) = await ValidateRequestAsync(request);
|
||||||
|
if (!IsValid)
|
||||||
|
{
|
||||||
|
throw new BadRequestException(errorMessage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// run validations and allow exceptions to bubble
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
|
||||||
|
// create PasswordHealthReportApplication entities
|
||||||
|
var passwordHealthReportApplications = requestsList.Select(request =>
|
||||||
|
{
|
||||||
|
var pwdHealthReportApplication = new PasswordHealthReportApplication
|
||||||
|
{
|
||||||
|
OrganizationId = request.OrganizationId,
|
||||||
|
Uri = request.Url,
|
||||||
|
};
|
||||||
|
pwdHealthReportApplication.SetNewId();
|
||||||
|
return pwdHealthReportApplication;
|
||||||
|
});
|
||||||
|
|
||||||
|
// create and return the entities
|
||||||
|
var response = new List<PasswordHealthReportApplication>();
|
||||||
|
foreach (var record in passwordHealthReportApplications)
|
||||||
|
{
|
||||||
|
var data = await _passwordHealthReportApplicationRepo.CreateAsync(record);
|
||||||
|
response.Add(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Tuple<AddPasswordHealthReportApplicationRequest, bool, string>> ValidateRequestAsync(
|
||||||
|
AddPasswordHealthReportApplicationRequest request)
|
||||||
|
{
|
||||||
|
// verify that the organization exists
|
||||||
|
var organization = await _organizationRepo.GetByIdAsync(request.OrganizationId);
|
||||||
|
if (organization == null)
|
||||||
|
{
|
||||||
|
return new Tuple<AddPasswordHealthReportApplicationRequest, bool, string>(request, false, "Invalid Organization");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure that we have a URL
|
||||||
|
if (string.IsNullOrWhiteSpace(request.Url))
|
||||||
|
{
|
||||||
|
return new Tuple<AddPasswordHealthReportApplicationRequest, bool, string>(request, false, "URL is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tuple<AddPasswordHealthReportApplicationRequest, bool, string>(request, true, string.Empty);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Tools.Entities;
|
||||||
|
using Bit.Core.Tools.ReportFeatures.Interfaces;
|
||||||
|
using Bit.Core.Tools.Repositories;
|
||||||
|
|
||||||
|
namespace Bit.Core.Tools.ReportFeatures;
|
||||||
|
|
||||||
|
public class GetPasswordHealthReportApplicationQuery : IGetPasswordHealthReportApplicationQuery
|
||||||
|
{
|
||||||
|
private IPasswordHealthReportApplicationRepository _passwordHealthReportApplicationRepo;
|
||||||
|
|
||||||
|
public GetPasswordHealthReportApplicationQuery(
|
||||||
|
IPasswordHealthReportApplicationRepository passwordHealthReportApplicationRepo)
|
||||||
|
{
|
||||||
|
_passwordHealthReportApplicationRepo = passwordHealthReportApplicationRepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<PasswordHealthReportApplication>> GetPasswordHealthReportApplicationAsync(Guid organizationId)
|
||||||
|
{
|
||||||
|
if (organizationId == Guid.Empty)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("OrganizationId is required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await _passwordHealthReportApplicationRepo.GetByOrganizationIdAsync(organizationId);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
using Bit.Core.Tools.Entities;
|
||||||
|
using Bit.Core.Tools.Requests;
|
||||||
|
|
||||||
|
namespace Bit.Core.Tools.ReportFeatures.Interfaces;
|
||||||
|
|
||||||
|
public interface IAddPasswordHealthReportApplicationCommand
|
||||||
|
{
|
||||||
|
Task<PasswordHealthReportApplication> AddPasswordHealthReportApplicationAsync(AddPasswordHealthReportApplicationRequest request);
|
||||||
|
Task<IEnumerable<PasswordHealthReportApplication>> AddPasswordHealthReportApplicationAsync(IEnumerable<AddPasswordHealthReportApplicationRequest> requests);
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
using Bit.Core.Tools.Entities;
|
||||||
|
|
||||||
|
namespace Bit.Core.Tools.ReportFeatures.Interfaces;
|
||||||
|
|
||||||
|
public interface IGetPasswordHealthReportApplicationQuery
|
||||||
|
{
|
||||||
|
Task<IEnumerable<PasswordHealthReportApplication>> GetPasswordHealthReportApplicationAsync(Guid organizationId);
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
|
using Bit.Core.Tools.ReportFeatures.Interfaces;
|
||||||
using Bit.Core.Tools.ReportFeatures.OrganizationReportMembers.Interfaces;
|
using Bit.Core.Tools.ReportFeatures.OrganizationReportMembers.Interfaces;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
@ -9,5 +9,7 @@ public static class ReportingServiceCollectionExtensions
|
|||||||
public static void AddReportingServices(this IServiceCollection services)
|
public static void AddReportingServices(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddScoped<IMemberAccessCipherDetailsQuery, MemberAccessCipherDetailsQuery>();
|
services.AddScoped<IMemberAccessCipherDetailsQuery, MemberAccessCipherDetailsQuery>();
|
||||||
|
services.AddScoped<IAddPasswordHealthReportApplicationCommand, AddPasswordHealthReportApplicationCommand>();
|
||||||
|
services.AddScoped<IGetPasswordHealthReportApplicationQuery, GetPasswordHealthReportApplicationQuery>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace Bit.Core.Tools.Requests;
|
||||||
|
|
||||||
|
public class AddPasswordHealthReportApplicationRequest
|
||||||
|
{
|
||||||
|
public Guid OrganizationId { get; set; }
|
||||||
|
public string Url { get; set; }
|
||||||
|
}
|
@ -95,6 +95,7 @@ public static class EntityFrameworkServiceCollectionExtensions
|
|||||||
services.AddSingleton<INotificationStatusRepository, NotificationStatusRepository>();
|
services.AddSingleton<INotificationStatusRepository, NotificationStatusRepository>();
|
||||||
services
|
services
|
||||||
.AddSingleton<IClientOrganizationMigrationRecordRepository, ClientOrganizationMigrationRecordRepository>();
|
.AddSingleton<IClientOrganizationMigrationRecordRepository, ClientOrganizationMigrationRecordRepository>();
|
||||||
|
services.AddSingleton<IPasswordHealthReportApplicationRepository, PasswordHealthReportApplicationRepository>();
|
||||||
|
|
||||||
if (selfHosted)
|
if (selfHosted)
|
||||||
{
|
{
|
||||||
|
49
test/Api.Test/Tools/Controllers/ReportsControllerTests.cs
Normal file
49
test/Api.Test/Tools/Controllers/ReportsControllerTests.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using Bit.Api.Tools.Controllers;
|
||||||
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Tools.ReportFeatures.Interfaces;
|
||||||
|
using Bit.Test.Common.AutoFixture;
|
||||||
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
using NSubstitute;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Api.Test.Tools.Controllers;
|
||||||
|
|
||||||
|
|
||||||
|
[ControllerCustomize(typeof(ReportsController))]
|
||||||
|
[SutProviderCustomize]
|
||||||
|
public class ReportsControllerTests
|
||||||
|
{
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task GetPasswordHealthReportApplicationAsync_Success(SutProvider<ReportsController> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(Arg.Any<Guid>()).Returns(true);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var orgId = Guid.NewGuid();
|
||||||
|
var result = await sutProvider.Sut.GetPasswordHealthReportApplications(orgId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_ = sutProvider.GetDependency<IGetPasswordHealthReportApplicationQuery>()
|
||||||
|
.Received(1)
|
||||||
|
.GetPasswordHealthReportApplicationAsync(Arg.Is<Guid>(_ => _ == orgId));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task GetPasswordHealthReportApplicationAsync_withoutAccess(SutProvider<ReportsController> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(Arg.Any<Guid>()).Returns(false);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var orgId = Guid.NewGuid();
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.GetPasswordHealthReportApplications(orgId));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_ = sutProvider.GetDependency<IGetPasswordHealthReportApplicationQuery>()
|
||||||
|
.Received(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,149 @@
|
|||||||
|
using AutoFixture;
|
||||||
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Tools.Entities;
|
||||||
|
using Bit.Core.Tools.ReportFeatures;
|
||||||
|
using Bit.Core.Tools.Repositories;
|
||||||
|
using Bit.Core.Tools.Requests;
|
||||||
|
using Bit.Test.Common.AutoFixture;
|
||||||
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
using NSubstitute;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Core.Test.Tools.ReportFeatures;
|
||||||
|
|
||||||
|
[SutProviderCustomize]
|
||||||
|
public class AddPasswordHealthReportApplicationCommandTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task AddPasswordHealthReportApplicationAsync_WithValidRequest_ShouldReturnPasswordHealthReportApplication(
|
||||||
|
SutProvider<AddPasswordHealthReportApplicationCommand> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var fixture = new Fixture();
|
||||||
|
var request = fixture.Create<AddPasswordHealthReportApplicationRequest>();
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
|
.GetByIdAsync(Arg.Any<Guid>())
|
||||||
|
.Returns(fixture.Create<Organization>());
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IPasswordHealthReportApplicationRepository>()
|
||||||
|
.CreateAsync(Arg.Any<PasswordHealthReportApplication>())
|
||||||
|
.Returns(c => c.Arg<PasswordHealthReportApplication>());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await sutProvider.Sut.AddPasswordHealthReportApplicationAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task AddPasswordHealthReportApplicationAsync_WithInvalidOrganizationId_ShouldThrowError(
|
||||||
|
SutProvider<AddPasswordHealthReportApplicationCommand> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var fixture = new Fixture();
|
||||||
|
var request = fixture.Create<AddPasswordHealthReportApplicationRequest>();
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
|
.GetByIdAsync(Arg.Any<Guid>())
|
||||||
|
.Returns(null as Organization);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.AddPasswordHealthReportApplicationAsync(request));
|
||||||
|
Assert.Equal("Invalid Organization", exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task AddPasswordHealthReportApplicationAsync_WithInvalidUrl_ShouldThrowError(
|
||||||
|
SutProvider<AddPasswordHealthReportApplicationCommand> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var fixture = new Fixture();
|
||||||
|
var request = fixture.Build<AddPasswordHealthReportApplicationRequest>()
|
||||||
|
.Without(_ => _.Url)
|
||||||
|
.Create();
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
|
.GetByIdAsync(Arg.Any<Guid>())
|
||||||
|
.Returns(fixture.Create<Organization>());
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.AddPasswordHealthReportApplicationAsync(request));
|
||||||
|
Assert.Equal("URL is required", exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task AddPasswordHealthReportApplicationAsync_Multiples_WithInvalidOrganizationId_ShouldThrowError(
|
||||||
|
SutProvider<AddPasswordHealthReportApplicationCommand> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var fixture = new Fixture();
|
||||||
|
var request = fixture.Build<AddPasswordHealthReportApplicationRequest>()
|
||||||
|
.Without(_ => _.OrganizationId)
|
||||||
|
.CreateMany(2).ToList();
|
||||||
|
|
||||||
|
request[0].OrganizationId = Guid.NewGuid();
|
||||||
|
request[1].OrganizationId = Guid.Empty;
|
||||||
|
|
||||||
|
// only return an organization for the first request and null for the second
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
|
.GetByIdAsync(Arg.Is<Guid>(x => x == request[0].OrganizationId))
|
||||||
|
.Returns(fixture.Create<Organization>());
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.AddPasswordHealthReportApplicationAsync(request));
|
||||||
|
Assert.Equal("Invalid Organization", exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task AddPasswordHealthReportApplicationAsync_Multiples_WithInvalidUrl_ShouldThrowError(
|
||||||
|
SutProvider<AddPasswordHealthReportApplicationCommand> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var fixture = new Fixture();
|
||||||
|
var request = fixture.Build<AddPasswordHealthReportApplicationRequest>()
|
||||||
|
.CreateMany(2).ToList();
|
||||||
|
|
||||||
|
request[1].Url = string.Empty;
|
||||||
|
|
||||||
|
// return an organization for both requests
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
|
.GetByIdAsync(Arg.Any<Guid>())
|
||||||
|
.Returns(fixture.Create<Organization>());
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.AddPasswordHealthReportApplicationAsync(request));
|
||||||
|
Assert.Equal("URL is required", exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task AddPasswordHealthReportApplicationAsync_Multiples_WithValidRequest_ShouldBeSuccessful(
|
||||||
|
SutProvider<AddPasswordHealthReportApplicationCommand> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var fixture = new Fixture();
|
||||||
|
var request = fixture.CreateMany<AddPasswordHealthReportApplicationRequest>(2);
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
|
.GetByIdAsync(Arg.Any<Guid>())
|
||||||
|
.Returns(fixture.Create<Organization>());
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IPasswordHealthReportApplicationRepository>()
|
||||||
|
.CreateAsync(Arg.Any<PasswordHealthReportApplication>())
|
||||||
|
.Returns(c => c.Arg<PasswordHealthReportApplication>());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await sutProvider.Sut.AddPasswordHealthReportApplicationAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result.Count() == 2);
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>().Received(2);
|
||||||
|
sutProvider.GetDependency<IPasswordHealthReportApplicationRepository>().Received(2);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
using AutoFixture;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Tools.Entities;
|
||||||
|
using Bit.Core.Tools.ReportFeatures;
|
||||||
|
using Bit.Core.Tools.Repositories;
|
||||||
|
using Bit.Test.Common.AutoFixture;
|
||||||
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
using NSubstitute;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Core.Test.Tools.ReportFeatures;
|
||||||
|
|
||||||
|
[SutProviderCustomize]
|
||||||
|
public class GetPasswordHealthReportApplicationQueryTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task GetPasswordHealthReportApplicationAsync_WithValidOrganizationId_ShouldReturnPasswordHealthReportApplication(
|
||||||
|
SutProvider<GetPasswordHealthReportApplicationQuery> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var fixture = new Fixture();
|
||||||
|
var organizationId = fixture.Create<Guid>();
|
||||||
|
sutProvider.GetDependency<IPasswordHealthReportApplicationRepository>()
|
||||||
|
.GetByOrganizationIdAsync(Arg.Any<Guid>())
|
||||||
|
.Returns(fixture.CreateMany<PasswordHealthReportApplication>(2).ToList());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await sutProvider.Sut.GetPasswordHealthReportApplicationAsync(organizationId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.True(result.Count() == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task GetPasswordHealthReportApplicationAsync_WithInvalidOrganizationId_ShouldFail(
|
||||||
|
SutProvider<GetPasswordHealthReportApplicationQuery> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var fixture = new Fixture();
|
||||||
|
sutProvider.GetDependency<IPasswordHealthReportApplicationRepository>()
|
||||||
|
.GetByOrganizationIdAsync(Arg.Is<Guid>(x => x == Guid.Empty))
|
||||||
|
.Returns(new List<PasswordHealthReportApplication>());
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.GetPasswordHealthReportApplicationAsync(Guid.Empty));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("OrganizationId is required.", exception.Message);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user