From ea968f59b1acac4ffe50e3818d4a80b9af8070ca Mon Sep 17 00:00:00 2001 From: Dmitrii Kiselev Date: Mon, 30 Sep 2024 21:25:29 +0500 Subject: [PATCH] feat(unit-tests): increase code coverage This commit adds: - Improvements to existing unit test code - Improvements to existing unit test naming - Unit tests for `EFCacheKeyPrefixProvider` class - Unit tests for `EFCacheKeyProvider` class - Unit tests for `EFCacheKey` class - Unit tests for `EFCacheServiceCheck` class - Unit tests for `EFDataReaderLoader` class - Unit tests for `EFDebugLogger` class - Unit tests for `EFServiceCollectionExtensions` class - Unit tests for `LockProvider` class - Unit tests for `StringExtensions` class - Unit tests for `TableEntityInfo` class - Unit tests for `XxHash64Unsafe` class --- .../StringExtensions.cs | 3 + .../DbCommandInterceptorProcessorTests.cs | 31 +- .../EFCacheKeyPrefixProviderTests.cs | 60 + .../EFCacheKeyProviderTests.cs | 294 ++++ .../EFCacheKeyTests.cs | 161 ++ .../EFCacheServiceCheckTests.cs | 157 ++ .../EFDataReaderLoaderTests.cs | 1413 +++++++++++++++++ .../EFDebugLoggerTests.cs | 172 ++ .../EFServiceCollectionExtensionsTests.cs | 138 ++ .../EFTableColumnInfoTests.cs | 9 +- .../EFTableRowTests.cs | 1 + .../EFTableRowsDataReaderTests.cs | 15 +- .../EFTableRowsTests.cs | 30 +- .../LockProviderTests.cs | 73 + .../SecondLevelCacheInterceptorTests.cs | 24 +- .../StringExtensionsTests.cs | 164 ++ .../TableEntityInfoTests.cs | 72 + .../TypeExtensionsTests.cs | 12 +- .../XxHash64UnsafeTests.cs | 125 ++ 19 files changed, 2900 insertions(+), 54 deletions(-) create mode 100644 src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheKeyPrefixProviderTests.cs create mode 100644 src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheKeyProviderTests.cs create mode 100644 src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheKeyTests.cs create mode 100644 src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheServiceCheckTests.cs create mode 100644 src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFDataReaderLoaderTests.cs create mode 100644 src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFDebugLoggerTests.cs create mode 100644 src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFServiceCollectionExtensionsTests.cs create mode 100644 src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/LockProviderTests.cs create mode 100644 src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/StringExtensionsTests.cs create mode 100644 src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/TableEntityInfoTests.cs create mode 100644 src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/XxHash64UnsafeTests.cs diff --git a/src/EFCoreSecondLevelCacheInterceptor/StringExtensions.cs b/src/EFCoreSecondLevelCacheInterceptor/StringExtensions.cs index 6f8c2ee..1eac436 100644 --- a/src/EFCoreSecondLevelCacheInterceptor/StringExtensions.cs +++ b/src/EFCoreSecondLevelCacheInterceptor/StringExtensions.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("EFCoreSecondLevelCacheInterceptor.UnitTests")] namespace EFCoreSecondLevelCacheInterceptor; diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/DbCommandInterceptorProcessorTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/DbCommandInterceptorProcessorTests.cs index c3fa159..b8e4059 100644 --- a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/DbCommandInterceptorProcessorTests.cs +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/DbCommandInterceptorProcessorTests.cs @@ -12,40 +12,39 @@ 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() { + var interceptorProcessorLoggerMock = new Mock>(); + var cacheSettingsMock = new Mock>(); + _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); + + cacheSettingsMock.SetupGet(x => x.Value).Returns(_cacheSettings); _processor = new DbCommandInterceptorProcessor( _loggerMock.Object, - _interceptorProcessorLoggerMock.Object, + interceptorProcessorLoggerMock.Object, _cacheServiceMock.Object, _cacheDependenciesProcessorMock.Object, _cacheKeyProviderMock.Object, _cachePolicyParserMock.Object, _sqlCommandsProcessorMock.Object, - _cacheSettingsMock.Object, + cacheSettingsMock.Object, _cacheServiceCheckMock.Object); } @@ -71,6 +70,7 @@ public void Constructor_ThrowsArgumentNullException_WhenCacheSettingsIsNull() cacheKeyProvider, cachePolicyParser, sqlCommandsProcessor, + // ReSharper disable once AssignNullToNotNullAttribute null, cacheServiceCheck)); } @@ -112,6 +112,8 @@ public void ProcessExecutedCommands_ReturnsExpectedResultWithoutPrecessing_WhenD DbContext context = null; // Act + // ReSharper disable once ExpressionIsAlwaysNull + // ReSharper disable once AssignNullToNotNullAttribute var actual = _processor.ProcessExecutedCommands(null, context, null); // Assert @@ -125,6 +127,7 @@ public void ProcessExecutedCommands_ReturnsExpectedResultWithoutPrecessing_WhenC var context = Mock.Of(); // Act + // ReSharper disable once AssignNullToNotNullAttribute var actual = _processor.ProcessExecutedCommands(null, context, null); // Assert @@ -222,7 +225,7 @@ public void _cacheSettings.SkipCachingDbContexts = new List { context.GetType() }; // Act - var actual = _processor.ProcessExecutedCommands(command, context, result); + _processor.ProcessExecutedCommands(command, context, result); // Assert _loggerMock.Verify(x => x.NotifyCacheableEvent( @@ -861,7 +864,7 @@ public void ProcessExecutedCommands_ReturnsExpectedResult_WhenObjectDataAddedToC public void ProcessExecutedCommands_ReturnsNull_WhenResultIsNull() { // Arrange - object expected = null; + object result = null; var commandMock = new Mock(); var transaction = Mock.Of(); @@ -879,7 +882,8 @@ public void ProcessExecutedCommands_ReturnsNull_WhenResultIsNull() _cacheSettings.AllowCachingWithExplicitTransactions = true; // Act - var actual = _processor.ProcessExecutedCommands(commandMock.Object, context, expected); + // ReSharper disable once ExpressionIsAlwaysNull + var actual = _processor.ProcessExecutedCommands(commandMock.Object, context, result); // Assert Assert.Null(actual); @@ -892,6 +896,8 @@ public void ProcessExecutingCommands_ReturnsExpectedResultWithoutPrecessing_When DbContext context = null; // Act + // ReSharper disable once AssignNullToNotNullAttribute + // ReSharper disable once ExpressionIsAlwaysNull var actual = _processor.ProcessExecutingCommands(null, context, null); // Assert @@ -905,6 +911,7 @@ public void ProcessExecutingCommands_ReturnsExpectedResultWithoutPrecessing_When var context = Mock.Of(); // Act + // ReSharper disable once AssignNullToNotNullAttribute var actual = _processor.ProcessExecutingCommands(null, context, null); // Assert @@ -1367,6 +1374,7 @@ public void ProcessExecutingCommands_NotifiesCachingSkippedEvent_WhenResultIsNul _cacheSettings.AllowCachingWithExplicitTransactions = true; // Act + // ReSharper disable once ExpressionIsAlwaysNull _processor.ProcessExecutingCommands(commandMock.Object, context, result); // Assert @@ -1397,6 +1405,7 @@ public void ProcessExecutingCommands_ReturnsNull_WhenResultIsNull() _cacheSettings.AllowCachingWithExplicitTransactions = true; // Act + // ReSharper disable once ExpressionIsAlwaysNull var actual = _processor.ProcessExecutingCommands(commandMock.Object, context, result); // Assert diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheKeyPrefixProviderTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheKeyPrefixProviderTests.cs new file mode 100644 index 0000000..3084d1f --- /dev/null +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheKeyPrefixProviderTests.cs @@ -0,0 +1,60 @@ +using Microsoft.Extensions.Options; +using Moq; + +namespace EFCoreSecondLevelCacheInterceptor.UnitTests; + +// ReSharper disable once InconsistentNaming +public class EFCacheKeyPrefixProviderTests +{ + [Fact] + public void Constructor_ThrowsArgumentNullException_WhenCacheSettingsIsNull() + { + // Arrange + var serviceProvider = new Mock().Object; + + // ReSharper disable once ObjectCreationAsStatement + void Act() => new EFCacheKeyPrefixProvider(serviceProvider, null!); + + // Act && Assert + Assert.Throws("cacheSettings", Act); + } + + [Fact] + public void GetCacheKeyPrefix_ReturnsCacheKeyPrefixSelectorResult_WhenSelectorIsNotNull() + { + // Arrange + var serviceProvider = new Mock().Object; + var cacheSettings = Options.Create(new EFCoreSecondLevelCacheSettings + { + CacheKeyPrefixSelector = _ => "CustomPrefix" + }); + + var provider = new EFCacheKeyPrefixProvider(serviceProvider, cacheSettings); + + // Act + var actual = provider.GetCacheKeyPrefix(); + + // Assert + Assert.Equal("CustomPrefix", actual); + } + + [Fact] + public void GetCacheKeyPrefix_ReturnsCacheKeyPrefix_WhenSelectorIsNull() + { + // Arrange + var serviceProvider = new Mock().Object; + var cacheSettings = Options.Create(new EFCoreSecondLevelCacheSettings + { + CacheKeyPrefix = "DefaultPrefix", + CacheKeyPrefixSelector = null + }); + + var provider = new EFCacheKeyPrefixProvider(serviceProvider, cacheSettings); + + // Act + var actual = provider.GetCacheKeyPrefix(); + + // Assert + Assert.Equal("DefaultPrefix", actual); + } +} \ No newline at end of file diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheKeyProviderTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheKeyProviderTests.cs new file mode 100644 index 0000000..fe30b43 --- /dev/null +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheKeyProviderTests.cs @@ -0,0 +1,294 @@ +using System.Data; +using System.Data.Common; +using System.Diagnostics.CodeAnalysis; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Moq; +using Moq.Protected; + +namespace EFCoreSecondLevelCacheInterceptor.UnitTests; + +// ReSharper disable once InconsistentNaming +public class EFCacheKeyProviderTests +{ + private readonly Mock _cacheDependenciesProcessorMock; + private readonly Mock _cacheKeyPrefixProviderMock; + private readonly Mock _hashProviderMock; + private readonly Mock _loggerMock; + private readonly IEFCacheKeyProvider _cacheKeyProvider; + + public EFCacheKeyProviderTests() + { + var cacheSettingsMock = new Mock>(); + var settings = new EFCoreSecondLevelCacheSettings(); + var cachePolicyParserMock = new Mock(); + var keyProviderLoggerMock = new Mock>(); + + _cacheDependenciesProcessorMock = new Mock(); + _cacheKeyPrefixProviderMock = new Mock(); + _hashProviderMock = new Mock(); + _loggerMock = new Mock(); + + cacheSettingsMock.Setup(c => c.Value).Returns(settings); + + _cacheKeyProvider = new EFCacheKeyProvider( + _cacheDependenciesProcessorMock.Object, + cachePolicyParserMock.Object, + _loggerMock.Object, + keyProviderLoggerMock.Object, + _hashProviderMock.Object, + _cacheKeyPrefixProviderMock.Object, + cacheSettingsMock.Object); + } + + [Fact] + public void EFCacheKeyProvider_ThrowsArgumentNullException_WhenHashProviderIsNull() + { + // Arrange + var cacheDependenciesProcessorMock = new Mock(); + var cachePolicyParserMock = new Mock(); + var loggerMock = new Mock(); + var keyProviderLoggerMock = new Mock>(); + var cacheKeyPrefixProviderMock = new Mock(); +#if NET5_0 || NET6_0 || NET7_0 || NET8_0 + var cacheSettingsMock = new Mock>(); +#endif + + // Act && Assert + Assert.Throws(() => new EFCacheKeyProvider( + cacheDependenciesProcessorMock.Object, + cachePolicyParserMock.Object, + loggerMock.Object, + keyProviderLoggerMock.Object, + // ReSharper disable once AssignNullToNotNullAttribute + null, + cacheKeyPrefixProviderMock.Object +#if NET5_0 || NET6_0 || NET7_0 || NET8_0 + , + cacheSettingsMock.Object +#endif + )); + } + + [Fact] + public void EFCacheKeyProvider_ThrowsArgumentNullException_WhenCacheSettingsIsNull() + { + // Arrange +#if NET5_0 || NET6_0 || NET7_0 || NET8_0 + var cacheDependenciesProcessorMock = new Mock(); + var cachePolicyParserMock = new Mock(); + var loggerMock = new Mock(); + var keyProviderLoggerMock = new Mock>(); + var hashProviderMock = new Mock(); + var cacheKeyPrefixProviderMock = new Mock(); + + // Act && Assert + Assert.Throws(() => new EFCacheKeyProvider( + cacheDependenciesProcessorMock.Object, + cachePolicyParserMock.Object, + loggerMock.Object, + keyProviderLoggerMock.Object, + hashProviderMock.Object, + cacheKeyPrefixProviderMock.Object, + // ReSharper disable once AssignNullToNotNullAttribute + null + )); +#endif + } + + [Fact] + public void EFCacheKeyProvider_CreatesInstanceSuccessfully() + { + // Arrange + var cacheDependenciesProcessorMock = new Mock(); + var cachePolicyParserMock = new Mock(); + var loggerMock = new Mock(); + var keyProviderLoggerMock = new Mock>(); + var hashProviderMock = new Mock(); + var cacheKeyPrefixProviderMock = new Mock(); +#if NET5_0 || NET6_0 || NET7_0 || NET8_0 + var cacheSettingsMock = new Mock>(); + cacheSettingsMock.Setup(c => c.Value).Returns(new EFCoreSecondLevelCacheSettings()); +#endif + + // Act + var provider = new EFCacheKeyProvider( + cacheDependenciesProcessorMock.Object, + cachePolicyParserMock.Object, + loggerMock.Object, + keyProviderLoggerMock.Object, + hashProviderMock.Object, + cacheKeyPrefixProviderMock.Object +#if NET5_0 || NET6_0 || NET7_0 || NET8_0 + , + cacheSettingsMock.Object +#endif + ); + + // Assert + Assert.NotNull(provider); + } + + [Fact] + [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] + public void GetEFCacheKey_ThrowsArgumentNullException_WhenContextIsNull() + { + // Arrange + DbCommand command = null; + DbContext context = null; + EFCachePolicy cachePolicy = null; + + // Act + void Act() => _cacheKeyProvider.GetEFCacheKey(command, context, cachePolicy); + + // Assert + Assert.Throws("context", Act); + } + + [Fact] + [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] + public void GetEFCacheKey_ThrowsArgumentNullException_WhenCommandIsNull() + { + // Arrange + DbCommand command = null; + DbContext context = Mock.Of(); + EFCachePolicy cachePolicy = null; + + // Act + void Act() => _cacheKeyProvider.GetEFCacheKey(command, context, cachePolicy); + + // Assert + Assert.Throws("command", Act); + } + + [Fact] + public void GetEFCacheKey_ThrowsArgumentNullException_WhenCachePolicyIsNull() + { + // Arrange + DbCommand command = Mock.Of(); + DbContext context = Mock.Of(); + EFCachePolicy cachePolicy = null; + + // Act + // ReSharper disable once AssignNullToNotNullAttribute + void Act() => _cacheKeyProvider.GetEFCacheKey(command, context, cachePolicy); + + // Assert + Assert.Throws("cachePolicy", Act); + } + + [Fact] + public void GetEFCacheKey_ReturnsExpectedCacheKey() + { + // Arrange + var context = Mock.Of(); + var expected = new EFCacheKey(new SortedSet()) + { + KeyHash = "75BCD15", + DbContext = context.GetType() + }; + + var commandMock = new Mock(); + var cachePolicy = new EFCachePolicy().SaltKey("CacheSaltKey"); + var dbParameterCollectionMock = new Mock(); + var dbParameterMock = new Mock(); + var parameters = new List + { + dbParameterMock.Object, + }; + + _hashProviderMock + .Setup(x => x.ComputeHash(@"ConnectionString +=Name=""Value"",Size=2147483647,Precision=255,Scale=255,Direction=Input,SaltKey +=CacheSaltKey")) + .Returns(123456789); + + _loggerMock.Setup(x => x.IsLoggerEnabled).Returns(true); + + commandMock + .Protected() + .SetupGet("DbParameterCollection") + .Returns(dbParameterCollectionMock.Object); + + dbParameterCollectionMock + .Setup(x => x.GetEnumerator()) + .Returns(parameters.GetEnumerator()); + + _cacheDependenciesProcessorMock + .Setup(x => x.GetCacheDependencies(commandMock.Object, context, cachePolicy)) + .Returns(expected.CacheDependencies as SortedSet); + + dbParameterMock.Setup(x => x.ParameterName).Returns("Name"); + dbParameterMock.Setup(x => x.Value).Returns("Value"); + dbParameterMock.Setup(x => x.Size).Returns(int.MaxValue); + dbParameterMock.Setup(x => x.Precision).Returns(byte.MaxValue); + dbParameterMock.Setup(x => x.Scale).Returns(byte.MaxValue); + dbParameterMock.Setup(x => x.Direction).Returns(ParameterDirection.Input); + + // Act + var actual = _cacheKeyProvider.GetEFCacheKey(commandMock.Object, context, cachePolicy); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void GetEFCacheKey_ReturnsExpectedCacheKeyWithPrefix() + { + // Arrange + var context = Mock.Of(); + var expected = new EFCacheKey(new SortedSet()) + { + KeyHash = "CacheKeyPrefix75BCD15", + DbContext = context.GetType() + }; + + var commandMock = new Mock(); + var cachePolicy = new EFCachePolicy().SaltKey("CacheSaltKey"); + var dbParameterCollectionMock = new Mock(); + var dbParameterMock = new Mock(); + var parameters = new List + { + dbParameterMock.Object, + }; + + _cacheKeyPrefixProviderMock + .Setup(x => x.GetCacheKeyPrefix()) + .Returns("CacheKeyPrefix"); + + _hashProviderMock + .Setup(x => x.ComputeHash(@"ConnectionString +=Name=""Value"",Size=2147483647,Precision=255,Scale=255,Direction=Input,SaltKey +=CacheSaltKey")) + .Returns(123456789); + + _loggerMock.Setup(x => x.IsLoggerEnabled).Returns(true); + + commandMock + .Protected() + .SetupGet("DbParameterCollection") + .Returns(dbParameterCollectionMock.Object); + + dbParameterCollectionMock + .Setup(x => x.GetEnumerator()) + .Returns(parameters.GetEnumerator()); + + _cacheDependenciesProcessorMock + .Setup(x => x.GetCacheDependencies(commandMock.Object, context, cachePolicy)) + .Returns(expected.CacheDependencies as SortedSet); + + dbParameterMock.Setup(x => x.ParameterName).Returns("Name"); + dbParameterMock.Setup(x => x.Value).Returns("Value"); + dbParameterMock.Setup(x => x.Size).Returns(int.MaxValue); + dbParameterMock.Setup(x => x.Precision).Returns(byte.MaxValue); + dbParameterMock.Setup(x => x.Scale).Returns(byte.MaxValue); + dbParameterMock.Setup(x => x.Direction).Returns(ParameterDirection.Input); + + // Act + var actual = _cacheKeyProvider.GetEFCacheKey(commandMock.Object, context, cachePolicy); + + // Assert + Assert.Equal(expected, actual); + } +} \ No newline at end of file diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheKeyTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheKeyTests.cs new file mode 100644 index 0000000..1368759 --- /dev/null +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheKeyTests.cs @@ -0,0 +1,161 @@ +using Microsoft.EntityFrameworkCore; +using Moq; + +namespace EFCoreSecondLevelCacheInterceptor.UnitTests; + +// ReSharper disable once InconsistentNaming +public class EFCacheKeyTests +{ + [Fact] + public void Equals_ReturnsTrue_WhenKeyHashAndDbContextAreEqual() + { + // Arrange + var cacheKey1 = new EFCacheKey(new HashSet { "Entity1" }) + { + KeyHash = "hash1", + DbContext = typeof(DbContext) + }; + + var cacheKey2 = new EFCacheKey(new HashSet { "Entity2" }) + { + KeyHash = "hash1", + DbContext = typeof(DbContext) + }; + + // Act + var actual = cacheKey1.Equals(cacheKey2); + + // Assert + Assert.True(actual); + } + + [Fact] + public void Equals_ReturnsFalse_WhenObjIsNotEFCacheKey() + { + // Arrange + var cacheKey1 = new EFCacheKey(new HashSet { "Entity1" }) + { + KeyHash = "hash1", + DbContext = typeof(DbContext) + }; + + var obj = new object(); + + // Act + var actual = cacheKey1.Equals(obj); + + // Assert + Assert.False(actual); + } + + [Fact] + public void Equals_ReturnsFalse_WhenKeyHashIsDifferent() + { + // Arrange + var cacheKey1 = new EFCacheKey(new HashSet { "Entity1" }) + { + KeyHash = "hash1", + DbContext = typeof(DbContext) + }; + + var cacheKey2 = new EFCacheKey(new HashSet { "Entity2" }) + { + KeyHash = "hash2", + DbContext = typeof(DbContext) + }; + + // Act + var actual = cacheKey1.Equals(cacheKey2); + + // Assert + Assert.False(actual); + } + + [Fact] + public void Equals_ReturnsFalse_WhenDbContextIsDifferent() + { + // Arrange + var cacheKey1 = new EFCacheKey(new HashSet { "Entity1" }) + { + KeyHash = "hash1", + DbContext = typeof(DbContext) + }; + + var cacheKey2 = new EFCacheKey(new HashSet { "Entity2" }) + { + KeyHash = "hash1", + DbContext = Mock.Of().GetType() + }; + + // Act + var actual = cacheKey1.Equals(cacheKey2); + + // Assert + Assert.False(actual); + } + + [Fact] + public void GetHashCode_ReturnsSameHashCode_ForEqualObjects() + { + // Arrange + var cacheKey1 = new EFCacheKey(new HashSet { "Entity1" }) + { + KeyHash = "hash1", + DbContext = typeof(DbContext) + }; + + var cacheKey2 = new EFCacheKey(new HashSet { "Entity2" }) + { + KeyHash = "hash1", + DbContext = typeof(DbContext) + }; + + // Act + var hashCode1 = cacheKey1.GetHashCode(); + var hashCode2 = cacheKey2.GetHashCode(); + + // Assert + Assert.Equal(hashCode1, hashCode2); + } + + [Fact] + public void GetHashCode_ReturnsDifferentHashCode_ForDifferentObjects() + { + // Arrange + var cacheKey1 = new EFCacheKey(new HashSet { "Entity1" }) + { + KeyHash = "hash1", + DbContext = typeof(DbContext) + }; + + var cacheKey2 = new EFCacheKey(new HashSet { "Entity2" }) + { + KeyHash = "hash2", + DbContext = typeof(DbContext) + }; + + // Act + var hashCode1 = cacheKey1.GetHashCode(); + var hashCode2 = cacheKey2.GetHashCode(); + + // Assert + Assert.NotEqual(hashCode1, hashCode2); + } + + [Fact] + public void ToString_ReturnsExpectedFormat() + { + // Arrange + var cacheKey = new EFCacheKey(new HashSet { "Entity1", "Entity2" }) + { + KeyHash = "hash1", + DbContext = typeof(DbContext) + }; + + // Act + var actual = cacheKey.ToString(); + + // Assert + Assert.Equal("KeyHash: hash1, DbContext: DbContext, CacheDependencies: Entity1, Entity2.", actual); + } +} \ No newline at end of file diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheServiceCheckTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheServiceCheckTests.cs new file mode 100644 index 0000000..bf2748a --- /dev/null +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFCacheServiceCheckTests.cs @@ -0,0 +1,157 @@ +using Microsoft.Extensions.Options; +using Moq; + +namespace EFCoreSecondLevelCacheInterceptor.UnitTests; + +// ReSharper disable once InconsistentNaming +public class EFCacheServiceCheckTests +{ + private readonly Mock _cacheServiceProviderMock; + private readonly EFCoreSecondLevelCacheSettings _cacheSettings; + private readonly IEFCacheServiceCheck _serviceCheck; + + public EFCacheServiceCheckTests() + { + var cacheOptionsMock = new Mock>(); + + _cacheServiceProviderMock = new Mock(); + _cacheSettings = new EFCoreSecondLevelCacheSettings(); + + cacheOptionsMock.Setup(c => c.Value).Returns(_cacheSettings); + + _serviceCheck = new EFCacheServiceCheck(cacheOptionsMock.Object, _cacheServiceProviderMock.Object); + } + + [Fact] + public void EFCacheServiceCheck_ThrowsArgumentNullException_WhenCacheSettingsIsNull() + { + // Arrange + var cacheServiceProviderMock = new Mock(); + + // Act && Assert + // ReSharper disable once AssignNullToNotNullAttribute + Assert.Throws(() => new EFCacheServiceCheck(null, cacheServiceProviderMock.Object)); + } + + [Fact] + public void EFCacheServiceCheck_CreatesInstanceSuccessfully() + { + // Arrange + var cacheSettingsMock = new Mock>(); + + cacheSettingsMock.Setup(c => c.Value).Returns(new EFCoreSecondLevelCacheSettings()); + + var cacheServiceProviderMock = new Mock(); + + // Act + var serviceCheck = new EFCacheServiceCheck(cacheSettingsMock.Object, cacheServiceProviderMock.Object); + + // Assert + Assert.NotNull(serviceCheck); + } + + [Fact] + public void EFCacheServiceCheck_ShouldNotThrowArgumentNullException_WhenCacheServiceProviderIsNull() + { + // Arrange + var cacheSettingsMock = new Mock>(); + + cacheSettingsMock.Setup(c => c.Value).Returns(new EFCoreSecondLevelCacheSettings()); + + // ReSharper disable once AssignNullToNotNullAttribute + // ReSharper disable once ObjectCreationAsStatement + void Act() => new EFCacheServiceCheck(cacheSettingsMock.Object, null); + + // Act + var actual = Record.Exception(Act); + + // Assert + Assert.Null(actual); + } + + [Fact] + public void IsCacheServiceAvailable_ReturnsFalse_WhenIsCachingInterceptorEnabledIsFalse() + { + // Arrange + _cacheSettings.IsCachingInterceptorEnabled = false; + + // Act + var result = _serviceCheck.IsCacheServiceAvailable(); + + // Assert + Assert.False(result); + } + + [Fact] + public void IsCacheServiceAvailable_ReturnsTrue_WhenUseDbCallsIfCachingProviderIsDownIsFalse() + { + // Arrange + _cacheSettings.IsCachingInterceptorEnabled = true; + _cacheSettings.UseDbCallsIfCachingProviderIsDown = false; + + // Act + var result = _serviceCheck.IsCacheServiceAvailable(); + + // Assert + Assert.True(result); + } + + [Fact] + public void + IsCacheServiceAvailable_ReturnsTrue_WhenUseDbCallsIfCachingProviderIsDownIsTrue_And_CacheServerIsAvailable() + { + // Arrange + _cacheSettings.IsCachingInterceptorEnabled = true; + _cacheSettings.UseDbCallsIfCachingProviderIsDown = true; + + _cacheServiceProviderMock + .Setup(c => c.GetValue(It.IsAny(), It.IsAny())) + .Returns(new EFCachedData()); + + // Act + var result = _serviceCheck.IsCacheServiceAvailable(); + + // Assert + Assert.True(result); + } + + [Fact] + public void + IsCacheServiceAvailable_ThrowsInvalidOperationException_WhenUseDbCallsIfCachingProviderIsDownIsTrue_And_CacheServerIsNotAvailable() + { + // Arrange + _cacheSettings.IsCachingInterceptorEnabled = true; + _cacheSettings.UseDbCallsIfCachingProviderIsDown = true; + + _cacheServiceProviderMock + .Setup(c => c.GetValue(It.IsAny(), It.IsAny())) + .Throws(); + + // Act + void Act() => _serviceCheck.IsCacheServiceAvailable(); + + // Assert + Assert.Throws(Act); + } + + [Fact] + public void IsCacheServiceAvailable_ReturnsTrue_WhenNotEnoughTimeHasPassedSinceTheLastCheck() + { + // Arrange + _cacheSettings.IsCachingInterceptorEnabled = true; + _cacheSettings.UseDbCallsIfCachingProviderIsDown = true; + _cacheSettings.NextCacheServerAvailabilityCheck = TimeSpan.MaxValue; + + _cacheServiceProviderMock + .Setup(c => c.GetValue(It.IsAny(), It.IsAny())) + .Returns(new EFCachedData()); + + _serviceCheck.IsCacheServiceAvailable(); + + // Act + var result = _serviceCheck.IsCacheServiceAvailable(); + + // Assert + Assert.True(result); + } +} \ No newline at end of file diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFDataReaderLoaderTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFDataReaderLoaderTests.cs new file mode 100644 index 0000000..7e4ecac --- /dev/null +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFDataReaderLoaderTests.cs @@ -0,0 +1,1413 @@ +#nullable enable +using System.Data; +using System.Data.Common; +using System.Reflection; +using Moq; + +namespace EFCoreSecondLevelCacheInterceptor.UnitTests; + +// ReSharper disable once InconsistentNaming +public class EFDataReaderLoaderTests +{ + [Fact] + public void HasRows_ReturnsTrue_WhenDbReaderHasRows() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.HasRows).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.HasRows; + + // Assert + Assert.True(actual); + } + + [Fact] + public void HasRows_ReturnsFalse_WhenDbReaderHasNoRows() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.HasRows).Returns(false); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.HasRows; + + // Assert + Assert.False(actual); + } + + [Fact] + public void RecordsAffected_ReturnsExpectedValue() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.RecordsAffected).Returns(5); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.RecordsAffected; + + // Assert + Assert.Equal(5, actual); + } + + [Fact] + public void RecordsAffected_ReturnsZero_WhenNoRecordsAffected() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.RecordsAffected).Returns(0); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.RecordsAffected; + + // Assert + Assert.Equal(0, actual); + } + + [Fact] + public void IsClosed_ReturnsTrue_WhenDbReaderIsClosed() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.IsClosed).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.IsClosed; + + // Assert + Assert.True(actual); + } + + [Fact] + public void IsClosed_ReturnsFalse_WhenDbReaderIsNotClosed() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.IsClosed).Returns(false); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.IsClosed; + + // Assert + Assert.False(actual); + } + + [Fact] + public void Depth_ReturnsExpectedValue() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.Depth).Returns(3); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.Depth; + + // Assert + Assert.Equal(3, actual); + } + + [Fact] + public void Depth_ReturnsZero_WhenDepthIsZero() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.Depth).Returns(0); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.Depth; + + // Assert + Assert.Equal(0, actual); + } + + [Fact] + public void FieldCount_ReturnsExpectedValue() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(10); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.FieldCount; + + // Assert + Assert.Equal(10, actual); + } + + [Fact] + public void FieldCount_ReturnsZero_WhenNoFields() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(0); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.FieldCount; + + // Assert + Assert.Equal(0, actual); + } + + [Fact] + public void VisibleFieldCount_ReturnsExpectedValue() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.VisibleFieldCount).Returns(7); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.VisibleFieldCount; + + // Assert + Assert.Equal(7, actual); + } + + [Fact] + public void VisibleFieldCount_ReturnsZero_WhenNoVisibleFields() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.VisibleFieldCount).Returns(0); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.VisibleFieldCount; + + // Assert + Assert.Equal(0, actual); + } + + [Fact] + public void Indexer_ReturnsExpectedValue_ByName() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetOrdinal("ColumnName")).Returns(0); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("Value"); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader["ColumnName"]; + + // Assert + Assert.Equal("Value", actual); + } + + [Fact] + public void Indexer_ThrowsIndexOutOfRangeException_WhenColumnNameIsInvalid() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetOrdinal("InvalidColumn")).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader["InvalidColumn"]); + } + + [Fact] + public void Indexer_ReturnsExpectedValue_ByOrdinal() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("Value"); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader[0]; + + // Assert + Assert.Equal("Value", actual); + } + + [Fact] + public void Indexer_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetValue(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader[100]); + } + + [Fact] + public void GetDataTypeName_ReturnsExpectedTypeName() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetDataTypeName(1)).Returns("Int32"); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.GetDataTypeName(1); + + // Assert + Assert.Equal("Int32", actual); + } + + [Fact] + public void GetDataTypeName_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetDataTypeName(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.GetDataTypeName(100)); + } + + [Fact] + public void GetFieldType_ReturnsExpectedType() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetFieldType(1)).Returns(typeof(int)); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.GetFieldType(1); + + // Assert + Assert.Equal(typeof(int), actual); + } + + [Fact] + public void GetFieldType_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetFieldType(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.GetFieldType(100)); + } + + [Fact] + public void GetName_ReturnsExpectedName() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetName(1)).Returns("ColumnName"); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.GetName(1); + + // Assert + Assert.Equal("ColumnName", actual); + } + + [Fact] + public void GetName_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetName(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.GetName(100)); + } + + [Fact] + public void GetOrdinal_ReturnsExpectedOrdinal() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetOrdinal("ColumnName")).Returns(1); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.GetOrdinal("ColumnName"); + + // Assert + Assert.Equal(1, actual); + } + + [Fact] + public void GetOrdinal_ThrowsIndexOutOfRangeException_WhenColumnNameIsInvalid() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetOrdinal("InvalidColumn")).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.GetOrdinal("InvalidColumn")); + } + + [Fact] + public void GetSchemaTable_ReturnsExpectedSchemaTable() + { + // Arrange + var dbReaderMock = new Mock(); + var schemaTable = new DataTable(); + + dbReaderMock.Setup(r => r.GetSchemaTable()).Returns(schemaTable); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.GetSchemaTable(); + + // Assert + Assert.Equal(schemaTable, actual); + } + + [Fact] + public void GetSchemaTable_ReturnsNull_WhenSchemaTableIsNull() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetSchemaTable()).Returns((DataTable?)null); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.GetSchemaTable(); + + // Assert + Assert.Null(actual); + } + + [Fact] + public void GetBoolean_ReturnsExpectedBooleanValue() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns(true); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader.GetBoolean(0); + + // Assert + Assert.True(actual); + } + + [Fact] + public void GetBoolean_ThrowsInvalidCastException_WhenValueIsNotBoolean() + { + // Arrange + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns(0); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act && Assert + Assert.Throws(() => loader.GetBoolean(0)); + } + + [Fact] + public void GetBoolean_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetValue(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.GetBoolean(100)); + } + + [Fact] + public void GetByte_ReturnsExpectedByteValue() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns((byte)123); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader.GetByte(0); + + // Assert + Assert.Equal((byte)123, actual); + } + + [Fact] + public void GetByte_ThrowsInvalidCastException_WhenValueIsNotByte() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("NotAByte"); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act && Assert + Assert.Throws(() => loader.GetByte(0)); + } + + [Fact] + public void GetByte_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetValue(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.GetByte(100)); + } + + [Fact] + public void GetBytes_ReturnsZero() + { + // Arrange + var dbReaderMock = new Mock(); + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.GetBytes(1, 0, null, 0, 0); + + // Assert + Assert.Equal(0L, actual); + } + + [Fact] + public void GetBytes_ReturnsZero_WhenBufferIsNotNull() + { + // Arrange + var buffer = new byte[10]; + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns(buffer); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader.GetBytes(0, 0, buffer, 0, 10); + + // Assert + Assert.Equal(0L, actual); + } + + [Fact] + public void GetChar_ReturnsExpectedCharValue() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns('A'); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader.GetChar(0); + + // Assert + Assert.Equal('A', actual); + } + + [Fact] + public void GetChar_ThrowsInvalidCastException_WhenValueIsNotChar() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("NotAChar"); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act && Assert + Assert.Throws(() => loader.GetChar(0)); + } + + [Fact] + public void GetChar_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetValue(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.GetChar(100)); + } + + [Fact] + public void GetChars_ReturnsZero_WhenBufferIsNull() + { + // Arrange + var dbReaderMock = new Mock(); + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.GetChars(0, 0, null, 0, 0); + + // Assert + Assert.Equal(0L, actual); + } + + [Fact] + public void GetChars_ReturnsZero_WhenBufferIsNotNull() + { + // Arrange + var dbReaderMock = new Mock(); + var loader = new EFDataReaderLoader(dbReaderMock.Object); + var buffer = new char[10]; + + loader.Read(); + + // Act + var actual = loader.GetChars(1, 0, buffer, 0, 10); + + // Assert + Assert.Equal(0L, actual); + } + + [Fact] + public void GetDateTime_ReturnsExpectedDateTimeValue() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns(new DateTime(2023, 10, 1)); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader.GetDateTime(0); + + // Assert + Assert.Equal(new DateTime(2023, 10, 1), actual); + } + + [Fact] + public void GetDateTime_ThrowsInvalidCastException_WhenValueIsNotDateTime() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("NotADateTime"); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act && Assert + Assert.Throws(() => loader.GetDateTime(0)); + } + + [Fact] + public void GetDateTime_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetValue(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.GetDateTime(100)); + } + + [Fact] + public void GetDecimal_ReturnsExpectedDecimalValue() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns(123.45m); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader.GetDecimal(0); + + // Assert + Assert.Equal(123.45m, actual); + } + + [Fact] + public void GetDecimal_ThrowsInvalidCastException_WhenValueIsNotDecimal() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("NotADecimal"); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act && Assert + Assert.Throws(() => loader.GetDecimal(0)); + } + + [Fact] + public void GetDecimal_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetValue(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.GetDecimal(100)); + } + + [Fact] + public void GetDouble_ReturnsExpectedDoubleValue() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns(123.45); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader.GetDouble(0); + + // Assert + Assert.Equal(123.45, actual); + } + + [Fact] + public void GetDouble_ThrowsInvalidCastException_WhenValueIsNotDouble() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("NotADouble"); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act && Assert + Assert.Throws(() => loader.GetDouble(0)); + } + + [Fact] + public void GetDouble_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetValue(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Assert + Assert.Throws(() => loader.GetDouble(100)); + } + + [Fact] + public void GetEnumerator_ThrowsNotSupportedException() + { + // Arrange + var dbReaderMock = new Mock(); + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.GetEnumerator()); + } + + [Fact] + public void GetFloat_ReturnsExpectedFloatValue() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns(123.45f); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader.GetFloat(0); + + // Assert + Assert.Equal(123.45f, actual); + } + + [Fact] + public void GetFloat_ThrowsInvalidCastException_WhenValueIsNotFloat() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("NotAFloat"); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act && Assert + Assert.Throws(() => loader.GetFloat(0)); + } + + [Fact] + public void GetFloat_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetValue(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.GetFloat(100)); + } + + [Fact] + public void GetGuid_ReturnsExpectedGuidValue() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns(new Guid("12345678-1234-1234-1234-1234567890ab")); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader.GetGuid(0); + + // Assert + Assert.Equal(new Guid("12345678-1234-1234-1234-1234567890ab"), actual); + } + + [Fact] + public void GetGuid_ThrowsInvalidCastException_WhenValueIsNotGuid() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("NotAGuid"); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + loader.Read(); + + // Assert + Assert.Throws(() => loader.GetGuid(0)); + } + + [Fact] + public void GetGuid_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetValue(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.GetGuid(100)); + } + + [Fact] + public void GetInt16_ReturnsExpectedInt16Value() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns((short)123); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + var actual = loader.GetInt16(0); + + // Act && Assert + Assert.Equal((short)123, actual); + } + + [Fact] + public void GetInt16_ThrowsInvalidCastException_WhenValueIsNotInt16() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("NotAnInt16"); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act && Assert + Assert.Throws(() => loader.GetInt16(0)); + } + + [Fact] + public void GetInt16_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetValue(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.GetInt16(100)); + } + + [Fact] + public void GetInt32_ReturnsExpectedInt32Value() + { + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns(123); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader.GetInt32(0); + + // Assert + Assert.Equal(123, actual); + } + + [Fact] + public void GetInt32_ThrowsInvalidCastException_WhenValueIsNotInt32() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("NotAnInt32"); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act && Assert + Assert.Throws(() => loader.GetInt32(0)); + } + + [Fact] + public void GetInt32_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetValue(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // ACt && Assert + Assert.Throws(() => loader.GetInt32(100)); + } + + [Fact] + public void GetInt64_ReturnsExpectedInt64Value() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns(123L); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader.GetInt64(0); + + // Assert + Assert.Equal(123L, actual); + } + + [Fact] + public void GetInt64_ThrowsInvalidCastException_WhenValueIsNotInt64() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("NotAnInt64"); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act && Assert + Assert.Throws(() => loader.GetInt64(0)); + } + + [Fact] + public void GetInt64_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetValue(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.GetInt64(100)); + } + + [Fact] + public void GetString_ReturnsExpectedStringValue() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("TestString"); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader.GetString(0); + + // Assert + Assert.Equal("TestString", actual); + } + + [Fact] + public void GetString_ThrowsInvalidCastException_WhenValueIsNotString() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns(123); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act && Assert + Assert.Throws(() => loader.GetString(0)); + } + + [Fact] + public void GetString_ThrowsIndexOutOfRangeException_WhenOrdinalIsOutOfRange() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.GetValue(It.IsAny())).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.GetString(100)); + } + + [Fact] + public void GetValues_ReturnsExpectedNumberOfValues() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(3); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + var values = new object[3]; + + // Act + var actual = loader.GetValues(values); + + // Assert + Assert.Equal(3, actual); + } + + [Fact] + public void GetValues_CopiesValuesExpectedly() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(3); + dbReaderMock.Setup(r => r.GetValue(0)).Returns(1); + dbReaderMock.Setup(r => r.GetValue(1)).Returns("Test"); + dbReaderMock.Setup(r => r.GetValue(2)).Returns(true); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + var values = new object[3]; + + // Act + loader.GetValues(values); + + // Assert + Assert.Equal(1, values[0]); + Assert.Equal("Test", values[1]); + Assert.Equal(true, values[2]); + } + + [Fact] + public void GetValues_ThrowsArgumentNullException_WhenValuesArrayIsNull() + { + // Arrange + var dbReaderMock = new Mock(); + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act && Assert + Assert.Throws(() => loader.GetValues(null!)); + } + + [Fact] + public void GetValues_ThrowsArgumentException_WhenValuesArrayIsTooSmall() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(3); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + var values = new object[2]; + + // Act && Assert + Assert.Throws(() => loader.GetValues(values)); + } + + [Fact] + public void NextResult_ReturnsTrue_WhenThereAreMoreResults() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.NextResult()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.NextResult(); + + // Assert + Assert.True(actual); + } + + [Fact] + public void NextResult_ReturnsFalse_WhenThereAreNoMoreResults() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.NextResult()).Returns(false); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.NextResult(); + + // Assert + Assert.False(actual); + } + + [Fact] + public void NextResult_ThrowsInvalidOperationException_WhenDbReaderThrowsException() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.NextResult()).Throws(); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act && Assert + Assert.Throws(() => loader.NextResult()); + } + + [Fact] + public void LoadAndClose_ReturnsTableRowsWithAllData() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.Read()).Returns(true) + .Callback(() => dbReaderMock.Setup(r => r.Read()).Returns(false)); + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("value"); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + var actual = loader.LoadAndClose(); + + // Assert + Assert.Single(actual.Rows); + Assert.Equal("value", actual.Rows[0][0]); + } + + [Fact] + public void Read_ReturnsFalse_WhenNoMoreRows() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.SetupSequence(r => r.Read()).Returns(true).Returns(false); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader.Read(); + + // Assert + Assert.False(actual); + } + + [Fact] + public void GetValue_ReturnsExpectedValue() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("value"); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader.GetValue(0); + + // Assert + Assert.Equal("value", actual); + } + + [Fact] + public void GetValue_ReturnsExpectedBinaryValue() + { + // Arrange + var expected = new byte[] { 1, 2, 3, 4, 5 }; + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns("value"); + dbReaderMock.Setup(r => r.Read()).Returns(true); + dbReaderMock.Setup(r => r.GetStream(0)).Returns(new MemoryStream(expected)); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + var tableRows = loader + .GetType() + .GetField("_tableRows", BindingFlags.NonPublic | BindingFlags.Instance) + ?.GetValue(loader) as EFTableRows; + + tableRows!.ColumnsInfo[0].TypeName = "Microsoft.SqlServer.Types.SqlGeography"; + + loader.Read(); + + // Act + var actual = loader.GetValue(0); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void IsDBNull_ReturnsTrue_WhenValueIsDBNull() + { + // Arrange + var dbReaderMock = new Mock(); + + dbReaderMock.Setup(r => r.FieldCount).Returns(1); + dbReaderMock.Setup(r => r.GetValue(0)).Returns(DBNull.Value); + dbReaderMock.Setup(r => r.Read()).Returns(true); + + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + loader.Read(); + + // Act + var actual = loader.IsDBNull(0); + + // Assert + Assert.True(actual); + } + + [Fact] + public void Close_ClosesDbReader() + { + // Arrange + var dbReaderMock = new Mock(); + var loader = new EFDataReaderLoader(dbReaderMock.Object); + + // Act + loader.Close(); + + // Assert + dbReaderMock.Verify(r => r.Close(), Times.Once); + } +} \ No newline at end of file diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFDebugLoggerTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFDebugLoggerTests.cs new file mode 100644 index 0000000..82e88f2 --- /dev/null +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFDebugLoggerTests.cs @@ -0,0 +1,172 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Moq; + +namespace EFCoreSecondLevelCacheInterceptor.UnitTests; + +// ReSharper disable once InconsistentNaming +public class EFDebugLoggerTests +{ + private readonly Mock> _cacheableEventMock; + private readonly Mock> _cacheInvalidationEventMock; + private readonly Mock _serviceProviderMock; + private readonly IEFDebugLogger _debugLogger; + + public EFDebugLoggerTests() + { + _cacheableEventMock = new Mock>(); + _cacheInvalidationEventMock = new Mock>(); + _serviceProviderMock = new Mock(); + + var cacheSettingsMock = new Mock>(); + var loggerMock = new Mock>(); + var cacheSettings = new EFCoreSecondLevelCacheSettings + { + EnableLogging = true, + CacheableEvent = _cacheableEventMock.Object, + CacheInvalidationEvent = _cacheInvalidationEventMock.Object + }; + + cacheSettingsMock.Setup(c => c.Value).Returns(cacheSettings); + loggerMock.Setup(l => l.IsEnabled(LogLevel.Debug)).Returns(true); + + _debugLogger = new EFDebugLogger( + cacheSettingsMock.Object, + loggerMock.Object, + _serviceProviderMock.Object); + } + + [Fact] + public void EFDebugLogger_ThrowsArgumentNullException_WhenCacheSettingsIsNull() + { + // Arrange + var loggerMock = new Mock>(); + var serviceProviderMock = new Mock(); + + Assert.Throws(() => + new EFDebugLogger(null!, loggerMock.Object, serviceProviderMock.Object)); + } + + [Fact] + public void EFDebugLogger_ThrowsArgumentNullException_WhenLoggerIsNull() + { + // Arrange + var cacheSettingsMock = new Mock>(); + var serviceProviderMock = new Mock(); + + // Act && Assert + Assert.Throws(() => + new EFDebugLogger(cacheSettingsMock.Object, null!, serviceProviderMock.Object)); + } + + [Fact] + public void EFDebugLogger_EnablesLogging_WhenCacheSettingsEnableLoggingIsTrue() + { + // Arrange + var cacheSettingsMock = new Mock>(); + var loggerMock = new Mock>(); + var serviceProviderMock = new Mock(); + + cacheSettingsMock.Setup(c => c.Value).Returns(new EFCoreSecondLevelCacheSettings { EnableLogging = true }); + loggerMock.Setup(l => l.IsEnabled(LogLevel.Debug)).Returns(true); + + // Act + var logger = new EFDebugLogger(cacheSettingsMock.Object, loggerMock.Object, serviceProviderMock.Object); + + // Assert + Assert.True(logger.IsLoggerEnabled); + } + + [Fact] + public void EFDebugLogger_DisablesLogging_WhenCacheSettingsEnableLoggingIsFalse() + { + // Arrange + var cacheSettingsMock = new Mock>(); + var loggerMock = new Mock>(); + var serviceProviderMock = new Mock(); + + cacheSettingsMock.Setup(c => c.Value).Returns(new EFCoreSecondLevelCacheSettings { EnableLogging = false }); + + // Act + var logger = new EFDebugLogger(cacheSettingsMock.Object, loggerMock.Object, serviceProviderMock.Object); + + // Assert + Assert.False(logger.IsLoggerEnabled); + } + + [Fact] + public void NotifyCacheableEvent_InvokesCacheableEvent_WhenLoggerIsEnabled() + { + // Arrange && Act + _debugLogger.NotifyCacheableEvent(CacheableLogEventId.CachingSystemStarted, "TestMessage", "TestCommand"); + + // Assert + _cacheableEventMock.Verify(e => e.Invoke(It.Is( + x => + x.EventId == CacheableLogEventId.CachingSystemStarted + && x.Message == "TestMessage" + && x.CommandText == "TestCommand" + && x.ServiceProvider == _serviceProviderMock.Object)), + Times.Once); + } + + [Fact] + public void NotifyCacheableEvent_DoesNotInvokeCacheableEvent_WhenLoggerIsDisabled() + { + // Arrange + var cacheSettingsMock = new Mock>(); + var loggerMock = new Mock>(); + var serviceProviderMock = new Mock(); + var cacheableEventMock = new Mock>(); + + cacheSettingsMock.Setup(c => c.Value).Returns(new EFCoreSecondLevelCacheSettings { EnableLogging = false }); + cacheSettingsMock.Object.Value.CacheableEvent = cacheableEventMock.Object; + + var logger = new EFDebugLogger(cacheSettingsMock.Object, loggerMock.Object, serviceProviderMock.Object); + + // Act + logger.NotifyCacheableEvent(CacheableLogEventId.CachingSystemStarted, "TestMessage", "TestCommand"); + + // Assert + cacheableEventMock.Verify(e => e.Invoke(It.IsAny()), Times.Never); + } + + [Fact] + public void NotifyCacheableEvent_DoesNotInvokeCacheableEvent_WhenCacheableEventIsNull() + { + // Arrange + var cacheSettingsMock = new Mock>(); + var serviceProviderMock = new Mock(); + var loggerMock = new Mock>(); + var cacheableEventMock = new Mock>(); + + cacheSettingsMock.Setup(c => c.Value).Returns(new EFCoreSecondLevelCacheSettings { EnableLogging = true }); + loggerMock.Setup(l => l.IsEnabled(LogLevel.Debug)).Returns(true); + + var logger = new EFDebugLogger(cacheSettingsMock.Object, loggerMock.Object, serviceProviderMock.Object); + + // Act + logger.NotifyCacheableEvent(CacheableLogEventId.CachingSystemStarted, "TestMessage", "TestCommand"); + + // Assert + cacheableEventMock.Verify(e => e.Invoke(It.IsAny()), Times.Never); + } + + [Fact] + public void NotifyCacheInvalidation_InvokesEFCacheInvalidationInfo() + { + // Arrange + var cacheDependencies = new HashSet(); + + // Act + _debugLogger.NotifyCacheInvalidation(true, cacheDependencies); + + // Assert + _cacheInvalidationEventMock.Verify(e => e.Invoke(It.Is( + x => + x.CacheDependencies == cacheDependencies + && x.ClearAllCachedEntries == true + && x.ServiceProvider == _serviceProviderMock.Object)), + Times.Once); + } +} \ No newline at end of file diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFServiceCollectionExtensionsTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFServiceCollectionExtensionsTests.cs new file mode 100644 index 0000000..ed74857 --- /dev/null +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFServiceCollectionExtensionsTests.cs @@ -0,0 +1,138 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; + +namespace EFCoreSecondLevelCacheInterceptor.UnitTests; + +// ReSharper disable once InconsistentNaming +public class EFServiceCollectionExtensionsTests +{ + [Fact] + public void AddEFSecondLevelCache_ThrowsArgumentNullException_WhenOptionsIsNull() + { + // Arrange + var services = new ServiceCollection(); + + // Act && Assert + Assert.Throws(() => services.AddEFSecondLevelCache(null!)); + } + + [Fact] + public void AddEFSecondLevelCache_RegistersRequiredServices() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.TryAddSingleton(typeof(ILogger<>), typeof(Logger<>)); + services.AddEFSecondLevelCache(_ => { }); + + // Assert + var serviceProvider = services.BuildServiceProvider(); + + Assert.NotNull(serviceProvider.GetService()); + Assert.NotNull(serviceProvider.GetService()); + Assert.NotNull(serviceProvider.GetService()); + Assert.NotNull(serviceProvider.GetService()); + Assert.NotNull(serviceProvider.GetService()); + Assert.NotNull(serviceProvider.GetService()); + Assert.NotNull(serviceProvider.GetService()); + Assert.NotNull(serviceProvider.GetService()); + Assert.NotNull(serviceProvider.GetService()); + Assert.NotNull(serviceProvider.GetService()); + Assert.NotNull(serviceProvider.GetService()); + } + + [Fact] + public void AddEFSecondLevelCache_RegistersDefaultHashProvider_WhenHashProviderIsNull() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddEFSecondLevelCache(options => { options.Settings.HashProvider = null; }); + + // Assert + var serviceProvider = services.BuildServiceProvider(); + + Assert.IsType(serviceProvider.GetService()); + } + + [Fact] + public void AddEFSecondLevelCache_RegistersCustomHashProvider_WhenHashProviderIsNotNull() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddEFSecondLevelCache(options => { options.Settings.HashProvider = typeof(CustomHashProvider); }); + + // Assert + var serviceProvider = services.BuildServiceProvider(); + + Assert.IsType(serviceProvider.GetService()); + } + + [Fact] + public void AddEFSecondLevelCache_RegistersDefaultCacheProvider_WhenCacheProviderIsNull() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddEFSecondLevelCache(options => { options.Settings.CacheProvider = null; }); + + // Assert + var serviceProvider = services.BuildServiceProvider(); + + Assert.IsType(serviceProvider.GetService()); + } + + [Fact] + public void AddEFSecondLevelCache_RegistersCustomCacheProvider_WhenCacheProviderIsNotNull() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddEFSecondLevelCache(options => { options.Settings.CacheProvider = typeof(CustomCacheProvider); }); + + // Assert + var serviceProvider = services.BuildServiceProvider(); + + Assert.IsType(serviceProvider.GetService()); + } + + private class Logger : ILogger + { + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, + Func formatter) => throw new NotImplementedException(); + + public bool IsEnabled(LogLevel logLevel) => throw new NotImplementedException(); + + public IDisposable BeginScope(TState state) where TState : notnull => + throw new NotImplementedException(); + } + + private class CustomHashProvider : IEFHashProvider + { + public ulong ComputeHash(string data) => throw new NotImplementedException(); + + public ulong ComputeHash(byte[] data) => throw new NotImplementedException(); + + public ulong ComputeHash(byte[] data, int offset, int len, uint seed) => throw new NotImplementedException(); + } + + private class CustomCacheProvider : IEFCacheServiceProvider + { + public void ClearAllCachedEntries() => throw new NotImplementedException(); + + public EFCachedData GetValue(EFCacheKey cacheKey, EFCachePolicy cachePolicy) => + throw new NotImplementedException(); + + public void InsertValue(EFCacheKey cacheKey, EFCachedData value, EFCachePolicy cachePolicy) => + throw new NotImplementedException(); + + public void InvalidateCacheDependencies(EFCacheKey cacheKey) => throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableColumnInfoTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableColumnInfoTests.cs index c2a92e2..05623c9 100644 --- a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableColumnInfoTests.cs +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableColumnInfoTests.cs @@ -1,5 +1,6 @@ namespace EFCoreSecondLevelCacheInterceptor.UnitTests; +// ReSharper disable once InconsistentNaming public class EFTableColumnInfoTests { [Fact] @@ -15,10 +16,10 @@ public void ToString_ReturnsFormattedString_WithValidProperties() }; // Act - var result = columnInfo.ToString(); + var actual = columnInfo.ToString(); // Assert - Assert.Equal("Ordinal: 1, Name: ColumnName, DbTypeName: DbType, TypeName= Type.", result); + Assert.Equal("Ordinal: 1, Name: ColumnName, DbTypeName: DbType, TypeName= Type.", actual); } [Fact] @@ -28,9 +29,9 @@ public void ToString_ReturnsFormattedString_WithDefaultProperties() var columnInfo = new EFTableColumnInfo(); // Act - var result = columnInfo.ToString(); + var actual = columnInfo.ToString(); // Assert - Assert.Equal("Ordinal: 0, Name: , DbTypeName: , TypeName= .", result); + Assert.Equal("Ordinal: 0, Name: , DbTypeName: , TypeName= .", actual); } } \ No newline at end of file diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowTests.cs index d3863ca..6884a28 100644 --- a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowTests.cs +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowTests.cs @@ -1,5 +1,6 @@ namespace EFCoreSecondLevelCacheInterceptor.UnitTests; +// ReSharper disable once InconsistentNaming public class EFTableRowTests { [Fact] diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowsDataReaderTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowsDataReaderTests.cs index 5e3a758..cb15374 100644 --- a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowsDataReaderTests.cs +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowsDataReaderTests.cs @@ -3,6 +3,7 @@ namespace EFCoreSecondLevelCacheInterceptor.UnitTests; +// ReSharper disable once InconsistentNaming public class EFTableRowsDataReaderTests { [Fact] @@ -310,7 +311,7 @@ public void GetDataTypeName_ReturnsEmptyString_WhenTypeNameIsEmpty() } [Fact] - public void GetFieldType_ReturnsCorrectType_WhenOrdinalIsValid() + public void GetFieldType_ReturnsExpectedType_WhenOrdinalIsValid() { // Arrange var tableRows = new EFTableRows @@ -1487,10 +1488,10 @@ public void GetFieldValue_ShouldNotThrowInvalidCastExceptionWhenConvertToDecimal dataReader.Read(); // Act - var exception = Record.Exception(() => dataReader.GetFieldValue(0)); + var actual = Record.Exception(() => dataReader.GetFieldValue(0)); // Assert - Assert.Null(exception); + Assert.Null(actual); } public static IEnumerable InvalidNumberData => @@ -1680,10 +1681,10 @@ public void GetFieldValue_ShouldNotThrowInvalidCastExceptionWhenValueConversionF dataReader.Read(); // Act - var exception = Record.Exception(() => dataReader.GetFieldValue(0)); + var actual = Record.Exception(() => dataReader.GetFieldValue(0)); // Assert - Assert.Null(exception); + Assert.Null(actual); } [Fact] @@ -1782,10 +1783,10 @@ public void GetFieldValue_ShouldNotThrowInvalidCastExceptionWhenValueConversionF dataReader.Read(); // Act - var exception = Record.Exception(() => dataReader.GetFieldValue(0)); + var actual = Record.Exception(() => dataReader.GetFieldValue(0)); // Assert - Assert.Null(exception); + Assert.Null(actual); } [Fact] diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowsTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowsTests.cs index 604b60b..2f16d38 100644 --- a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowsTests.cs +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/EFTableRowsTests.cs @@ -3,6 +3,7 @@ namespace EFCoreSecondLevelCacheInterceptor.UnitTests; +// ReSharper disable once InconsistentNaming public class EFTableRowsTests { [Fact] @@ -12,6 +13,7 @@ public void Constructor_ShouldThrowArgumentNullException_WhenReaderIsNull() DbDataReader reader = null; // Act & Assert + // ReSharper disable once AssignNullToNotNullAttribute Assert.Throws("reader", () => new EFTableRows(reader)); } @@ -75,7 +77,7 @@ public void Constructor_ShouldInitializeColumnsInfo_WhenReaderIsValid() } [Fact] - public void Indexer_ReturnsCorrectRow_WhenIndexIsValid() + public void Indexer_ReturnsExpectedRow_WhenIndexIsValid() { // Arrange var rows = new List @@ -87,14 +89,14 @@ public void Indexer_ReturnsCorrectRow_WhenIndexIsValid() var tableRows = new EFTableRows { Rows = rows }; // Act - var result = tableRows[1]; + var actual = tableRows[1]; // Assert - Assert.Equal(2, result.Values[0]); + Assert.Equal(2, actual.Values[0]); } [Fact] - public void Indexer_SetsCorrectRow_WhenIndexIsValid() + public void Indexer_SetsExpectedRow_WhenIndexIsValid() { // Arrange var rows = new List @@ -160,7 +162,7 @@ public void Add_ShouldAddItemToRows() } [Fact] - public void Get_ShouldReturnCorrectRow() + public void Get_ShouldReturnExpectedRow() { // Arrange var tableRows = new EFTableRows(); @@ -169,14 +171,14 @@ public void Get_ShouldReturnCorrectRow() tableRows.Add(row); // Act - var result = tableRows.Get(0); + var actual = tableRows.Get(0); // Assert - Assert.Equal(row, result); + Assert.Equal(row, actual); } [Fact] - public void GetOrdinal_ShouldReturnCorrectOrdinal() + public void GetOrdinal_ShouldReturnExpectedOrdinal() { // Arrange var tableRows = new EFTableRows() @@ -195,7 +197,7 @@ public void GetOrdinal_ShouldReturnCorrectOrdinal() } [Fact] - public void GetName_ShouldReturnCorrectName() + public void GetName_ShouldReturnExpectedName() { // Arrange var tableRows = new EFTableRows() @@ -240,7 +242,7 @@ public void RecordsAffected_ReturnsMinusOne() } [Fact] - public void VisibleFieldCount_ReturnsCorrectValue_WhenSet() + public void VisibleFieldCount_ReturnsExpectedValue_WhenSet() { // Arrange var tableRows = new EFTableRows { VisibleFieldCount = 5 }; @@ -269,9 +271,9 @@ public void GetFieldTypeName_ReturnsTypeName_WhenOrdinalIsValid() var tableRows = new EFTableRows { ColumnsInfo = columnsInfo }; // Act - var result = tableRows.GetFieldTypeName(0); + var actual = tableRows.GetFieldTypeName(0); - Assert.Equal("System.String", result); + Assert.Equal("System.String", actual); } [Fact] @@ -286,9 +288,9 @@ public void GetFieldTypeName_ThrowsArgumentOutOfRangeException_WhenOrdinalIsInva var tableRows = new EFTableRows { ColumnsInfo = columnsInfo }; // Act - var exception = Assert.Throws(() => tableRows.GetFieldTypeName(0)); + var actual = Assert.Throws(() => tableRows.GetFieldTypeName(0)); // Assert - Assert.Equal("Index[0] was outside of array's bounds. (Parameter 'ordinal')", exception.Message); + Assert.Equal("Index[0] was outside of array's bounds. (Parameter 'ordinal')", actual.Message); } } \ No newline at end of file diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/LockProviderTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/LockProviderTests.cs new file mode 100644 index 0000000..38f243f --- /dev/null +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/LockProviderTests.cs @@ -0,0 +1,73 @@ +using AsyncKeyedLock; + +namespace EFCoreSecondLevelCacheInterceptor.UnitTests; + +public class LockProviderTests +{ + [Fact] + public void Lock_ReturnsNonNullReleaser() + { + // Arrange + var lockProvider = new LockProvider(); + + // Act + var releaser = lockProvider.Lock(); + + // Assert + Assert.IsType(releaser); + } + + [Fact] + public async Task LockAsync_ReturnsNonNullReleaser() + { + // Arrange + var lockProvider = new LockProvider(); + + // Act + var releaser = await lockProvider.LockAsync(); + + // Assert + Assert.IsType(releaser); + } + + [Fact] + public void Lock_CanBeDisposed() + { + // Arrange + var lockProvider = new LockProvider(); + var releaser = lockProvider.Lock(); + + // Act + releaser.Dispose(); + + // Assert + Assert.True(true); // If no exception is thrown, the test passes + } + + [Fact] + public async Task LockAsync_CanBeDisposed() + { + // Arrange + var lockProvider = new LockProvider(); + var releaser = await lockProvider.LockAsync(); + + // Act + releaser.Dispose(); + + // Assert + Assert.True(true); // If no exception is thrown, the test passes + } + + [Fact] + public void Dispose_DisposesLockProvider() + { + // Arrange + var lockProvider = new LockProvider(); + + // Act + lockProvider.Dispose(); + + // Assert + Assert.True(true); // If no exception is thrown, the test passes + } +} \ No newline at end of file diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/SecondLevelCacheInterceptorTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/SecondLevelCacheInterceptorTests.cs index 8c97e93..4e2bf95 100644 --- a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/SecondLevelCacheInterceptorTests.cs +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/SecondLevelCacheInterceptorTests.cs @@ -115,7 +115,7 @@ public void NonQueryExecuted_ReturnsProcessedResult() } [Fact] - public void NonQueryExecuted_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + public void NonQueryExecuted_ShouldNotThrowArgumentNullException_WhenAnyParameterIsNull() { // Arrange const int expected = int.MaxValue; @@ -235,7 +235,7 @@ public async Task NonQueryExecutedAsync_ReturnsProcessedResult() } [Fact] - public async Task NonQueryExecutedAsync_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + public async Task NonQueryExecutedAsync_ShouldNotThrowArgumentNullException_WhenAnyParameterIsNull() { // Arrange const int expected = 1; @@ -353,7 +353,7 @@ public void NonQueryExecuting_ReturnsExpectedInterceptionResult() } [Fact] - public void NonQueryExecuting_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + public void NonQueryExecuting_ShouldNotThrowArgumentNullException_WhenAnyParameterIsNull() { // Arrange var expected = InterceptionResult.SuppressWithResult(int.MaxValue); @@ -472,7 +472,7 @@ public async Task NonQueryExecutingAsync_ReturnsExpectedInterceptionResult() } [Fact] - public async Task NonQueryExecutingAsync_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + public async Task NonQueryExecutingAsync_ShouldNotThrowArgumentNullException_WhenAnyParameterIsNull() { // Arrange var expected = InterceptionResult.SuppressWithResult(int.MaxValue); @@ -586,7 +586,7 @@ public void ReaderExecuted_ReturnsExpectedDbDataReader() } [Fact] - public void ReaderExecuted_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + public void ReaderExecuted_ShouldNotThrowArgumentNullException_WhenAnyParameterIsNull() { // Arrange var expected = Mock.Of(); @@ -699,7 +699,7 @@ public async Task ReaderExecutedAsync_ReturnsExpectedDbDataReader() } [Fact] - public async Task ReaderExecutedAsync_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + public async Task ReaderExecutedAsync_ShouldNotThrowArgumentNullException_WhenAnyParameterIsNull() { // Arrange var expected = Mock.Of(); @@ -817,7 +817,7 @@ public void ReaderExecuting_ReturnsExpectedInterceptionResult() } [Fact] - public void ReaderExecuting_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + public void ReaderExecuting_ShouldNotThrowArgumentNullException_WhenAnyParameterIsNull() { // Arrange var dataReader = Mock.Of(); @@ -939,7 +939,7 @@ public async Task ReaderExecutingAsync_ReturnsExpectedInterceptionResult() } [Fact] - public async Task ReaderExecutingAsync_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + public async Task ReaderExecutingAsync_ShouldNotThrowArgumentNullException_WhenAnyParameterIsNull() { // Arrange var dataReader = Mock.Of(); @@ -1058,7 +1058,7 @@ public void ScalarExecuted_ReturnsProcessedResult() } [Fact] - public void ScalarExecuted_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + public void ScalarExecuted_ShouldNotThrowArgumentNullException_WhenAnyParameterIsNull() { // Arrange var expected = new object(); @@ -1178,7 +1178,7 @@ public async Task ScalarExecutedAsync_ReturnsProcessedResult() } [Fact] - public async Task ScalarExecutedAsync_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + public async Task ScalarExecutedAsync_ShouldNotThrowArgumentNullException_WhenAnyParameterIsNull() { // Arrange var expected = new object(); @@ -1296,7 +1296,7 @@ public void ScalarExecuting_ReturnsExpectedInterceptionResult() } [Fact] - public void ScalarExecuting_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + public void ScalarExecuting_ShouldNotThrowArgumentNullException_WhenAnyParameterIsNull() { // Arrange var expected = InterceptionResult.SuppressWithResult(new object()); @@ -1415,7 +1415,7 @@ public async Task ScalarExecutingAsync_ReturnsExpectedInterceptionResult() } [Fact] - public async Task ScalarExecutingAsync_ShouldNotArgumentNullException_WhenAnyParameterIsNull() + public async Task ScalarExecutingAsync_ShouldNotThrowArgumentNullException_WhenAnyParameterIsNull() { // Arrange var expected = InterceptionResult.SuppressWithResult(new object()); diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/StringExtensionsTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/StringExtensionsTests.cs new file mode 100644 index 0000000..4117eb5 --- /dev/null +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/StringExtensionsTests.cs @@ -0,0 +1,164 @@ +namespace EFCoreSecondLevelCacheInterceptor.UnitTests; + +public class StringExtensionsTests +{ + [Fact] + public void EndsWith_ReturnsFalse_WhenValueIsNull() + { + // Arrange + var collection = new List { "testValue", "anotherValue" }; + + // Act + var actual = collection.EndsWith(null, StringComparison.OrdinalIgnoreCase); + + // Assert + Assert.False(actual); + } + + [Fact] + public void EndsWith_ReturnsTrue_WhenCollectionContainsItemEndingWithValue() + { + // Arrange + var collection = new List { "testValue", "anotherValue" }; + + // Act + var actual = collection.EndsWith("Value", StringComparison.OrdinalIgnoreCase); + + // Assert + Assert.True(actual); + } + + [Fact] + public void EndsWith_ReturnsFalse_WhenCollectionDoesNotContainItemEndingWithValue() + { + // Arrange + var collection = new List { "testValue", "anotherValue" }; + + // Act + var actual = collection.EndsWith("NotExist", StringComparison.OrdinalIgnoreCase); + + // Assert + Assert.False(actual); + } + + [Fact] + public void StartsWith_ReturnsFalse_WhenValueIsNull() + { + // Arrange + var collection = new List { "ValueTest", "ValueAnother" }; + + // Act + var actual = collection.StartsWith(null, StringComparison.OrdinalIgnoreCase); + + // Assert + Assert.False(actual); + } + + [Fact] + public void StartsWith_ReturnsTrue_WhenCollectionContainsItemStartingWithValue() + { + // Arrange + var collection = new List { "ValueTest", "ValueAnother" }; + + // Act + var actual = collection.StartsWith("Value", StringComparison.OrdinalIgnoreCase); + + // Assert + Assert.True(actual); + } + + [Fact] + public void StartsWith_ReturnsFalse_WhenCollectionDoesNotContainItemStartingWithValue() + { + // Arrange + var collection = new List { "ValueTest", "ValueAnother" }; + + // Act + var actual = collection.StartsWith("NotExist", StringComparison.OrdinalIgnoreCase); + + // Assert + Assert.False(actual); + } + + [Fact] + public void ContainsEvery_ReturnsFalse_WhenCollectionIsNull() + { + // Arrange + var source = new List { "item1", "item2", "item3" }; + + // Act + var actual = source.ContainsEvery(null, StringComparer.OrdinalIgnoreCase); + + // Assert + Assert.False(actual); + } + + [Fact] + public void ContainsEvery_ReturnsTrue_WhenSourceContainsEveryItemInCollection() + { + // Arrange + var source = new List { "item1", "item2", "item3" }; + var collection = new List { "item1", "item2", "item3" }; + + // Act + var actual = source.ContainsEvery(collection, StringComparer.OrdinalIgnoreCase); + + // Assert + Assert.True(actual); + } + + [Fact] + public void ContainsEvery_ReturnsFalse_WhenSourceDoesNotContainEveryItemInCollection() + { + // Arrange + var source = new List { "item1", "item2" }; + var collection = new List { "item1", "item2", "item3" }; + + // Act + var actual = source.ContainsEvery(collection, StringComparer.OrdinalIgnoreCase); + + // Assert + Assert.False(actual); + } + + [Fact] + public void ContainsOnly_ReturnsFalse_WhenCollectionIsNull() + { + // Arrange + var source = new List { "item1", "item2" }; + + // Act + var actual = source.ContainsOnly(null, StringComparer.OrdinalIgnoreCase); + + // Assert + Assert.False(actual); + } + + [Fact] + public void ContainsOnly_ReturnsTrue_WhenSourceContainsOnlyItemsInCollection() + { + // Arrange + var source = new List { "item1", "item2" }; + var collection = new List { "item1", "item2", "item3" }; + + // Act + var actual = source.ContainsOnly(collection, StringComparer.OrdinalIgnoreCase); + + // Assert + Assert.True(actual); + } + + [Fact] + public void ContainsOnly_ReturnsFalse_WhenSourceContainsItemsNotInCollection() + { + // Arrange + var source = new List { "item1", "item4" }; + var collection = new List { "item1", "item2", "item3" }; + + // Act + var actual = source.ContainsOnly(collection, StringComparer.OrdinalIgnoreCase); + + // Assert + Assert.False(actual); + } +} \ No newline at end of file diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/TableEntityInfoTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/TableEntityInfoTests.cs new file mode 100644 index 0000000..30dc321 --- /dev/null +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/TableEntityInfoTests.cs @@ -0,0 +1,72 @@ +namespace EFCoreSecondLevelCacheInterceptor.UnitTests; + +public class TableEntityInfoTests +{ + [Fact] + public void ToString_ReturnsExpectedFormat_WhenClrTypeAndTableNameAreSet() + { + // Arrange + var entityInfo = new TableEntityInfo + { + ClrType = typeof(string), + TableName = "TestTable" + }; + + // Act + var actual = entityInfo.ToString(); + + // Assert + Assert.Equal("System.String::TestTable", actual); + } + + [Fact] + public void ToString_ReturnsExpectedFormat_WhenClrTypeIsNull() + { + // Arrange + var entityInfo = new TableEntityInfo + { + ClrType = null!, + TableName = "TestTable" + }; + + // Act + var actual = entityInfo.ToString(); + + // Assert + Assert.Equal("::TestTable", actual); + } + + [Fact] + public void ToString_ReturnsExpectedFormat_WhenTableNameIsNull() + { + // Arrange + var entityInfo = new TableEntityInfo + { + ClrType = typeof(string), + TableName = null! + }; + + // Act + var actual = entityInfo.ToString(); + + // Assert + Assert.Equal("System.String::", actual); + } + + [Fact] + public void ToString_ReturnsExpectedFormat_WhenBothClrTypeAndTableNameAreNull() + { + // Arrange + var entityInfo = new TableEntityInfo + { + ClrType = null!, + TableName = null! + }; + + // Act + var actual = entityInfo.ToString(); + + // Assert + Assert.Equal("::", actual); + } +} \ No newline at end of file diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/TypeExtensionsTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/TypeExtensionsTests.cs index f441e7a..2def323 100644 --- a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/TypeExtensionsTests.cs +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/TypeExtensionsTests.cs @@ -8,10 +8,10 @@ public class TypeExtensionsTests public void IsNull_ShouldReturnTrue_WhenValueIsNull() { // Arrange && Act - var result = ((object)null).IsNull(); + var actual = ((object)null).IsNull(); // Assert - Assert.True(result); + Assert.True(actual); } [Fact] @@ -21,10 +21,10 @@ public void IsNull_ShouldReturnTrue_WhenValueIsDBNull() object value = DBNull.Value; // Act - var result = value.IsNull(); + var actual = value.IsNull(); // Assert - Assert.True(result); + Assert.True(actual); } [Fact] @@ -34,10 +34,10 @@ public void IsNull_ShouldReturnFalse_WhenValueIsNotNullOrDBNull() object value = new object(); // Act - var result = value.IsNull(); + var actual = value.IsNull(); // Assert - Assert.False(result); + Assert.False(actual); } [Theory] diff --git a/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/XxHash64UnsafeTests.cs b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/XxHash64UnsafeTests.cs new file mode 100644 index 0000000..cf44325 --- /dev/null +++ b/src/Tests/EFCoreSecondLevelCacheInterceptor.UnitTests/XxHash64UnsafeTests.cs @@ -0,0 +1,125 @@ +namespace EFCoreSecondLevelCacheInterceptor.UnitTests; + +public class XxHash64UnsafeTests +{ + [Fact] + public void ComputeHash_ThrowsArgumentNullException_WhenStringDataIsNull() + { + // Arrange + var hashProvider = new XxHash64Unsafe(); + + // Act && Assert + Assert.Throws(() => hashProvider.ComputeHash(((string)null)!)); + } + + [Fact] + public void ComputeHash_ThrowsArgumentNullException_WhenByteArrayDataIsNull() + { + // Arrange + var hashProvider = new XxHash64Unsafe(); + + // Act && Assert + Assert.Throws(() => hashProvider.ComputeHash(((byte[])null)!)); + } + + [Fact] + public void ComputeHash_ThrowsArgumentNullException_WhenByteArrayDataWithOffsetIsNull() + { + // Arrange + var hashProvider = new XxHash64Unsafe(); + + // Act && Assert + Assert.Throws(() => hashProvider.ComputeHash(null!, 0, 0, 0)); + } + + [Fact] + public void ComputeHash_ReturnsSameHash_ForSameStringData() + { + // Arrange + var hashProvider = new XxHash64Unsafe(); + var data = "test"; + + // Act + var hash1 = hashProvider.ComputeHash(data); + var hash2 = hashProvider.ComputeHash(data); + + // Act && Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void ComputeHash_ReturnsSameHash_ForSameByteArrayData() + { + // Arrange + var hashProvider = new XxHash64Unsafe(); + var data = "test"u8.ToArray(); + + // Act + var hash1 = hashProvider.ComputeHash(data); + var hash2 = hashProvider.ComputeHash(data); + + // Act && Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void ComputeHash_ReturnsSameHash_ForSameByteArrayDataWithOffset() + { + // Arrange + var hashProvider = new XxHash64Unsafe(); + var data = "test"u8.ToArray(); + + // Act + var hash1 = hashProvider.ComputeHash(data, 0, data.Length, 0); + var hash2 = hashProvider.ComputeHash(data, 0, data.Length, 0); + + // Act && Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void ComputeHash_ReturnsDifferentHash_ForDifferentStringData() + { + // Arrange + var hashProvider = new XxHash64Unsafe(); + + // Act + var hash1 = hashProvider.ComputeHash("test1"); + var hash2 = hashProvider.ComputeHash("test2"); + + // Act && Assert + Assert.NotEqual(hash1, hash2); + } + + [Fact] + public void ComputeHash_ReturnsDifferentHash_ForDifferentByteArrayData() + { + // Arrange + var hashProvider = new XxHash64Unsafe(); + var data1 = "test1"u8.ToArray(); + var data2 = "test2"u8.ToArray(); + + // Act + var hash1 = hashProvider.ComputeHash(data1); + var hash2 = hashProvider.ComputeHash(data2); + + // Act && Assert + Assert.NotEqual(hash1, hash2); + } + + [Fact] + public void ComputeHash_ReturnsDifferentHash_ForDifferentByteArrayDataWithOffset() + { + // Arrange + var hashProvider = new XxHash64Unsafe(); + var data1 = "test1"u8.ToArray(); + var data2 = "test2"u8.ToArray(); + + // Act + var hash1 = hashProvider.ComputeHash(data1, 0, data1.Length, 0); + var hash2 = hashProvider.ComputeHash(data2, 0, data2.Length, 0); + + // Act && Assert + Assert.NotEqual(hash1, hash2); + } +} \ No newline at end of file