1
0
mirror of https://github.com/bitwarden/server synced 2026-01-03 17:14:00 +00:00

Merge branch 'main' into jmccannon/ac/pm-27131-auto-confirm-req

This commit is contained in:
jrmccannon
2025-11-26 09:06:52 -06:00
61 changed files with 4751 additions and 813 deletions

View File

@@ -0,0 +1,414 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Update;
using Bit.Core.Billing.Organizations.Services;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Organizations;
[SutProviderCustomize]
public class OrganizationUpdateCommandTests
{
[Theory, BitAutoData]
public async Task UpdateAsync_WhenValidOrganization_UpdatesOrganization(
Guid organizationId,
string name,
string billingEmail,
Organization organization,
SutProvider<OrganizationUpdateCommand> sutProvider)
{
// Arrange
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
var organizationService = sutProvider.GetDependency<IOrganizationService>();
var organizationBillingService = sutProvider.GetDependency<IOrganizationBillingService>();
organization.Id = organizationId;
organization.GatewayCustomerId = null; // No Stripe customer, so no billing update
organizationRepository
.GetByIdAsync(organizationId)
.Returns(organization);
var request = new OrganizationUpdateRequest
{
OrganizationId = organizationId,
Name = name,
BillingEmail = billingEmail
};
// Act
var result = await sutProvider.Sut.UpdateAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(organizationId, result.Id);
Assert.Equal(name, result.Name);
Assert.Equal(billingEmail.ToLowerInvariant().Trim(), result.BillingEmail);
await organizationRepository
.Received(1)
.GetByIdAsync(Arg.Is<Guid>(id => id == organizationId));
await organizationService
.Received(1)
.ReplaceAndUpdateCacheAsync(
result,
EventType.Organization_Updated);
await organizationBillingService
.DidNotReceiveWithAnyArgs()
.UpdateOrganizationNameAndEmail(Arg.Any<Organization>());
}
[Theory, BitAutoData]
public async Task UpdateAsync_WhenOrganizationNotFound_ThrowsNotFoundException(
Guid organizationId,
string name,
string billingEmail,
SutProvider<OrganizationUpdateCommand> sutProvider)
{
// Arrange
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
organizationRepository
.GetByIdAsync(organizationId)
.Returns((Organization)null);
var request = new OrganizationUpdateRequest
{
OrganizationId = organizationId,
Name = name,
BillingEmail = billingEmail
};
// Act/Assert
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateAsync(request));
}
[Theory]
[BitAutoData("")]
[BitAutoData((string)null)]
public async Task UpdateAsync_WhenGatewayCustomerIdIsNullOrEmpty_SkipsBillingUpdate(
string gatewayCustomerId,
Guid organizationId,
Organization organization,
SutProvider<OrganizationUpdateCommand> sutProvider)
{
// Arrange
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
var organizationService = sutProvider.GetDependency<IOrganizationService>();
var organizationBillingService = sutProvider.GetDependency<IOrganizationBillingService>();
organization.Id = organizationId;
organization.Name = "Old Name";
organization.GatewayCustomerId = gatewayCustomerId;
organizationRepository
.GetByIdAsync(organizationId)
.Returns(organization);
var request = new OrganizationUpdateRequest
{
OrganizationId = organizationId,
Name = "New Name",
BillingEmail = organization.BillingEmail
};
// Act
var result = await sutProvider.Sut.UpdateAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(organizationId, result.Id);
Assert.Equal("New Name", result.Name);
await organizationService
.Received(1)
.ReplaceAndUpdateCacheAsync(
result,
EventType.Organization_Updated);
await organizationBillingService
.DidNotReceiveWithAnyArgs()
.UpdateOrganizationNameAndEmail(Arg.Any<Organization>());
}
[Theory, BitAutoData]
public async Task UpdateAsync_WhenKeysProvided_AndNotAlreadySet_SetsKeys(
Guid organizationId,
string publicKey,
string encryptedPrivateKey,
Organization organization,
SutProvider<OrganizationUpdateCommand> sutProvider)
{
// Arrange
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
var organizationService = sutProvider.GetDependency<IOrganizationService>();
organization.Id = organizationId;
organization.PublicKey = null;
organization.PrivateKey = null;
organizationRepository
.GetByIdAsync(organizationId)
.Returns(organization);
var request = new OrganizationUpdateRequest
{
OrganizationId = organizationId,
Name = organization.Name,
BillingEmail = organization.BillingEmail,
PublicKey = publicKey,
EncryptedPrivateKey = encryptedPrivateKey
};
// Act
var result = await sutProvider.Sut.UpdateAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(organizationId, result.Id);
Assert.Equal(publicKey, result.PublicKey);
Assert.Equal(encryptedPrivateKey, result.PrivateKey);
await organizationService
.Received(1)
.ReplaceAndUpdateCacheAsync(
result,
EventType.Organization_Updated);
}
[Theory, BitAutoData]
public async Task UpdateAsync_WhenKeysProvided_AndAlreadySet_DoesNotOverwriteKeys(
Guid organizationId,
string newPublicKey,
string newEncryptedPrivateKey,
Organization organization,
SutProvider<OrganizationUpdateCommand> sutProvider)
{
// Arrange
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
var organizationService = sutProvider.GetDependency<IOrganizationService>();
organization.Id = organizationId;
var existingPublicKey = organization.PublicKey;
var existingPrivateKey = organization.PrivateKey;
organizationRepository
.GetByIdAsync(organizationId)
.Returns(organization);
var request = new OrganizationUpdateRequest
{
OrganizationId = organizationId,
Name = organization.Name,
BillingEmail = organization.BillingEmail,
PublicKey = newPublicKey,
EncryptedPrivateKey = newEncryptedPrivateKey
};
// Act
var result = await sutProvider.Sut.UpdateAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(organizationId, result.Id);
Assert.Equal(existingPublicKey, result.PublicKey);
Assert.Equal(existingPrivateKey, result.PrivateKey);
await organizationService
.Received(1)
.ReplaceAndUpdateCacheAsync(
result,
EventType.Organization_Updated);
}
[Theory, BitAutoData]
public async Task UpdateAsync_UpdatingNameOnly_UpdatesNameAndNotBillingEmail(
Guid organizationId,
string newName,
Organization organization,
SutProvider<OrganizationUpdateCommand> sutProvider)
{
// Arrange
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
var organizationService = sutProvider.GetDependency<IOrganizationService>();
var organizationBillingService = sutProvider.GetDependency<IOrganizationBillingService>();
organization.Id = organizationId;
organization.Name = "Old Name";
var originalBillingEmail = organization.BillingEmail;
organizationRepository
.GetByIdAsync(organizationId)
.Returns(organization);
var request = new OrganizationUpdateRequest
{
OrganizationId = organizationId,
Name = newName,
BillingEmail = null
};
// Act
var result = await sutProvider.Sut.UpdateAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(organizationId, result.Id);
Assert.Equal(newName, result.Name);
Assert.Equal(originalBillingEmail, result.BillingEmail);
await organizationService
.Received(1)
.ReplaceAndUpdateCacheAsync(
result,
EventType.Organization_Updated);
await organizationBillingService
.Received(1)
.UpdateOrganizationNameAndEmail(result);
}
[Theory, BitAutoData]
public async Task UpdateAsync_UpdatingBillingEmailOnly_UpdatesBillingEmailAndNotName(
Guid organizationId,
string newBillingEmail,
Organization organization,
SutProvider<OrganizationUpdateCommand> sutProvider)
{
// Arrange
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
var organizationService = sutProvider.GetDependency<IOrganizationService>();
var organizationBillingService = sutProvider.GetDependency<IOrganizationBillingService>();
organization.Id = organizationId;
organization.BillingEmail = "old@example.com";
var originalName = organization.Name;
organizationRepository
.GetByIdAsync(organizationId)
.Returns(organization);
var request = new OrganizationUpdateRequest
{
OrganizationId = organizationId,
Name = null,
BillingEmail = newBillingEmail
};
// Act
var result = await sutProvider.Sut.UpdateAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(organizationId, result.Id);
Assert.Equal(originalName, result.Name);
Assert.Equal(newBillingEmail.ToLowerInvariant().Trim(), result.BillingEmail);
await organizationService
.Received(1)
.ReplaceAndUpdateCacheAsync(
result,
EventType.Organization_Updated);
await organizationBillingService
.Received(1)
.UpdateOrganizationNameAndEmail(result);
}
[Theory, BitAutoData]
public async Task UpdateAsync_WhenNoChanges_PreservesBothFields(
Guid organizationId,
Organization organization,
SutProvider<OrganizationUpdateCommand> sutProvider)
{
// Arrange
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
var organizationService = sutProvider.GetDependency<IOrganizationService>();
var organizationBillingService = sutProvider.GetDependency<IOrganizationBillingService>();
organization.Id = organizationId;
var originalName = organization.Name;
var originalBillingEmail = organization.BillingEmail;
organizationRepository
.GetByIdAsync(organizationId)
.Returns(organization);
var request = new OrganizationUpdateRequest
{
OrganizationId = organizationId,
Name = null,
BillingEmail = null
};
// Act
var result = await sutProvider.Sut.UpdateAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(organizationId, result.Id);
Assert.Equal(originalName, result.Name);
Assert.Equal(originalBillingEmail, result.BillingEmail);
await organizationService
.Received(1)
.ReplaceAndUpdateCacheAsync(
result,
EventType.Organization_Updated);
await organizationBillingService
.DidNotReceiveWithAnyArgs()
.UpdateOrganizationNameAndEmail(Arg.Any<Organization>());
}
[Theory, BitAutoData]
public async Task UpdateAsync_SelfHosted_OnlyUpdatesKeysNotOrganizationDetails(
Guid organizationId,
string newName,
string newBillingEmail,
string publicKey,
string encryptedPrivateKey,
Organization organization,
SutProvider<OrganizationUpdateCommand> sutProvider)
{
// Arrange
var organizationBillingService = sutProvider.GetDependency<IOrganizationBillingService>();
var globalSettings = sutProvider.GetDependency<IGlobalSettings>();
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
globalSettings.SelfHosted.Returns(true);
organization.Id = organizationId;
organization.Name = "Original Name";
organization.BillingEmail = "original@example.com";
organization.PublicKey = null;
organization.PrivateKey = null;
organizationRepository.GetByIdAsync(organizationId).Returns(organization);
var request = new OrganizationUpdateRequest
{
OrganizationId = organizationId,
Name = newName, // Should be ignored
BillingEmail = newBillingEmail, // Should be ignored
PublicKey = publicKey,
EncryptedPrivateKey = encryptedPrivateKey
};
// Act
var result = await sutProvider.Sut.UpdateAsync(request);
// Assert
Assert.Equal("Original Name", result.Name); // Not changed
Assert.Equal("original@example.com", result.BillingEmail); // Not changed
Assert.Equal(publicKey, result.PublicKey); // Changed
Assert.Equal(encryptedPrivateKey, result.PrivateKey); // Changed
await organizationBillingService
.DidNotReceiveWithAnyArgs()
.UpdateOrganizationNameAndEmail(Arg.Any<Organization>());
}
}

View File

@@ -14,6 +14,7 @@ using Bit.Test.Common.Helpers;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
using ZiggyCreatures.Caching.Fusion;
namespace Bit.Core.Test.Services;
@@ -25,7 +26,6 @@ public class EventIntegrationHandlerTests
private const string _templateWithOrganization = "Org: #OrganizationName#";
private const string _templateWithUser = "#UserName#, #UserEmail#, #UserType#";
private const string _templateWithActingUser = "#ActingUserName#, #ActingUserEmail#, #ActingUserType#";
private static readonly Guid _groupId = Guid.NewGuid();
private static readonly Guid _organizationId = Guid.NewGuid();
private static readonly Uri _uri = new Uri("https://localhost");
private static readonly Uri _uri2 = new Uri("https://example.com");
@@ -113,6 +113,232 @@ public class EventIntegrationHandlerTests
return [config];
}
[Theory, BitAutoData]
public async Task BuildContextAsync_ActingUserIdPresent_UsesCache(EventMessage eventMessage, OrganizationUserUserDetails actingUser)
{
var sutProvider = GetSutProvider(OneConfiguration(_templateWithActingUser));
var cache = sutProvider.GetDependency<IFusionCache>();
eventMessage.OrganizationId ??= Guid.NewGuid();
eventMessage.ActingUserId ??= Guid.NewGuid();
cache.GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<OrganizationUserUserDetails?>, CancellationToken, Task<OrganizationUserUserDetails?>>>()
).Returns(actingUser);
var context = await sutProvider.Sut.BuildContextAsync(eventMessage, _templateWithActingUser);
await cache.Received(1).GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<OrganizationUserUserDetails?>, CancellationToken, Task<OrganizationUserUserDetails?>>>()
);
Assert.Equal(actingUser, context.ActingUser);
}
[Theory, BitAutoData]
public async Task BuildContextAsync_ActingUserIdNull_SkipsCache(EventMessage eventMessage)
{
var sutProvider = GetSutProvider(OneConfiguration(_templateWithActingUser));
var cache = sutProvider.GetDependency<IFusionCache>();
eventMessage.OrganizationId ??= Guid.NewGuid();
eventMessage.ActingUserId = null;
var context = await sutProvider.Sut.BuildContextAsync(eventMessage, _templateWithActingUser);
await cache.DidNotReceive().GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<OrganizationUserUserDetails?>, CancellationToken, Task<OrganizationUserUserDetails?>>>()
);
Assert.Null(context.ActingUser);
}
[Theory, BitAutoData]
public async Task BuildContextAsync_ActingUserOrganizationIdNull_SkipsCache(EventMessage eventMessage)
{
var sutProvider = GetSutProvider(OneConfiguration(_templateWithActingUser));
var cache = sutProvider.GetDependency<IFusionCache>();
eventMessage.OrganizationId = null;
eventMessage.ActingUserId ??= Guid.NewGuid();
var context = await sutProvider.Sut.BuildContextAsync(eventMessage, _templateWithActingUser);
await cache.DidNotReceive().GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<OrganizationUserUserDetails?>, CancellationToken, Task<OrganizationUserUserDetails?>>>()
);
Assert.Null(context.ActingUser);
}
[Theory, BitAutoData]
public async Task BuildContextAsync_GroupIdPresent_UsesCache(EventMessage eventMessage, Group group)
{
var sutProvider = GetSutProvider(OneConfiguration(_templateWithGroup));
var cache = sutProvider.GetDependency<IFusionCache>();
eventMessage.GroupId ??= Guid.NewGuid();
cache.GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<Group?>, CancellationToken, Task<Group?>>>()
).Returns(group);
var context = await sutProvider.Sut.BuildContextAsync(eventMessage, _templateWithGroup);
await cache.Received(1).GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<Group?>, CancellationToken, Task<Group?>>>()
);
Assert.Equal(group, context.Group);
}
[Theory, BitAutoData]
public async Task BuildContextAsync_GroupIdNull_SkipsCache(EventMessage eventMessage)
{
var sutProvider = GetSutProvider(OneConfiguration(_templateWithGroup));
var cache = sutProvider.GetDependency<IFusionCache>();
eventMessage.GroupId = null;
var context = await sutProvider.Sut.BuildContextAsync(eventMessage, _templateWithGroup);
await cache.DidNotReceive().GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<Group?>, CancellationToken, Task<Group?>>>()
);
Assert.Null(context.Group);
}
[Theory, BitAutoData]
public async Task BuildContextAsync_OrganizationIdPresent_UsesCache(EventMessage eventMessage, Organization organization)
{
var sutProvider = GetSutProvider(OneConfiguration(_templateWithOrganization));
var cache = sutProvider.GetDependency<IFusionCache>();
eventMessage.OrganizationId ??= Guid.NewGuid();
cache.GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<Organization?>, CancellationToken, Task<Organization?>>>()
).Returns(organization);
var context = await sutProvider.Sut.BuildContextAsync(eventMessage, _templateWithOrganization);
await cache.Received(1).GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<Organization?>, CancellationToken, Task<Organization?>>>()
);
Assert.Equal(organization, context.Organization);
}
[Theory, BitAutoData]
public async Task BuildContextAsync_OrganizationIdNull_SkipsCache(EventMessage eventMessage)
{
var sutProvider = GetSutProvider(OneConfiguration(_templateWithOrganization));
var cache = sutProvider.GetDependency<IFusionCache>();
eventMessage.OrganizationId = null;
var context = await sutProvider.Sut.BuildContextAsync(eventMessage, _templateWithOrganization);
await cache.DidNotReceive().GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<Organization?>, CancellationToken, Task<Organization?>>>()
);
Assert.Null(context.Organization);
}
[Theory, BitAutoData]
public async Task BuildContextAsync_UserIdPresent_UsesCache(EventMessage eventMessage, OrganizationUserUserDetails userDetails)
{
var sutProvider = GetSutProvider(OneConfiguration(_templateWithUser));
var cache = sutProvider.GetDependency<IFusionCache>();
eventMessage.OrganizationId ??= Guid.NewGuid();
eventMessage.UserId ??= Guid.NewGuid();
cache.GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<OrganizationUserUserDetails?>, CancellationToken, Task<OrganizationUserUserDetails?>>>()
).Returns(userDetails);
var context = await sutProvider.Sut.BuildContextAsync(eventMessage, _templateWithUser);
await cache.Received(1).GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<OrganizationUserUserDetails?>, CancellationToken, Task<OrganizationUserUserDetails?>>>()
);
Assert.Equal(userDetails, context.User);
}
[Theory, BitAutoData]
public async Task BuildContextAsync_UserIdNull_SkipsCache(EventMessage eventMessage)
{
var sutProvider = GetSutProvider(OneConfiguration(_templateWithUser));
var cache = sutProvider.GetDependency<IFusionCache>();
eventMessage.OrganizationId = null;
eventMessage.UserId ??= Guid.NewGuid();
var context = await sutProvider.Sut.BuildContextAsync(eventMessage, _templateWithUser);
await cache.DidNotReceive().GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<OrganizationUserUserDetails?>, CancellationToken, Task<OrganizationUserUserDetails?>>>()
);
Assert.Null(context.User);
}
[Theory, BitAutoData]
public async Task BuildContextAsync_OrganizationUserIdNull_SkipsCache(EventMessage eventMessage)
{
var sutProvider = GetSutProvider(OneConfiguration(_templateWithUser));
var cache = sutProvider.GetDependency<IFusionCache>();
eventMessage.OrganizationId ??= Guid.NewGuid();
eventMessage.UserId = null;
var context = await sutProvider.Sut.BuildContextAsync(eventMessage, _templateWithUser);
await cache.DidNotReceive().GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<OrganizationUserUserDetails?>, CancellationToken, Task<OrganizationUserUserDetails?>>>()
);
Assert.Null(context.User);
}
[Theory, BitAutoData]
public async Task BuildContextAsync_NoSpecialTokens_DoesNotCallAnyCache(EventMessage eventMessage)
{
var sutProvider = GetSutProvider(OneConfiguration(_templateWithUser));
var cache = sutProvider.GetDependency<IFusionCache>();
eventMessage.ActingUserId ??= Guid.NewGuid();
eventMessage.GroupId ??= Guid.NewGuid();
eventMessage.OrganizationId ??= Guid.NewGuid();
eventMessage.UserId ??= Guid.NewGuid();
await sutProvider.Sut.BuildContextAsync(eventMessage, _templateBase);
await cache.DidNotReceive().GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<Group?>, CancellationToken, Task<Group?>>>()
);
await cache.DidNotReceive().GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<Organization?>, CancellationToken, Task<Organization?>>>()
);
await cache.DidNotReceive().GetOrSetAsync(
key: Arg.Any<string>(),
factory: Arg.Any<Func<FusionCacheFactoryExecutionContext<OrganizationUserUserDetails?>, CancellationToken, Task<OrganizationUserUserDetails?>>>()
);
}
[Theory, BitAutoData]
public async Task HandleEventAsync_BaseTemplateNoConfigurations_DoesNothing(EventMessage eventMessage)
@@ -176,99 +402,6 @@ public class EventIntegrationHandlerTests
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs().GetDetailsByOrganizationIdUserIdAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
}
[Theory, BitAutoData]
public async Task HandleEventAsync_ActingUserTemplate_LoadsUserFromRepository(EventMessage eventMessage)
{
var sutProvider = GetSutProvider(OneConfiguration(_templateWithActingUser));
var user = Substitute.For<OrganizationUserUserDetails>();
user.Email = "test@example.com";
user.Name = "Test";
eventMessage.OrganizationId = _organizationId;
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetDetailsByOrganizationIdUserIdAsync(Arg.Any<Guid>(), Arg.Any<Guid>()).Returns(user);
await sutProvider.Sut.HandleEventAsync(eventMessage);
var expectedMessage = EventIntegrationHandlerTests.ExpectedMessage($"{user.Name}, {user.Email}, {user.Type}");
Assert.Single(_eventIntegrationPublisher.ReceivedCalls());
await _eventIntegrationPublisher.Received(1).PublishAsync(Arg.Is(
AssertHelper.AssertPropertyEqual(expectedMessage, new[] { "MessageId" })));
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().GetByIdAsync(Arg.Any<Guid>());
await sutProvider.GetDependency<IOrganizationRepository>().DidNotReceiveWithAnyArgs().GetByIdAsync(Arg.Any<Guid>());
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).GetDetailsByOrganizationIdUserIdAsync(Arg.Any<Guid>(), eventMessage.ActingUserId ?? Guid.Empty);
}
[Theory, BitAutoData]
public async Task HandleEventAsync_GroupTemplate_LoadsGroupFromRepository(EventMessage eventMessage)
{
var sutProvider = GetSutProvider(OneConfiguration(_templateWithGroup));
var group = Substitute.For<Group>();
group.Name = "Test";
eventMessage.GroupId = _groupId;
eventMessage.OrganizationId = _organizationId;
sutProvider.GetDependency<IGroupRepository>().GetByIdAsync(Arg.Any<Guid>()).Returns(group);
await sutProvider.Sut.HandleEventAsync(eventMessage);
Assert.Single(_eventIntegrationPublisher.ReceivedCalls());
var expectedMessage = EventIntegrationHandlerTests.ExpectedMessage($"Group: {group.Name}");
Assert.Single(_eventIntegrationPublisher.ReceivedCalls());
await _eventIntegrationPublisher.Received(1).PublishAsync(Arg.Is(
AssertHelper.AssertPropertyEqual(expectedMessage, new[] { "MessageId" })));
await sutProvider.GetDependency<IGroupRepository>().Received(1).GetByIdAsync(eventMessage.GroupId ?? Guid.Empty);
await sutProvider.GetDependency<IOrganizationRepository>().DidNotReceiveWithAnyArgs().GetByIdAsync(Arg.Any<Guid>());
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs().GetDetailsByOrganizationIdUserIdAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
}
[Theory, BitAutoData]
public async Task HandleEventAsync_OrganizationTemplate_LoadsOrganizationFromRepository(EventMessage eventMessage)
{
var sutProvider = GetSutProvider(OneConfiguration(_templateWithOrganization));
var organization = Substitute.For<Organization>();
organization.Name = "Test";
eventMessage.OrganizationId = _organizationId;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(Arg.Any<Guid>()).Returns(organization);
await sutProvider.Sut.HandleEventAsync(eventMessage);
Assert.Single(_eventIntegrationPublisher.ReceivedCalls());
var expectedMessage = EventIntegrationHandlerTests.ExpectedMessage($"Org: {organization.Name}");
Assert.Single(_eventIntegrationPublisher.ReceivedCalls());
await _eventIntegrationPublisher.Received(1).PublishAsync(Arg.Is(
AssertHelper.AssertPropertyEqual(expectedMessage, new[] { "MessageId" })));
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().GetByIdAsync(Arg.Any<Guid>());
await sutProvider.GetDependency<IOrganizationRepository>().Received(1).GetByIdAsync(eventMessage.OrganizationId ?? Guid.Empty);
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs().GetDetailsByOrganizationIdUserIdAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
}
[Theory, BitAutoData]
public async Task HandleEventAsync_UserTemplate_LoadsUserFromRepository(EventMessage eventMessage)
{
var sutProvider = GetSutProvider(OneConfiguration(_templateWithUser));
var user = Substitute.For<OrganizationUserUserDetails>();
user.Email = "test@example.com";
user.Name = "Test";
eventMessage.OrganizationId = _organizationId;
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetDetailsByOrganizationIdUserIdAsync(Arg.Any<Guid>(), Arg.Any<Guid>()).Returns(user);
await sutProvider.Sut.HandleEventAsync(eventMessage);
var expectedMessage = EventIntegrationHandlerTests.ExpectedMessage($"{user.Name}, {user.Email}, {user.Type}");
Assert.Single(_eventIntegrationPublisher.ReceivedCalls());
await _eventIntegrationPublisher.Received(1).PublishAsync(Arg.Is(
AssertHelper.AssertPropertyEqual(expectedMessage, new[] { "MessageId" })));
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().GetByIdAsync(Arg.Any<Guid>());
await sutProvider.GetDependency<IOrganizationRepository>().DidNotReceiveWithAnyArgs().GetByIdAsync(Arg.Any<Guid>());
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).GetDetailsByOrganizationIdUserIdAsync(Arg.Any<Guid>(), eventMessage.UserId ?? Guid.Empty);
}
[Theory, BitAutoData]
public async Task HandleEventAsync_FilterReturnsFalse_DoesNothing(EventMessage eventMessage)
{