Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
43 changes: 34 additions & 9 deletions .github/workflows/dotnet-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,37 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v1
- name: Restore
run: dotnet restore
- name: Build
run: dotnet build --no-restore --configuration Release
- name: Test
run: dotnet test --no-build --configuration Release --filter "FullyQualifiedName!~AcceptanceTests"
- name: Checkout
uses: actions/checkout@v3

- name: Setup .NET
uses: actions/setup-dotnet@v1

- name: Restore
run: dotnet restore

- name: Build
run: dotnet build --no-restore --configuration Release

- name: Test
run: dotnet test --no-build --configuration Release --verbosity normal --collect:"XPlat Code Coverage" --results-directory ./coverage

- name: Code Coverage Report
uses: irongut/CodeCoverageSummary@v1.3.0
with:
filename: coverage/**/coverage.cobertura.xml
badge: true
fail_below_min: false
format: markdown
hide_branch_rate: false
hide_complexity: true
indicators: true
output: both
thresholds: '60 80'

- name: Add Coverage PR Comment
uses: marocchino/sticky-pull-request-comment@v2
if: github.event_name == 'pull_request'
with:
recreate: true
path: code-coverage-results.md
14 changes: 14 additions & 0 deletions src/HttpClient.Cache/CacheData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace HttpClient.Cache;

public class CacheData
{
public CacheData(byte[] data, HttpResponseMessage response)
{
Data = data;
Response = response;
}

public byte[] Data { get; }

public HttpResponseMessage Response { get; }
}
31 changes: 31 additions & 0 deletions src/HttpClient.Cache/CacheDataExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Text.Json;

namespace HttpClient.Cache;

public static class CacheDataExtensions
{
public static byte[] Serialize(this CacheData cacheData)
{
string json = JsonSerializer.Serialize(cacheData);
byte[] bytes = new byte[json.Length * sizeof(char)];

Buffer.BlockCopy(json.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}

public static CacheData? Deserialize(this byte[] cacheData)
{
try
{
char[] chars = new char[cacheData.Length / sizeof(char)];
Buffer.BlockCopy(cacheData, 0, chars, 0, cacheData.Length);
string json = new string(chars);
CacheData? data = JsonSerializer.Deserialize<CacheData>(json);
return data;
}
catch
{
return null;
}
}
}
9 changes: 9 additions & 0 deletions src/HttpClient.Cache/CacheItemPriority.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace HttpClient.Cache;

public enum CacheItemPriority
{
Low,
Normal,
High,
NeverRemove
}
5 changes: 0 additions & 5 deletions src/HttpClient.Cache/Class1.cs

This file was deleted.

18 changes: 18 additions & 0 deletions src/HttpClient.Cache/ICacheEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace HttpClient.Cache;

public interface ICacheEntry: IDisposable
{
object Value { get; set; }

DateTimeOffset? AbsoluteExpiration { get; set; }

TimeSpan? AbsoluteExpirationRelativeToNow { get; set; }

TimeSpan? SlidingExpiration { get; set; }

IList<IChangeToken> ExpirationTokens { get; }

IList<PostEvictionCallbackRegistration> PostEvictionCallbacks { get; }

CacheItemPriority Priority { get; set; }
}
10 changes: 10 additions & 0 deletions src/HttpClient.Cache/IChangeToken.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace HttpClient.Cache;

public interface IChangeToken
{
bool HasChanged { get; }

bool ActiveChangeCallbacks { get; }

IDisposable RegisterChangeCallback(Action<object> callback, object state);
}
10 changes: 10 additions & 0 deletions src/HttpClient.Cache/PostEvictionCallbackRegistration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace HttpClient.Cache;

public class PostEvictionCallbackRegistration
{
public PostEvictionDelegate EvictionCallback { get; set; }

public object State { get; set; }
}

public delegate void PostEvictionDelegate(object key, object value, string reason, object state);
25 changes: 25 additions & 0 deletions src/HttpClient.Cache/Stats/CacheStatsReport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Net;

namespace HttpClient.Cache.Stats;

public class CacheStatsReport
{
public CacheStatsReport(string cacheType)
{
CacheType = cacheType;
PerStatusCode = new Dictionary<HttpStatusCode, CacheStatsResult>();
CreatedAt = DateTimeOffset.Now;
}

public DateTimeOffset CreatedAt { get; }

public string CacheType { get; }

public Dictionary<HttpStatusCode, CacheStatsResult> PerStatusCode { get; init; }

public CacheStatsResult Total => new()
{
CacheHit = PerStatusCode.Sum(status => status.Value.CacheHit),
CacheMiss = PerStatusCode.Sum(status => status.Value.CacheMiss)
};
}
12 changes: 12 additions & 0 deletions src/HttpClient.Cache/Stats/CacheStatsResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace HttpClient.Cache.Stats;

public class CacheStatsResult
{
public long CacheHit { get; set; }
public long CacheMiss { get; set; }
public long TotalRequests => CacheHit + CacheMiss;

public double TotalHitsPercent => CacheHit * 1.0 / TotalRequests;

public double TotalMissPercent => CacheMiss * 1.0 / TotalRequests;
}
42 changes: 42 additions & 0 deletions src/HttpClient.Cache/Stats/DefaultCacheStatsProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Collections.Concurrent;
using System.Net;

namespace HttpClient.Cache.Stats;

public class DefaultCacheStatsProvider : ICacheStatsProvider
{
private readonly string _cacheType;
private readonly ConcurrentDictionary<HttpStatusCode, CacheStatsResult> _values;

public DefaultCacheStatsProvider(string cacheType)
{
_cacheType = cacheType;
_values = new ConcurrentDictionary<HttpStatusCode, CacheStatsResult>();
}

public void ReportHit(HttpStatusCode code)
{
_values.AddOrUpdate(code, _ => new CacheStatsResult { CacheHit = 1 }, (_, existing) =>
{
existing.CacheHit++;
return existing;
});
}

public void ReportMiss(HttpStatusCode code)
{
_values.AddOrUpdate(code, _ => new CacheStatsResult { CacheMiss = 1 }, (_, existing) =>
{
existing.CacheMiss++;
return existing;
});
}

public CacheStatsReport GetReport()
{
return new CacheStatsReport(_cacheType)
{
PerStatusCode = new Dictionary<HttpStatusCode, CacheStatsResult>(_values)
};
}
}
12 changes: 12 additions & 0 deletions src/HttpClient.Cache/Stats/ICacheStatsProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Net;

namespace HttpClient.Cache.Stats;

public interface ICacheStatsProvider
{
void ReportHit(HttpStatusCode code);

void ReportMiss(HttpStatusCode code);

CacheStatsReport GetReport();
}
13 changes: 11 additions & 2 deletions tests/HttpClient.Cache.Tests/HttpClient.Cache.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2"/>
<PackageReference Include="xunit" Version="2.4.2"/>
<PackageReference Include="FluentAssertions" Version="6.10.0" />
<PackageReference Include="FluentAssertions.Analyzers" Version="0.17.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<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>
Expand All @@ -21,4 +26,8 @@
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\HttpClient.Cache\HttpClient.Cache.csproj" />
</ItemGroup>

</Project>
75 changes: 75 additions & 0 deletions tests/HttpClient.Cache.Tests/Stats/DefaultStatsProviderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System.Net;
using FluentAssertions;
using FluentAssertions.Execution;
using HttpClient.Cache.Stats;

namespace HttpClient.Cache.Tests.Stats;

public class DefaultStatsProviderTests
{
[Fact]
public void GetReport_GetDefaultEmptyReportIfNoActions_ReportSuccessful()
{
DefaultCacheStatsProvider provider = new("test-cache");
var expected = new CacheStatsReport("test-cache");

var stats = provider.GetReport();

stats.Should().BeEquivalentTo(expected, ignore => ignore.Excluding(x => x.CreatedAt));
}

[Fact]
public void ReportHit_ReportCacheHitWith201_ReportSuccessful()
{
DefaultCacheStatsProvider provider = new("test-cache");
var expected = new CacheStatsReport("test-cache")
{
PerStatusCode = new Dictionary<HttpStatusCode, CacheStatsResult>
{
{ HttpStatusCode.Created, new CacheStatsResult { CacheHit = 1L, CacheMiss = 0L } }
}
};

provider.ReportHit(HttpStatusCode.Created);
var stats = provider.GetReport();

stats.Should().BeEquivalentTo(expected, ignore => ignore.Excluding(x => x.CreatedAt));
}

[Fact]
public void ReportHit_ReportCacheMissWith503_ReportSuccessful()
{
DefaultCacheStatsProvider provider = new("test-cache");
var expected = new CacheStatsReport("test-cache")
{
PerStatusCode = new Dictionary<HttpStatusCode, CacheStatsResult>
{
{ HttpStatusCode.ServiceUnavailable, new CacheStatsResult { CacheHit = 0L, CacheMiss = 1L } }
}
};

provider.ReportMiss(HttpStatusCode.ServiceUnavailable);
var stats = provider.GetReport();

stats.Should().BeEquivalentTo(expected, ignore => ignore.Excluding(x => x.CreatedAt));
}

[Fact]
public void GetReport_ReportOneMissAndHit_ReportTotalIsSuccessful()
{
DefaultCacheStatsProvider provider = new("test-cache");

provider.ReportHit(HttpStatusCode.Created);
provider.ReportMiss(HttpStatusCode.Created);
var stats = provider.GetReport();

using (new AssertionScope())
{
stats.Total.CacheMiss.Should().Be(1L);
stats.Total.CacheHit.Should().Be(1L);
stats.Total.TotalRequests.Should().Be(2L);
stats.Total.TotalHitsPercent.Should().Be(0.5);
stats.Total.TotalMissPercent.Should().Be(0.5);
}
}
}
10 changes: 0 additions & 10 deletions tests/HttpClient.Cache.Tests/UnitTest1.cs

This file was deleted.