From 88b850eb88071e84decc26c81177f642461ba3ec Mon Sep 17 00:00:00 2001 From: kubagdynia Date: Wed, 8 May 2024 22:00:54 +0200 Subject: [PATCH] Added the ability to hash the key. Can be used for long keys --- CacheDrive.ExampleConsoleApp/App.cs | 24 ++- .../CachingObjectsHashKeyTests.cs | 132 ++++++++++++++ .../CachingSimpleTypesHashKeyTests.cs | 166 ++++++++++++++++++ CacheDrive.Tests/MemoryAndFileHashKeyTests.cs | 83 +++++++++ CacheDrive.Tests/MemoryHashKeyTests.cs | 99 +++++++++++ 5 files changed, 501 insertions(+), 3 deletions(-) create mode 100644 CacheDrive.Tests/CachingObjectsHashKeyTests.cs create mode 100644 CacheDrive.Tests/CachingSimpleTypesHashKeyTests.cs create mode 100644 CacheDrive.Tests/MemoryAndFileHashKeyTests.cs create mode 100644 CacheDrive.Tests/MemoryHashKeyTests.cs diff --git a/CacheDrive.ExampleConsoleApp/App.cs b/CacheDrive.ExampleConsoleApp/App.cs index 3daaabc..8da1e16 100644 --- a/CacheDrive.ExampleConsoleApp/App.cs +++ b/CacheDrive.ExampleConsoleApp/App.cs @@ -15,11 +15,29 @@ public async Task Run() Console.WriteLine(cacheService.TryGetValue(cacheKey1, out string cachedValue2) ? $"TryGetValue OK - cached value: {cachedValue2}" : $"TryGetValue NOK - cached value: {cachedValue2}"); + + // SetAsync, GetAsync and TryGetValue with hashKey + string cacheKey2 = "testKey"; + await cacheService.SetAsync(cacheKey2, "test text...", hashKey: true); + var cachedValueWithHashKey1 = await cacheService.GetAsync(cacheKey2, hashKey: true); + Console.WriteLine($"GetAsync with hashKey - cached value: {cachedValueWithHashKey1}"); + + Console.WriteLine(cacheService.TryGetValue(cacheKey2, hashKey: true, out string cachedValueWithHashKey2) + ? $"TryGetValue with haskkey OK - cached value: {cachedValueWithHashKey2}" + : $"TryGetValue with haskkey NOK - cached value: {cachedValueWithHashKey2}"); + Console.WriteLine(); + // Set, Get - string cacheKey2 = "testKey2"; - cacheService.Set(cacheKey2, 1234567); - int cachedValue3 = cacheService.Get(cacheKey2); + string cacheKey3 = "testKey2"; + cacheService.Set(cacheKey3, 1234567); + int cachedValue3 = cacheService.Get(cacheKey3); Console.WriteLine($"Get - cached value: {cachedValue3} "); + + // Set, Get with hashKey + string cacheKey4 = "testKey2"; + cacheService.Set(cacheKey4, 1234567, hashKey: true); + int cachedValueWithHashKey3 = cacheService.Get(cacheKey4, hashKey: true); + Console.WriteLine($"Get with hashKey - cached value: {cachedValueWithHashKey3} "); } } \ No newline at end of file diff --git a/CacheDrive.Tests/CachingObjectsHashKeyTests.cs b/CacheDrive.Tests/CachingObjectsHashKeyTests.cs new file mode 100644 index 0000000..7c5d095 --- /dev/null +++ b/CacheDrive.Tests/CachingObjectsHashKeyTests.cs @@ -0,0 +1,132 @@ +using System.Text.Json.Serialization; +using CacheDrive.Configuration; +using CacheDrive.Services; +using CacheDrive.Tests.Helpers; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; + +namespace CacheDrive.Tests; + +public class CachingObjectsHashKeyTests +{ + private TestClass _testClass; + + [SetUp] + public void SetUp() + { + _testClass = new TestClass + { + Id = Guid.Parse("5b8e8e5c-6ad5-43c0-94d8-ef68e96eaef8"), + FirstName = "John", + LastName = "Doe", + Age = 50 + }; + } + + [Test] + public async Task ObjectShouldBeProperlyCachedAndRestoredFromTheMemory() + { + // Arrange + ServiceProvider serviceProvider = TestHelper.CreateServiceProvider( + DateTime.Now, + cacheEnabled: true, + cacheExpirationType: CacheExpirationType.Hours, + cacheExpiration: 2, + cacheType: CacheType.Memory); + + ICacheService cacheService = serviceProvider.GetRequiredService(); + + // Act + await cacheService.SetAsync(_testClass.Id.ToString(), _testClass, hashKey: true); + + var cachedTestClass = await cacheService.GetAsync(_testClass.Id.ToString(), hashKey: true); + + await cacheService.FlushAsync(); + + // Assert + cachedTestClass.Should().BeEquivalentTo(_testClass); + } + + [Test, Order(1)] + public async Task ObjectShoulBeProperlyCachedInMemoryAndPersistedToTheFile() + { + // Arrange + ServiceProvider serviceProvider = TestHelper.CreateServiceProvider( + DateTime.Now, + cacheEnabled: true, + cacheExpirationType: CacheExpirationType.Hours, + cacheExpiration: 2, + cacheType: CacheType.MemoryAndFile); + + ICacheService cacheService = serviceProvider.GetRequiredService(); + + // Act + await cacheService.SetAsync(_testClass.Id.ToString(), _testClass, hashKey: true); + + var cachedTestClass = await cacheService.GetAsync(_testClass.Id.ToString(), hashKey: true); + + await cacheService.FlushAsync(); + + // Assert + cachedTestClass.Should().BeEquivalentTo(_testClass); + } + + [Test, Order(2)] + public async Task CacheShouldBeProperlyLoadedFromTheFile_Then_ObjectShouldBeProperlyRestoredFromTheMemory() + { + // Arrange + ServiceProvider serviceProvider = TestHelper.CreateServiceProvider( + DateTime.Now, + cacheEnabled: true, + cacheExpirationType: CacheExpirationType.Hours, + cacheExpiration: 2, + cacheType: CacheType.MemoryAndFile); + + ICacheService cacheService = serviceProvider.GetRequiredService(); + + // Act + var cachedTestClass = await cacheService.GetAsync(_testClass.Id.ToString(), hashKey: true); + + await cacheService.FlushAsync(); + + // Assert + cachedTestClass.Should().BeEquivalentTo(_testClass); + } + + [Test, Order(3)] + public async Task CacheShouldExpiredDuringLoadedFromTheFile_Then_ObjectShouldNotBeProperlyRestoredFromTheMemory() + { + // Arrange + ServiceProvider serviceProvider = TestHelper.CreateServiceProvider( + DateTime.Now.AddHours(3), + cacheEnabled: true, + cacheExpirationType: CacheExpirationType.Hours, + cacheExpiration: 2, + cacheType: CacheType.MemoryAndFile); + + ICacheService cacheService = serviceProvider.GetRequiredService(); + + // Act + var cachedTestClass = await cacheService.GetAsync(_testClass.Id.ToString(), hashKey: true); + + await cacheService.FlushAsync(); + + // Assert + cachedTestClass.Should().BeNull(); + } + + private class TestClass + { + [JsonPropertyName("id")] + public Guid Id { get; set; } + + [JsonPropertyName("firstName")] + public string FirstName { get; set; } + + [JsonPropertyName("lastName")] + public string LastName { get; set; } + + [JsonPropertyName("age")] + public int Age { get; set; } + } +} \ No newline at end of file diff --git a/CacheDrive.Tests/CachingSimpleTypesHashKeyTests.cs b/CacheDrive.Tests/CachingSimpleTypesHashKeyTests.cs new file mode 100644 index 0000000..151b627 --- /dev/null +++ b/CacheDrive.Tests/CachingSimpleTypesHashKeyTests.cs @@ -0,0 +1,166 @@ +using CacheDrive.Configuration; +using CacheDrive.Services; +using CacheDrive.Tests.Helpers; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; + +namespace CacheDrive.Tests; + +public class CachingSimpleTypesHashKeyTests +{ + [Test] + public async Task SimpleTypesShouldBeProperlyCachedAndRestoredFromTheMemoryAsync() + { + // Arrange + ServiceProvider serviceProvider = TestHelper.CreateServiceProvider( + DateTime.Now, + cacheEnabled: true, + cacheExpirationType: CacheExpirationType.Hours, + cacheExpiration: 2, + cacheType: CacheType.Memory); + + ICacheService cacheService = serviceProvider.GetRequiredService(); + + // Act + (string key, int value) intItem = ("int_test_async", 10); + await cacheService.SetAsync(intItem.key, intItem.value, hashKey: true); + + (string key, char value) charItem = ("char_test_async", 'p'); + await cacheService.SetAsync(charItem.key, charItem.value, hashKey: true); + + (string key, float value) floatItem = ("float_test_async", 4.8f); + await cacheService.SetAsync(floatItem.key, floatItem.value, hashKey: true); + + (string key, double value) doubleItem = ("double_test_async", 6.8d); + await cacheService.SetAsync(doubleItem.key, doubleItem.value, hashKey: true); + + (string key, bool value) boolItem = ("bool_test_async", true); + await cacheService.SetAsync(boolItem.key, boolItem.value, hashKey: true); + + int cachedIntItem = await cacheService.GetAsync(intItem.key, hashKey: true); + char cachedCharItem = await cacheService.GetAsync(charItem.key, hashKey: true); + float cachedFloatItem = await cacheService.GetAsync(floatItem.key, hashKey: true); + double cachedDoubleItem = await cacheService.GetAsync(doubleItem.key, hashKey: true); + bool cachedBoolItem = await cacheService.GetAsync(boolItem.key, hashKey: true); + + // Assert + cachedIntItem.Should().Be(intItem.value); + cachedCharItem.Should().Be(charItem.value); + cachedFloatItem.Should().Be(floatItem.value); + cachedDoubleItem.Should().Be(doubleItem.value); + cachedBoolItem.Should().Be(boolItem.value); + } + + [Test] + public void SimpleTypesShouldBeProperlyCachedAndRestoredFromTheMemory() + { + // Arrange + ServiceProvider serviceProvider = TestHelper.CreateServiceProvider( + DateTime.Now, + cacheEnabled: true, + cacheExpirationType: CacheExpirationType.Hours, + cacheExpiration: 2, + cacheType: CacheType.Memory); + + ICacheService cacheService = serviceProvider.GetRequiredService(); + + // Act + (string key, int value) intItem = ("int_test", 10); + cacheService.Set(intItem.key, intItem.value, hashKey: true); + + (string key, char value) charItem = ("char_test", 'p'); + cacheService.Set(charItem.key, charItem.value, hashKey: true); + + (string key, float value) floatItem = ("float_test", 4.8f); + cacheService.Set(floatItem.key, floatItem.value, hashKey: true); + + (string key, double value) doubleItem = ("double_test", 6.8d); + cacheService.Set(doubleItem.key, doubleItem.value, hashKey: true); + + (string key, bool value) boolItem = ("bool_test", true); + cacheService.Set(boolItem.key, boolItem.value, hashKey: true); + + int cachedIntItem = cacheService.Get(intItem.key, hashKey: true); + char cachedCharItem = cacheService.Get(charItem.key, hashKey: true); + float cachedFloatItem = cacheService.Get(floatItem.key, hashKey: true); + double cachedDoubleItem = cacheService.Get(doubleItem.key, hashKey: true); + bool cachedBoolItem = cacheService.Get(boolItem.key, hashKey: true); + + // Assert + cachedIntItem.Should().Be(intItem.value); + cachedCharItem.Should().Be(charItem.value); + cachedFloatItem.Should().Be(floatItem.value); + cachedDoubleItem.Should().Be(doubleItem.value); + cachedBoolItem.Should().Be(boolItem.value); + } + + [Test] + public async Task SimpleTypesShouldBeProperlyDeletedFromTheMemoryCacheAsync() + { + // Arrange + ServiceProvider serviceProvider = TestHelper.CreateServiceProvider( + DateTime.Now, + cacheEnabled: true, + cacheExpirationType: CacheExpirationType.Hours, + cacheExpiration: 2, + cacheType: CacheType.Memory); + + ICacheService cacheService = serviceProvider.GetRequiredService(); + + // Act + + // Add 50 items + for (int i = 0; i < 50; i++) + { + await cacheService.SetAsync($"test_{i}_async", i, hashKey: true); + } + + // Delete every second element + for (int i = 0; i < 50; i++) + { + if ((i + 1) % 2 == 0) + { + await cacheService.DeleteAsync($"test_{i}_async", hashKey: true); + } + } + + // // Assert + int countCachedItems = cacheService.CountCachedObjects(); + countCachedItems.Should().Be(25); + } + + [Test] + public void SimpleTypesShouldBeProperlyDeletedFromTheMemoryCache() + { + // Arrange + ServiceProvider serviceProvider = TestHelper.CreateServiceProvider( + DateTime.Now, + cacheEnabled: true, + cacheExpirationType: CacheExpirationType.Hours, + cacheExpiration: 2, + cacheType: CacheType.Memory); + + ICacheService cacheService = serviceProvider.GetRequiredService(); + + // Act + + // Add 50 items + for (int i = 0; i < 50; i++) + { + cacheService.Set($"test_{i}", i, hashKey: true); + } + + // Delete every second element + for (int i = 0; i < 50; i++) + { + if ((i + 1) % 2 == 0) + { + cacheService.Delete($"test_{i}", hashKey: true); + } + } + + // // Assert + int countCachedItems = cacheService.CountCachedObjects(); + countCachedItems.Should().Be(25); + } +} \ No newline at end of file diff --git a/CacheDrive.Tests/MemoryAndFileHashKeyTests.cs b/CacheDrive.Tests/MemoryAndFileHashKeyTests.cs new file mode 100644 index 0000000..81622ad --- /dev/null +++ b/CacheDrive.Tests/MemoryAndFileHashKeyTests.cs @@ -0,0 +1,83 @@ +using CacheDrive.Services; +using CacheDrive.Tests.Helpers; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; + +namespace CacheDrive.Tests; + +public class MemoryAndFileHashKeyTests +{ + [Test, Order(1)] + public async Task CacheShouldBeSavedCorrectly() + { + // Arrange + ServiceProvider serviceProvider = TestHelper.CreateServiceProvider(DateTime.Now); + + ICacheService cacheService = serviceProvider.GetRequiredService(); + + // Act + string key = "name"; + + await cacheService.SetAsync(key, "John", hashKey: true); + + // Assert + if (cacheService.TryGetValue(key, hashKey: true, out string cachedValue)) + { + cachedValue.Should().Be("John"); + await cacheService.FlushAsync(); + } + else + { + await cacheService.FlushAsync(); + Assert.Fail(); + } + + await cacheService.FlushAsync(); + } + + [Test, Order(2)] + public async Task CacheShouldBeLoadCorrectly() + { + // Arrange + ServiceProvider serviceProvider = TestHelper.CreateServiceProvider(DateTime.Now); + + ICacheService cacheService = serviceProvider.GetRequiredService(); + + string key = "name"; + + // Assert + if (cacheService.TryGetValue(key, hashKey: true, out string cachedValue)) + { + cachedValue.Should().Be("John"); + await cacheService.FlushAsync(); + } + else + { + await cacheService.FlushAsync(); + Assert.Fail(); + } + } + + [Test, Order(3)] + public async Task CacheShouldExpired() + { + // Arrange + ServiceProvider serviceProvider = TestHelper.CreateServiceProvider(DateTime.Now.AddHours(3)); + + ICacheService cacheService = serviceProvider.GetRequiredService(); + + string key = "name"; + + // Assert + if (!cacheService.TryGetValue(key, hashKey: true, out string _)) + { + await cacheService.FlushAsync(); + Assert.Pass(); + } + else + { + await cacheService.FlushAsync(); + Assert.Fail(); + } + } +} \ No newline at end of file diff --git a/CacheDrive.Tests/MemoryHashKeyTests.cs b/CacheDrive.Tests/MemoryHashKeyTests.cs new file mode 100644 index 0000000..3a02cae --- /dev/null +++ b/CacheDrive.Tests/MemoryHashKeyTests.cs @@ -0,0 +1,99 @@ +using CacheDrive.Configuration; +using CacheDrive.Services; +using CacheDrive.Tests.Helpers; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; + +namespace CacheDrive.Tests; + +public class MemoryHashKeyTests +{ + [TestCase("key1", "one", false)] + [TestCase("key2","Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", false)] + [TestCase("long-key-long-key-long-key-long-key&long-key-long-key&long-key&long-key-long-key-long-key-long-key-long-key","Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", false)] + [TestCase("key1", "one", true)] + [TestCase("key2","Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", true)] + [TestCase("long-key-long-key-long-key-long-key&long-key-long-key&long-key&long-key-long-key-long-key-long-key-long-key","Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", true)] + public async Task StringShouldBeCorrectlySavedAndReadFromTheCacheInMemory(string key, string text, bool hashKey) + { + // Arrange + ServiceProvider serviceProvider = TestHelper.CreateServiceProvider( + DateTime.Now, + cacheEnabled: true, + cacheExpirationType: CacheExpirationType.Hours, + cacheExpiration: 2, + cacheType: CacheType.Memory); + + ICacheService cacheService = serviceProvider.GetRequiredService(); + + // Act + await cacheService.SetAsync(key, text, hashKey); + + string resultValue = await cacheService.GetAsync(key, hashKey); + + // Assert + resultValue.Should().Be(text); + } + + [Test] + public void ClearingTheCacheShouldRemoveAllObjectsFromTheCache() + { + // Arrange + ServiceProvider serviceProvider = TestHelper.CreateServiceProvider( + DateTime.Now, + cacheEnabled: true, + cacheExpirationType: CacheExpirationType.Hours, + cacheExpiration: 2, + cacheType: CacheType.Memory); + + ICacheService cacheService = serviceProvider.GetRequiredService(); + + // Act + for (int i = 0; i < 100; i++) + { + cacheService.Set($"k-{i}", i, hashKey: true); + } + + // Assert + cacheService.CountCachedObjects().Should().Be(100); + cacheService.ClearCache(); + cacheService.CountCachedObjects().Should().Be(0); + } + + [Test] + public void ClearingTheCacheShouldRemoveAllObjectsFromTheCache2() + { + // Arrange + ServiceProvider serviceProvider = TestHelper.CreateServiceProvider( + DateTime.Now, + cacheEnabled: true, + cacheExpirationType: CacheExpirationType.Hours, + cacheExpiration: 2, + cacheType: CacheType.Memory); + + ICacheService cacheService = serviceProvider.GetRequiredService(); + IDateService dateService = serviceProvider.GetRequiredService(); + + // Act + for (int i = 0; i < 50; i++) + { + cacheService.Set($"h0-{i}", i, hashKey: true); + } + + dateService.SetUtcNow(dateService.GetUtcNow().AddHours(1)); + + for (int i = 0; i < 50; i++) + { + cacheService.Set($"h1-{i}", i); + } + + dateService.SetUtcNow(dateService.GetUtcNow().AddHours(2)); + + // Assert + cacheService.CountCachedObjects().Should().Be(100); + + cacheService.ClearExpiredObjects(); + + cacheService.CountCachedObjects().Should().Be(50); + } +} \ No newline at end of file