using Bit.Core.AdminConsole.AbilitiesCache; using Bit.Core.AdminConsole.Models.Data.Provider; using Bit.Core.AdminConsole.Repositories; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; using Xunit; using ZiggyCreatures.Caching.Fusion; namespace Bit.Core.Test.AdminConsole.AbilitiesCache; [SutProviderCustomize] public class ExtendedProviderAbilityCacheServiceTests { [Theory, BitAutoData] public async Task GetProviderAbilitiesAsync_ReturnsAvailableAbilitiesForRequestedIds( SutProvider sutProvider, ProviderAbility requestedAbility1, ProviderAbility requestedAbility2, ProviderAbility nonRequestedAbility, ProviderAbility nonExistAbility) { // Arrange SetupCacheReturns(sutProvider, requestedAbility1, requestedAbility2, nonRequestedAbility); // Act var result = await sutProvider.Sut.GetProviderAbilitiesAsync([requestedAbility1.Id, requestedAbility2.Id, nonExistAbility.Id]); // Assert Assert.Equal(2, result.Count); Assert.Equal(requestedAbility1, result[requestedAbility1.Id]); Assert.Equal(requestedAbility2, result[requestedAbility2.Id]); Assert.DoesNotContain(nonRequestedAbility.Id, result.Keys); Assert.DoesNotContain(nonExistAbility.Id, result.Keys); } [Theory, BitAutoData] public async Task GetProviderAbilitiesAsync_WhenProviderNotFound_ExcludesNullAbilities( SutProvider sutProvider, ProviderAbility ability, Guid missingProviderId) { // Arrange SetupCacheReturns(sutProvider, ability); sutProvider.GetDependency() .GetOrSetAsync( $"{missingProviderId}", Arg.Any, CancellationToken, Task>>(), token: Arg.Any()) .Returns((ProviderAbility?)null); // Act var result = await sutProvider.Sut.GetProviderAbilitiesAsync([ability.Id, missingProviderId]); // Assert Assert.Single(result); Assert.Equal(ability, result[ability.Id]); } [Theory, BitAutoData] public async Task GetProviderAbilitiesAsync_WhenDuplicateIdsProvided_DoesNotThrowAndReturnsSingleEntry( SutProvider sutProvider, ProviderAbility ability) { // Arrange SetupCacheReturns(sutProvider, ability); // Act var result = await sutProvider.Sut.GetProviderAbilitiesAsync([ability.Id, ability.Id, ability.Id]); // Assert Assert.Single(result); Assert.Equal(ability, result[ability.Id]); await sutProvider.GetDependency() .Received(1) .GetOrSetAsync( $"{ability.Id}", Arg.Any, CancellationToken, Task>>(), token: Arg.Any()); } [Theory, BitAutoData] public async Task GetProviderAbilitiesAsync_WhenEmptyList_ReturnsEmptyDictionary( SutProvider sutProvider) { // Act var result = await sutProvider.Sut.GetProviderAbilitiesAsync([]); // Assert Assert.Empty(result); await sutProvider.GetDependency() .DidNotReceiveWithAnyArgs() .GetAbilityAsync(default); } [Theory, BitAutoData] public async Task GetProviderAbilitiesAsync_WhenAllProvidersNotFound_ReturnsEmptyDictionary( SutProvider sutProvider, Guid missingId1, Guid missingId2) { // Arrange sutProvider.GetDependency() .GetOrSetAsync( Arg.Any(), Arg.Any, CancellationToken, Task>>(), token: Arg.Any()) .Returns((ProviderAbility?)null); // Act var result = await sutProvider.Sut.GetProviderAbilitiesAsync([missingId1, missingId2]); // Assert Assert.Empty(result); } private static void SetupCacheReturns( SutProvider sutProvider, params ProviderAbility[] abilities) { foreach (var ability in abilities) { sutProvider.GetDependency() .GetOrSetAsync( $"{ability.Id}", Arg.Any, CancellationToken, Task>>(), token: Arg.Any()) .Returns(ability); } } }