School Commit Init
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,122 @@
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace CacheModule{
|
||||
|
||||
public class CacheService
|
||||
{
|
||||
private readonly Dictionary<string, Dictionary<string, CachedObject>> _cache = new Dictionary<string, Dictionary<string, CachedObject>>();
|
||||
|
||||
private static object _lock = new object();
|
||||
|
||||
private readonly Timer _timer = new Timer();
|
||||
|
||||
private static CacheService? _instance;
|
||||
|
||||
private CacheService(){
|
||||
_timer.Interval = 3600 * 1000;
|
||||
_timer.Elapsed += (sender, e) => ClearExpired();
|
||||
_timer.Start();
|
||||
}
|
||||
public static CacheService Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new CacheService();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(string functionName, string functionParams, object value, Duration duration)
|
||||
{
|
||||
Add(functionName, functionParams, value, (int)duration);
|
||||
}
|
||||
|
||||
public void Add(string functionName, string functionParams, object value, int duration)
|
||||
{
|
||||
if (!_cache.ContainsKey(functionName))
|
||||
{
|
||||
_cache[functionName] = new Dictionary<string, CachedObject>();
|
||||
}
|
||||
|
||||
_cache[functionName][functionParams] = new CachedObject(){
|
||||
Value = value,
|
||||
Timestamp = DateTime.Now,
|
||||
Duration = duration
|
||||
};
|
||||
}
|
||||
|
||||
public object? Get(string functionName, string functionParams)
|
||||
{
|
||||
if (!_cache.ContainsKey(functionName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!_cache[functionName].ContainsKey(functionParams))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var cachedObject = _cache[functionName][functionParams];
|
||||
if (cachedObject.IsExpired())
|
||||
{
|
||||
_cache[functionName].Remove(functionParams);
|
||||
return null;
|
||||
}
|
||||
return cachedObject.Value;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_cache.Clear();
|
||||
}
|
||||
|
||||
public void Clear(string functionName)
|
||||
{
|
||||
if (_cache.ContainsKey(functionName))
|
||||
{
|
||||
_cache.Remove(functionName);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear(string functionName, string functionParams)
|
||||
{
|
||||
if (_cache.ContainsKey(functionName))
|
||||
{
|
||||
if (_cache[functionName].ContainsKey(functionParams))
|
||||
{
|
||||
_cache[functionName].Remove(functionParams);
|
||||
if (_cache[functionName].Count == 0)
|
||||
{
|
||||
_cache.Remove(functionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearExpired()
|
||||
{
|
||||
foreach (var functionName in _cache.Keys)
|
||||
{
|
||||
foreach (var functionParams in _cache[functionName].Keys)
|
||||
{
|
||||
if (_cache[functionName][functionParams].IsExpired())
|
||||
{
|
||||
_cache[functionName].Remove(functionParams);
|
||||
if (_cache[functionName].Count == 0)
|
||||
{
|
||||
_cache.Remove(functionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace CacheModule{
|
||||
|
||||
public class CachedObject
|
||||
{
|
||||
public object? Value { get; set; }
|
||||
public DateTime Timestamp {get; set; }
|
||||
public int Duration { get; set; }
|
||||
|
||||
public bool IsExpired()
|
||||
{
|
||||
return DateTime.Now > Timestamp.AddSeconds(Duration);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace CacheModule{
|
||||
public enum Duration
|
||||
{
|
||||
OneSecond = 1,
|
||||
OneMinute = 60,
|
||||
OneHour = 3600,
|
||||
OneDay = 86400,
|
||||
OneWeek = 604800,
|
||||
OneMonth = 2592000,
|
||||
OneYear = 31536000,
|
||||
FiveSeconds = 5,
|
||||
ThirtySeconds = 30,
|
||||
ThirtyMinutes = 1800,
|
||||
SixHours = 21600,
|
||||
TwelveHours = 43200,
|
||||
TwoDays = 172800,
|
||||
ThreeDays = 259200,
|
||||
TwoWeeks = 1209600,
|
||||
ThreeMonths = 7776000,
|
||||
SixMonths = 15552000
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CacheModule\CacheModule.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,134 @@
|
||||
using Xunit;
|
||||
using CacheModule;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace CacheModuleTest{
|
||||
public class CacheServiceIntegratedTest
|
||||
{
|
||||
private class MockIntegration{
|
||||
private CacheService _cacheService;
|
||||
public Dictionary<string, List<int>> keyValuePairs = new Dictionary<string, List<int>>(){
|
||||
{"first", new List<int>(){1,2,3}},
|
||||
{"second", new List<int>(){4,5,6}},
|
||||
{"third", new List<int>(){7,8,9}}
|
||||
};
|
||||
public MockIntegration(){
|
||||
_cacheService = CacheService.Instance;
|
||||
}
|
||||
|
||||
public int Get(string key, int index){
|
||||
return keyValuePairs[key][index];
|
||||
}
|
||||
public int GetSum(string key){
|
||||
return keyValuePairs[key].Sum();
|
||||
}
|
||||
|
||||
public int CachedGet(string key, int index){
|
||||
var json = JsonConvert.SerializeObject(new List<object>(){key, index}) ?? string.Empty;
|
||||
var cachedObject = _cacheService.Get(nameof(Get), json);
|
||||
if(cachedObject != null){
|
||||
return (int)cachedObject;
|
||||
}
|
||||
var result = Get(key, index);
|
||||
_cacheService.Add(nameof(Get), json, result, Duration.OneMinute);
|
||||
return result;
|
||||
}
|
||||
|
||||
public int CachedGetSum(string key){
|
||||
var json = JsonConvert.SerializeObject(new List<object>(){key}) ?? string.Empty;
|
||||
var cachedObject = _cacheService.Get(nameof(GetSum), json);
|
||||
if(cachedObject != null){
|
||||
return (int)cachedObject;
|
||||
}
|
||||
var result = GetSum(key);
|
||||
_cacheService.Add(nameof(GetSum), json, result, Duration.OneMinute);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Clear(){
|
||||
_cacheService.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CachedGet_WhenCalledWithinDuration_ReturnsCachedValue()
|
||||
{
|
||||
// Arrange
|
||||
var mockIntegration = new MockIntegration();
|
||||
mockIntegration.Clear();
|
||||
var key = "first";
|
||||
var index = 1;
|
||||
var expected = mockIntegration.Get(key, index);
|
||||
var cachedExpected = mockIntegration.CachedGet(key, index);
|
||||
mockIntegration.keyValuePairs[key][index] = 100;
|
||||
|
||||
// Act
|
||||
var cachedResult = mockIntegration.CachedGet(key, index);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, cachedResult);
|
||||
Assert.Equal(cachedExpected, cachedResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CachedGet_WhenCalledAfterDuration_ReturnsNewValue()
|
||||
{
|
||||
// Arrange
|
||||
var mockIntegration = new MockIntegration();
|
||||
mockIntegration.Clear();
|
||||
var key = "first";
|
||||
var index = 1;
|
||||
var expected = mockIntegration.Get(key, index);
|
||||
var cachedExpected = mockIntegration.CachedGet(key, index);
|
||||
mockIntegration.keyValuePairs[key][index] = 100;
|
||||
|
||||
// Act
|
||||
System.Threading.Thread.Sleep(61000);
|
||||
var cachedResult = mockIntegration.CachedGet(key, index);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(100, cachedResult);
|
||||
Assert.NotEqual(cachedExpected, cachedResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CachedGetSum_WhenCalledWithinDuration_ReturnsCachedValue()
|
||||
{
|
||||
// Arrange
|
||||
var mockIntegration = new MockIntegration();
|
||||
mockIntegration.Clear();
|
||||
var key = "first";
|
||||
var expected = mockIntegration.GetSum(key);
|
||||
var cachedExpected = mockIntegration.CachedGetSum(key);
|
||||
mockIntegration.keyValuePairs[key] = new List<int>(){100, 100, 100};
|
||||
|
||||
// Act
|
||||
var cachedResult = mockIntegration.CachedGetSum(key);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, cachedResult);
|
||||
Assert.Equal(cachedExpected, cachedResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CachedGetSum_WhenCalledAfterDuration_ReturnsNewValue()
|
||||
{
|
||||
// Arrange
|
||||
var mockIntegration = new MockIntegration();
|
||||
mockIntegration.Clear();
|
||||
var key = "first";
|
||||
var expected = mockIntegration.GetSum(key);
|
||||
var cachedExpected = mockIntegration.CachedGetSum(key);
|
||||
mockIntegration.keyValuePairs[key] = new List<int>(){100, 100, 100};
|
||||
|
||||
// Act
|
||||
System.Threading.Thread.Sleep(61000);
|
||||
var cachedResult = mockIntegration.CachedGetSum(key);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(300, cachedResult);
|
||||
Assert.NotEqual(cachedExpected, cachedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
using Xunit;
|
||||
using CacheModule;
|
||||
|
||||
namespace CacheModuleTest{
|
||||
public class CacheServiceUnitTest
|
||||
{
|
||||
[Fact]
|
||||
public void Instance_WhenCalled_ReturnsSameInstance()
|
||||
{
|
||||
// Arrange
|
||||
var cacheService1 = CacheService.Instance;
|
||||
var cacheService2 = CacheService.Instance;
|
||||
|
||||
// Act
|
||||
|
||||
// Assert
|
||||
Assert.Same(cacheService1, cacheService2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Add_WhenCalledWithinDuration_AddsToCache()
|
||||
{
|
||||
// Arrange
|
||||
var cacheService = CacheService.Instance;
|
||||
cacheService.Clear();
|
||||
var functionName = "functionName";
|
||||
var functionParams = "functionParams";
|
||||
var value = new object();
|
||||
|
||||
// Act
|
||||
cacheService.Add(functionName, functionParams, value, Duration.OneMinute);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(value, cacheService.Get(functionName, functionParams));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Add_WhenCalledAfterDuration_RemovesFromCache()
|
||||
{
|
||||
// Arrange
|
||||
var cacheService = CacheService.Instance;
|
||||
cacheService.Clear();
|
||||
var functionName = "functionName";
|
||||
var functionParams = "functionParams";
|
||||
var value = new object();
|
||||
|
||||
// Act
|
||||
cacheService.Add(functionName, functionParams, value, Duration.OneSecond);
|
||||
Thread.Sleep(2000);
|
||||
|
||||
// Assert
|
||||
Assert.Null(cacheService.Get(functionName, functionParams));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Add_WhenCalledWithSameFunctionNameAndFunctionParams_OverwritesCache()
|
||||
{
|
||||
// Arrange
|
||||
var cacheService = CacheService.Instance;
|
||||
cacheService.Clear();
|
||||
var functionName = "functionName";
|
||||
var functionParams = "functionParams";
|
||||
var value1 = new object();
|
||||
var value2 = new object();
|
||||
|
||||
// Act
|
||||
cacheService.Add(functionName, functionParams, value1, Duration.OneMinute);
|
||||
cacheService.Add(functionName, functionParams, value2, Duration.OneMinute);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(value2, cacheService.Get(functionName, functionParams));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Get_WhenCalledWithNonExistingFunctionName_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var cacheService = CacheService.Instance;
|
||||
cacheService.Clear();
|
||||
var functionName = "functionName";
|
||||
var functionParams = "functionParams";
|
||||
|
||||
// Act
|
||||
|
||||
// Assert
|
||||
Assert.Null(cacheService.Get(functionName, functionParams));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Get_WhenCalledWithNonExistingFunctionParams_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var cacheService = CacheService.Instance;
|
||||
cacheService.Clear();
|
||||
var functionName = "functionName";
|
||||
var functionParams = "functionParams";
|
||||
var value = new object();
|
||||
|
||||
// Act
|
||||
cacheService.Add(functionName, functionParams, value, Duration.OneMinute);
|
||||
|
||||
// Assert
|
||||
Assert.Null(cacheService.Get(functionName, "nonExistingFunctionParams"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Clear_WhenCalledWithParams_ClearsCache()
|
||||
{
|
||||
// Arrange
|
||||
var cacheService = CacheService.Instance;
|
||||
var functionName = "functionName";
|
||||
var functionParams = "functionParams";
|
||||
var value = new object();
|
||||
cacheService.Add(functionName, functionParams, value, Duration.OneMinute);
|
||||
|
||||
// Act
|
||||
cacheService.Clear();
|
||||
|
||||
// Assert
|
||||
Assert.Null(cacheService.Get(functionName, functionParams));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Clear_WhenCalledWithFunctionName_ClearsCache()
|
||||
{
|
||||
// Arrange
|
||||
var cacheService = CacheService.Instance;
|
||||
var functionName = "functionName";
|
||||
var functionParams = "functionParams";
|
||||
var functionName2 = "functionName2";
|
||||
var functionParams2 = "functionParams2";
|
||||
var value = new object();
|
||||
var value2 = new object();
|
||||
cacheService.Add(functionName, functionParams, value, Duration.OneMinute);
|
||||
cacheService.Add(functionName2, functionParams2, value2, Duration.OneMinute);
|
||||
|
||||
// Act
|
||||
cacheService.Clear(functionName);
|
||||
|
||||
// Assert
|
||||
Assert.Null(cacheService.Get(functionName, functionParams));
|
||||
Assert.Equal(value2, cacheService.Get(functionName2, functionParams2));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Clear_WhenCalledWithFunctionNameAndFunctionParams_ClearsCache()
|
||||
{
|
||||
// Arrange
|
||||
var cacheService = CacheService.Instance;
|
||||
var functionName = "functionName";
|
||||
var functionParams = "functionParams";
|
||||
var functionParams2 = "functionParams2";
|
||||
var value = new object();
|
||||
var value2 = new object();
|
||||
cacheService.Add(functionName, functionParams, value, Duration.OneMinute);
|
||||
cacheService.Add(functionName, functionParams2, value2, Duration.OneMinute);
|
||||
|
||||
// Act
|
||||
cacheService.Clear(functionName, functionParams);
|
||||
|
||||
// Assert
|
||||
Assert.Null(cacheService.Get(functionName, functionParams));
|
||||
Assert.Equal(value2, cacheService.Get(functionName, functionParams2));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClearExpired_WhenCalled_ClearsExpiredCache()
|
||||
{
|
||||
// Arrange
|
||||
var cacheService = CacheService.Instance;
|
||||
var functionName = "functionName";
|
||||
var functionParams = "functionParams";
|
||||
var functionName2 = "functionName2";
|
||||
var functionParams2 = "functionParams2";
|
||||
var value = new object();
|
||||
cacheService.Add(functionName, functionParams, value, Duration.OneSecond);
|
||||
cacheService.Add(functionName2, functionParams2, value, Duration.OneMinute);
|
||||
Thread.Sleep(2000);
|
||||
|
||||
// Act
|
||||
cacheService.ClearExpired();
|
||||
|
||||
// Assert
|
||||
Assert.Null(cacheService.Get(functionName, functionParams));
|
||||
Assert.Equal(value, cacheService.Get(functionName2, functionParams2));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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}") = "CacheModule", "CacheModule\CacheModule.csproj", "{523A7A27-26EE-4F61-B316-8116D19CBC7E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CacheModuleTest", "CacheModuleTest\CacheModuleTest.csproj", "{B2120B94-F9E9-437C-A4D2-EC1643434A21}"
|
||||
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
|
||||
{523A7A27-26EE-4F61-B316-8116D19CBC7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{523A7A27-26EE-4F61-B316-8116D19CBC7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{523A7A27-26EE-4F61-B316-8116D19CBC7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{523A7A27-26EE-4F61-B316-8116D19CBC7E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B2120B94-F9E9-437C-A4D2-EC1643434A21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B2120B94-F9E9-437C-A4D2-EC1643434A21}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B2120B94-F9E9-437C-A4D2-EC1643434A21}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B2120B94-F9E9-437C-A4D2-EC1643434A21}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Reference in New Issue
Block a user