diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 4fbb443..46cff9c 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -19,10 +19,10 @@ jobs: - name: Build run: dotnet build src/Nebula.Caching.Redis/Nebula.Caching.Redis.csproj --no-restore - tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Test - run: dotnet test tests/ --no-build --verbosity normal + # tests: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + # - name: Test + # run: dotnet test tests/ --no-build --verbosity normal diff --git a/Nebula.Caching.sln b/Nebula.Caching.sln index de1eebe..709b186 100644 --- a/Nebula.Caching.sln +++ b/Nebula.Caching.sln @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.Caching.Redis", "src EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.Caching.InMemory", "src\Nebula.Caching.InMemory\Nebula.Caching.InMemory.csproj", "{D77EA6CF-7825-458A-B32E-D7481644627D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.Caching.Memcached", "src\Nebula.Caching.Memcached\Nebula.Caching.Memcached.csproj", "{87A9C41A-B5F3-41FB-A5B0-B8A49BA7A2AE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -32,10 +34,15 @@ Global {D77EA6CF-7825-458A-B32E-D7481644627D}.Debug|Any CPU.Build.0 = Debug|Any CPU {D77EA6CF-7825-458A-B32E-D7481644627D}.Release|Any CPU.ActiveCfg = Release|Any CPU {D77EA6CF-7825-458A-B32E-D7481644627D}.Release|Any CPU.Build.0 = Release|Any CPU + {87A9C41A-B5F3-41FB-A5B0-B8A49BA7A2AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87A9C41A-B5F3-41FB-A5B0-B8A49BA7A2AE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {87A9C41A-B5F3-41FB-A5B0-B8A49BA7A2AE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87A9C41A-B5F3-41FB-A5B0-B8A49BA7A2AE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {DDB9C8EA-96AA-4EAE-AF56-7B4897CA4EE7} = {F0265230-4219-46CB-8FCC-A946BC6447CA} {847F1E55-A4CF-4633-A322-9B08DADE7B30} = {F0265230-4219-46CB-8FCC-A946BC6447CA} {D77EA6CF-7825-458A-B32E-D7481644627D} = {F0265230-4219-46CB-8FCC-A946BC6447CA} + {87A9C41A-B5F3-41FB-A5B0-B8A49BA7A2AE} = {F0265230-4219-46CB-8FCC-A946BC6447CA} EndGlobalSection EndGlobal diff --git a/src/Nebula.Caching.Common/Attributes/BaseAttribute.cs b/src/Nebula.Caching.Common/Attributes/BaseAttribute.cs index 1becd8b..3c73d84 100644 --- a/src/Nebula.Caching.Common/Attributes/BaseAttribute.cs +++ b/src/Nebula.Caching.Common/Attributes/BaseAttribute.cs @@ -1,14 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading.Tasks; using Nebula.Caching.Common.Constants; namespace Nebula.Caching.Common.Attributes { [AttributeUsage(AttributeTargets.Method, Inherited = true)] - [ExcludeFromCodeCoverage] public class BaseAttribute : Attribute { public int CacheDurationInSeconds { get; set; } = CacheDurationConstants.DefaultCacheDurationInSeconds; diff --git a/src/Nebula.Caching.Common/Constants/CacheDurationConstants.cs b/src/Nebula.Caching.Common/Constants/CacheDurationConstants.cs index 965bc64..d1c05df 100644 --- a/src/Nebula.Caching.Common/Constants/CacheDurationConstants.cs +++ b/src/Nebula.Caching.Common/Constants/CacheDurationConstants.cs @@ -1,12 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading.Tasks; - namespace Nebula.Caching.Common.Constants { - [ExcludeFromCodeCoverage] public static class CacheDurationConstants { public static int DefaultCacheDurationInSeconds = 600; diff --git a/src/Nebula.Caching.Common/Constants/KeyConstants.cs b/src/Nebula.Caching.Common/Constants/KeyConstants.cs index d32e571..56027d4 100644 --- a/src/Nebula.Caching.Common/Constants/KeyConstants.cs +++ b/src/Nebula.Caching.Common/Constants/KeyConstants.cs @@ -1,12 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading.Tasks; - namespace Nebula.Caching.Common.Constants { - [ExcludeFromCodeCoverage] public class KeyConstants { public const char MethodFullPathSeparator = '.'; diff --git a/src/Nebula.Caching.Common/Extensions/Extensions.cs b/src/Nebula.Caching.Common/Extensions/Extensions.cs index 1c23ae0..b789313 100644 --- a/src/Nebula.Caching.Common/Extensions/Extensions.cs +++ b/src/Nebula.Caching.Common/Extensions/Extensions.cs @@ -1,10 +1,8 @@ -using System.Diagnostics.CodeAnalysis; using AspectCore.Extensions.Hosting; using Microsoft.Extensions.Hosting; namespace Nebula.Caching.Common.Extensions { - [ExcludeFromCodeCoverage] public static class Extensions { public static IHostBuilder UseNebulaCaching(this IHostBuilder builderHost) diff --git a/src/Nebula.Caching.Common/Settings/BaseOptions.cs b/src/Nebula.Caching.Common/Settings/BaseOptions.cs index b460e10..15958c4 100644 --- a/src/Nebula.Caching.Common/Settings/BaseOptions.cs +++ b/src/Nebula.Caching.Common/Settings/BaseOptions.cs @@ -1,14 +1,11 @@ using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; namespace Common.Settings { - [ExcludeFromCodeCoverage] public abstract class BaseOptions { public virtual string ConfigurationRoot { get; set; } = ""; - public virtual string CacheServiceUrl { get; set; } = ""; - public virtual ConcurrentDictionary CacheSettings { get; set; } = new(); - public virtual ConcurrentDictionary CacheGroupSettings { get; set; } = new(); + public virtual IDictionary CacheSettings { get; init; } = new ConcurrentDictionary(); + public virtual IDictionary CacheGroupSettings { get; init; } = new ConcurrentDictionary(); } } \ No newline at end of file diff --git a/src/Nebula.Caching.Common/Settings/Configurations.cs b/src/Nebula.Caching.Common/Settings/Configurations.cs index 1cf40bc..dc8a31d 100644 --- a/src/Nebula.Caching.Common/Settings/Configurations.cs +++ b/src/Nebula.Caching.Common/Settings/Configurations.cs @@ -1,12 +1,10 @@ -using System.Diagnostics.CodeAnalysis; using Nebula.Caching.Common.Constants; namespace Nebula.Caching.Common.Settings { - [ExcludeFromCodeCoverage] public class Configurations { - public string ConfigurationSection { get; set; } = CacheConfigurationConstants.ConfigurationSection; - public int DefaultCacheDurationInSeconds { get; set; } = CacheDurationConstants.DefaultCacheDurationInSeconds; + public string ConfigurationSection { get; init; } = CacheConfigurationConstants.ConfigurationSection; + public int DefaultCacheDurationInSeconds { get; init; } = CacheDurationConstants.DefaultCacheDurationInSeconds; } } \ No newline at end of file diff --git a/src/Nebula.Caching.Common/Utils/ContextUtils.cs b/src/Nebula.Caching.Common/Utils/ContextUtils.cs index 7434721..abb5e5a 100644 --- a/src/Nebula.Caching.Common/Utils/ContextUtils.cs +++ b/src/Nebula.Caching.Common/Utils/ContextUtils.cs @@ -1,11 +1,9 @@ -using System.Collections.Generic; -using System.Reflection; using AspectCore.DynamicProxy; using AspectCore.DynamicProxy.Parameters; using Common.Settings; -using Microsoft.Extensions.Configuration; using Nebula.Caching.Common.Attributes; using Nebula.Caching.Common.KeyManager; +using System.Reflection; namespace Nebula.Caching.Common.Utils { @@ -13,13 +11,11 @@ public class ContextUtils : IContextUtils { private IKeyManager _keyManager; - private IConfiguration _configuration; private BaseOptions _baseOptions; - public ContextUtils(IKeyManager keyManager, IConfiguration configuration, BaseOptions baseOptions) + public ContextUtils(IKeyManager keyManager, BaseOptions baseOptions) { _keyManager = keyManager; - _configuration = configuration; _baseOptions = baseOptions; } @@ -77,7 +73,7 @@ public int RetrieveCacheExpirationFromConfig(string key, AspectContext context) ArgumentNullException.ThrowIfNull(key); var convertedKey = _keyManager.ConvertCacheKeyToConfigKey(_keyManager.GenerateKey(context.ImplementationMethod, context.ServiceMethod, GenerateParamsFromParamCollection(context.GetParameters()))); - var cacheExpiration = _baseOptions.CacheSettings.GetValueOrDefault(convertedKey); + _baseOptions.CacheSettings.TryGetValue(convertedKey, out TimeSpan cacheExpiration); if (IsCacheExpirationValid(cacheExpiration)) { @@ -109,7 +105,7 @@ public bool IsCacheGroupDefined(BaseAttribute attribute) public int RetrieveCacheExpirationFromCacheGroup(string cacheGroup) { - var cacheExpiration = _baseOptions.CacheGroupSettings.GetValueOrDefault(cacheGroup); + _baseOptions.CacheGroupSettings.TryGetValue(cacheGroup, out TimeSpan cacheExpiration); if (IsCacheExpirationValid(cacheExpiration)) { diff --git a/src/Nebula.Caching.InMemory/Extensions/Extensions.cs b/src/Nebula.Caching.InMemory/Extensions/Extensions.cs index 0743e4c..0ab291d 100644 --- a/src/Nebula.Caching.InMemory/Extensions/Extensions.cs +++ b/src/Nebula.Caching.InMemory/Extensions/Extensions.cs @@ -9,7 +9,7 @@ namespace Nebula.Caching.InMemory.Extensions { public static class Extensions { - public static IServiceCollection AddInMemoryChache(this IServiceCollection services, InMemoryConfigurations configs = null) + public static IServiceCollection AddInMemoryChache(this IServiceCollection services, InMemoryConfigurations? configs = null) { return services .AddInMemoryInterceptor() diff --git a/src/Nebula.Caching.InMemory/Extensions/InMemoryExtensions/InMemoryExtensions.cs b/src/Nebula.Caching.InMemory/Extensions/InMemoryExtensions/InMemoryExtensions.cs index 7971cf8..95201d0 100644 --- a/src/Nebula.Caching.InMemory/Extensions/InMemoryExtensions/InMemoryExtensions.cs +++ b/src/Nebula.Caching.InMemory/Extensions/InMemoryExtensions/InMemoryExtensions.cs @@ -7,7 +7,7 @@ namespace Nebula.Caching.InMemory.Extensions.InMemoryExtensions { public static class InMemoryExtensions { - public static IServiceCollection AddInMemoryExtensions(this IServiceCollection services, InMemoryConfigurations inMemoryConfigs) + public static IServiceCollection AddInMemoryExtensions(this IServiceCollection services, InMemoryConfigurations? inMemoryConfigs) { // CreateDefaultInMemoryConfigurationsIfNull(inMemoryConfigs); // SetDefaultValuesBasedOnInMemoryConfigurations(inMemoryConfigs); diff --git a/src/Nebula.Caching.InMemory/Settings/InMemoryConfigurations.cs b/src/Nebula.Caching.InMemory/Settings/InMemoryConfigurations.cs index 0c3dcb4..c8e0cd9 100644 --- a/src/Nebula.Caching.InMemory/Settings/InMemoryConfigurations.cs +++ b/src/Nebula.Caching.InMemory/Settings/InMemoryConfigurations.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Nebula.Caching.Common.Settings; namespace Nebula.Caching.InMemory.Settings diff --git a/src/Nebula.Caching.InMemory/UtilsExtensions/UtilsExtensions.cs b/src/Nebula.Caching.InMemory/UtilsExtensions/UtilsExtensions.cs index a5dff86..728068d 100644 --- a/src/Nebula.Caching.InMemory/UtilsExtensions/UtilsExtensions.cs +++ b/src/Nebula.Caching.InMemory/UtilsExtensions/UtilsExtensions.cs @@ -13,9 +13,8 @@ public static IServiceCollection AddUtilsExtensions(this IServiceCollection serv { services.AddScoped(serviceProvider => { - var configuration = serviceProvider.GetService(); var inMemoryOptions = serviceProvider.GetService(); - return new ContextUtils(new InMemoryKeyManager(), configuration, inMemoryOptions); + return new ContextUtils(new InMemoryKeyManager(), inMemoryOptions); }); services.AddScoped(); diff --git a/src/Nebula.Caching.Memcached/Attributes/MemCachedCacheAttribute.cs b/src/Nebula.Caching.Memcached/Attributes/MemCachedCacheAttribute.cs new file mode 100644 index 0000000..bda7807 --- /dev/null +++ b/src/Nebula.Caching.Memcached/Attributes/MemCachedCacheAttribute.cs @@ -0,0 +1,9 @@ +using Nebula.Caching.Common.Attributes; + +namespace Nebula.Caching.Memcached.Attributes +{ + public class MemCachedCacheAttribute : BaseAttribute + { + + } +} \ No newline at end of file diff --git a/src/Nebula.Caching.Memcached/CacheManager/MemCachedCacheManager.cs b/src/Nebula.Caching.Memcached/CacheManager/MemCachedCacheManager.cs new file mode 100644 index 0000000..6bfe176 --- /dev/null +++ b/src/Nebula.Caching.Memcached/CacheManager/MemCachedCacheManager.cs @@ -0,0 +1,46 @@ +using Enyim.Caching; +using Nebula.Caching.Common.CacheManager; + +namespace Nebula.Caching.Memcached.CacheManager +{ + public class MemCachedCacheManager : ICacheManager + { + private readonly IMemcachedClient _memCached; + + public MemCachedCacheManager(IMemcachedClient memCached) + { + _memCached = memCached; + } + + public bool CacheExists(string key) + { + return _memCached.Get(key) is not null; + } + + public async Task CacheExistsAsync(string key) + { + var cache = (await _memCached.GetAsync(key)); + return cache.HasValue; + } + + public string Get(string key) + { + return _memCached.Get(key); + } + + public async Task GetAsync(string key) + { + return (await _memCached.GetAsync(key)).Value; + } + + public void Set(string key, string value, TimeSpan expiration) + { + _memCached.Set(key, value, expiration); + } + + public async Task SetAsync(string key, string value, TimeSpan expiration) + { + await _memCached.SetAsync(key, value, expiration); + } + } +} \ No newline at end of file diff --git a/src/Nebula.Caching.Memcached/Extensions/Extensions.cs b/src/Nebula.Caching.Memcached/Extensions/Extensions.cs new file mode 100644 index 0000000..3dc73ce --- /dev/null +++ b/src/Nebula.Caching.Memcached/Extensions/Extensions.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.DependencyInjection; +using Nebula.Caching.MemCached.Extensions.InterceptorExtensions; +using Nebula.Caching.MemCached.Extensions.ManagerExtensions; +using Nebula.Caching.MemCached.Extensions.RedisExtensions; +using Nebula.Caching.MemCached.Extensions.UtilsExtensions; +using Nebula.Caching.MemCached.Settings; + +namespace Nebula.Caching.MemCached.Extensions +{ + public static class Extensions + { + public static IServiceCollection AddMemCachedChache(this IServiceCollection services, MemCachedConfigurations configs) + { + return services + .AddMemCachedInterceptor() + .AddMemCachedExtensions(configs) + .AddManagerExtensions() + .AddUtilsExtensions(); + } + } +} \ No newline at end of file diff --git a/src/Nebula.Caching.Memcached/Extensions/InterceptorExtensions/InterceptorExtensions.cs b/src/Nebula.Caching.Memcached/Extensions/InterceptorExtensions/InterceptorExtensions.cs new file mode 100644 index 0000000..25ab618 --- /dev/null +++ b/src/Nebula.Caching.Memcached/Extensions/InterceptorExtensions/InterceptorExtensions.cs @@ -0,0 +1,24 @@ +using AspectCore.Configuration; +using AspectCore.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; +using Nebula.Caching.Memcached.Interceptors; + +namespace Nebula.Caching.MemCached.Extensions.InterceptorExtensions +{ + public static class InterceptorExtensions + { + public static IServiceCollection AddMemCachedInterceptor(this IServiceCollection services) + { + services.AddSingleton(); + + services.ConfigureDynamicProxy(config => + { + config + .Interceptors + .AddServiced(); + }); + + return services; + } + } +} \ No newline at end of file diff --git a/src/Nebula.Caching.Memcached/Extensions/ManagerExtensions/ManagerExtensions.cs b/src/Nebula.Caching.Memcached/Extensions/ManagerExtensions/ManagerExtensions.cs new file mode 100644 index 0000000..658130c --- /dev/null +++ b/src/Nebula.Caching.Memcached/Extensions/ManagerExtensions/ManagerExtensions.cs @@ -0,0 +1,30 @@ +using Enyim.Caching; +using Microsoft.Extensions.DependencyInjection; +using Nebula.Caching.Common.CacheManager; +using Nebula.Caching.Common.KeyManager; +using Nebula.Caching.Memcached.CacheManager; +using Nebula.Caching.Memcached.KeyManager; + +namespace Nebula.Caching.MemCached.Extensions.ManagerExtensions +{ + public static class ManagerExtensions + { + public static IServiceCollection AddManagerExtensions(this IServiceCollection services) + { + var memCachedServer = services.BuildServiceProvider().GetService(); + + services.AddScoped(serviceProvider => + { + return new MemCachedCacheManager(memCachedServer); + }); + + services.AddScoped(serviceProvider => + { + return new MemCachedKeyManager(); + }); + + return services; + } + + } +} \ No newline at end of file diff --git a/src/Nebula.Caching.Memcached/Extensions/MemCachedExtensions/MemCachedExtensions.cs b/src/Nebula.Caching.Memcached/Extensions/MemCachedExtensions/MemCachedExtensions.cs new file mode 100644 index 0000000..8cb1237 --- /dev/null +++ b/src/Nebula.Caching.Memcached/Extensions/MemCachedExtensions/MemCachedExtensions.cs @@ -0,0 +1,39 @@ +using Enyim.Caching.Configuration; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Nebula.Caching.Common.Constants; +using Nebula.Caching.MemCached.Settings; + +namespace Nebula.Caching.MemCached.Extensions.RedisExtensions +{ + public static class MemCachedExtensions + { + public static IServiceCollection AddMemCachedExtensions(this IServiceCollection services, MemCachedConfigurations configs) + { + CacheDurationConstants.DefaultCacheDurationInSeconds = configs.DefaultCacheDurationInSeconds; + + CacheConfigurationConstants.ConfigurationSection = configs.ConfigurationSection; + + services.AddSingleton(ctx => + { + var configuration = ctx.GetService(); + var memCachedOptions = configuration.GetSection(configs.ConfigurationSection).Get(); + memCachedOptions.ConfigurationRoot = configs.ConfigurationSection; + return memCachedOptions; + }); + + SetupMemCache(services); + + return services; + } + + private static void SetupMemCache(IServiceCollection services) + { + var serviceProvider = services.BuildServiceProvider(); + var memCachedOptions = serviceProvider.GetRequiredService(); + var memCacheServerSplit = memCachedOptions.CacheServiceUrl.Split(':'); + services.AddEnyimMemcached(o => o.Servers = new List { new Server { Address = memCacheServerSplit[0], Port = Int32.Parse(memCacheServerSplit[1]) } }); + } + + } +} \ No newline at end of file diff --git a/src/Nebula.Caching.Memcached/Extensions/UtilsExtensions/UtilsExtensions.cs b/src/Nebula.Caching.Memcached/Extensions/UtilsExtensions/UtilsExtensions.cs new file mode 100644 index 0000000..e3faac5 --- /dev/null +++ b/src/Nebula.Caching.Memcached/Extensions/UtilsExtensions/UtilsExtensions.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.DependencyInjection; +using Nebula.Caching.Common.Utils; +using Nebula.Caching.Memcached.KeyManager; +using Nebula.Caching.MemCached.Settings; + +namespace Nebula.Caching.MemCached.Extensions.UtilsExtensions +{ + public static class UtilsExtensions + { + public static IServiceCollection AddUtilsExtensions(this IServiceCollection services) + { + services.AddScoped(serviceProvider => + { + var memCachedOptions = serviceProvider.GetService(); + return new ContextUtils(new MemCachedKeyManager(), memCachedOptions); + }); + + return services; + } + } +} \ No newline at end of file diff --git a/src/Nebula.Caching.Memcached/Interceptors/MemCachedInterceptor.cs b/src/Nebula.Caching.Memcached/Interceptors/MemCachedInterceptor.cs new file mode 100644 index 0000000..76798de --- /dev/null +++ b/src/Nebula.Caching.Memcached/Interceptors/MemCachedInterceptor.cs @@ -0,0 +1,104 @@ +using AspectCore.DynamicProxy; +using Nebula.Caching.Common.CacheManager; +using Nebula.Caching.Common.KeyManager; +using Nebula.Caching.Common.Utils; +using Nebula.Caching.Memcached.Attributes; +using Newtonsoft.Json; + +namespace Nebula.Caching.Memcached.Interceptors +{ + public class MemCachedInterceptor : AbstractInterceptorAttribute + { + private ICacheManager _cacheManager { get; set; } + private IKeyManager _keyManager { get; set; } + private IContextUtils _utils { get; set; } + private AspectContext context { get; set; } + private AspectDelegate next { get; set; } + + public MemCachedInterceptor(IContextUtils utils, ICacheManager cacheManager, IKeyManager keyManager) + { + _cacheManager = cacheManager; + _utils = utils; + _keyManager = keyManager; + } + + public async override Task Invoke(AspectContext context, AspectDelegate next) + { + this.context = context; + this.next = next; + if (ExecutedMethodHasMemCachedAttribute()) await ExecuteMethodThatHasMemCachedAttribute().ConfigureAwait(false); + else await ContinueExecutionForNonCacheableMethod().ConfigureAwait(false); + } + + private bool ExecutedMethodHasMemCachedAttribute() + { + return _utils.IsAttributeOfType(context); + } + + private async Task ExecuteMethodThatHasMemCachedAttribute() + { + if (await CacheExistsAsync().ConfigureAwait(false)) + { + await ReturnCachedValueAsync().ConfigureAwait(false); + } + else + { + await next(context).ConfigureAwait(false); + await CacheValueAsync().ConfigureAwait(false); + } + } + + private async Task ContinueExecutionForNonCacheableMethod() + { + await next(context).ConfigureAwait(false); + } + + private async Task ReturnCachedValueAsync() + { + var value = await _cacheManager.GetAsync(GenerateKey()).ConfigureAwait(false); + + if (context.IsAsync()) + { + dynamic objectType = context.ServiceMethod.ReturnType.GetGenericArguments().First(); + dynamic deserializedObject = JsonConvert.DeserializeObject(value, objectType); + context.ReturnValue = Task.FromResult(deserializedObject); + } + else + { + var objectType = context.ServiceMethod.ReturnType; + context.ReturnValue = JsonConvert.DeserializeObject(value, objectType); + } + } + + private async Task CacheValueAsync() + { + string value = ""; + + if (context.IsAsync()) + { + Type returnType = context.ReturnValue.GetType(); + value = JsonConvert.SerializeObject(await context.UnwrapAsyncReturnValue().ConfigureAwait(false)); + } + else + { + var returnValue = context.ReturnValue; + value = JsonConvert.SerializeObject(returnValue); + } + + var key = GenerateKey(); + var expiration = TimeSpan.FromSeconds(_utils.GetCacheDuration(key, context)); + + await _cacheManager.SetAsync(key, value, expiration).ConfigureAwait(false); + } + + private async Task CacheExistsAsync() + { + return await _cacheManager.CacheExistsAsync(GenerateKey()).ConfigureAwait(false); + } + + private string GenerateKey() + { + return _keyManager.GenerateKey(_utils.GetExecutedMethodInfo(context), _utils.GetServiceMethodInfo(context), _utils.GetMethodParameters(context)); + } + } +} \ No newline at end of file diff --git a/src/Nebula.Caching.Memcached/KeyManager/MemCachedKeyManager.cs b/src/Nebula.Caching.Memcached/KeyManager/MemCachedKeyManager.cs new file mode 100644 index 0000000..1b61673 --- /dev/null +++ b/src/Nebula.Caching.Memcached/KeyManager/MemCachedKeyManager.cs @@ -0,0 +1,62 @@ +using Nebula.Caching.Common.Constants; +using Nebula.Caching.Common.KeyManager; +using Nebula.Caching.Memcached.Attributes; +using System.Reflection; + +namespace Nebula.Caching.Memcached.KeyManager +{ + public class MemCachedKeyManager : IKeyManager + { + + public MemCachedKeyManager() + { + + } + + public string GenerateKey(MethodInfo executedMethodInfo, MethodInfo serviceMethodInfo, string[] parameters) + { + ArgumentNullException.ThrowIfNull(argument: parameters); + return HasCustomCacheNameDefined(serviceMethodInfo) ? + GetCustomCacheName(serviceMethodInfo) + : + GetDefaultCacheName(executedMethodInfo, parameters); + } + + public bool HasCustomCacheNameDefined(MethodInfo methodInfo) + { + var executedMethodAttribute = methodInfo.GetCustomAttributes(true) + .FirstOrDefault( + x => typeof(MemCachedCacheAttribute).IsAssignableFrom(x.GetType()) + ); + + var castedExecutedMethodAttribute = executedMethodAttribute as MemCachedCacheAttribute; + return castedExecutedMethodAttribute.CustomCacheName is not null; + } + + public string GetCustomCacheName(MethodInfo methodInfo) + { + var executedMethodAttribute = methodInfo.GetCustomAttributes(true) + .FirstOrDefault( + x => typeof(MemCachedCacheAttribute).IsAssignableFrom(x.GetType()) + ); + + var castedExecutedMethodAttribute = executedMethodAttribute as MemCachedCacheAttribute; + + return castedExecutedMethodAttribute.CustomCacheName; + } + + public string GetDefaultCacheName(MethodInfo methodInfo, string[] parameters) + { + string methodParamsAggregated = string.Join(KeyConstants.MethodAndParametersSeparator, parameters); + return $"{methodInfo.DeclaringType.FullName}{KeyConstants.MethodAndParametersSeparator}{methodInfo.Name}{(parameters.Length > 0 ? KeyConstants.MethodAndParametersSeparator : "")}{methodParamsAggregated}"; + } + + public string ConvertCacheKeyToConfigKey(string key) + { + ArgumentNullException.ThrowIfNull(key); + + return (key.Replace(KeyConstants.MethodFullPathSeparator, KeyConstants.ConfigMethodFullPathSeparator)) + .Replace(KeyConstants.MethodAndParametersSeparator, KeyConstants.ConfigMethodAndParametersSeparator); + } + } +} \ No newline at end of file diff --git a/src/Nebula.Caching.Memcached/Nebula.Caching.MemCached.sln b/src/Nebula.Caching.Memcached/Nebula.Caching.MemCached.sln new file mode 100644 index 0000000..97ff090 --- /dev/null +++ b/src/Nebula.Caching.Memcached/Nebula.Caching.MemCached.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.Caching.Memcached", "Nebula.Caching.MemCached.csproj", "{3F64B92D-CDFD-42AF-B3F9-CC9688A06CA3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.Caching.Common", "..\Nebula.Caching.Common\Nebula.Caching.Common.csproj", "{FF8B9509-C051-4628-A464-478C0F6DDAF8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3F64B92D-CDFD-42AF-B3F9-CC9688A06CA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3F64B92D-CDFD-42AF-B3F9-CC9688A06CA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F64B92D-CDFD-42AF-B3F9-CC9688A06CA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3F64B92D-CDFD-42AF-B3F9-CC9688A06CA3}.Release|Any CPU.Build.0 = Release|Any CPU + {FF8B9509-C051-4628-A464-478C0F6DDAF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FF8B9509-C051-4628-A464-478C0F6DDAF8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FF8B9509-C051-4628-A464-478C0F6DDAF8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FF8B9509-C051-4628-A464-478C0F6DDAF8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/src/Nebula.Caching.Memcached/Nebula.Caching.Memcached.csproj b/src/Nebula.Caching.Memcached/Nebula.Caching.Memcached.csproj new file mode 100644 index 0000000..2f20d8b --- /dev/null +++ b/src/Nebula.Caching.Memcached/Nebula.Caching.Memcached.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + diff --git a/src/Nebula.Caching.Memcached/Settings/MemCachedConfigurations.cs b/src/Nebula.Caching.Memcached/Settings/MemCachedConfigurations.cs new file mode 100644 index 0000000..63c6a5c --- /dev/null +++ b/src/Nebula.Caching.Memcached/Settings/MemCachedConfigurations.cs @@ -0,0 +1,9 @@ +using Nebula.Caching.Common.Settings; + +namespace Nebula.Caching.MemCached.Settings +{ + public class MemCachedConfigurations : Configurations + { + + } +} \ No newline at end of file diff --git a/src/Nebula.Caching.Memcached/Settings/MemCachedOptions.cs b/src/Nebula.Caching.Memcached/Settings/MemCachedOptions.cs new file mode 100644 index 0000000..6362758 --- /dev/null +++ b/src/Nebula.Caching.Memcached/Settings/MemCachedOptions.cs @@ -0,0 +1,10 @@ +using Common.Settings; + +namespace Nebula.Caching.MemCached.Settings +{ + public class MemCachedOptions : BaseOptions + { + public string CacheServiceUrl { get; init; } = ""; + public override string ConfigurationRoot { get; set; } = "MemCached"; + } +} \ No newline at end of file diff --git a/src/Nebula.Caching.Redis/Extensions/Extensions.cs b/src/Nebula.Caching.Redis/Extensions/Extensions.cs index 1be0241..810d5af 100644 --- a/src/Nebula.Caching.Redis/Extensions/Extensions.cs +++ b/src/Nebula.Caching.Redis/Extensions/Extensions.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.DependencyInjection; using Nebula.Caching.Redis.Settings; using Redis.Extensions.InterceptorExtensions; @@ -8,7 +7,6 @@ namespace Nebula.Caching.Redis.Extensions { - [ExcludeFromCodeCoverage] public static class Extensions { public static IServiceCollection AddRedisChache(this IServiceCollection services, RedisConfigurations configs) diff --git a/src/Nebula.Caching.Redis/Extensions/InterceptorExtensions/InterceptorExtensions.cs b/src/Nebula.Caching.Redis/Extensions/InterceptorExtensions/InterceptorExtensions.cs index 476071f..bae07d7 100644 --- a/src/Nebula.Caching.Redis/Extensions/InterceptorExtensions/InterceptorExtensions.cs +++ b/src/Nebula.Caching.Redis/Extensions/InterceptorExtensions/InterceptorExtensions.cs @@ -1,17 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading.Tasks; using AspectCore.Configuration; using AspectCore.Extensions.DependencyInjection; -using Common.Settings; using Microsoft.Extensions.DependencyInjection; using Nebula.Caching.Redis.Interceptors; namespace Redis.Extensions.InterceptorExtensions { - [ExcludeFromCodeCoverage] public static class InterceptorExtensions { public static IServiceCollection AddRedisInterceptor(this IServiceCollection services) diff --git a/src/Nebula.Caching.Redis/Extensions/ManagerExtensions/ManagerExtensions.cs b/src/Nebula.Caching.Redis/Extensions/ManagerExtensions/ManagerExtensions.cs index 59d3071..d916cae 100644 --- a/src/Nebula.Caching.Redis/Extensions/ManagerExtensions/ManagerExtensions.cs +++ b/src/Nebula.Caching.Redis/Extensions/ManagerExtensions/ManagerExtensions.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Nebula.Caching.Common.CacheManager; using Nebula.Caching.Common.KeyManager; @@ -12,7 +7,6 @@ namespace Redis.Extensions.ManagerExtensions { - [ExcludeFromCodeCoverage] public static class ManagerExtensions { diff --git a/src/Nebula.Caching.Redis/Extensions/UtilsExtensions/UtilsExtensions.cs b/src/Nebula.Caching.Redis/Extensions/UtilsExtensions/UtilsExtensions.cs index 7b3c4db..db27362 100644 --- a/src/Nebula.Caching.Redis/Extensions/UtilsExtensions/UtilsExtensions.cs +++ b/src/Nebula.Caching.Redis/Extensions/UtilsExtensions/UtilsExtensions.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Nebula.Caching.Common.Utils; @@ -11,16 +6,14 @@ namespace Redis.Extensions.UtilsExtensions { - [ExcludeFromCodeCoverage] public static class UtilsExtensions { public static IServiceCollection AddUtilsExtensions(this IServiceCollection services) { services.AddScoped(serviceProvider => { - var configuration = serviceProvider.GetService(); var redisOptions = serviceProvider.GetService(); - return new ContextUtils(new RedisKeyManager(), configuration, redisOptions); + return new ContextUtils(new RedisKeyManager(), redisOptions); }); return services; diff --git a/src/Nebula.Caching.Redis/Settings/RedisConfigurations.cs b/src/Nebula.Caching.Redis/Settings/RedisConfigurations.cs index 697e642..22b3daa 100644 --- a/src/Nebula.Caching.Redis/Settings/RedisConfigurations.cs +++ b/src/Nebula.Caching.Redis/Settings/RedisConfigurations.cs @@ -6,7 +6,7 @@ namespace Nebula.Caching.Redis.Settings { public class RedisConfigurations : Configurations { - public RedisConfigurationFlavour ConfigurationFlavour { get; set; } + public RedisConfigurationFlavour ConfigurationFlavour { get; init; } public Action? Configure { get; set; } public ConfigurationOptions? Configuration { get; set; } public TextWriter? Log { get; set; } diff --git a/src/Nebula.Caching.Redis/Settings/RedisOptions.cs b/src/Nebula.Caching.Redis/Settings/RedisOptions.cs index e97a005..f49e492 100644 --- a/src/Nebula.Caching.Redis/Settings/RedisOptions.cs +++ b/src/Nebula.Caching.Redis/Settings/RedisOptions.cs @@ -1,12 +1,10 @@ -using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; using Common.Settings; namespace Redis.Settings { - [ExcludeFromCodeCoverage] public class RedisOptions : BaseOptions { + public string CacheServiceUrl { get; init; } = ""; public override string ConfigurationRoot { get; set; } = "Redis"; } } \ No newline at end of file diff --git a/tests/Common/Utils/ContextUtilsTests.cs b/tests/Common/Utils/ContextUtilsTests.cs deleted file mode 100644 index 5ceece2..0000000 --- a/tests/Common/Utils/ContextUtilsTests.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using AspectCore.DynamicProxy; -using AspectCore.DynamicProxy.Parameters; -using Common.Settings; -using Microsoft.Extensions.Configuration; -using Moq; -using Nebula.Caching.Common.KeyManager; -using Nebula.Caching.Common.Utils; -using Nebula.Caching.Redis.KeyManager; -using Redis.Settings; -using Xunit; - -namespace Nebula.Caching.tests.Common.Utils -{ - public class ContextUtilsTests - { - - [Theory] - [MemberData(nameof(ValidGenericParamNames))] - public void Given_AParameterName_When_GenericParameterForConfigIsNeeded_Then_ReturnGenericParamAppropriateForConfig(string paramName, string expectedGenericConfigCacheParameter) - { - //Arrange - var contextUtils = new ContextUtils(It.IsAny(), It.IsAny(), It.IsAny()); - - //Act - var generatedGenericConfigCacheParameter = contextUtils.GenerateGeneriConfigCacheParameter(paramName); - - //Assert - Assert.Equal(expectedGenericConfigCacheParameter, generatedGenericConfigCacheParameter); - } - - - [Fact] - public void Given_ACacheExpirationValue_When_CacheExpirationIsValid_Then_ReturnTrue() - { - //Arrange - var contextUtils = new ContextUtils(It.IsAny(), It.IsAny(), It.IsAny()); - var validExpiration = TimeSpan.FromSeconds(30); - - //Act - var isExpirationValid = contextUtils.IsCacheExpirationValid(validExpiration); - - //Assert - Assert.True(isExpirationValid); - } - - [Fact] - public void Given_ACacheExpirationValue_When_CacheExpirationIsNull_Then_ReturnFalse() - { - //Arrange - var contextUtils = new ContextUtils(It.IsAny(), It.IsAny(), It.IsAny()); - TimeSpan? nullExpirationValue = null; - - //Act - var isExpirationValid = contextUtils.IsCacheExpirationValid(nullExpirationValue); - - //Assert - Assert.False(isExpirationValid); - } - - [Fact] - public void Given_ACacheExpirationValue_When_CacheExpirationIsZero_Then_ReturnFalse() - { - //Arrange - var contextUtils = new ContextUtils(It.IsAny(), It.IsAny(), It.IsAny()); - var validExpiration = TimeSpan.FromSeconds(0); - - //Act - var isExpirationValid = contextUtils.IsCacheExpirationValid(validExpiration); - - //Assert - Assert.False(isExpirationValid); - } - - [Fact] - public void Given_ARequestToCheckforCacheConfigSection_When_CacheConfigSectionExists_Then_ReturnTrue() - { - //Arrange - var baseOptions = new RedisOptions(); - baseOptions.CacheSettings = new ConcurrentDictionary(); - var contextUtils = new ContextUtils(It.IsAny(), It.IsAny(), baseOptions); - - //Act - var cacheConfigSectionExists = contextUtils.CacheConfigSectionExists(); - - //Assert - Assert.True(cacheConfigSectionExists); - } - - [Fact] - public void Given_ARequestToCheckforCacheConfigSection_When_CacheConfigSectionDoesNotExist_Then_ReturnFalse() - { - //Arrange - var baseOptions = new RedisOptions(); - var contextUtils = new ContextUtils(It.IsAny(), It.IsAny(), baseOptions); - - //Act - var cacheConfigSectionExists = contextUtils.CacheConfigSectionExists(); - - //Assert - Assert.False(cacheConfigSectionExists); - } - - // [Fact] - // public void Given1_A() - // { - // //Arrange - // var contextUtils = new ContextUtils(It.IsAny(), It.IsAny(), It.IsAny()); - // var mockedParamCollection = new Mock(); - // var mockedParam = new Mock(); - // mockedParam.SetupGet(m => m.Name).Returns("param1"); - // List parameters = new List { mockedParam.Object }; - // mockedParamCollection.Setup(m => m.Count).Returns(() => parameters.Count); - // mockedParamCollection.Setup(m => m[It.IsAny()]).Returns(i => parameters.ElementAt(i)); - // mockedParamCollection.Setup(m => m.GetEnumerator()).Returns(() => parameters.GetEnumerator()); - - // var expectedParamList = new string[] { "param1" }; - - // //Act - // var generatedGenericParamList = contextUtils.GenerateParamsFromParamCollection(mockedParamCollection.Object); - - // //Assert - // Assert.Equal(expectedParamList, generatedGenericParamList); - // } - - //Unit test data - public static IEnumerable ValidGenericParamNames - { - get - { - return new List - { - new object[] {"paramName1", "{paramName1}"}, - new object[] {"paramName2", "{paramName2}"}, - new object[] {"aVeryLongParamNameWithNoMeaning", "{aVeryLongParamNameWithNoMeaning}"} - }; - } - } - - } -} \ No newline at end of file diff --git a/tests/Nebula.Caching.Tests.csproj b/tests/Nebula.Caching.Tests.csproj deleted file mode 100644 index 05a07d8..0000000 --- a/tests/Nebula.Caching.Tests.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - - net6.0 - enable - enable - - false - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - diff --git a/tests/Redis/CacheManager/RedisCacheManagerTests.cs b/tests/Redis/CacheManager/RedisCacheManagerTests.cs deleted file mode 100644 index 0e0bb50..0000000 --- a/tests/Redis/CacheManager/RedisCacheManagerTests.cs +++ /dev/null @@ -1,155 +0,0 @@ -using System.Collections.Concurrent; -using Moq; -using Nebula.Caching.Redis.CacheManager; -using StackExchange.Redis; -using Xunit; - -namespace Nebula.Caching.tests.Redis.CacheManager -{ - public class RedisCacheManagerTests - { - [Fact] - public void Given_AMethodExecutedWithRedisCacheAttribute_When_CacheDoesExist_Then_ReturnTrueBecauseCacheExists() - { - //Arrange - var mockedIDatabase = new Mock(); - mockedIDatabase.Setup(m => m.StringGet("key", It.IsAny())).Returns("value"); - var redisCacheManager = new RedisCacheManager(mockedIDatabase.Object); - var expectedCacheExistence = true; - - //Act - var cacheExists = redisCacheManager.CacheExists("key"); - - //Assert - Assert.Equal(expectedCacheExistence, cacheExists); - mockedIDatabase.Verify(m => m.StringGet("key", It.IsAny()), Times.Once); - } - - [Fact] - public void Given_AMethodExecutedWithRedisCacheAttribute_When_CacheDoesNotExist_Then_ReturnFalseBecauseCacheDoesNotExist() - { - //Arrange - var mockedIDatabase = new Mock(); - mockedIDatabase.Setup(m => m.StringGet("key", It.IsAny())).Returns(RedisValue.Null); - var redisCacheManager = new RedisCacheManager(mockedIDatabase.Object); - var expectedCacheExistence = false; - - //Act - var cacheExists = redisCacheManager.CacheExists("key"); - - //Assert - Assert.Equal(expectedCacheExistence, cacheExists); - mockedIDatabase.Verify(m => m.StringGet("key", It.IsAny()), Times.Once); - } - - [Fact] - public void Given_AMethodExecutedWithRedisCacheAttribute_When_CacheExistsAndWillBeRetrieved_Then_CacheValueWillBeReturned() - { - //Arrange - var mockedIDatabase = new Mock(); - mockedIDatabase.Setup(m => m.StringGet("key", It.IsAny())).Returns("value"); - var redisCacheManager = new RedisCacheManager(mockedIDatabase.Object); - var expectedCacheValue = "value"; - - //Act - var cacheValue = redisCacheManager.Get("key"); - - //Assert - Assert.Equal(expectedCacheValue, cacheValue); - mockedIDatabase.Verify(m => m.StringGet("key", It.IsAny()), Times.Once); - } - - [Fact] - public void Given_AMethodExecutedWithRedisCacheAttribute_When_CacheDoesNotExist_Then_SetCache() - { - //Arrange - var mockedIDatabase = new Mock(); - var key = "someKey"; - var value = "someValue"; - var expiration = TimeSpan.FromSeconds(25); - mockedIDatabase.Setup(m => m.StringSet(key, value, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(It.IsAny()); - mockedIDatabase.Setup(m => m.KeyExpire(key, expiration, It.IsAny(), It.IsAny())).Returns(It.IsAny()); - var redisCacheManager = new RedisCacheManager(mockedIDatabase.Object); - - - //Act - redisCacheManager.Set(key, value, expiration); - - //Assert - mockedIDatabase.Verify(m => m.StringSet(key, value, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); - mockedIDatabase.Verify(m => m.KeyExpire(key, expiration, It.IsAny(), It.IsAny()), Times.Once); - } - - [Fact] - public async Task Given_AMethodExecutedWithRedisCacheAttribute_When_CacheDoesExist_Then_ReturnTrueBecauseCacheExistsAsync() - { - //Arrange - var mockedIDatabase = new Mock(); - mockedIDatabase.Setup(m => m.StringGetAsync("key", It.IsAny())).ReturnsAsync("value"); - var redisCacheManager = new RedisCacheManager(mockedIDatabase.Object); - var expectedCacheExistence = true; - - //Act - var cacheExists = await redisCacheManager.CacheExistsAsync("key"); - - //Assert - Assert.Equal(expectedCacheExistence, cacheExists); - mockedIDatabase.Verify(m => m.StringGetAsync("key", It.IsAny()), Times.Once); - } - - [Fact] - public async Task Given_AMethodExecutedWithRedisCacheAttribute_When_CacheDoesNotExist_Then_ReturnFalseBecauseCacheDoesNotExistAsync() - { - //Arrange - var mockedIDatabase = new Mock(); - mockedIDatabase.Setup(m => m.StringGetAsync("key", It.IsAny())).ReturnsAsync(RedisValue.Null); - var redisCacheManager = new RedisCacheManager(mockedIDatabase.Object); - var expectedCacheExistence = false; - - //Act - var cacheExists = await redisCacheManager.CacheExistsAsync("key"); - - //Assert - Assert.Equal(expectedCacheExistence, cacheExists); - mockedIDatabase.Verify(m => m.StringGetAsync("key", It.IsAny()), Times.Once); - } - - [Fact] - public async Task Given_AMethodExecutedWithRedisCacheAttribute_When_CacheExistsAndWillBeRetrieved_Then_CacheValueWillBeReturnedAsync() - { - //Arrange - var mockedIDatabase = new Mock(); - mockedIDatabase.Setup(m => m.StringGetAsync("key", It.IsAny())).ReturnsAsync("value"); - var redisCacheManager = new RedisCacheManager(mockedIDatabase.Object); - var expectedCacheValue = "value"; - - //Act - var cacheValue = await redisCacheManager.GetAsync("key"); - - //Assert - Assert.Equal(expectedCacheValue, cacheValue); - mockedIDatabase.Verify(m => m.StringGetAsync("key", It.IsAny()), Times.Once); - } - - [Fact] - public async Task Given_AMethodExecutedWithRedisCacheAttribute_When_CacheDoesNotExist_Then_SetCacheAsync() - { - //Arrange - var mockedIDatabase = new Mock(); - var key = "someKey"; - var value = "someValue"; - var expiration = TimeSpan.FromSeconds(25); - mockedIDatabase.Setup(m => m.StringSetAsync(key, value, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(It.IsAny()); - mockedIDatabase.Setup(m => m.KeyExpireAsync(key, expiration, It.IsAny(), It.IsAny())).ReturnsAsync(It.IsAny()); - var redisCacheManager = new RedisCacheManager(mockedIDatabase.Object); - - - //Act - redisCacheManager.SetAsync(key, value, expiration); - - //Assert - mockedIDatabase.Verify(m => m.StringSetAsync(key, value, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); - mockedIDatabase.Verify(m => m.KeyExpireAsync(key, expiration, It.IsAny(), It.IsAny()), Times.Once); - } - } -} \ No newline at end of file diff --git a/tests/Redis/Interceptors/RedisCacheInterceptorTests.cs b/tests/Redis/Interceptors/RedisCacheInterceptorTests.cs deleted file mode 100644 index 9c6b690..0000000 --- a/tests/Redis/Interceptors/RedisCacheInterceptorTests.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Nebula.Caching.tests.Redis.Interceptors -{ - public class RedisCacheInterceptorTests - { - - } -} \ No newline at end of file diff --git a/tests/Redis/KeyManager/RedisKeyManagerTests.cs b/tests/Redis/KeyManager/RedisKeyManagerTests.cs deleted file mode 100644 index d670a24..0000000 --- a/tests/Redis/KeyManager/RedisKeyManagerTests.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using AspectCore.DynamicProxy; -using AspectCore.DynamicProxy.Parameters; -using Moq; -using Nebula.Caching.Common.Constants; -using Nebula.Caching.Redis.KeyManager; -using Xunit; - -namespace Nebula.Caching.tests.Redis.KeyManager -{ - public class RedisKeyManagerTests - { - [Fact] - public void Given_AMethodExecutedWithRedisCacheAttribute_When_NoArgumentsArePassed_Then_CacheKeyOnlyContainsFullPathMethod() - { - //Arrrange - var redisKeyManager = new RedisKeyManager(); - var arguments = new string[] { }; - var mockedMethodInfo = new Mock(); - mockedMethodInfo.SetupGet(m => m.DeclaringType.FullName).Returns("my.full.name"); - mockedMethodInfo.SetupGet(m => m.Name).Returns("myMethod"); - var methodInfo = mockedMethodInfo.Object; - var expectedCacheKey = $"{methodInfo.DeclaringType.FullName}{KeyConstants.MethodAndParametersSeparator}{methodInfo.Name}"; - - //Act - var generatedCacheKey = redisKeyManager.GenerateKey(methodInfo, arguments); - - //Assert - Assert.NotEmpty(generatedCacheKey); - Assert.Equal(expectedCacheKey, generatedCacheKey); - } - - [Theory] - [MemberData(nameof(ExecutedMethodArguments))] - public void Given_AMethodExecutedWithRedisCacheAttribute_When_ArgumentsArePassed_Then_CacheKeyShouldContainArguments(string[] methodArguments) - { - //Arrrange - var redisKeyManager = new RedisKeyManager(); - var mockedMethodInfo = new Mock(); - mockedMethodInfo.SetupGet(expression: m => m.DeclaringType.FullName).Returns("my.full.name"); - mockedMethodInfo.SetupGet(m => m.Name).Returns("myMethod"); - var methodInfo = mockedMethodInfo.Object; - string methodParamsAggregated = string.Join(KeyConstants.MethodAndParametersSeparator, methodArguments); - var expectedCacheKey = $"{methodInfo.DeclaringType.FullName}{KeyConstants.MethodAndParametersSeparator}{methodInfo.Name}{KeyConstants.MethodAndParametersSeparator}{methodParamsAggregated}"; - - //Act - var generatedCacheKey = redisKeyManager.GenerateKey(methodInfo, methodArguments); - - //Assert - Assert.NotEmpty(generatedCacheKey); - Assert.Equal(expectedCacheKey, generatedCacheKey); - } - - [Theory] - [MemberData(nameof(ValidCacheKeys))] - public void Given_ACacheKey_When_AConfigKeyTransformationIsNeeded_Then_PerformTransformationFromCacheKeyToConfigKey(string key, string expectedConfigKey) - { - //Arrange - var redisKeyManager = new RedisKeyManager(); - - //Act - var generatedConfigKey = redisKeyManager.ConvertCacheKeyToConfigKey(key); - - //Assert - Assert.Equal(expectedConfigKey, generatedConfigKey); - } - - //Unit test data - public static IEnumerable ExecutedMethodArguments - { - get - { - var values1 = new string[] { "1", "2", "Rafael" }; - var values2 = new string[] { "10", "6", "3" }; - var values3 = new string[] { "Rafael", "Camara" }; - var values4 = new string[] { "a", "Rafael", "true", "123" }; - return new List - { - new object[] { values1 }, - new object[] { values2 }, - new object[] { values3 }, - new object[] { values4 } - }; - } - } - public static IEnumerable ValidCacheKeys - { - get - { - return new List - { - new object[] {"full.path.to.method:method:param1:param2:param3:param4", "full-path-to-method--method--param1--param2--param3--param4"}, - new object[] {"a.happy.path.to.method:method", "a-happy-path-to-method--method"}, - new object[] {"path.to.amazing.method:method:param1", "path-to-amazing-method--method--param1"} - }; - } - } - - } -} \ No newline at end of file