diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..a4a2aaa --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,35 @@ +# PR Checklist + +Please check if your PR fulfills the following requirements: + +- [ ] The commit message follows [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) +- [ ] Tests for the changes have been added (for bug fixes / features) +- [ ] Docs have been added / updated (for bug fixes / features) + +## PR Type + +What kind of change does this PR introduce? + + + +- [ ] Bugfix +- [ ] Feature +- [ ] Refactoring (no functional changes, no api changes) +- [ ] Other... Please describe: + +## What is the current behavior? + + + +Closes # + +## What is the new behavior? + +## Does this PR introduce a breaking change? + +- [ ] Yes +- [ ] No + + + +## Other information diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index befae87..78e360f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,9 @@ jobs: - name: Build EFCoreSecondLevelCacheInterceptor lib run: dotnet build ./src/EFCoreSecondLevelCacheInterceptor/EFCoreSecondLevelCacheInterceptor.csproj --configuration Release + - name: Run EFCoreSecondLevelCacheInterceptor lib unit tests + run: dotnet test ./src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCoreSecondLevelCacheInterceptor.UnitTests.csproj --logger "console;verbosity=detailed" + - name: Push Package to NuGet.org if: github.event_name == 'push' run: dotnet nuget push **\*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/DbCommandInterceptorProcessorTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/DbCommandInterceptorProcessorTests.cs new file mode 100644 index 0000000..c3fa159 --- /dev/null +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/DbCommandInterceptorProcessorTests.cs @@ -0,0 +1,1405 @@ +using System.Data.Common; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Moq; +using Moq.Protected; + +namespace EFCoreSecondLevelCacheInterceptor.UnitTests; + +public class DbCommandInterceptorProcessorTests +{ + private readonly IDbCommandInterceptorProcessor _processor; + private readonly Mock _loggerMock; + private readonly Mock> _interceptorProcessorLoggerMock; + private readonly Mock _cacheServiceMock; + private readonly Mock _cacheDependenciesProcessorMock; + private readonly Mock _cacheKeyProviderMock; + private readonly Mock _cachePolicyParserMock; + private readonly Mock _sqlCommandsProcessorMock; + private readonly Mock> _cacheSettingsMock; + private readonly Mock _cacheServiceCheckMock; + private readonly EFCoreSecondLevelCacheSettings _cacheSettings; + + public DbCommandInterceptorProcessorTests() + { + _loggerMock = new Mock(); + _interceptorProcessorLoggerMock = new Mock>(); + _cacheServiceMock = new Mock(); + _cacheDependenciesProcessorMock = new Mock(); + _cacheKeyProviderMock = new Mock(); + _cachePolicyParserMock = new Mock(); + _sqlCommandsProcessorMock = new Mock(); + _cacheSettingsMock = new Mock>(); + _cacheServiceCheckMock = new Mock(); + _cacheSettings = new EFCoreSecondLevelCacheSettings(); + + _cacheSettingsMock.SetupGet(x => x.Value).Returns(_cacheSettings); + + _processor = new DbCommandInterceptorProcessor( + _loggerMock.Object, + _interceptorProcessorLoggerMock.Object, + _cacheServiceMock.Object, + _cacheDependenciesProcessorMock.Object, + _cacheKeyProviderMock.Object, + _cachePolicyParserMock.Object, + _sqlCommandsProcessorMock.Object, + _cacheSettingsMock.Object, + _cacheServiceCheckMock.Object); + } + + [Fact] + public void Constructor_ThrowsArgumentNullException_WhenCacheSettingsIsNull() + { + // Arrange + var logger = new Mock().Object; + var interceptorProcessorLogger = new Mock>().Object; + var cacheService = new Mock().Object; + var cacheDependenciesProcessor = new Mock().Object; + var cacheKeyProvider = new Mock().Object; + var cachePolicyParser = new Mock().Object; + var sqlCommandsProcessor = new Mock().Object; + var cacheServiceCheck = new Mock().Object; + + // Act & Assert + Assert.Throws(() => new DbCommandInterceptorProcessor( + logger, + interceptorProcessorLogger, + cacheService, + cacheDependenciesProcessor, + cacheKeyProvider, + cachePolicyParser, + sqlCommandsProcessor, + null, + cacheServiceCheck)); + } + + [Fact] + public void Constructor_InitializesNewInstance() + { + // Arrange + var logger = new Mock().Object; + var interceptorProcessorLogger = new Mock>().Object; + var cacheService = new Mock().Object; + var cacheDependenciesProcessor = new Mock().Object; + var cacheKeyProvider = new Mock().Object; + var cachePolicyParser = new Mock().Object; + var sqlCommandsProcessor = new Mock().Object; + var cacheSettings = Options.Create(new EFCoreSecondLevelCacheSettings()); + var cacheServiceCheck = new Mock().Object; + + // Act + var processor = new DbCommandInterceptorProcessor( + logger, + interceptorProcessorLogger, + cacheService, + cacheDependenciesProcessor, + cacheKeyProvider, + cachePolicyParser, + sqlCommandsProcessor, + cacheSettings, + cacheServiceCheck); + + // Assert + Assert.NotNull(processor); + } + + [Fact] + public void ProcessExecutedCommands_ReturnsExpectedResultWithoutPrecessing_WhenDbContextIsNull() + { + // Arrange + DbContext context = null; + + // Act + var actual = _processor.ProcessExecutedCommands(null, context, null); + + // Assert + Assert.Null(actual); + } + + [Fact] + public void ProcessExecutedCommands_ReturnsExpectedResultWithoutPrecessing_WhenCommandIsNull() + { + // Arrange + var context = Mock.Of(); + + // Act + var actual = _processor.ProcessExecutedCommands(null, context, null); + + // Assert + Assert.Null(actual); + } + + [Fact] + public void ProcessExecutedCommands_ThrowsInvalidOperationException_WhenNotUseDbCallsIfCachingProviderIsDown() + { + // Arrange + var expected = new object(); + var command = Mock.Of(); + var context = Mock.Of(); + + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Throws(); + + // Act + void Act() => _processor.ProcessExecutedCommands(command, context, expected); + + // Assert + Assert.Throws(Act); + } + + [Fact] + public void ProcessExecutedCommands_NotifiesCachingErrorEvent_WhenThrowsInvalidOperationException() + { + // Arrange + var expected = new object(); + var command = Mock.Of(); + var context = Mock.Of(); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Throws(); + _cacheSettings.UseDbCallsIfCachingProviderIsDown = true; + + // Act + _processor.ProcessExecutedCommands(command, context, expected); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.CachingError, + It.Is(message => + message.Contains( + "System.InvalidOperationException: Operation is not valid due to the current state of the object.")), + null), Times.Once); + } + + [Fact] + public void ProcessExecutedCommands_ReturnsExpectedResultWithoutPrecessing_WhenCacheServiceIsNotAvailable() + { + // Arrange + var expected = new object(); + var command = Mock.Of(); + var context = Mock.Of(); + + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(false); + + // Act + var actual = _processor.ProcessExecutedCommands(command, context, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void + ProcessExecutedCommands_ReturnsExpectedResultWithoutPrecessing_WhenSkipCachingDbContextsSettingIsNotNullAndContainsType() + { + // Arrange + var expected = new object(); + var command = Mock.Of(); + var context = Mock.Of(); + + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheSettings.SkipCachingDbContexts = new List { context.GetType() }; + + // Act + var actual = _processor.ProcessExecutedCommands(command, context, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void + ProcessExecutedCommands_NotifiesCachingSkippedEvent_WhenSkipCachingDbContextsSettingIsNotNullAndContainsTypeAndLoggerEnabled() + { + // Arrange + var result = new object(); + var command = Mock.Of(); + var context = Mock.Of(); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheSettings.SkipCachingDbContexts = new List { context.GetType() }; + + // Act + var actual = _processor.ProcessExecutedCommands(command, context, result); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.CachingSkipped, + "Skipped caching of this DbContext: Castle.Proxies.DbContextProxy", + string.Empty), Times.Once); + } + + [Fact] + public void + ProcessExecutedCommands_ReturnsExpectedResultWithoutPrecessing_WhenShouldSkipQueriesInsideExplicitTransactionAndTransactionIsNotNull() + { + // Arrange + var expected = new object(); + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + + // Act + var actual = _processor.ProcessExecutedCommands(commandMock.Object, context, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ProcessExecutedCommands_ReturnsExpectedResultWithoutPrecessing_WhenCachePolicyIsNull() + { + // Arrange + var expected = new object(); + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + var actual = _processor.ProcessExecutedCommands(commandMock.Object, context, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ProcessExecutedCommands_ReturnsExpectedResultWithoutPrecessing_WhenIsCrudCommandAndCachePolicyIsNull() + { + // Arrange + var expected = new object(); + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + var actual = _processor.ProcessExecutedCommands(commandMock.Object, context, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void + ProcessExecutedCommands_ReturnsExpectedResultWithoutPrecessing_WhenInvalidateCacheDependenciesReturnsTrue() + { + // Arrange + var expected = new object(); + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheDependenciesProcessorMock.Setup(x => x.InvalidateCacheDependencies(null, efCacheKey)).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + var actual = _processor.ProcessExecutedCommands(commandMock.Object, context, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ProcessExecutedCommands_NotifiesCachingSkippedEvent_WhenIsCrudCommandAndCachePolicyIsNull() + { + // Arrange + var result = new object(); + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + _processor.ProcessExecutedCommands(commandMock.Object, context, result); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.CachingSkipped, + "Skipping a none-cachable command[].", + null), Times.Once); + } + + [Fact] + public void ProcessExecutedCommands_ReturnsCachedTableRows() + { + // Arrange + var expected = new EFTableRowsDataReader(new EFTableRows()); + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + var actual = _processor.ProcessExecutedCommands(commandMock.Object, context, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ProcessExecutedCommands_NotifiesCacheHitEvent_WhenReturningCachedTableRows() + { + // Arrange + var result = new EFTableRowsDataReader(new EFTableRows + { + TableName = string.Empty + }); + + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + _processor.ProcessExecutedCommands(commandMock.Object, context, result); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.CacheHit, + "Returning the cached TableRows[].", + null), Times.Once); + } + + [Fact] + public void ProcessExecutedCommands_SkipsCachingResultsIfResultIsIntType() + { + // Arrange + const int expected = int.MaxValue; + + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + _cacheSettings.SkipCachingResults = _ => true; + + // Act + _processor.ProcessExecutedCommands(commandMock.Object, context, expected); + + // Assert + _cacheServiceMock.Verify(x => x.InsertValue( + efCacheKey, + It.Is(data => data.NonQuery == expected), + cachePolicy), Times.Never); + } + + [Fact] + public void ProcessExecutedCommands_NotifiesCacheHitEvent_WhenSkipsCachingResultsIfResultIsIntType() + { + // Arrange + const int result = int.MaxValue; + + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + _cacheSettings.SkipCachingResults = _ => true; + + // Act + _processor.ProcessExecutedCommands(commandMock.Object, context, result); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.CachingSkipped, + "Skipped caching of this result based on the provided predicate.", + null), Times.Once); + } + + [Fact] + public void ProcessExecutedCommands_AddsIntDataToCache() + { + // Arrange + const int expected = int.MaxValue; + + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + _processor.ProcessExecutedCommands(commandMock.Object, context, expected); + + // Assert + _cacheServiceMock.Verify(x => x.InsertValue( + efCacheKey, + It.Is(data => data.NonQuery == expected), + cachePolicy), Times.Once); + } + + [Fact] + public void ProcessExecutedCommands_NotifiesQueryResultCachedEvent_WhenIntDataAddedToCache() + { + // Arrange + const int result = int.MaxValue; + + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + _processor.ProcessExecutedCommands(commandMock.Object, context, result); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.QueryResultCached, + "[2147483647] added to the cache[KeyHash: , DbContext: , CacheDependencies: .].", + null), Times.Once); + } + + [Fact] + public void ProcessExecutedCommands_ReturnsExpectedResult_WhenIntDataAddedToCache() + { + // Arrange + const int expected = int.MaxValue; + + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + var actual = _processor.ProcessExecutedCommands(commandMock.Object, context, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ProcessExecutedCommands_ReturnsEFTableRowsDataReader_WhenSkipsCachingResultsIfResultIsDbDataReaderType() + { + // Arrange + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var dataReaderMock = new Mock(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + _cacheSettings.SkipCachingResults = _ => true; + + // Act + var actual = _processor.ProcessExecutedCommands(commandMock.Object, context, dataReaderMock.Object); + + // Assert + Assert.IsType(actual); + } + + [Fact] + public void ProcessExecutedCommands_SkipsCachingResultsIfResultIsDbDataReaderType() + { + // Arrange + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var dataReaderMock = new Mock(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + _cacheSettings.SkipCachingResults = _ => true; + + // Act + _processor.ProcessExecutedCommands(commandMock.Object, context, dataReaderMock.Object); + + // Assert + _cacheServiceMock.Verify(x => x.InsertValue( + efCacheKey, + It.Is(data => data.TableRows != null), + cachePolicy), Times.Never); + } + + [Fact] + public void ProcessExecutedCommands_NotifiesCacheHitEvent_WhenSkipsCachingResultsIfDataIsEFTableRowsType() + { + // Arrange + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var dataReaderMock = new Mock(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + _cacheSettings.SkipCachingResults = _ => true; + + // Act + _processor.ProcessExecutedCommands(commandMock.Object, context, dataReaderMock.Object); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.CachingSkipped, + "Skipped caching of this result based on the provided predicate.", + null), Times.Once); + } + + [Fact] + public void ProcessExecutedCommands_AddsEFTableRowsDataToCache() + { + // Arrange + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var dataReaderMock = new Mock(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + _processor.ProcessExecutedCommands(commandMock.Object, context, dataReaderMock.Object); + + // Assert + _cacheServiceMock.Verify(x => x.InsertValue( + efCacheKey, + It.Is(data => data.TableRows != null), + cachePolicy), Times.Once); + } + + [Fact] + public void ProcessExecutedCommands_NotifiesQueryResultCachedEvent_WhenEFTableRowsDataAddedToCache() + { + // Arrange + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var dataReaderMock = new Mock(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + _processor.ProcessExecutedCommands(commandMock.Object, context, dataReaderMock.Object); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.QueryResultCached, + It.Is(message => + message.Contains(" added to the cache[KeyHash: , DbContext: , CacheDependencies: .].")), + null), Times.Once); + } + + + [Fact] + public void ProcessExecutedCommands_SkipsCachingResultsIfResultIsObjectType() + { + // Arrange + var result = new object(); + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + _cacheSettings.SkipCachingResults = _ => true; + + // Act + _processor.ProcessExecutedCommands(commandMock.Object, context, result); + + // Assert + _cacheServiceMock.Verify(x => x.InsertValue( + efCacheKey, + It.Is(data => data.Scalar == result), + cachePolicy), Times.Never); + } + + [Fact] + public void ProcessExecutedCommands_NotifiesCacheHitEvent_WhenSkipsCachingResultsIfResultIsObjectType() + { + // Arrange + var result = new object(); + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + _cacheSettings.SkipCachingResults = _ => true; + + // Act + _processor.ProcessExecutedCommands(commandMock.Object, context, result); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.CachingSkipped, + "Skipped caching of this result based on the provided predicate.", + null), Times.Once); + } + + [Fact] + public void ProcessExecutedCommands_AddsObjectDataToCache() + { + // Arrange + var result = new object(); + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + _processor.ProcessExecutedCommands(commandMock.Object, context, result); + + // Assert + _cacheServiceMock.Verify(x => x.InsertValue( + efCacheKey, + It.Is(data => data.Scalar == result), + cachePolicy), Times.Once); + } + + [Fact] + public void ProcessExecutedCommands_NotifiesQueryResultCachedEvent_WhenObjectDataAddedToCache() + { + // Arrange + var result = new object(); + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + _processor.ProcessExecutedCommands(commandMock.Object, context, result); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.QueryResultCached, + "[System.Object] added to the cache[KeyHash: , DbContext: , CacheDependencies: .].", + null), Times.Once); + } + + [Fact] + public void ProcessExecutedCommands_ReturnsExpectedResult_WhenObjectDataAddedToCache() + { + // Arrange + var expected = new object(); + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + var actual = _processor.ProcessExecutedCommands(commandMock.Object, context, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ProcessExecutedCommands_ReturnsNull_WhenResultIsNull() + { + // Arrange + object expected = null; + + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + var actual = _processor.ProcessExecutedCommands(commandMock.Object, context, expected); + + // Assert + Assert.Null(actual); + } + + [Fact] + public void ProcessExecutingCommands_ReturnsExpectedResultWithoutPrecessing_WhenDbContextIsNull() + { + // Arrange + DbContext context = null; + + // Act + var actual = _processor.ProcessExecutingCommands(null, context, null); + + // Assert + Assert.Null(actual); + } + + [Fact] + public void ProcessExecutingCommands_ReturnsExpectedResultWithoutPrecessing_WhenCommandIsNull() + { + // Arrange + var context = Mock.Of(); + + // Act + var actual = _processor.ProcessExecutingCommands(null, context, null); + + // Assert + Assert.Null(actual); + } + + [Fact] + public void ProcessExecutingCommands_ThrowsInvalidOperationException_WhenNotUseDbCallsIfCachingProviderIsDown() + { + // Arrange + var expected = new object(); + var command = Mock.Of(); + var context = Mock.Of(); + + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Throws(); + + // Act + void Act() => _processor.ProcessExecutingCommands(command, context, expected); + + // Assert + Assert.Throws(Act); + } + + [Fact] + public void ProcessExecutingCommands_NotifiesCachingErrorEvent_WhenThrowsInvalidOperationException() + { + // Arrange + var expected = new object(); + var command = Mock.Of(); + var context = Mock.Of(); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Throws(); + _cacheSettings.UseDbCallsIfCachingProviderIsDown = true; + + // Act + _processor.ProcessExecutingCommands(command, context, expected); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.CachingError, + It.Is(message => + message.Contains( + "System.InvalidOperationException: Operation is not valid due to the current state of the object.")), + null), Times.Once); + } + + [Fact] + public void ProcessExecutingCommands_ReturnsExpectedResultWithoutPrecessing_WhenCacheServiceIsNotAvailable() + { + // Arrange + var expected = new object(); + var command = Mock.Of(); + var context = Mock.Of(); + + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(false); + + // Act + var actual = _processor.ProcessExecutingCommands(command, context, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void + ProcessExecutingCommands_ReturnsExpectedResultWithoutPrecessing_WhenSkipCachingDbContextsSettingIsNotNullAndContainsType() + { + // Arrange + var expected = new object(); + var command = Mock.Of(); + var context = Mock.Of(); + + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheSettings.SkipCachingDbContexts = new List { context.GetType() }; + + // Act + var actual = _processor.ProcessExecutingCommands(command, context, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void + ProcessExecutingCommands_NotifiesCachingSkippedEvent_WhenSkipCachingDbContextsSettingIsNotNullAndContainsTypeAndLoggerEnabled() + { + // Arrange + var result = new object(); + var command = Mock.Of(); + var context = Mock.Of(); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheSettings.SkipCachingDbContexts = new List { context.GetType() }; + + // Act + _processor.ProcessExecutingCommands(command, context, result); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.CachingSkipped, + "Skipped caching of this DbContext: Castle.Proxies.DbContextProxy", + string.Empty), Times.Once); + } + + [Fact] + public void + ProcessExecutingCommands_ReturnsExpectedResultWithoutPrecessing_WhenShouldSkipQueriesInsideExplicitTransactionAndTransactionIsNotNull() + { + // Arrange + var expected = new object(); + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + + // Act + var actual = _processor.ProcessExecutingCommands(commandMock.Object, context, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ProcessExecutingCommands_ReturnsExpectedResultWithoutPrecessing_WhenCachePolicyIsNull() + { + // Arrange + var expected = new object(); + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + var actual = _processor.ProcessExecutingCommands(commandMock.Object, context, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ProcessExecutingCommands_ReturnsExpectedResultWithoutPrecessing_WhenIsCrudCommandAndCachePolicyIsNull() + { + // Arrange + var expected = new object(); + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + var actual = _processor.ProcessExecutingCommands(commandMock.Object, context, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ProcessExecutingCommands_NotifiesCachingSkippedEvent_WhenCachePolicyIsNull() + { + // Arrange + var result = new object(); + var commandMock = new Mock(); + var transaction = Mock.Of(); + var context = Mock.Of(); + + commandMock.Protected().Setup("DbTransaction").Returns(transaction); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + _processor.ProcessExecutingCommands(commandMock.Object, context, result); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.CachingSkipped, + "Skipping a none-cachable command[].", + null), Times.Once); + } + + [Fact] + public void ProcessExecutingCommands_ReturnsExpectedResultWithoutPrecessing_WhenCacheKeyWasNotPresentInTheCache() + { + // Arrange + var expected = new object(); + var commandMock = new Mock(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + var actual = _processor.ProcessExecutingCommands(commandMock.Object, context, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ProcessExecutingCommands_NotifiesQueryResultSuppressedEvent_WhenThrowsInvalidOperationException() + { + // Arrange + var result = new InterceptionResult(); + var commandMock = new Mock(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + var cacheResult = new EFCachedData(); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _cacheServiceMock.Setup(x => x.GetValue(efCacheKey, cachePolicy)).Returns(cacheResult); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + _processor.ProcessExecutingCommands(commandMock.Object, context, result); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.QueryResultSuppressed, + "Suppressed the result with an empty TableRows.", + null), Times.Once); + } + + [Fact] + public void ProcessExecutingCommands_ReturnsSuppressedResult() + { + // Arrange + var result = new InterceptionResult(); + var commandMock = new Mock(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + var cacheResult = new EFCachedData(); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _cacheServiceMock.Setup(x => x.GetValue(efCacheKey, cachePolicy)).Returns(cacheResult); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + var actual = _processor.ProcessExecutingCommands(commandMock.Object, context, result); + + // Assert + Assert.IsType>(actual); + } + + [Fact] + public void ProcessExecutingCommands_NotifiesQueryResultSuppressedEvent_WhenCacheResultIsNutNull() + { + // Arrange + var result = new InterceptionResult(); + var commandMock = new Mock(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + var cacheResult = new EFCachedData + { + IsNull = false, + TableRows = new EFTableRows + { + TableName = string.Empty + } + }; + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _cacheServiceMock.Setup(x => x.GetValue(efCacheKey, cachePolicy)).Returns(cacheResult); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + _processor.ProcessExecutingCommands(commandMock.Object, context, result); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.QueryResultSuppressed, + "Suppressed the result with the TableRows[] from the cache[KeyHash: , DbContext: , CacheDependencies: .].", + null), Times.Once); + } + + [Fact] + public void ProcessExecutingCommands_NotifiesQueryResultSuppressedEvent_WhenInterceptionResultGenericIsIntType() + { + // Arrange + var result = new InterceptionResult(); + var commandMock = new Mock(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + var cacheResult = new EFCachedData + { + IsNull = false, + NonQuery = int.MaxValue + }; + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _cacheServiceMock.Setup(x => x.GetValue(efCacheKey, cachePolicy)).Returns(cacheResult); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + _processor.ProcessExecutingCommands(commandMock.Object, context, result); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.QueryResultSuppressed, + "Suppressed the result with 2147483647 from the cache[KeyHash: , DbContext: , CacheDependencies: .].", + null), Times.Once); + } + + [Fact] + public void ProcessExecutingCommands_ReturnsCachedResult_WhenInterceptionResultGenericIsIntType() + { + // Arrange + const int expected = int.MaxValue; + + var result = InterceptionResult.SuppressWithResult(expected); + var commandMock = new Mock(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + var cacheResult = new EFCachedData + { + IsNull = false, + NonQuery = expected + }; + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _cacheServiceMock.Setup(x => x.GetValue(efCacheKey, cachePolicy)).Returns(cacheResult); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + var actual = _processor.ProcessExecutingCommands(commandMock.Object, context, result); + + // Assert + Assert.Equal(expected, actual.Result); + } + + [Fact] + public void ProcessExecutingCommands_NotifiesQueryResultSuppressedEvent_WhenInterceptionResultGenericIsObjectType() + { + // Arrange + var expected = new object(); + var result = new InterceptionResult(); + var commandMock = new Mock(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + var cacheResult = new EFCachedData + { + IsNull = false, + Scalar = expected + }; + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _cacheServiceMock.Setup(x => x.GetValue(efCacheKey, cachePolicy)).Returns(cacheResult); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + _processor.ProcessExecutingCommands(commandMock.Object, context, result); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.QueryResultSuppressed, + "Suppressed the result with System.Object from the cache[KeyHash: , DbContext: , CacheDependencies: .].", + null), Times.Once); + } + + [Fact] + public void ProcessExecutingCommands_ReturnsCachedResult_WhenInterceptionResultGenericIsObjectType() + { + // Arrange + var expected = new object(); + var result = InterceptionResult.SuppressWithResult(expected); + var commandMock = new Mock(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + var cacheResult = new EFCachedData + { + IsNull = false, + Scalar = expected + }; + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _cacheServiceMock.Setup(x => x.GetValue(efCacheKey, cachePolicy)).Returns(cacheResult); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + var actual = _processor.ProcessExecutingCommands(commandMock.Object, context, result); + + // Assert + Assert.Equal(expected, actual.Result); + } + + [Fact] + public void ProcessExecutingCommands_NotifiesCachingSkippedEvent_WhenResultIsNull() + { + // Arrange + object result = null; + + var commandMock = new Mock(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + var cacheResult = new EFCachedData(); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _cacheServiceMock.Setup(x => x.GetValue(efCacheKey, cachePolicy)).Returns(cacheResult); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + _processor.ProcessExecutingCommands(commandMock.Object, context, result); + + // Assert + _loggerMock.Verify(x => x.NotifyCacheableEvent( + CacheableLogEventId.CachingSkipped, + "Skipped the result with type.", + null), Times.Once); + } + + [Fact] + public void ProcessExecutingCommands_ReturnsNull_WhenResultIsNull() + { + // Arrange + object result = null; + + var commandMock = new Mock(); + var context = Mock.Of(); + var cachePolicy = new EFCachePolicy(); + var efCacheKey = new EFCacheKey(new HashSet()); + var cacheResult = new EFCachedData(); + + _loggerMock.SetupGet(x => x.IsLoggerEnabled).Returns(true); + _cacheServiceMock.Setup(x => x.GetValue(efCacheKey, cachePolicy)).Returns(cacheResult); + _sqlCommandsProcessorMock.Setup(x => x.IsCrudCommand(string.Empty)).Returns(true); + _cacheServiceCheckMock.Setup(x => x.IsCacheServiceAvailable()).Returns(true); + _cacheKeyProviderMock.Setup(x => x.GetEFCacheKey(commandMock.Object, context, cachePolicy)).Returns(efCacheKey); + _cachePolicyParserMock.Setup(x => x.GetEFCachePolicy(string.Empty, null)).Returns(cachePolicy); + _cacheSettings.AllowCachingWithExplicitTransactions = true; + + // Act + var actual = _processor.ProcessExecutingCommands(commandMock.Object, context, result); + + // Assert + Assert.Null(actual); + } +} \ No newline at end of file diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowsDataReaderTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowsDataReaderTests.cs index 059d814..5e3a758 100644 --- a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowsDataReaderTests.cs +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowsDataReaderTests.cs @@ -1102,8 +1102,7 @@ public void GetInt64_ReturnsExpectedValue(object value, long expected) new List { new object[] { null, string.Empty }, - new object[] { string.Empty, string.Empty }, - new object[] { 123.45, "123,45" } + new object[] { string.Empty, string.Empty } }; [Theory] @@ -1125,6 +1124,24 @@ public void GetString_ReturnsExpectedValue(object value, string expected) Assert.Equal(expected, actual); } + [Fact] + public void GetString_ReturnsExpectedValueFromInvariantDecimal() + { + // Arrange + var values = new List { 123.45.ToString(CultureInfo.InvariantCulture) }; + var tableRow = new EFTableRow(values); + var tableRows = new EFTableRows { Rows = new List { tableRow } }; + var dataReader = new EFTableRowsDataReader(tableRows); + + dataReader.Read(); + + // Act + var actual = dataReader.GetString(0); + + // Assert + Assert.Equal("123.45", actual); + } + [Fact] public void GetValue_ReturnsExpectedValue_WhenOrdinalIsValid() { diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/SecondLevelCacheInterceptorTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/SecondLevelCacheInterceptorTests.cs new file mode 100644 index 0000000..8c97e93 --- /dev/null +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/SecondLevelCacheInterceptorTests.cs @@ -0,0 +1,1456 @@ +using System.Data.Common; +using System.Diagnostics.CodeAnalysis; +using AsyncKeyedLock; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Moq; + +namespace EFCoreSecondLevelCacheInterceptor.UnitTests; + +[SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] +public class SecondLevelCacheInterceptorTests +{ + [Fact] + public void Constructor_InitializesFields_WhenArgumentsAreValid() + { + var processorMock = new Mock(); + var lockProviderMock = new Mock(); + + var interceptor = new SecondLevelCacheInterceptor(processorMock.Object, lockProviderMock.Object); + + Assert.NotNull(interceptor); + } + + [Fact] + public void Constructor_ThrowsArgumentNullException_WhenProcessorIsNull() + { + var lockProviderMock = new Mock(); + + Assert.Throws(() => new SecondLevelCacheInterceptor(null, lockProviderMock.Object)); + } + + [Fact] + public void Constructor_ThrowsArgumentNullException_WhenLockProviderIsNull() + { + var processorMock = new Mock(); + + Assert.Throws(() => new SecondLevelCacheInterceptor(processorMock.Object, null)); + } + + [Fact] + public void NonQueryExecuted_ShouldLock() + { + // Arrange + const int expected = int.MaxValue; + const int result = int.MinValue; + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + interceptor.NonQueryExecuted(command, eventData, result); + + // Assert + lockProvider.Verify(lp => lp.Lock(CancellationToken.None), Times.Once); + } + + [Fact] + public void NonQueryExecuted_ReturnsProcessedResult() + { + // Arrange + const int expected = int.MaxValue; + const int result = int.MinValue; + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = interceptor.NonQueryExecuted(command, eventData, result); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void NonQueryExecuted_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + { + // Arrange + const int expected = int.MaxValue; + const int result = int.MinValue; + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = Record.Exception(() => interceptor.NonQueryExecuted(null, null, result)); + + // Assert + Assert.Null(actual); + } + + + [Fact] + public async Task NonQueryExecutedAsync_ShouldLock() + { + // Arrange + const int expected = int.MaxValue; + const int result = int.MinValue; + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.LockAsync(CancellationToken.None)) + .Returns(new ValueTask(new AsyncNonKeyedLockReleaser())); + + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + await interceptor.NonQueryExecutedAsync(command, eventData, result); + + // Assert + lockProvider.Verify(lp => lp.LockAsync(CancellationToken.None), Times.Once); + } + + [Fact] + public async Task NonQueryExecutedAsync_ReturnsProcessedResult() + { + // Arrange + const int expected = int.MaxValue; + const int result = int.MinValue; + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = await interceptor.NonQueryExecutedAsync(command, eventData, result); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public async Task NonQueryExecutedAsync_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + { + // Arrange + const int expected = 1; + const int result = 1; + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = await Record + .ExceptionAsync(async () => await interceptor.NonQueryExecutedAsync(null, null, result)); + + // Assert + Assert.Null(actual); + } + + [Fact] + public void NonQueryExecuting_ShouldLock() + { + // Arrange + var expected = InterceptionResult.SuppressWithResult(int.MaxValue); + var result = InterceptionResult.SuppressWithResult(int.MinValue); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + interceptor.NonQueryExecuting(command, eventData, result); + + // Assert + lockProvider.Verify(lp => lp.Lock(CancellationToken.None), Times.Once); + } + + [Fact] + public void NonQueryExecuting_ReturnsExpectedInterceptionResult() + { + // Arrange + var expected = InterceptionResult.SuppressWithResult(int.MaxValue); + var result = InterceptionResult.SuppressWithResult(int.MinValue); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = interceptor.NonQueryExecuting(command, eventData, result); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void NonQueryExecuting_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + { + // Arrange + var expected = InterceptionResult.SuppressWithResult(int.MaxValue); + var result = InterceptionResult.SuppressWithResult(int.MinValue); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = Record.Exception(() => interceptor.NonQueryExecuting(null, null, result)); + + // Assert + Assert.Null(actual); + } + + [Fact] + public async Task NonQueryExecutingAsync_ShouldLock() + { + // Arrange + var expected = InterceptionResult.SuppressWithResult(int.MaxValue); + var result = InterceptionResult.SuppressWithResult(int.MinValue); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.LockAsync(CancellationToken.None)) + .Returns(new ValueTask(new AsyncNonKeyedLockReleaser())); + + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + await interceptor.NonQueryExecutingAsync(command, eventData, result); + + // Assert + lockProvider.Verify(lp => lp.LockAsync(CancellationToken.None), Times.Once); + } + + [Fact] + public async Task NonQueryExecutingAsync_ReturnsExpectedInterceptionResult() + { + // Arrange + var expected = InterceptionResult.SuppressWithResult(int.MaxValue); + var result = InterceptionResult.SuppressWithResult(int.MinValue); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = await interceptor.NonQueryExecutingAsync(command, eventData, result); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public async Task NonQueryExecutingAsync_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + { + // Arrange + var expected = InterceptionResult.SuppressWithResult(int.MaxValue); + var result = InterceptionResult.SuppressWithResult(int.MinValue); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = await Record + .ExceptionAsync(async () => await interceptor.NonQueryExecutingAsync(null, null, result)); + + // Assert + Assert.Null(actual); + } + + [Fact] + public void ReaderExecuted_ShouldLock() + { + // Arrange + var expected = Mock.Of(); + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, expected)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + interceptor.ReaderExecuted(command, eventData, expected); + + // Assert + lockProvider.Verify(lp => lp.Lock(CancellationToken.None), Times.Once); + } + + [Fact] + public void ReaderExecuted_ReturnsExpectedDbDataReader() + { + // Arrange + var expected = Mock.Of(); + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, expected)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = interceptor.ReaderExecuted(command, eventData, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ReaderExecuted_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + { + // Arrange + var expected = Mock.Of(); + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, expected)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = Record.Exception(() => interceptor.ReaderExecuted(null, null, expected)); + + // Assert + Assert.Null(actual); + } + + [Fact] + public async Task ReaderExecutedAsync_ShouldLock() + { + // Arrange + var expected = Mock.Of(); + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.LockAsync(CancellationToken.None)) + .Returns(new ValueTask(new AsyncNonKeyedLockReleaser())); + + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, expected)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + await interceptor.ReaderExecutedAsync(command, eventData, expected); + + // Assert + lockProvider.Verify(lp => lp.LockAsync(CancellationToken.None), Times.Once); + } + + [Fact] + public async Task ReaderExecutedAsync_ReturnsExpectedDbDataReader() + { + // Arrange + var expected = Mock.Of(); + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, expected)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = await interceptor.ReaderExecutedAsync(command, eventData, expected); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public async Task ReaderExecutedAsync_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + { + // Arrange + var expected = Mock.Of(); + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, expected)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = await Record + .ExceptionAsync(async () => await interceptor.ReaderExecutedAsync(null, null, expected)); + + // Assert + Assert.Null(actual); + } + + [Fact] + public void ReaderExecuting_ShouldLock() + { + // Arrange + var dataReader = Mock.Of(); + var expected = InterceptionResult.SuppressWithResult(dataReader); + var result = InterceptionResult.SuppressWithResult(dataReader); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + interceptor.ReaderExecuting(command, eventData, result); + + // Assert + lockProvider.Verify(lp => lp.Lock(CancellationToken.None), Times.Once); + } + + [Fact] + public void ReaderExecuting_ReturnsExpectedInterceptionResult() + { + // Arrange + var dataReader = Mock.Of(); + var expected = InterceptionResult.SuppressWithResult(dataReader); + var result = InterceptionResult.SuppressWithResult(dataReader); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = interceptor.ReaderExecuting(command, eventData, result); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ReaderExecuting_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + { + // Arrange + var dataReader = Mock.Of(); + var expected = InterceptionResult.SuppressWithResult(dataReader); + var result = InterceptionResult.SuppressWithResult(dataReader); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = Record.Exception(() => interceptor.ReaderExecuting(null, null, result)); + + // Assert + Assert.Null(actual); + } + + [Fact] + public async Task ReaderExecutingAsync_ShouldLock() + { + // Arrange + var dataReader = Mock.Of(); + var expected = InterceptionResult.SuppressWithResult(dataReader); + var result = InterceptionResult.SuppressWithResult(dataReader); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.LockAsync(CancellationToken.None)) + .Returns(new ValueTask(new AsyncNonKeyedLockReleaser())); + + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + await interceptor.ReaderExecutingAsync(command, eventData, result); + + // Assert + lockProvider.Verify(lp => lp.LockAsync(CancellationToken.None), Times.Once); + } + + [Fact] + public async Task ReaderExecutingAsync_ReturnsExpectedInterceptionResult() + { + // Arrange + var dataReader = Mock.Of(); + var expected = InterceptionResult.SuppressWithResult(dataReader); + var result = InterceptionResult.SuppressWithResult(dataReader); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = await interceptor.ReaderExecutingAsync(command, eventData, result); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public async Task ReaderExecutingAsync_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + { + // Arrange + var dataReader = Mock.Of(); + var expected = InterceptionResult.SuppressWithResult(dataReader); + var result = InterceptionResult.SuppressWithResult(dataReader); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = await Record + .ExceptionAsync(async () => await interceptor.ReaderExecutingAsync(null, null, result)); + + // Assert + Assert.Null(actual); + } + + [Fact] + public void ScalarExecuted_ShouldLock() + { + // Arrange + var expected = new object(); + var result = new object(); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + interceptor.ScalarExecuted(command, eventData, result); + + // Assert + lockProvider.Verify(lp => lp.Lock(CancellationToken.None), Times.Once); + } + + [Fact] + public void ScalarExecuted_ReturnsProcessedResult() + { + // Arrange + var expected = new object(); + var result = new object(); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = interceptor.ScalarExecuted(command, eventData, result); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ScalarExecuted_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + { + // Arrange + var expected = new object(); + var result = new object(); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = Record.Exception(() => interceptor.ScalarExecuted(null, null, result)); + + // Assert + Assert.Null(actual); + } + + + [Fact] + public async Task ScalarExecutedAsync_ShouldLock() + { + // Arrange + var expected = new object(); + var result = new object(); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.LockAsync(CancellationToken.None)) + .Returns(new ValueTask(new AsyncNonKeyedLockReleaser())); + + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + await interceptor.ScalarExecutedAsync(command, eventData, result); + + // Assert + lockProvider.Verify(lp => lp.LockAsync(CancellationToken.None), Times.Once); + } + + [Fact] + public async Task ScalarExecutedAsync_ReturnsProcessedResult() + { + // Arrange + var expected = new object(); + var result = new object(); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = await interceptor.ScalarExecutedAsync(command, eventData, result); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public async Task ScalarExecutedAsync_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + { + // Arrange + var expected = new object(); + var result = new object(); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutedCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = await Record + .ExceptionAsync(async () => await interceptor.ScalarExecutedAsync(null, null, result)); + + // Assert + Assert.Null(actual); + } + + [Fact] + public void ScalarExecuting_ShouldLock() + { + // Arrange + var expected = InterceptionResult.SuppressWithResult(new object()); + var result = InterceptionResult.SuppressWithResult(new object()); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + interceptor.ScalarExecuting(command, eventData, result); + + // Assert + lockProvider.Verify(lp => lp.Lock(CancellationToken.None), Times.Once); + } + + [Fact] + public void ScalarExecuting_ReturnsExpectedInterceptionResult() + { + // Arrange + var expected = InterceptionResult.SuppressWithResult(new object()); + var result = InterceptionResult.SuppressWithResult(new object()); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = interceptor.ScalarExecuting(command, eventData, result); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ScalarExecuting_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + { + // Arrange + var expected = InterceptionResult.SuppressWithResult(new object()); + var result = InterceptionResult.SuppressWithResult(new object()); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = Record.Exception(() => interceptor.ScalarExecuting(null, null, result)); + + // Assert + Assert.Null(actual); + } + + [Fact] + public async Task ScalarExecutingAsync_ShouldLock() + { + // Arrange + var expected = InterceptionResult.SuppressWithResult(new object()); + var result = InterceptionResult.SuppressWithResult(new object()); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.LockAsync(CancellationToken.None)) + .Returns(new ValueTask(new AsyncNonKeyedLockReleaser())); + + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + await interceptor.ScalarExecutingAsync(command, eventData, result); + + // Assert + lockProvider.Verify(lp => lp.LockAsync(CancellationToken.None), Times.Once); + } + + [Fact] + public async Task ScalarExecutingAsync_ReturnsExpectedInterceptionResult() + { + // Arrange + var expected = InterceptionResult.SuppressWithResult(new object()); + var result = InterceptionResult.SuppressWithResult(new object()); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = await interceptor.ScalarExecutingAsync(command, eventData, result); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public async Task ScalarExecutingAsync_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + { + // Arrange + var expected = InterceptionResult.SuppressWithResult(new object()); + var result = InterceptionResult.SuppressWithResult(new object()); + + var command = Mock.Of(); + var eventData = new CommandExecutedEventData( + null, + null, + null, + command, + null, + DbCommandMethod.ExecuteNonQuery, + Guid.Empty, + Guid.Empty, + null, + false, + false, + DateTimeOffset.Now, + TimeSpan.Zero, + CommandSource.LinqQuery); + + var lockProvider = new Mock(); + var processor = new Mock(); + + lockProvider.Setup(lp => lp.Lock(CancellationToken.None)).Returns(new AsyncNonKeyedLockReleaser()); + processor.Setup(p => p.ProcessExecutingCommands(command, eventData.Context, result)).Returns(expected); + + var interceptor = new SecondLevelCacheInterceptor(processor.Object, lockProvider.Object); + + // Act + var actual = await Record + .ExceptionAsync(async () => await interceptor.ScalarExecutingAsync(null, null, result)); + + // Assert + Assert.Null(actual); + } +} \ No newline at end of file