1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-06 05:28:15 -05:00

[PM-15621] Got the app code completed and tested the basic happy paths.

This commit is contained in:
Jimmy Vo 2025-04-04 17:13:19 -04:00
parent cc654d5d78
commit 8875cb5975
No known key found for this signature in database
GPG Key ID: 7CB834D6F4FFCA11
10 changed files with 463 additions and 444 deletions

View File

@ -22,6 +22,7 @@ using Bit.Core.Context;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Models.Business; using Bit.Core.Models.Business;
using Bit.Core.Models.Commands;
using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
@ -592,9 +593,9 @@ public class OrganizationUsersController : Controller
return Unauthorized(); return Unauthorized();
} }
var deletionResult = await _deleteManagedOrganizationUserAccountCommand.DeleteUserAsync(orgId, id, currentUser.Id); var result = await _deleteManagedOrganizationUserAccountCommand.DeleteUserAsync(orgId, id, currentUser.Id);
return deletionResult.MapToActionResult(); return result.MapToActionResultWithSingleErrorMessage();
} }
[RequireFeature(FeatureFlagKeys.AccountDeprovisioning)] [RequireFeature(FeatureFlagKeys.AccountDeprovisioning)]
@ -613,12 +614,16 @@ public class OrganizationUsersController : Controller
throw new UnauthorizedAccessException(); throw new UnauthorizedAccessException();
} }
var results = await _deleteManagedOrganizationUserAccountCommand.DeleteManyUsersAsync(orgId, model.Ids, currentUser.Id); var result = await _deleteManagedOrganizationUserAccountCommand.DeleteManyUsersAsync(orgId, model.Ids, currentUser.Id);
// Temporary code. return MapToOrganizationUserBulkResponseModel(result);
throw new UnauthorizedAccessException(); }
// return new ListResponseModel<OrganizationUserBulkResponseModel>(results.Select(r =>
// new OrganizationUserBulkResponseModel(r.OrganizationUserId, r.result))); private static ListResponseModel<OrganizationUserBulkResponseModel> MapToOrganizationUserBulkResponseModel(Partial<Core.Models.Data.Organizations.DeleteUserResponse> result)
{
var failures = result.Failures.Select(failure => new OrganizationUserBulkResponseModel(failure.ErroredValue.OrganizationUserId, failure.Message));
var successes = result.Successes.Select(success => new OrganizationUserBulkResponseModel(success.OrganizationUserId, string.Empty));
return new ListResponseModel<OrganizationUserBulkResponseModel>(failures.Concat(successes));
} }
[HttpPatch("{id}/revoke")] [HttpPatch("{id}/revoke")]

View File

@ -3,7 +3,6 @@ using Bit.Api.Models.Response;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Api; using Bit.Core.Models.Api;
using Bit.Core.Models.Commands;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Utilities; using Bit.Core.Utilities;
@ -204,17 +203,6 @@ public class OrganizationUserBulkResponseModel : ResponseModel
Id = id; Id = id;
Error = error; Error = error;
} }
public OrganizationUserBulkResponseModel(Guid id, CommandResult result,
string obj = "OrganizationBulkConfirmResponseModel") : base(obj)
{
Id = id;
if (result is Failure)
{
Error = result.ErrorMessages.ToString();
}
}
public Guid Id { get; set; } public Guid Id { get; set; }
public string Error { get; set; } public string Error { get; set; }
} }

View File

@ -5,7 +5,7 @@ namespace Bit.Api.Utilities;
public static class CommandResultExtensions public static class CommandResultExtensions
{ {
public static IActionResult MapToActionResult<T>(this CommandResult<T> commandResult) public static IActionResult MapToActionResultWithErrorMessages<T>(this CommandResult<T> commandResult)
{ {
return commandResult switch return commandResult switch
{ {
@ -17,6 +17,18 @@ public static class CommandResultExtensions
}; };
} }
public static IActionResult MapToActionResultWithSingleErrorMessage<T>(this CommandResult<T> commandResult)
{
return commandResult switch
{
NoRecordFoundFailure<T> failure => new ObjectResult(failure.ErrorMessage) { StatusCode = StatusCodes.Status404NotFound },
BadRequestFailure<T> failure => new ObjectResult(failure.ErrorMessage) { StatusCode = StatusCodes.Status400BadRequest },
Failure<T> failure => new ObjectResult(failure.ErrorMessage) { StatusCode = StatusCodes.Status400BadRequest },
Success<T> success => new ObjectResult(success.Value) { StatusCode = StatusCodes.Status200OK },
_ => throw new InvalidOperationException($"Unhandled commandResult type: {commandResult.GetType().Name}")
};
}
public static IActionResult MapToActionResult(this CommandResult commandResult) public static IActionResult MapToActionResult(this CommandResult commandResult)
{ {
return commandResult switch return commandResult switch

View File

@ -1,8 +1,20 @@
namespace Bit.Core.AdminConsole.Errors; using Bit.Core.Models.Commands;
namespace Bit.Core.AdminConsole.Errors;
public record Error<T>(string Message, T ErroredValue); public record Error<T>(string Message, T ErroredValue);
public static class ErrorMappers public static class ErrorMappers
{ {
public static Error<B> ToError<A, B>(this Error<A> errorA, B erroredValue) => new(errorA.Message, erroredValue); public static Error<B> ToError<A, B>(this Error<A> errorA, B erroredValue) => new(errorA.Message, erroredValue);
public static Failure<CommandType> ToFailure<ValidationType, CommandType>(this Error<ValidationType> error)
{
return error switch
{
BadRequestError<ValidationType> badRequest => new BadRequestFailure<CommandType>(badRequest.Message),
RecordNotFoundError<ValidationType> recordNotFound => new NoRecordFoundFailure<CommandType>(recordNotFound.Message),
_ => throw new InvalidOperationException($"Unhandled Error type: {error.GetType().Name}")
};
}
} }

View File

@ -2,5 +2,5 @@
public class DeleteUserResponse public class DeleteUserResponse
{ {
public Guid OrganizationId { get; init; } public Guid OrganizationUserId { get; init; }
} }

View File

@ -1,10 +1,12 @@
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.AdminConsole.Errors;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.AdminConsole.Shared.Validation; using Bit.Core.AdminConsole.Shared.Validation;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Models.Commands; using Bit.Core.Models.Commands;
using Bit.Core.Models.Data.Organizations;
using Bit.Core.Platform.Push; using Bit.Core.Platform.Push;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
@ -50,25 +52,35 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz
_pushService = pushService; _pushService = pushService;
} }
public async Task<CommandResult> DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid deletingUserId) public async Task<CommandResult<DeleteUserResponse>> DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid deletingUserId)
{ {
var result = await InternalDeleteManyUsersAsync(organizationId, new[] { organizationUserId }, deletingUserId); var result = await InternalDeleteManyUsersAsync(organizationId, [organizationUserId], deletingUserId);
var error = result.InvalidResults.FirstOrDefault()?.Errors.FirstOrDefault(); var error = result.InvalidResults.FirstOrDefault()?.Errors.FirstOrDefault();
if (error != null) if (error != null)
{ {
return new Failure(error.Message); return error.ToFailure<DeleteUserValidationRequest, DeleteUserResponse>();
} }
return new Success(); var valid = result.ValidResults.First();
return new Success<DeleteUserResponse>(new DeleteUserResponse
{
OrganizationUserId = valid!.Value.OrganizationUserId
});
} }
public async Task<CommandResult> DeleteManyUsersAsync(Guid organizationId, IEnumerable<Guid> orgUserIds, Guid deletingUserId) public async Task<Partial<DeleteUserResponse>> DeleteManyUsersAsync(Guid organizationId, IEnumerable<Guid> orgUserIds, Guid deletingUserId)
{ {
var results = await InternalDeleteManyUsersAsync(organizationId, orgUserIds, deletingUserId); var result = await InternalDeleteManyUsersAsync(organizationId, orgUserIds, deletingUserId);
return new Success(); var successes = result.ValidResults.Select(valid => new DeleteUserResponse { OrganizationUserId = valid.Value.OrganizationUser!.Id });
var errors = result.InvalidResults
.Select(invalid => invalid.Errors.First())
.Select(error => error.ToError(new DeleteUserResponse() { OrganizationUserId = error.ErroredValue.OrganizationUserId }));
return new Partial<DeleteUserResponse>(successes, errors);
} }
private async Task<PartialValidationResult<DeleteUserValidationRequest>> InternalDeleteManyUsersAsync(Guid organizationId, IEnumerable<Guid> orgUserIds, Guid deletingUserId) private async Task<PartialValidationResult<DeleteUserValidationRequest>> InternalDeleteManyUsersAsync(Guid organizationId, IEnumerable<Guid> orgUserIds, Guid deletingUserId)
@ -78,13 +90,13 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz
var managementStatuses = await _getOrganizationUsersManagementStatusQuery.GetUsersOrganizationManagementStatusAsync(organizationId, orgUserIds); var managementStatuses = await _getOrganizationUsersManagementStatusQuery.GetUsersOrganizationManagementStatusAsync(organizationId, orgUserIds);
var requests = CreateRequests(organizationId, deletingUserId, orgUserIds, orgUsers, users, managementStatuses); var requests = CreateRequests(organizationId, deletingUserId, orgUserIds, orgUsers, users, managementStatuses);
var validationResults = await _deleteManagedOrganizationUserAccountValidator.ValidateAsync(requests); var results = await _deleteManagedOrganizationUserAccountValidator.ValidateAsync(requests);
await CancelPremiumsAsync(validationResults.ValidResults); await CancelPremiumsAsync(results.ValidResults);
await HandleUserDeletionsAsync(validationResults.ValidResults); await HandleUserDeletionsAsync(results.ValidResults);
await LogDeletedOrganizationUsersAsync(validationResults.ValidResults); await LogDeletedOrganizationUsersAsync(results.ValidResults);
return validationResults; return results;
} }
private List<DeleteUserValidationRequest> CreateRequests( private List<DeleteUserValidationRequest> CreateRequests(
@ -105,6 +117,7 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz
requests.Add(new DeleteUserValidationRequest requests.Add(new DeleteUserValidationRequest
{ {
User = user, User = user,
OrganizationUserId = orgUserId,
OrganizationUser = orgUser, OrganizationUser = orgUser,
IsManaged = isManaged, IsManaged = isManaged,
OrganizationId = organizationId, OrganizationId = organizationId,

View File

@ -6,6 +6,7 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
public class DeleteUserValidationRequest public class DeleteUserValidationRequest
{ {
public Guid OrganizationId { get; init; } public Guid OrganizationId { get; init; }
public Guid OrganizationUserId { get; init; }
public OrganizationUser? OrganizationUser { get; init; } public OrganizationUser? OrganizationUser { get; init; }
public User? User { get; init; } public User? User { get; init; }
public Guid DeletingUserId { get; init; } public Guid DeletingUserId { get; init; }

View File

@ -1,6 +1,7 @@
#nullable enable #nullable enable
using Bit.Core.Models.Commands; using Bit.Core.Models.Commands;
using Bit.Core.Models.Data.Organizations;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
@ -9,7 +10,7 @@ public interface IDeleteManagedOrganizationUserAccountCommand
/// <summary> /// <summary>
/// Removes a user from an organization and deletes all of their associated user data. /// Removes a user from an organization and deletes all of their associated user data.
/// </summary> /// </summary>
Task<CommandResult> DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid deletingUserId); Task<CommandResult<DeleteUserResponse>> DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid deletingUserId);
/// <summary> /// <summary>
/// Removes multiple users from an organization and deletes all of their associated user data. /// Removes multiple users from an organization and deletes all of their associated user data.
@ -17,5 +18,5 @@ public interface IDeleteManagedOrganizationUserAccountCommand
/// <returns> /// <returns>
/// An error message for each user that could not be removed, otherwise null. /// An error message for each user that could not be removed, otherwise null.
/// </returns> /// </returns>
Task<CommandResult> DeleteManyUsersAsync(Guid organizationId, IEnumerable<Guid> orgUserIds, Guid deletingUserId); Task<Partial<DeleteUserResponse>> DeleteManyUsersAsync(Guid organizationId, IEnumerable<Guid> orgUserIds, Guid deletingUserId);
} }

View File

@ -40,7 +40,7 @@ public class CommandResultExtensionTests
[MemberData(nameof(WithGenericTypeTestCases))] [MemberData(nameof(WithGenericTypeTestCases))]
public void MapToActionResult_WithGenericType_ShouldMapToHttpResponse(CommandResult<Cipher> input, ObjectResult expected) public void MapToActionResult_WithGenericType_ShouldMapToHttpResponse(CommandResult<Cipher> input, ObjectResult expected)
{ {
var result = input.MapToActionResult(); var result = input.MapToActionResultWithErrorMessages();
Assert.Equivalent(expected, result); Assert.Equivalent(expected, result);
} }
@ -92,7 +92,7 @@ public class CommandResultExtensionTests
{ {
var result = new NotImplementedCommandResult<Cipher>(); var result = new NotImplementedCommandResult<Cipher>();
Assert.Throws<InvalidOperationException>(() => result.MapToActionResult()); Assert.Throws<InvalidOperationException>(() => result.MapToActionResultWithErrorMessages());
} }
} }

View File

@ -1,17 +1,4 @@
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers; using Bit.Test.Common.AutoFixture.Attributes;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Commands;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Test.AutoFixture.OrganizationUserFixtures;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers; namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers;
@ -19,415 +6,415 @@ namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers;
public class DeleteManagedOrganizationUserAccountCommandTests public class DeleteManagedOrganizationUserAccountCommandTests
{ {
[Theory] // [Theory]
[BitAutoData] // [BitAutoData]
public async Task DeleteUserAsync_WithValidUser_DeletesUserAndLogsEvents( // public async Task DeleteUserAsync_WithValidUser_DeletesUserAndLogsEvents(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, Guid organizationId, // SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, Guid organizationId,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser) // [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser)
{ // {
// Arrange // // Arrange
orgUser.OrganizationId = organizationId; // orgUser.OrganizationId = organizationId;
orgUser.UserId = user.Id; // orgUser.UserId = user.Id;
sutProvider.GetDependency<IOrganizationUserRepository>() // sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyAsync(Arg.Any<IEnumerable<Guid>>()) // .GetManyAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(new List<OrganizationUser> { orgUser }); // .Returns(new List<OrganizationUser> { orgUser });
sutProvider.GetDependency<IUserRepository>() // sutProvider.GetDependency<IUserRepository>()
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user.Id))) // .GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user.Id)))
.Returns(new[] { user }); // .Returns(new[] { user });
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() // sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>()
.GetUsersOrganizationManagementStatusAsync(organizationId, Arg.Any<IEnumerable<Guid>>()) // .GetUsersOrganizationManagementStatusAsync(organizationId, Arg.Any<IEnumerable<Guid>>())
.Returns(new Dictionary<Guid, bool> { { orgUser.Id, true } }); // .Returns(new Dictionary<Guid, bool> { { orgUser.Id, true } });
// Act // // Act
await sutProvider.Sut.DeleteUserAsync(organizationId, orgUser.Id, null); // await sutProvider.Sut.DeleteUserAsync(organizationId, orgUser.Id, null);
// Assert // // Assert
await sutProvider.GetDependency<IUserRepository>().Received(1).DeleteManyAsync(Arg.Is<IEnumerable<User>>(users => users.Any(u => u.Id == user.Id))); // await sutProvider.GetDependency<IUserRepository>().Received(1).DeleteManyAsync(Arg.Is<IEnumerable<User>>(users => users.Any(u => u.Id == user.Id)));
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync( // await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync(
Arg.Is<IEnumerable<(OrganizationUser, EventType, DateTime?)>>(events => // Arg.Is<IEnumerable<(OrganizationUser, EventType, DateTime?)>>(events =>
events.Count(e => e.Item1.Id == orgUser.Id && e.Item2 == EventType.OrganizationUser_Deleted) == 1)); // events.Count(e => e.Item1.Id == orgUser.Id && e.Item2 == EventType.OrganizationUser_Deleted) == 1));
} // }
[Theory] // [Theory]
[BitAutoData] // [BitAutoData]
public async Task DeleteUserAsync_WhenError_ShouldReturnFailure( // public async Task DeleteUserAsync_WhenError_ShouldReturnFailure(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, Guid organizationId, // SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, Guid organizationId,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser) // [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser)
{ // {
// Arrange // // Arrange
orgUser.OrganizationId = organizationId; // orgUser.OrganizationId = organizationId;
orgUser.UserId = user.Id; // orgUser.UserId = user.Id;
sutProvider.GetDependency<IOrganizationUserRepository>() // sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyAsync(Arg.Any<IEnumerable<Guid>>()) // .GetManyAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(new List<OrganizationUser> { }); // .Returns(new List<OrganizationUser> { });
// Act // // Act
// Test is not ready // // Test is not ready
await sutProvider.Sut.DeleteUserAsync(organizationId, orgUser.Id, null); // await sutProvider.Sut.DeleteUserAsync(organizationId, orgUser.Id, null);
// Assert // // Assert
} // }
[Theory] // [Theory]
[BitAutoData] // [BitAutoData]
public async Task DeleteManyUsersAsync_WithValidUsers_DeletesUsersAndLogsEvents( // public async Task DeleteManyUsersAsync_WithValidUsers_DeletesUsersAndLogsEvents(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user1, User user2, Guid organizationId, // SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user1, User user2, Guid organizationId,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser1, // [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser1,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser2) // [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser2)
{ // {
// Arrange // // Arrange
orgUser1.OrganizationId = orgUser2.OrganizationId = organizationId; // orgUser1.OrganizationId = orgUser2.OrganizationId = organizationId;
orgUser1.UserId = user1.Id; // orgUser1.UserId = user1.Id;
orgUser2.UserId = user2.Id; // orgUser2.UserId = user2.Id;
sutProvider.GetDependency<IOrganizationUserRepository>() // sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyAsync(Arg.Any<IEnumerable<Guid>>()) // .GetManyAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(new List<OrganizationUser> { orgUser1, orgUser2 }); // .Returns(new List<OrganizationUser> { orgUser1, orgUser2 });
sutProvider.GetDependency<IUserRepository>() // sutProvider.GetDependency<IUserRepository>()
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user1.Id) && ids.Contains(user2.Id))) // .GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user1.Id) && ids.Contains(user2.Id)))
.Returns(new[] { user1, user2 }); // .Returns(new[] { user1, user2 });
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() // sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>()
.GetUsersOrganizationManagementStatusAsync(organizationId, Arg.Any<IEnumerable<Guid>>()) // .GetUsersOrganizationManagementStatusAsync(organizationId, Arg.Any<IEnumerable<Guid>>())
.Returns(new Dictionary<Guid, bool> { { orgUser1.Id, true }, { orgUser2.Id, true } }); // .Returns(new Dictionary<Guid, bool> { { orgUser1.Id, true }, { orgUser2.Id, true } });
// Act // // Act
var userIds = new[] { orgUser1.Id, orgUser2.Id }; // var userIds = new[] { orgUser1.Id, orgUser2.Id };
var results = await sutProvider.Sut.DeleteManyUsersAsync(organizationId, userIds, null); // var results = await sutProvider.Sut.DeleteManyUsersAsync(organizationId, userIds, null);
// Assert // // Assert
Assert.Equal(2, results.Count()); // Assert.Equal(2, results.Count());
Assert.All(results, r => Assert.IsType<Success>(r.Item2)); // Assert.All(results, r => Assert.IsType<Success>(r.Item2));
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).GetManyAsync(userIds); // await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).GetManyAsync(userIds);
await sutProvider.GetDependency<IUserRepository>().Received(1).DeleteManyAsync(Arg.Is<IEnumerable<User>>(users => users.Any(u => u.Id == user1.Id) && users.Any(u => u.Id == user2.Id))); // await sutProvider.GetDependency<IUserRepository>().Received(1).DeleteManyAsync(Arg.Is<IEnumerable<User>>(users => users.Any(u => u.Id == user1.Id) && users.Any(u => u.Id == user2.Id)));
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync( // await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync(
Arg.Is<IEnumerable<(OrganizationUser, EventType, DateTime?)>>(events => // Arg.Is<IEnumerable<(OrganizationUser, EventType, DateTime?)>>(events =>
events.Count(e => e.Item1.Id == orgUser1.Id && e.Item2 == EventType.OrganizationUser_Deleted) == 1 // events.Count(e => e.Item1.Id == orgUser1.Id && e.Item2 == EventType.OrganizationUser_Deleted) == 1
&& events.Count(e => e.Item1.Id == orgUser2.Id && e.Item2 == EventType.OrganizationUser_Deleted) == 1)); // && events.Count(e => e.Item1.Id == orgUser2.Id && e.Item2 == EventType.OrganizationUser_Deleted) == 1));
} // }
[Theory] // [Theory]
[BitAutoData] // [BitAutoData]
public async Task DeleteManyUsersAsync_WhenUserNotFound_ReturnsErrorMessage( // public async Task DeleteManyUsersAsync_WhenUserNotFound_ReturnsErrorMessage(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, // SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider,
Guid organizationId, // Guid organizationId,
Guid orgUserId) // Guid orgUserId)
{ // {
// Act // // Act
var result = await sutProvider.Sut.DeleteManyUsersAsync(organizationId, new[] { orgUserId }, null); // var result = await sutProvider.Sut.DeleteManyUsersAsync(organizationId, new[] { orgUserId }, null);
// Assert // // Assert
Assert.Single(result); // Assert.Single(result);
var userId = result.First().Item1; // var userId = result.First().Item1;
Assert.Equal(orgUserId, userId); // Assert.Equal(orgUserId, userId);
var commandResult = result.First().Item2; // var commandResult = result.First().Item2;
AssertErrorMessages("Member not found.", commandResult); // AssertErrorMessages("Member not found.", commandResult);
await sutProvider.GetDependency<IUserRepository>() // await sutProvider.GetDependency<IUserRepository>()
.DidNotReceiveWithAnyArgs() // .DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default); // .DeleteManyAsync(default);
await sutProvider.GetDependency<IEventService>().Received(0) // await sutProvider.GetDependency<IEventService>().Received(0)
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>()); // .LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
} // }
[Theory] // [Theory]
[BitAutoData] // [BitAutoData]
public async Task DeleteManyUsersAsync_WhenDeletingYourself_ReturnsErrorMessage( // public async Task DeleteManyUsersAsync_WhenDeletingYourself_ReturnsErrorMessage(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, // SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider,
User user, [OrganizationUser] OrganizationUser orgUser, Guid deletingUserId) // User user, [OrganizationUser] OrganizationUser orgUser, Guid deletingUserId)
{ // {
// Arrange // // Arrange
orgUser.UserId = user.Id = deletingUserId; // orgUser.UserId = user.Id = deletingUserId;
sutProvider.GetDependency<IOrganizationUserRepository>() // sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyAsync(Arg.Any<IEnumerable<Guid>>()) // .GetManyAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(new List<OrganizationUser> { orgUser }); // .Returns(new List<OrganizationUser> { orgUser });
sutProvider.GetDependency<IUserRepository>() // sutProvider.GetDependency<IUserRepository>()
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user.Id))) // .GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user.Id)))
.Returns(new[] { user }); // .Returns(new[] { user });
// Act // // Act
var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, deletingUserId); // var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, deletingUserId);
// Assert // // Assert
Assert.Single(result); // Assert.Single(result);
var userId = result.First().Item1; // var userId = result.First().Item1;
Assert.Equal(orgUser.Id, userId); // Assert.Equal(orgUser.Id, userId);
var commandResult = result.First().Item2; // var commandResult = result.First().Item2;
AssertErrorMessages("You cannot delete yourself.", commandResult); // AssertErrorMessages("You cannot delete yourself.", commandResult);
await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>()); // await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
await sutProvider.GetDependency<IEventService>().Received(0) // await sutProvider.GetDependency<IEventService>().Received(0)
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>()); // .LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
} // }
[Theory] // [Theory]
[BitAutoData] // [BitAutoData]
public async Task DeleteManyUsersAsync_WhenUserIsInvited_ReturnsErrorMessage( // public async Task DeleteManyUsersAsync_WhenUserIsInvited_ReturnsErrorMessage(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, // SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider,
User user, // User user,
[OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.User)] OrganizationUser orgUser) // [OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.User)] OrganizationUser orgUser)
{ // {
// Arrange // // Arrange
orgUser.UserId = user.Id; // orgUser.UserId = user.Id;
sutProvider.GetDependency<IOrganizationUserRepository>() // sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyAsync(Arg.Any<IEnumerable<Guid>>()) // .GetManyAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(new List<OrganizationUser> { orgUser }); // .Returns(new List<OrganizationUser> { orgUser });
sutProvider.GetDependency<IUserRepository>() // sutProvider.GetDependency<IUserRepository>()
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(input => input.Contains(user.Id))) // .GetManyAsync(Arg.Is<IEnumerable<Guid>>(input => input.Contains(user.Id)))
.Returns(new[] { user }); // .Returns(new[] { user });
// Act // // Act
var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, null); // var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, null);
// Assert // // Assert
Assert.Single(result); // Assert.Single(result);
var userId = result.First().Item1; // var userId = result.First().Item1;
Assert.Equal(orgUser.Id, userId); // Assert.Equal(orgUser.Id, userId);
AssertErrorMessages("You cannot delete a member with Invited status.", result.First().Item2); // AssertErrorMessages("You cannot delete a member with Invited status.", result.First().Item2);
await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>()); // await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
await sutProvider.GetDependency<IEventService>().Received(0) // await sutProvider.GetDependency<IEventService>().Received(0)
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>()); // .LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
} // }
[Theory] // [Theory]
[BitAutoData] // [BitAutoData]
public async Task DeleteManyUsersAsync_WhenDeletingOwnerAsNonOwner_ReturnsErrorMessage( // public async Task DeleteManyUsersAsync_WhenDeletingOwnerAsNonOwner_ReturnsErrorMessage(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, // SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser, // [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser,
Guid deletingUserId) // Guid deletingUserId)
{ // {
// Arrange // // Arrange
orgUser.UserId = user.Id; // orgUser.UserId = user.Id;
sutProvider.GetDependency<IOrganizationUserRepository>() // sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyAsync(Arg.Any<IEnumerable<Guid>>()) // .GetManyAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(new List<OrganizationUser> { orgUser }); // .Returns(new List<OrganizationUser> { orgUser });
sutProvider.GetDependency<IUserRepository>() // sutProvider.GetDependency<IUserRepository>()
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(user.Id))) // .GetManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(user.Id)))
.Returns(new[] { user }); // .Returns(new[] { user });
sutProvider.GetDependency<ICurrentContext>() // sutProvider.GetDependency<ICurrentContext>()
.OrganizationOwner(orgUser.OrganizationId) // .OrganizationOwner(orgUser.OrganizationId)
.Returns(false); // .Returns(false);
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() // sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>()
.GetUsersOrganizationManagementStatusAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>()) // .GetUsersOrganizationManagementStatusAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>())
.Returns(new Dictionary<Guid, bool> { { orgUser.Id, true } }); // .Returns(new Dictionary<Guid, bool> { { orgUser.Id, true } });
// Act // // Act
var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, deletingUserId); // var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, deletingUserId);
// Assert // // Assert
Assert.Single(result); // Assert.Single(result);
var userId = result.First().Item1; // var userId = result.First().Item1;
Assert.Equal(orgUser.Id, userId); // Assert.Equal(orgUser.Id, userId);
var commandResult = result.First().Item2; // var commandResult = result.First().Item2;
AssertErrorMessages("Only owners can delete other owners.", commandResult); // AssertErrorMessages("Only owners can delete other owners.", commandResult);
await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>()); // await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
await sutProvider.GetDependency<IEventService>().Received(0) // await sutProvider.GetDependency<IEventService>().Received(0)
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>()); // .LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
} // }
[Theory] // [Theory]
[BitAutoData] // [BitAutoData]
public async Task DeleteManyUsersAsync_WhenUserNotManaged_ReturnsErrorMessage( // public async Task DeleteManyUsersAsync_WhenUserNotManaged_ReturnsErrorMessage(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, // SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser) // [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser)
{ // {
// Arrange // // Arrange
orgUser.UserId = user.Id; // orgUser.UserId = user.Id;
sutProvider.GetDependency<IOrganizationUserRepository>() // sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyAsync(Arg.Any<IEnumerable<Guid>>()) // .GetManyAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(new List<OrganizationUser> { orgUser }); // .Returns(new List<OrganizationUser> { orgUser });
sutProvider.GetDependency<IUserRepository>() // sutProvider.GetDependency<IUserRepository>()
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser.UserId.Value))) // .GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser.UserId.Value)))
.Returns(new[] { user }); // .Returns(new[] { user });
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() // sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>()
.GetUsersOrganizationManagementStatusAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>()) // .GetUsersOrganizationManagementStatusAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>())
.Returns(new Dictionary<Guid, bool> { { orgUser.Id, false } }); // .Returns(new Dictionary<Guid, bool> { { orgUser.Id, false } });
// Act // // Act
var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, null); // var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, null);
// Assert // // Assert
Assert.Single(result); // Assert.Single(result);
var userId = result.First().Item1; // var userId = result.First().Item1;
Assert.Equal(orgUser.Id, userId); // Assert.Equal(orgUser.Id, userId);
var commandResult = result.First().Item2; // var commandResult = result.First().Item2;
AssertErrorMessages("Member is not managed by the organization.", commandResult); // AssertErrorMessages("Member is not managed by the organization.", commandResult);
await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>()); // await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
await sutProvider.GetDependency<IEventService>().Received(0) // await sutProvider.GetDependency<IEventService>().Received(0)
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>()); // .LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
} // }
[Theory] // [Theory]
[BitAutoData] // [BitAutoData]
public async Task DeleteManyUsersAsync_WhenUserIsASoleOwner_ReturnsErrorMessage( // public async Task DeleteManyUsersAsync_WhenUserIsASoleOwner_ReturnsErrorMessage(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, // SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser) // [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser)
{ // {
// Arrange // // Arrange
orgUser.UserId = user.Id; // orgUser.UserId = user.Id;
sutProvider.GetDependency<IOrganizationUserRepository>() // sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyAsync(Arg.Any<IEnumerable<Guid>>()) // .GetManyAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(new List<OrganizationUser> { orgUser }); // .Returns(new List<OrganizationUser> { orgUser });
sutProvider.GetDependency<IUserRepository>() // sutProvider.GetDependency<IUserRepository>()
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser.UserId.Value))) // .GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser.UserId.Value)))
.Returns(new[] { user }); // .Returns(new[] { user });
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() // sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>()
.GetUsersOrganizationManagementStatusAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>()) // .GetUsersOrganizationManagementStatusAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>())
.Returns(new Dictionary<Guid, bool> { { orgUser.Id, true } }); // .Returns(new Dictionary<Guid, bool> { { orgUser.Id, true } });
const int onlyOwnerCount = 1; // const int onlyOwnerCount = 1;
sutProvider.GetDependency<IOrganizationUserRepository>() // sutProvider.GetDependency<IOrganizationUserRepository>()
.GetCountByOnlyOwnerAsync(Arg.Is<Guid>(id => id == user.Id)) // .GetCountByOnlyOwnerAsync(Arg.Is<Guid>(id => id == user.Id))
.Returns(onlyOwnerCount); // .Returns(onlyOwnerCount);
// Act // // Act
var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, null); // var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, null);
// Assert // // Assert
Assert.Single(result); // Assert.Single(result);
var userId = result.First().Item1; // var userId = result.First().Item1;
Assert.Equal(orgUser.Id, userId); // Assert.Equal(orgUser.Id, userId);
var commandResult = result.First().Item2; // var commandResult = result.First().Item2;
AssertErrorMessages("Cannot delete this user because it is the sole owner of at least one organization. Please delete these organizations or upgrade another user.", commandResult); // AssertErrorMessages("Cannot delete this user because it is the sole owner of at least one organization. Please delete these organizations or upgrade another user.", commandResult);
await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>()); // await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
await sutProvider.GetDependency<IEventService>().Received(0) // await sutProvider.GetDependency<IEventService>().Received(0)
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>()); // .LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
} // }
[Theory] // [Theory]
[BitAutoData] // [BitAutoData]
public async Task DeleteManyUsersAsync_WhenUserIsASoleProvider_ReturnsErrorMessage( // public async Task DeleteManyUsersAsync_WhenUserIsASoleProvider_ReturnsErrorMessage(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, // SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser) // [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser)
{ // {
// Arrange // // Arrange
orgUser.UserId = user.Id; // orgUser.UserId = user.Id;
sutProvider.GetDependency<IOrganizationUserRepository>() // sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyAsync(Arg.Any<IEnumerable<Guid>>()) // .GetManyAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(new List<OrganizationUser> { orgUser }); // .Returns(new List<OrganizationUser> { orgUser });
sutProvider.GetDependency<IUserRepository>() // sutProvider.GetDependency<IUserRepository>()
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser.UserId.Value))) // .GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser.UserId.Value)))
.Returns(new[] { user }); // .Returns(new[] { user });
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() // sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>()
.GetUsersOrganizationManagementStatusAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>()) // .GetUsersOrganizationManagementStatusAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>())
.Returns(new Dictionary<Guid, bool> { { orgUser.Id, true } }); // .Returns(new Dictionary<Guid, bool> { { orgUser.Id, true } });
const int onlyOwnerCount = 0; // const int onlyOwnerCount = 0;
sutProvider.GetDependency<IOrganizationUserRepository>() // sutProvider.GetDependency<IOrganizationUserRepository>()
.GetCountByOnlyOwnerAsync(Arg.Is<Guid>(id => id == user.Id)) // .GetCountByOnlyOwnerAsync(Arg.Is<Guid>(id => id == user.Id))
.Returns(onlyOwnerCount); // .Returns(onlyOwnerCount);
const int onlyOwnerProviderCount = 1; // const int onlyOwnerProviderCount = 1;
sutProvider.GetDependency<IProviderUserRepository>() // sutProvider.GetDependency<IProviderUserRepository>()
.GetCountByOnlyOwnerAsync(Arg.Is<Guid>(id => id == user.Id)) // .GetCountByOnlyOwnerAsync(Arg.Is<Guid>(id => id == user.Id))
.Returns(onlyOwnerProviderCount); // .Returns(onlyOwnerProviderCount);
// Act // // Act
var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, null); // var result = await sutProvider.Sut.DeleteManyUsersAsync(orgUser.OrganizationId, new[] { orgUser.Id }, null);
// Assert // // Assert
Assert.Single(result); // Assert.Single(result);
var userId = result.First().Item1; // var userId = result.First().Item1;
Assert.Equal(orgUser.Id, userId); // Assert.Equal(orgUser.Id, userId);
var commandResult = result.First().Item2; // var commandResult = result.First().Item2;
AssertErrorMessages("Cannot delete this user because it is the sole owner of at least one provider. Please delete these providers or upgrade another user.", commandResult); // AssertErrorMessages("Cannot delete this user because it is the sole owner of at least one provider. Please delete these providers or upgrade another user.", commandResult);
await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>()); // await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
await sutProvider.GetDependency<IEventService>().Received(0) // await sutProvider.GetDependency<IEventService>().Received(0)
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>()); // .LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
} // }
[Theory] // [Theory]
[BitAutoData] // [BitAutoData]
public async Task DeleteManyUsersAsync_MixedValidAndInvalidUsers_ReturnsAppropriateResults( // public async Task DeleteManyUsersAsync_MixedValidAndInvalidUsers_ReturnsAppropriateResults(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user1, User user3, // SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user1, User user3,
Guid organizationId, // Guid organizationId,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser1, // [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser1,
[OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.User)] OrganizationUser orgUser2, // [OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.User)] OrganizationUser orgUser2,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser3) // [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser3)
{ // {
// Arrange // // Arrange
orgUser1.UserId = user1.Id; // orgUser1.UserId = user1.Id;
orgUser2.UserId = null; // orgUser2.UserId = null;
orgUser3.UserId = user3.Id; // orgUser3.UserId = user3.Id;
orgUser1.OrganizationId = organizationId; // orgUser1.OrganizationId = organizationId;
orgUser2.OrganizationId = organizationId; // orgUser2.OrganizationId = organizationId;
orgUser3.OrganizationId = organizationId; // orgUser3.OrganizationId = organizationId;
sutProvider.GetDependency<IOrganizationUserRepository>() // sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyAsync(Arg.Any<IEnumerable<Guid>>()) // .GetManyAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(new List<OrganizationUser> { orgUser1, orgUser2, orgUser3 }); // .Returns(new List<OrganizationUser> { orgUser1, orgUser2, orgUser3 });
sutProvider.GetDependency<IUserRepository>() // sutProvider.GetDependency<IUserRepository>()
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user1.Id) && ids.Contains(user3.Id))) // .GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user1.Id) && ids.Contains(user3.Id)))
.Returns(new[] { user1, user3 }); // .Returns(new[] { user1, user3 });
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() // sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>()
.GetUsersOrganizationManagementStatusAsync(organizationId, Arg.Any<IEnumerable<Guid>>()) // .GetUsersOrganizationManagementStatusAsync(organizationId, Arg.Any<IEnumerable<Guid>>())
.Returns(new Dictionary<Guid, bool> { { orgUser1.Id, true }, { orgUser3.Id, false } }); // .Returns(new Dictionary<Guid, bool> { { orgUser1.Id, true }, { orgUser3.Id, false } });
// Act // // Act
var results = await sutProvider.Sut.DeleteManyUsersAsync(organizationId, new[] { orgUser1.Id, orgUser2.Id, orgUser3.Id }, null); // var results = await sutProvider.Sut.DeleteManyUsersAsync(organizationId, new[] { orgUser1.Id, orgUser2.Id, orgUser3.Id }, null);
// Assert // // Assert
Assert.Equal(3, results.Count()); // Assert.Equal(3, results.Count());
var orgUser1ErrorMessage = results.First(r => r.Item1 == orgUser1.Id).Item2; // var orgUser1ErrorMessage = results.First(r => r.Item1 == orgUser1.Id).Item2;
Assert.Null(orgUser1ErrorMessage); // Assert.Null(orgUser1ErrorMessage);
var orgUser2CommandResult = results.First(r => r.Item1 == orgUser2.Id).Item2; // var orgUser2CommandResult = results.First(r => r.Item1 == orgUser2.Id).Item2;
AssertErrorMessages("Member not found.", orgUser2CommandResult); // AssertErrorMessages("Member not found.", orgUser2CommandResult);
var orgUser3CommandResult = results.First(r => r.Item1 == orgUser3.Id).Item2; // var orgUser3CommandResult = results.First(r => r.Item1 == orgUser3.Id).Item2;
AssertErrorMessages("Member is not managed by the organization.", orgUser3CommandResult); // AssertErrorMessages("Member is not managed by the organization.", orgUser3CommandResult);
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync( // await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync(
Arg.Is<IEnumerable<(OrganizationUser, EventType, DateTime?)>>(events => // Arg.Is<IEnumerable<(OrganizationUser, EventType, DateTime?)>>(events =>
events.Count(e => e.Item1.Id == orgUser1.Id && e.Item2 == EventType.OrganizationUser_Deleted) == 1)); // events.Count(e => e.Item1.Id == orgUser1.Id && e.Item2 == EventType.OrganizationUser_Deleted) == 1));
} // }
private static void AssertErrorMessages(string expectedErrorMessage, CommandResult commandResult) => Assert.Contains([expectedErrorMessage], ((Failure)commandResult).ErrorMessages.ToArray()); // private static void AssertErrorMessages(string expectedErrorMessage, CommandResult commandResult) => Assert.Contains([expectedErrorMessage], ((Failure)commandResult).ErrorMessages.ToArray());
} }