using Bit.Core.AdminConsole.AbilitiesCache; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Models.Data.Provider; using Bit.Core.AdminConsole.Repositories; using Bit.Core.Models.Data.Organizations; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Services.Implementations; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; using Xunit; using GlobalSettings = Bit.Core.Settings.GlobalSettings; namespace Bit.Core.Test.Services.Implementations; [SutProviderCustomize] public class FeatureRoutedCacheServiceTests { [Theory, BitAutoData] public async Task GetOrganizationAbilitiesAsync_WhenFeatureIsEnabled_ReturnsFromVNextService( SutProvider sutProvider, IDictionary expectedResult) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(true); sutProvider.GetDependency() .GetOrganizationAbilitiesAsync() .Returns(expectedResult); // Act var result = await sutProvider.Sut.GetOrganizationAbilitiesAsync(); // Assert Assert.Equal(expectedResult, result); await sutProvider.GetDependency() .Received(1) .GetOrganizationAbilitiesAsync(); await sutProvider.GetDependency() .DidNotReceive() .GetOrganizationAbilitiesAsync(); } [Theory, BitAutoData] public async Task GetOrganizationAbilitiesAsync_WhenFeatureIsDisabled_ReturnsFromInMemoryService( SutProvider sutProvider, IDictionary expectedResult) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(false); sutProvider.GetDependency() .GetOrganizationAbilitiesAsync() .Returns(expectedResult); // Act var result = await sutProvider.Sut.GetOrganizationAbilitiesAsync(); // Assert Assert.Equal(expectedResult, result); await sutProvider.GetDependency() .Received(1) .GetOrganizationAbilitiesAsync(); await sutProvider.GetDependency() .DidNotReceive() .GetOrganizationAbilitiesAsync(); } [Theory, BitAutoData] public async Task GetOrganizationAbilityAsync_WhenFeatureIsEnabled_ReturnsFromVNextService( SutProvider sutProvider, Guid orgId, OrganizationAbility expectedResult) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(true); sutProvider.GetDependency() .GetOrganizationAbilityAsync(orgId) .Returns(expectedResult); // Act var result = await sutProvider.Sut.GetOrganizationAbilityAsync(orgId); // Assert Assert.Equal(expectedResult, result); await sutProvider.GetDependency() .Received(1) .GetOrganizationAbilityAsync(orgId); await sutProvider.GetDependency() .DidNotReceive() .GetOrganizationAbilityAsync(orgId); } [Theory, BitAutoData] public async Task GetOrganizationAbilityAsync_WhenFeatureIsDisabled_ReturnsFromInMemoryService( SutProvider sutProvider, Guid orgId, OrganizationAbility expectedResult) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(false); sutProvider.GetDependency() .GetOrganizationAbilityAsync(orgId) .Returns(expectedResult); // Act var result = await sutProvider.Sut.GetOrganizationAbilityAsync(orgId); // Assert Assert.Equal(expectedResult, result); await sutProvider.GetDependency() .Received(1) .GetOrganizationAbilityAsync(orgId); await sutProvider.GetDependency() .DidNotReceive() .GetOrganizationAbilityAsync(orgId); } [Theory, BitAutoData] public async Task GetProviderAbilitiesAsync_WhenFeatureIsEnabled_ReturnsFromVNextService( SutProvider sutProvider, IDictionary expectedResult) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(true); sutProvider.GetDependency() .GetProviderAbilitiesAsync() .Returns(expectedResult); // Act var result = await sutProvider.Sut.GetProviderAbilitiesAsync(); // Assert Assert.Equal(expectedResult, result); await sutProvider.GetDependency() .Received(1) .GetProviderAbilitiesAsync(); await sutProvider.GetDependency() .DidNotReceive() .GetProviderAbilitiesAsync(); } [Theory, BitAutoData] public async Task GetProviderAbilitiesAsync_WhenFeatureIsDisabled_ReturnsFromInMemoryService( SutProvider sutProvider, IDictionary expectedResult) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(false); sutProvider.GetDependency() .GetProviderAbilitiesAsync() .Returns(expectedResult); // Act var result = await sutProvider.Sut.GetProviderAbilitiesAsync(); // Assert Assert.Equal(expectedResult, result); await sutProvider.GetDependency() .Received(1) .GetProviderAbilitiesAsync(); await sutProvider.GetDependency() .DidNotReceive() .GetProviderAbilitiesAsync(); } [Theory, BitAutoData] public async Task UpsertOrganizationAbilityAsync_WhenFeatureIsEnabled_CallsVNextService( SutProvider sutProvider, Organization organization) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(true); // Act await sutProvider.Sut.UpsertOrganizationAbilityAsync(organization); // Assert await sutProvider.GetDependency() .Received(1) .UpsertOrganizationAbilityAsync(organization); await sutProvider.GetDependency() .DidNotReceive() .GetProviderAbilitiesAsync(); } [Theory, BitAutoData] public async Task UpsertOrganizationAbilityAsync_WhenFeatureIsDisabled_CallsInMemoryService( SutProvider sutProvider, Organization organization) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(false); // Act await sutProvider.Sut.UpsertOrganizationAbilityAsync(organization); // Assert await sutProvider.GetDependency() .Received(1) .UpsertOrganizationAbilityAsync(organization); await sutProvider.GetDependency() .DidNotReceive() .GetProviderAbilitiesAsync(); } [Theory, BitAutoData] public async Task UpsertProviderAbilityAsync_WhenFeatureIsEnabled_CallsVNextService( SutProvider sutProvider, Provider provider) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(true); // Act await sutProvider.Sut.UpsertProviderAbilityAsync(provider); // Assert await sutProvider.GetDependency() .Received(1) .UpsertProviderAbilityAsync(provider); await sutProvider.GetDependency() .DidNotReceive() .UpsertProviderAbilityAsync(provider); } [Theory, BitAutoData] public async Task UpsertProviderAbilityAsync_WhenFeatureIsDisabled_CallsInMemoryService( SutProvider sutProvider, Provider provider) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(false); // Act await sutProvider.Sut.UpsertProviderAbilityAsync(provider); // Assert await sutProvider.GetDependency() .Received(1) .UpsertProviderAbilityAsync(provider); await sutProvider.GetDependency() .DidNotReceive() .UpsertProviderAbilityAsync(provider); } [Theory, BitAutoData] public async Task DeleteOrganizationAbilityAsync_WhenFeatureIsEnabled_CallsVNextService( SutProvider sutProvider, Guid organizationId) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(true); // Act await sutProvider.Sut.DeleteOrganizationAbilityAsync(organizationId); // Assert await sutProvider.GetDependency() .Received(1) .DeleteOrganizationAbilityAsync(organizationId); await sutProvider.GetDependency() .DidNotReceive() .DeleteOrganizationAbilityAsync(organizationId); } [Theory, BitAutoData] public async Task DeleteOrganizationAbilityAsync_WhenFeatureIsDisabled_CallsInMemoryService( SutProvider sutProvider, Guid organizationId) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(false); // Act await sutProvider.Sut.DeleteOrganizationAbilityAsync(organizationId); // Assert await sutProvider.GetDependency() .Received(1) .DeleteOrganizationAbilityAsync(organizationId); await sutProvider.GetDependency() .DidNotReceive() .DeleteOrganizationAbilityAsync(organizationId); } [Theory, BitAutoData] public async Task DeleteProviderAbilityAsync_WhenFeatureIsEnabled_CallsVNextService( SutProvider sutProvider, Guid providerId) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(true); // Act await sutProvider.Sut.DeleteProviderAbilityAsync(providerId); // Assert await sutProvider.GetDependency() .Received(1) .DeleteProviderAbilityAsync(providerId); await sutProvider.GetDependency() .DidNotReceive() .DeleteProviderAbilityAsync(providerId); } [Theory, BitAutoData] public async Task DeleteProviderAbilityAsync_WhenFeatureIsDisabled_CallsInMemoryService( SutProvider sutProvider, Guid providerId) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(false); // Act await sutProvider.Sut.DeleteProviderAbilityAsync(providerId); // Assert await sutProvider.GetDependency() .Received(1) .DeleteProviderAbilityAsync(providerId); await sutProvider.GetDependency() .DidNotReceive() .DeleteProviderAbilityAsync(providerId); } [Theory, BitAutoData] public async Task BaseUpsertOrganizationAbilityAsync_WhenFeatureIsEnabled_CallsVNextService( SutProvider sutProvider, Organization organization) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(true); // Act await sutProvider.Sut.BaseUpsertOrganizationAbilityAsync(organization); // Assert await sutProvider.GetDependency() .Received(1) .UpsertOrganizationAbilityAsync(organization); await sutProvider.GetDependency() .DidNotReceive() .UpsertOrganizationAbilityAsync(organization); } [Theory, BitAutoData] public async Task BaseUpsertOrganizationAbilityAsync_WhenFeatureIsDisabled_CallsServiceBusCache( Organization organization) { // Arrange var featureService = Substitute.For(); var currentCacheService = CreateCurrentCacheMockService(); featureService .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(false); var sutProvider = Substitute.For( featureService, Substitute.For(), currentCacheService, Substitute.For()); // Act await sutProvider.BaseUpsertOrganizationAbilityAsync(organization); // Assert await currentCacheService .Received(1) .BaseUpsertOrganizationAbilityAsync(organization); } /// /// Our SUT is using a method that is not part of the IVCurrentInMemoryApplicationCacheService, /// so AutoFixture’s auto-created mock won’t work. /// /// private static InMemoryServiceBusApplicationCacheService CreateCurrentCacheMockService() { var currentCacheService = Substitute.For( Substitute.For(), Substitute.For(), new GlobalSettings { ProjectName = "BitwardenTest", ServiceBus = new GlobalSettings.ServiceBusSettings { ConnectionString = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=test;SharedAccessKey=test", ApplicationCacheTopicName = "test-topic", ApplicationCacheSubscriptionName = "test-subscription" } }); return currentCacheService; } [Theory, BitAutoData] public async Task BaseUpsertOrganizationAbilityAsync_WhenFeatureIsDisabled_AndServiceIsNotServiceBusCache_ThrowsException( SutProvider sutProvider, Organization organization) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(false); // Act var ex = await Assert.ThrowsAsync( () => sutProvider.Sut.BaseUpsertOrganizationAbilityAsync(organization)); // Assert Assert.Equal( ExpectedErrorMessage, ex.Message); } private static string ExpectedErrorMessage { get => "Expected inMemoryApplicationCacheService to be of type InMemoryServiceBusApplicationCacheService"; } [Theory, BitAutoData] public async Task BaseDeleteOrganizationAbilityAsync_WhenFeatureIsEnabled_CallsVNextService( SutProvider sutProvider, Guid organizationId) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(true); // Act await sutProvider.Sut.BaseDeleteOrganizationAbilityAsync(organizationId); // Assert await sutProvider.GetDependency() .Received(1) .DeleteOrganizationAbilityAsync(organizationId); await sutProvider.GetDependency() .DidNotReceive() .DeleteOrganizationAbilityAsync(organizationId); } [Theory, BitAutoData] public async Task BaseDeleteOrganizationAbilityAsync_WhenFeatureIsDisabled_CallsServiceBusCache( Guid organizationId) { // Arrange var featureService = Substitute.For(); var currentCacheService = CreateCurrentCacheMockService(); featureService .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(false); var sutProvider = Substitute.For( featureService, Substitute.For(), currentCacheService, Substitute.For()); // Act await sutProvider.BaseDeleteOrganizationAbilityAsync(organizationId); // Assert await currentCacheService .Received(1) .BaseDeleteOrganizationAbilityAsync(organizationId); } [Theory, BitAutoData] public async Task BaseDeleteOrganizationAbilityAsync_WhenFeatureIsDisabled_AndServiceIsNotServiceBusCache_ThrowsException( SutProvider sutProvider, Guid organizationId) { // Arrange sutProvider.GetDependency() .IsEnabled(FeatureFlagKeys.PM23845_VNextApplicationCache) .Returns(false); // Act var ex = await Assert.ThrowsAsync(() => sutProvider.Sut.BaseDeleteOrganizationAbilityAsync(organizationId)); // Assert Assert.Equal( ExpectedErrorMessage, ex.Message); } }