mirror of
https://github.com/bitwarden/server
synced 2026-01-02 08:33:48 +00:00
Merge branch 'main' into jmccannon/ac/pm-27131-auto-confirm-req
This commit is contained in:
@@ -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>());
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Auth.Models.Api.Request.Accounts;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Auth.UserFeatures.Registration.Implementations;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Exceptions;
|
||||
@@ -40,22 +41,55 @@ public class SendVerificationEmailForRegistrationCommandTests
|
||||
.HasVerifiedDomainWithBlockClaimedDomainPolicyAsync(Arg.Any<string>())
|
||||
.Returns(false);
|
||||
|
||||
sutProvider.GetDependency<IMailService>()
|
||||
.SendRegistrationVerificationEmailAsync(email, Arg.Any<string>())
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
var mockedToken = "token";
|
||||
sutProvider.GetDependency<IDataProtectorTokenFactory<RegistrationEmailVerificationTokenable>>()
|
||||
.Protect(Arg.Any<RegistrationEmailVerificationTokenable>())
|
||||
.Returns(mockedToken);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.Run(email, name, receiveMarketingEmails);
|
||||
var result = await sutProvider.Sut.Run(email, name, receiveMarketingEmails, null);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendRegistrationVerificationEmailAsync(email, mockedToken);
|
||||
.SendRegistrationVerificationEmailAsync(email, mockedToken, null);
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SendVerificationEmailForRegistrationCommand_WhenFromMarketingIsPremium_SendsEmailWithMarketingParameterAndReturnsNull(SutProvider<SendVerificationEmailForRegistrationCommand> sutProvider,
|
||||
string email, string name, bool receiveMarketingEmails)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IUserRepository>()
|
||||
.GetByEmailAsync(email)
|
||||
.ReturnsNull();
|
||||
|
||||
sutProvider.GetDependency<GlobalSettings>()
|
||||
.EnableEmailVerification = true;
|
||||
|
||||
sutProvider.GetDependency<GlobalSettings>()
|
||||
.DisableUserRegistration = false;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationDomainRepository>()
|
||||
.HasVerifiedDomainWithBlockClaimedDomainPolicyAsync(Arg.Any<string>())
|
||||
.Returns(false);
|
||||
|
||||
var mockedToken = "token";
|
||||
sutProvider.GetDependency<IDataProtectorTokenFactory<RegistrationEmailVerificationTokenable>>()
|
||||
.Protect(Arg.Any<RegistrationEmailVerificationTokenable>())
|
||||
.Returns(mockedToken);
|
||||
|
||||
var fromMarketing = MarketingInitiativeConstants.Premium;
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.Run(email, name, receiveMarketingEmails, fromMarketing);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendRegistrationVerificationEmailAsync(email, mockedToken, fromMarketing);
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
@@ -87,12 +121,12 @@ public class SendVerificationEmailForRegistrationCommandTests
|
||||
.Returns(mockedToken);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.Run(email, name, receiveMarketingEmails);
|
||||
var result = await sutProvider.Sut.Run(email, name, receiveMarketingEmails, null);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.DidNotReceive()
|
||||
.SendRegistrationVerificationEmailAsync(email, mockedToken);
|
||||
.SendRegistrationVerificationEmailAsync(email, mockedToken, null);
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
@@ -124,7 +158,7 @@ public class SendVerificationEmailForRegistrationCommandTests
|
||||
.Returns(mockedToken);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.Run(email, name, receiveMarketingEmails);
|
||||
var result = await sutProvider.Sut.Run(email, name, receiveMarketingEmails, null);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(mockedToken, result);
|
||||
@@ -140,7 +174,7 @@ public class SendVerificationEmailForRegistrationCommandTests
|
||||
.DisableUserRegistration = true;
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.Run(email, name, receiveMarketingEmails));
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.Run(email, name, receiveMarketingEmails, null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -166,7 +200,7 @@ public class SendVerificationEmailForRegistrationCommandTests
|
||||
.Returns(false);
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.Run(email, name, receiveMarketingEmails));
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.Run(email, name, receiveMarketingEmails, null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -177,7 +211,7 @@ public class SendVerificationEmailForRegistrationCommandTests
|
||||
sutProvider.GetDependency<GlobalSettings>()
|
||||
.DisableUserRegistration = false;
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(async () => await sutProvider.Sut.Run(null, name, receiveMarketingEmails));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(async () => await sutProvider.Sut.Run(null, name, receiveMarketingEmails, null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -187,7 +221,7 @@ public class SendVerificationEmailForRegistrationCommandTests
|
||||
{
|
||||
sutProvider.GetDependency<GlobalSettings>()
|
||||
.DisableUserRegistration = false;
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(async () => await sutProvider.Sut.Run("", name, receiveMarketingEmails));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(async () => await sutProvider.Sut.Run("", name, receiveMarketingEmails, null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -210,7 +244,7 @@ public class SendVerificationEmailForRegistrationCommandTests
|
||||
.Returns(true);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.Run(email, name, receiveMarketingEmails));
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.Run(email, name, receiveMarketingEmails, null));
|
||||
Assert.Equal("This email address is claimed by an organization using Bitwarden.", exception.Message);
|
||||
}
|
||||
|
||||
@@ -246,7 +280,7 @@ public class SendVerificationEmailForRegistrationCommandTests
|
||||
.Returns(mockedToken);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.Run(email, name, receiveMarketingEmails);
|
||||
var result = await sutProvider.Sut.Run(email, name, receiveMarketingEmails, null);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(mockedToken, result);
|
||||
@@ -270,7 +304,7 @@ public class SendVerificationEmailForRegistrationCommandTests
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.Run(email, name, receiveMarketingEmails));
|
||||
sutProvider.Sut.Run(email, name, receiveMarketingEmails, null));
|
||||
Assert.Equal("Invalid email address format.", exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Billing;
|
||||
using Bit.Core.Billing.Constants;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Billing.Models.Sales;
|
||||
@@ -353,4 +354,97 @@ public class OrganizationBillingServiceTests
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task UpdateOrganizationNameAndEmail_UpdatesStripeCustomer(
|
||||
Organization organization,
|
||||
SutProvider<OrganizationBillingService> sutProvider)
|
||||
{
|
||||
organization.Name = "Short name";
|
||||
|
||||
CustomerUpdateOptions capturedOptions = null;
|
||||
sutProvider.GetDependency<IStripeAdapter>()
|
||||
.CustomerUpdateAsync(
|
||||
Arg.Is<string>(id => id == organization.GatewayCustomerId),
|
||||
Arg.Do<CustomerUpdateOptions>(options => capturedOptions = options))
|
||||
.Returns(new Customer());
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.UpdateOrganizationNameAndEmail(organization);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IStripeAdapter>()
|
||||
.Received(1)
|
||||
.CustomerUpdateAsync(
|
||||
organization.GatewayCustomerId,
|
||||
Arg.Any<CustomerUpdateOptions>());
|
||||
|
||||
Assert.NotNull(capturedOptions);
|
||||
Assert.Equal(organization.BillingEmail, capturedOptions.Email);
|
||||
Assert.Equal(organization.DisplayName(), capturedOptions.Description);
|
||||
Assert.NotNull(capturedOptions.InvoiceSettings);
|
||||
Assert.NotNull(capturedOptions.InvoiceSettings.CustomFields);
|
||||
Assert.Single(capturedOptions.InvoiceSettings.CustomFields);
|
||||
|
||||
var customField = capturedOptions.InvoiceSettings.CustomFields.First();
|
||||
Assert.Equal(organization.SubscriberType(), customField.Name);
|
||||
Assert.Equal(organization.DisplayName(), customField.Value);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task UpdateOrganizationNameAndEmail_WhenNameIsLong_TruncatesTo30Characters(
|
||||
Organization organization,
|
||||
SutProvider<OrganizationBillingService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
organization.Name = "This is a very long organization name that exceeds thirty characters";
|
||||
|
||||
CustomerUpdateOptions capturedOptions = null;
|
||||
sutProvider.GetDependency<IStripeAdapter>()
|
||||
.CustomerUpdateAsync(
|
||||
Arg.Is<string>(id => id == organization.GatewayCustomerId),
|
||||
Arg.Do<CustomerUpdateOptions>(options => capturedOptions = options))
|
||||
.Returns(new Customer());
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.UpdateOrganizationNameAndEmail(organization);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IStripeAdapter>()
|
||||
.Received(1)
|
||||
.CustomerUpdateAsync(
|
||||
organization.GatewayCustomerId,
|
||||
Arg.Any<CustomerUpdateOptions>());
|
||||
|
||||
Assert.NotNull(capturedOptions);
|
||||
Assert.NotNull(capturedOptions.InvoiceSettings);
|
||||
Assert.NotNull(capturedOptions.InvoiceSettings.CustomFields);
|
||||
|
||||
var customField = capturedOptions.InvoiceSettings.CustomFields.First();
|
||||
Assert.Equal(30, customField.Value.Length);
|
||||
|
||||
var expectedCustomFieldDisplayName = "This is a very long organizati";
|
||||
Assert.Equal(expectedCustomFieldDisplayName, customField.Value);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task UpdateOrganizationNameAndEmail_WhenGatewayCustomerIdIsNull_ThrowsBillingException(
|
||||
Organization organization,
|
||||
SutProvider<OrganizationBillingService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
organization.GatewayCustomerId = null;
|
||||
organization.Name = "Test Organization";
|
||||
organization.BillingEmail = "billing@example.com";
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BillingException>(
|
||||
() => sutProvider.Sut.UpdateOrganizationNameAndEmail(organization));
|
||||
|
||||
Assert.Contains("Cannot update an organization in Stripe without a GatewayCustomerId.", exception.Response);
|
||||
|
||||
await sutProvider.GetDependency<IStripeAdapter>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.CustomerUpdateAsync(Arg.Any<string>(), Arg.Any<CustomerUpdateOptions>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Utilities;
|
||||
|
||||
public class EventIntegrationsCacheConstantsTests
|
||||
{
|
||||
[Theory, BitAutoData]
|
||||
public void BuildCacheKeyForGroup_ReturnsExpectedKey(Guid groupId)
|
||||
{
|
||||
var expected = $"Group:{groupId:N}";
|
||||
var key = EventIntegrationsCacheConstants.BuildCacheKeyForGroup(groupId);
|
||||
|
||||
Assert.Equal(expected, key);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void BuildCacheKeyForOrganization_ReturnsExpectedKey(Guid orgId)
|
||||
{
|
||||
var expected = $"Organization:{orgId:N}";
|
||||
var key = EventIntegrationsCacheConstants.BuildCacheKeyForOrganization(orgId);
|
||||
|
||||
Assert.Equal(expected, key);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void BuildCacheKeyForOrganizationUser_ReturnsExpectedKey(Guid orgId, Guid userId)
|
||||
{
|
||||
var expected = $"OrganizationUserUserDetails:{orgId:N}:{userId:N}";
|
||||
var key = EventIntegrationsCacheConstants.BuildCacheKeyForOrganizationUser(orgId, userId);
|
||||
|
||||
Assert.Equal(expected, key);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CacheName_ReturnsExpected()
|
||||
{
|
||||
Assert.Equal("EventIntegrations", EventIntegrationsCacheConstants.CacheName);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user