From 123f57926fee1ff5ca70c86be5296553a0aba20b Mon Sep 17 00:00:00 2001 From: Rui Tome Date: Fri, 5 Dec 2025 15:01:36 +0000 Subject: [PATCH] Refactor TwoFactorIsEnabledQuery to introduce VNextAsync methods for improved premium access checks and user detail handling. Removed obsolete feature service dependency and enhanced test coverage for new functionality. --- .../Interfaces/ITwoFactorIsEnabledQuery.cs | 21 + .../TwoFactorAuth/TwoFactorIsEnabledQuery.cs | 152 ++++-- .../TwoFactorIsEnabledQueryTests.cs | 503 +++++++++++++++++- 3 files changed, 613 insertions(+), 63 deletions(-) diff --git a/src/Core/Auth/UserFeatures/TwoFactorAuth/Interfaces/ITwoFactorIsEnabledQuery.cs b/src/Core/Auth/UserFeatures/TwoFactorAuth/Interfaces/ITwoFactorIsEnabledQuery.cs index 697c10690c..9beb658a32 100644 --- a/src/Core/Auth/UserFeatures/TwoFactorAuth/Interfaces/ITwoFactorIsEnabledQuery.cs +++ b/src/Core/Auth/UserFeatures/TwoFactorAuth/Interfaces/ITwoFactorIsEnabledQuery.cs @@ -22,4 +22,25 @@ public interface ITwoFactorIsEnabledQuery /// /// The user to check. Task TwoFactorIsEnabledAsync(ITwoFactorProvidersUser user); + + /// + /// Returns a list of user IDs and whether two factor is enabled for each user. + /// This version uses PremiumAccessQuery with cached organization abilities for better performance. + /// + /// The list of user IDs to check. + Task> TwoFactorIsEnabledVNextAsync(IEnumerable userIds); + /// + /// Returns a list of users and whether two factor is enabled for each user. + /// This version uses PremiumAccessQuery with cached organization abilities for better performance. + /// + /// The list of users to check. + /// The type of user in the list. Must implement . + Task> TwoFactorIsEnabledVNextAsync(IEnumerable users) where T : ITwoFactorProvidersUser; + /// + /// Returns whether two factor is enabled for the user. A user is able to have a TwoFactorProvider that is enabled but requires Premium. + /// If the user does not have premium then the TwoFactorProvider is considered _not_ enabled. + /// This version uses PremiumAccessQuery with cached organization abilities for better performance. + /// + /// The user to check. + Task TwoFactorIsEnabledVNextAsync(ITwoFactorProvidersUser user); } diff --git a/src/Core/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQuery.cs b/src/Core/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQuery.cs index d765164892..9ed022184f 100644 --- a/src/Core/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQuery.cs +++ b/src/Core/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQuery.cs @@ -8,7 +8,6 @@ using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces; using Bit.Core.Entities; using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Repositories; -using Bit.Core.Services; namespace Bit.Core.Auth.UserFeatures.TwoFactorAuth; @@ -16,16 +15,13 @@ public class TwoFactorIsEnabledQuery : ITwoFactorIsEnabledQuery { private readonly IUserRepository _userRepository; private readonly IPremiumAccessQuery _premiumAccessQuery; - private readonly IFeatureService _featureService; public TwoFactorIsEnabledQuery( IUserRepository userRepository, - IPremiumAccessQuery premiumAccessQuery, - IFeatureService featureService) + IPremiumAccessQuery premiumAccessQuery) { _userRepository = userRepository; _premiumAccessQuery = premiumAccessQuery; - _featureService = featureService; } public async Task> TwoFactorIsEnabledAsync(IEnumerable userIds) @@ -36,34 +32,15 @@ public class TwoFactorIsEnabledQuery : ITwoFactorIsEnabledQuery return result; } - if (_featureService.IsEnabled(FeatureFlagKeys.PremiumAccessCacheCheck)) + var userDetails = await _userRepository.GetManyWithCalculatedPremiumAsync([.. userIds]); + foreach (var userDetail in userDetails) { - var users = await _userRepository.GetManyAsync([.. userIds]); - var premiumStatus = await _premiumAccessQuery.CanAccessPremiumAsync(users); - - foreach (var user in users) - { - result.Add( - (user.Id, - await TwoFactorEnabledAsync( - user.GetTwoFactorProviders(), - () => Task.FromResult(premiumStatus.GetValueOrDefault(user.Id, false)) - )) - ); - } - } - else - { - var userDetails = await _userRepository.GetManyWithCalculatedPremiumAsync([.. userIds]); - foreach (var userDetail in userDetails) - { - result.Add( - (userDetail.Id, - await TwoFactorEnabledAsync(userDetail.GetTwoFactorProviders(), - () => Task.FromResult(userDetail.HasPremiumAccess)) - ) - ); - } + result.Add( + (userDetail.Id, + await TwoFactorEnabledAsync(userDetail.GetTwoFactorProviders(), + () => Task.FromResult(userDetail.HasPremiumAccess)) + ) + ); } return result; @@ -106,43 +83,102 @@ public class TwoFactorIsEnabledQuery : ITwoFactorIsEnabledQuery return false; } - if (_featureService.IsEnabled(FeatureFlagKeys.PremiumAccessCacheCheck)) + return await TwoFactorEnabledAsync( + user.GetTwoFactorProviders(), + async () => + { + var calcUser = await _userRepository.GetCalculatedPremiumAsync(userId.Value); + return calcUser?.HasPremiumAccess ?? false; + }); + } + + public async Task> TwoFactorIsEnabledVNextAsync(IEnumerable userIds) + { + var result = new List<(Guid userId, bool hasTwoFactor)>(); + if (userIds == null || !userIds.Any()) { - // Try to get premium status without fetching User entity if possible - bool hasPersonalPremium; - if (user is User userEntity) + return result; + } + + var users = await _userRepository.GetManyAsync([.. userIds]); + var premiumStatus = await _premiumAccessQuery.CanAccessPremiumAsync(users); + + foreach (var user in users) + { + result.Add( + (user.Id, + await TwoFactorEnabledAsync( + user.GetTwoFactorProviders(), + () => Task.FromResult(premiumStatus.GetValueOrDefault(user.Id, false)) + )) + ); + } + + return result; + } + + public async Task> TwoFactorIsEnabledVNextAsync(IEnumerable users) + where T : ITwoFactorProvidersUser + { + var userIds = users + .Select(u => u.GetUserId()) + .Where(u => u.HasValue) + .Select(u => u.Value) + .ToList(); + + var twoFactorResults = await TwoFactorIsEnabledVNextAsync(userIds); + + var result = new List<(T user, bool twoFactorIsEnabled)>(); + + foreach (var user in users) + { + var userId = user.GetUserId(); + if (userId.HasValue) { - hasPersonalPremium = userEntity.Premium; - } - else if (user is OrganizationUserUserDetails orgUserDetails) - { - hasPersonalPremium = orgUserDetails.Premium.GetValueOrDefault(false); + var hasTwoFactor = twoFactorResults.FirstOrDefault(res => res.userId == userId.Value).twoFactorIsEnabled; + result.Add((user, hasTwoFactor)); } else { - // Fallback: fetch the User entity - var fetchedUser = await _userRepository.GetByIdAsync(userId.Value); - if (fetchedUser == null) - { - return false; - } - hasPersonalPremium = fetchedUser.Premium; + result.Add((user, false)); } + } - return await TwoFactorEnabledAsync( - user.GetTwoFactorProviders(), - async () => await _premiumAccessQuery.CanAccessPremiumAsync(userId.Value, hasPersonalPremium)); + return result; + } + + public async Task TwoFactorIsEnabledVNextAsync(ITwoFactorProvidersUser user) + { + var userId = user.GetUserId(); + if (!userId.HasValue) + { + return false; + } + + // Try to get premium status without fetching User entity if possible + bool hasPersonalPremium; + if (user is User userEntity) + { + hasPersonalPremium = userEntity.Premium; + } + else if (user is OrganizationUserUserDetails orgUserDetails) + { + hasPersonalPremium = orgUserDetails.Premium.GetValueOrDefault(false); } else { - return await TwoFactorEnabledAsync( - user.GetTwoFactorProviders(), - async () => - { - var calcUser = await _userRepository.GetCalculatedPremiumAsync(userId.Value); - return calcUser?.HasPremiumAccess ?? false; - }); + // Fallback: fetch the User entity + var fetchedUser = await _userRepository.GetByIdAsync(userId.Value); + if (fetchedUser == null) + { + return false; + } + hasPersonalPremium = fetchedUser.Premium; } + + return await TwoFactorEnabledAsync( + user.GetTwoFactorProviders(), + async () => await _premiumAccessQuery.CanAccessPremiumAsync(userId.Value, hasPersonalPremium)); } /// diff --git a/test/Core.Test/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQueryTests.cs b/test/Core.Test/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQueryTests.cs index adeac45d06..7619331009 100644 --- a/test/Core.Test/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQueryTests.cs +++ b/test/Core.Test/Auth/UserFeatures/TwoFactorAuth/TwoFactorIsEnabledQueryTests.cs @@ -1,5 +1,6 @@ using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models; +using Bit.Core.Auth.UserFeatures.PremiumAccess; using Bit.Core.Auth.UserFeatures.TwoFactorAuth; using Bit.Core.Entities; using Bit.Core.Models.Data; @@ -404,6 +405,503 @@ public class TwoFactorIsEnabledQueryTests .GetCalculatedPremiumAsync(default); } + [Theory] + [BitAutoData(TwoFactorProviderType.Authenticator)] + [BitAutoData(TwoFactorProviderType.Email)] + [BitAutoData(TwoFactorProviderType.Remember)] + [BitAutoData(TwoFactorProviderType.OrganizationDuo)] + [BitAutoData(TwoFactorProviderType.WebAuthn)] + public async Task TwoFactorIsEnabledVNextAsync_WithProviderTypeNotRequiringPremium_ReturnsAllTwoFactorEnabled( + TwoFactorProviderType freeProviderType, + SutProvider sutProvider, + List users) + { + // Arrange + var userIds = users.Select(u => u.Id).ToList(); + var twoFactorProviders = new Dictionary + { + { freeProviderType, new TwoFactorProvider { Enabled = true } } + }; + + foreach (var user in users) + { + user.Premium = false; + user.SetTwoFactorProviders(twoFactorProviders); + } + + var premiumStatus = users.ToDictionary(u => u.Id, u => false); + + sutProvider.GetDependency() + .GetManyAsync(Arg.Is>(i => i.All(userIds.Contains))) + .Returns(users); + + sutProvider.GetDependency() + .CanAccessPremiumAsync(Arg.Is>(u => u.All(users.Contains))) + .Returns(premiumStatus); + + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(userIds); + + // Assert + foreach (var user in users) + { + Assert.Contains(result, res => res.userId == user.Id && res.twoFactorIsEnabled == true); + } + } + + [Theory, BitAutoData] + public async Task TwoFactorIsEnabledVNextAsync_DatabaseReturnsEmpty_ResultEmpty( + SutProvider sutProvider, + List users) + { + // Arrange + var userIds = users.Select(u => u.Id).ToList(); + + sutProvider.GetDependency() + .GetManyAsync(Arg.Any>()) + .Returns([]); + + sutProvider.GetDependency() + .CanAccessPremiumAsync(Arg.Any>()) + .Returns(new Dictionary()); + + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(userIds); + + // Assert + Assert.Empty(result); + } + + [Theory] + [BitAutoData((IEnumerable)null)] + [BitAutoData([])] + public async Task TwoFactorIsEnabledVNextAsync_UserIdsNullorEmpty_ResultEmpty( + IEnumerable userIds, + SutProvider sutProvider) + { + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(userIds); + + // Assert + Assert.Empty(result); + } + + [Theory] + [BitAutoData] + public async Task TwoFactorIsEnabledVNextAsync_WithNoTwoFactorEnabled_ReturnsAllTwoFactorDisabled( + SutProvider sutProvider, + List users) + { + // Arrange + var userIds = users.Select(u => u.Id).ToList(); + var twoFactorProviders = new Dictionary + { + { TwoFactorProviderType.Email, new TwoFactorProvider { Enabled = false } } + }; + + foreach (var user in users) + { + user.SetTwoFactorProviders(twoFactorProviders); + } + + var premiumStatus = users.ToDictionary(u => u.Id, u => false); + + sutProvider.GetDependency() + .GetManyAsync(Arg.Is>(i => i.All(userIds.Contains))) + .Returns(users); + + sutProvider.GetDependency() + .CanAccessPremiumAsync(Arg.Is>(u => u.All(users.Contains))) + .Returns(premiumStatus); + + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(userIds); + + // Assert + foreach (var user in users) + { + Assert.Contains(result, res => res.userId == user.Id && res.twoFactorIsEnabled == false); + } + } + + [Theory] + [BitAutoData(TwoFactorProviderType.Duo)] + [BitAutoData(TwoFactorProviderType.YubiKey)] + public async Task TwoFactorIsEnabledVNextAsync_WithProviderTypeRequiringPremium_ReturnsMixedResults( + TwoFactorProviderType premiumProviderType, + SutProvider sutProvider, + List users) + { + // Arrange + var userIds = users.Select(u => u.Id).ToList(); + var twoFactorProviders = new Dictionary + { + { TwoFactorProviderType.Email, new TwoFactorProvider { Enabled = false } }, + { premiumProviderType, new TwoFactorProvider { Enabled = true } } + }; + + foreach (var user in users) + { + user.Premium = false; + user.SetTwoFactorProviders(twoFactorProviders); + } + + // Only the first user has premium access + var premiumStatus = users.ToDictionary( + u => u.Id, + u => users.IndexOf(u) == 0); + + sutProvider.GetDependency() + .GetManyAsync(Arg.Is>(i => i.All(userIds.Contains))) + .Returns(users); + + sutProvider.GetDependency() + .CanAccessPremiumAsync(Arg.Is>(u => u.All(users.Contains))) + .Returns(premiumStatus); + + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(userIds); + + // Assert + foreach (var user in users) + { + var expectedEnabled = premiumStatus[user.Id]; + Assert.Contains(result, res => res.userId == user.Id && res.twoFactorIsEnabled == expectedEnabled); + } + } + + [Theory] + [BitAutoData("")] + [BitAutoData("{}")] + [BitAutoData((string)null)] + public async Task TwoFactorIsEnabledVNextAsync_WithNullOrEmptyTwoFactorProviders_ReturnsAllTwoFactorDisabled( + string twoFactorProviders, + SutProvider sutProvider, + List users) + { + // Arrange + var userIds = users.Select(u => u.Id).ToList(); + + foreach (var user in users) + { + user.TwoFactorProviders = twoFactorProviders; + } + + var premiumStatus = users.ToDictionary(u => u.Id, u => false); + + sutProvider.GetDependency() + .GetManyAsync(Arg.Is>(i => i.All(userIds.Contains))) + .Returns(users); + + sutProvider.GetDependency() + .CanAccessPremiumAsync(Arg.Is>(u => u.All(users.Contains))) + .Returns(premiumStatus); + + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(userIds); + + // Assert + foreach (var user in users) + { + Assert.Contains(result, res => res.userId == user.Id && res.twoFactorIsEnabled == false); + } + } + + [Theory] + [BitAutoData] + public async Task TwoFactorIsEnabledVNextAsync_Generic_WithNoUserIds_ReturnsAllTwoFactorDisabled( + SutProvider sutProvider, + List users) + { + // Arrange + foreach (var user in users) + { + user.UserId = null; + } + + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(users); + + // Assert + foreach (var user in users) + { + Assert.Contains(result, res => res.user.Equals(user) && res.twoFactorIsEnabled == false); + } + + // No UserIds were supplied so no calls to the UserRepository should have been made + await sutProvider.GetDependency() + .DidNotReceiveWithAnyArgs() + .GetManyAsync(default); + } + + [Theory] + [BitAutoData] + public async Task TwoFactorIsEnabledVNextAsync_SingleUser_UserIdNull_ReturnsFalse( + SutProvider sutProvider) + { + // Arrange + var user = new TestTwoFactorProviderUser + { + Id = null + }; + + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(user); + + // Assert + Assert.False(result); + } + + [Theory] + [BitAutoData(TwoFactorProviderType.Authenticator)] + [BitAutoData(TwoFactorProviderType.Email)] + [BitAutoData(TwoFactorProviderType.Remember)] + [BitAutoData(TwoFactorProviderType.OrganizationDuo)] + [BitAutoData(TwoFactorProviderType.WebAuthn)] + public async Task TwoFactorIsEnabledVNextAsync_SingleUser_WithProviderTypeNotRequiringPremium_ReturnsTrue( + TwoFactorProviderType freeProviderType, + SutProvider sutProvider, + User user) + { + // Arrange + var twoFactorProviders = new Dictionary + { + { freeProviderType, new TwoFactorProvider { Enabled = true } } + }; + + user.Premium = false; + user.SetTwoFactorProviders(twoFactorProviders); + + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(user); + + // Assert + Assert.True(result); + + // Should not need to check premium access for free providers + await sutProvider.GetDependency() + .DidNotReceiveWithAnyArgs() + .CanAccessPremiumAsync(default(Guid), default); + } + + [Theory] + [BitAutoData] + public async Task TwoFactorIsEnabledVNextAsync_SingleUser_WithNoTwoFactorEnabled_ReturnsFalse( + SutProvider sutProvider, + User user) + { + // Arrange + var twoFactorProviders = new Dictionary + { + { TwoFactorProviderType.Email, new TwoFactorProvider { Enabled = false } } + }; + + user.SetTwoFactorProviders(twoFactorProviders); + + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(user); + + // Assert + Assert.False(result); + + await sutProvider.GetDependency() + .DidNotReceiveWithAnyArgs() + .CanAccessPremiumAsync(default(Guid), default); + } + + [Theory] + [BitAutoData(TwoFactorProviderType.Duo)] + [BitAutoData(TwoFactorProviderType.YubiKey)] + public async Task TwoFactorIsEnabledVNextAsync_SingleUser_WithProviderTypeRequiringPremium_WithoutPremium_ReturnsFalse( + TwoFactorProviderType premiumProviderType, + SutProvider sutProvider, + User user) + { + // Arrange + var twoFactorProviders = new Dictionary + { + { premiumProviderType, new TwoFactorProvider { Enabled = true } } + }; + + user.Premium = false; + user.SetTwoFactorProviders(twoFactorProviders); + + sutProvider.GetDependency() + .CanAccessPremiumAsync(user.Id, false) + .Returns(false); + + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(user); + + // Assert + Assert.False(result); + + await sutProvider.GetDependency() + .Received(1) + .CanAccessPremiumAsync(user.Id, false); + } + + [Theory] + [BitAutoData(TwoFactorProviderType.Duo)] + [BitAutoData(TwoFactorProviderType.YubiKey)] + public async Task TwoFactorIsEnabledVNextAsync_SingleUser_WithProviderTypeRequiringPremium_WithPersonalPremium_ReturnsTrue( + TwoFactorProviderType premiumProviderType, + SutProvider sutProvider, + User user) + { + // Arrange + var twoFactorProviders = new Dictionary + { + { premiumProviderType, new TwoFactorProvider { Enabled = true } } + }; + + user.Premium = true; + user.SetTwoFactorProviders(twoFactorProviders); + + sutProvider.GetDependency() + .CanAccessPremiumAsync(user.Id, true) + .Returns(true); + + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(user); + + // Assert + Assert.True(result); + + await sutProvider.GetDependency() + .Received(1) + .CanAccessPremiumAsync(user.Id, true); + } + + [Theory] + [BitAutoData(TwoFactorProviderType.Duo)] + [BitAutoData(TwoFactorProviderType.YubiKey)] + public async Task TwoFactorIsEnabledVNextAsync_SingleUser_WithProviderTypeRequiringPremium_WithOrgPremium_ReturnsTrue( + TwoFactorProviderType premiumProviderType, + SutProvider sutProvider, + User user) + { + // Arrange + var twoFactorProviders = new Dictionary + { + { premiumProviderType, new TwoFactorProvider { Enabled = true } } + }; + + user.Premium = false; + user.SetTwoFactorProviders(twoFactorProviders); + + sutProvider.GetDependency() + .CanAccessPremiumAsync(user.Id, false) + .Returns(true); // Has premium from org + + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(user); + + // Assert + Assert.True(result); + + await sutProvider.GetDependency() + .Received(1) + .CanAccessPremiumAsync(user.Id, false); + } + + [Theory] + [BitAutoData] + public async Task TwoFactorIsEnabledVNextAsync_SingleUser_WithNullTwoFactorProviders_ReturnsFalse( + SutProvider sutProvider, + User user) + { + // Arrange + user.TwoFactorProviders = null; + + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(user); + + // Assert + Assert.False(result); + await sutProvider.GetDependency() + .DidNotReceiveWithAnyArgs() + .CanAccessPremiumAsync(default(Guid), default); + } + + [Theory] + [BitAutoData(TwoFactorProviderType.Duo)] + [BitAutoData(TwoFactorProviderType.YubiKey)] + public async Task TwoFactorIsEnabledVNextAsync_SingleUser_OrganizationUserUserDetails_WithPremium_ReturnsTrue( + TwoFactorProviderType premiumProviderType, + SutProvider sutProvider, + OrganizationUserUserDetails orgUserDetails) + { + // Arrange + var twoFactorProviders = new Dictionary + { + { premiumProviderType, new TwoFactorProvider { Enabled = true } } + }; + + orgUserDetails.Premium = false; + orgUserDetails.TwoFactorProviders = JsonHelpers.LegacySerialize(twoFactorProviders, JsonHelpers.LegacyEnumKeyResolver); + + sutProvider.GetDependency() + .CanAccessPremiumAsync(orgUserDetails.UserId!.Value, false) + .Returns(true); + + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(orgUserDetails); + + // Assert + Assert.True(result); + + await sutProvider.GetDependency() + .Received(1) + .CanAccessPremiumAsync(orgUserDetails.UserId.Value, false); + } + + [Theory] + [BitAutoData(TwoFactorProviderType.Duo)] + [BitAutoData(TwoFactorProviderType.YubiKey)] + public async Task TwoFactorIsEnabledVNextAsync_SingleUser_UnknownType_FetchesUser( + TwoFactorProviderType premiumProviderType, + SutProvider sutProvider, + User fetchedUser) + { + // Arrange + var twoFactorProviders = new Dictionary + { + { premiumProviderType, new TwoFactorProvider { Enabled = true } } + }; + + var testUser = new TestTwoFactorProviderUser + { + Id = fetchedUser.Id, + Premium = false, + TwoFactorProviders = JsonHelpers.LegacySerialize(twoFactorProviders, JsonHelpers.LegacyEnumKeyResolver) + }; + + fetchedUser.Premium = false; + + sutProvider.GetDependency() + .GetByIdAsync(fetchedUser.Id) + .Returns(fetchedUser); + + sutProvider.GetDependency() + .CanAccessPremiumAsync(fetchedUser.Id, false) + .Returns(true); + + // Act + var result = await sutProvider.Sut.TwoFactorIsEnabledVNextAsync(testUser); + + // Assert + Assert.True(result); + + await sutProvider.GetDependency() + .Received(1) + .GetByIdAsync(fetchedUser.Id); + + await sutProvider.GetDependency() + .Received(1) + .CanAccessPremiumAsync(fetchedUser.Id, false); + } + private class TestTwoFactorProviderUser : ITwoFactorProvidersUser { public Guid? Id { get; set; } @@ -418,10 +916,5 @@ public class TwoFactorIsEnabledQueryTests { return Id; } - - public bool GetPremium() - { - return Premium; - } } }