Skip to content

Commit ad4e636

Browse files
authored
Merge pull request #1 from sahansera/agent/upgrade-distributed-cache-dotnet-10
2 parents 2be4b0d + 01bdeaa commit ad4e636

23 files changed

Lines changed: 422 additions & 288 deletions

‎.github/workflows/codeql.yml‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ jobs:
3838

3939
steps:
4040
- name: Checkout repository
41-
uses: actions/checkout@v3
41+
uses: actions/checkout@v4
4242

4343
# Initializes the CodeQL tools for scanning.
4444
- name: Initialize CodeQL
45-
uses: github/codeql-action/init@v2
45+
uses: github/codeql-action/init@v3
4646
with:
4747
languages: ${{ matrix.language }}
4848
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
5656
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
5757
# If this step fails, then you should remove it and run the build manually (see below)
5858
- name: Autobuild
59-
uses: github/codeql-action/autobuild@v2
59+
uses: github/codeql-action/autobuild@v3
6060

6161
# ℹ️ Command-line programs to run using the OS shell.
6262
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -69,6 +69,6 @@ jobs:
6969
# ./location_of_script_within_repo/buildscript.sh
7070

7171
- name: Perform CodeQL Analysis
72-
uses: github/codeql-action/analyze@v2
72+
uses: github/codeql-action/analyze@v3
7373
with:
7474
category: "/language:${{matrix.language}}"

‎.github/workflows/dotnet.yml‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ jobs:
1212
runs-on: ubuntu-latest
1313

1414
steps:
15-
- uses: actions/checkout@v3
15+
- uses: actions/checkout@v4
1616
- name: Setup .NET
17-
uses: actions/setup-dotnet@v2
17+
uses: actions/setup-dotnet@v4
1818
with:
19-
dotnet-version: 6.0.x
19+
dotnet-version: 10.0.x
2020
- name: Restore dependencies
2121
run: dotnet restore
2222
- name: Build
23-
run: dotnet build --no-restore
23+
run: dotnet build --no-restore --configuration Release
2424
- name: Test
25-
run: dotnet test --no-build --verbosity normal
25+
run: dotnet test --no-build --configuration Release --verbosity normal
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System.Text;
2+
using System.Text.Json;
3+
using DistributedCache.Infrastructure;
4+
using DistributedCache.Models;
5+
using Microsoft.Extensions.Caching.Distributed;
6+
using Moq;
7+
8+
namespace DistributedCache.Tests;
9+
10+
public class CacheProviderTests
11+
{
12+
[Fact]
13+
public async Task GetFromCacheAsync_WhenKeyExists_ReturnsDeserializedValue()
14+
{
15+
var users = new[] { new User { Id = 1, Email = "leanne@example.com" } };
16+
var serializedUsers = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(users));
17+
var cache = new Mock<IDistributedCache>();
18+
cache.Setup(x => x.GetAsync("users", It.IsAny<CancellationToken>()))
19+
.ReturnsAsync(serializedUsers);
20+
var provider = new CacheProvider(cache.Object);
21+
22+
var result = await provider.GetFromCacheAsync<IReadOnlyList<User>>("users");
23+
24+
Assert.NotNull(result);
25+
Assert.Single(result);
26+
Assert.Equal("leanne@example.com", result[0].Email);
27+
}
28+
29+
[Fact]
30+
public async Task SetCacheAsync_SerializesAndStoresValue()
31+
{
32+
var cache = new Mock<IDistributedCache>();
33+
var provider = new CacheProvider(cache.Object);
34+
var users = new[] { new User { Id = 1, Email = "leanne@example.com" } };
35+
36+
await provider.SetCacheAsync("users", users, new DistributedCacheEntryOptions());
37+
38+
cache.Verify(x => x.SetAsync(
39+
"users",
40+
It.Is<byte[]>(value => Encoding.UTF8.GetString(value).Contains("leanne@example.com")),
41+
It.IsAny<DistributedCacheEntryOptions>(),
42+
It.IsAny<CancellationToken>()), Times.Once);
43+
}
44+
45+
[Fact]
46+
public async Task ClearCacheAsync_RemovesKey()
47+
{
48+
var cache = new Mock<IDistributedCache>();
49+
var provider = new CacheProvider(cache.Object);
50+
51+
await provider.ClearCacheAsync("users");
52+
53+
cache.Verify(x => x.RemoveAsync("users", It.IsAny<CancellationToken>()), Times.Once);
54+
}
55+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using DistributedCache.Infrastructure;
2+
using DistributedCache.Models;
3+
using DistributedCache.Services;
4+
using Microsoft.Extensions.Caching.Distributed;
5+
using Moq;
6+
7+
namespace DistributedCache.Tests;
8+
9+
public class CachedUserServiceTests
10+
{
11+
private static readonly IReadOnlyList<User> Users =
12+
[new User { Id = 1, Email = "leanne@example.com" }];
13+
14+
[Fact]
15+
public async Task GetUsersAsync_WhenCacheExists_DoesNotCallUsersApi()
16+
{
17+
var usersApi = new Mock<IUsersApiClient>();
18+
var cache = new Mock<ICacheProvider>();
19+
cache.Setup(x => x.GetFromCacheAsync<IReadOnlyList<User>>(CacheKeys.Users))
20+
.ReturnsAsync(Users);
21+
var service = new CachedUserService(new UserService(usersApi.Object), cache.Object);
22+
23+
var result = await service.GetUsersAsync();
24+
25+
Assert.Same(Users, result);
26+
usersApi.Verify(x => x.GetUsersAsync(), Times.Never);
27+
}
28+
29+
[Fact]
30+
public async Task GetUsersAsync_WhenCacheIsEmpty_LoadsAndCachesUsers()
31+
{
32+
var usersApi = new Mock<IUsersApiClient>();
33+
usersApi.Setup(x => x.GetUsersAsync()).ReturnsAsync(Users);
34+
var cache = new Mock<ICacheProvider>();
35+
cache.Setup(x => x.GetFromCacheAsync<IReadOnlyList<User>>(CacheKeys.Users))
36+
.ReturnsAsync((IReadOnlyList<User>?)null);
37+
var service = new CachedUserService(new UserService(usersApi.Object), cache.Object);
38+
39+
var result = await service.GetUsersAsync();
40+
41+
Assert.Same(Users, result);
42+
usersApi.Verify(x => x.GetUsersAsync(), Times.Once);
43+
cache.Verify(x => x.SetCacheAsync(
44+
CacheKeys.Users,
45+
Users,
46+
It.IsAny<DistributedCacheEntryOptions>()), Times.Once);
47+
}
48+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net10.0</TargetFramework>
4+
<ImplicitUsings>enable</ImplicitUsings>
5+
<Nullable>enable</Nullable>
6+
<IsPackable>false</IsPackable>
7+
</PropertyGroup>
8+
<ItemGroup>
9+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
10+
<PackageReference Include="Moq" Version="4.20.72" />
11+
<PackageReference Include="xunit" Version="2.9.3" />
12+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
13+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
14+
<PrivateAssets>all</PrivateAssets>
15+
</PackageReference>
16+
<PackageReference Include="coverlet.collector" Version="10.0.1">
17+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
18+
<PrivateAssets>all</PrivateAssets>
19+
</PackageReference>
20+
</ItemGroup>
21+
<ItemGroup>
22+
<ProjectReference Include="../DistributedCache/DistributedCache.csproj" />
23+
</ItemGroup>
24+
</Project>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System.Net;
2+
using DistributedCache.Infrastructure;
3+
using Moq;
4+
using Moq.Protected;
5+
6+
namespace DistributedCache.Tests;
7+
8+
public class HttpClientTests
9+
{
10+
[Fact]
11+
public async Task GetUsersAsync_ReturnsUsersFromApi()
12+
{
13+
var handler = new Mock<HttpMessageHandler>();
14+
handler.Protected()
15+
.Setup<Task<HttpResponseMessage>>(
16+
"SendAsync",
17+
ItExpr.IsAny<HttpRequestMessage>(),
18+
ItExpr.IsAny<CancellationToken>())
19+
.ReturnsAsync(new HttpResponseMessage
20+
{
21+
StatusCode = HttpStatusCode.OK,
22+
Content = new StringContent(
23+
"""[{"id":1,"email":"leanne@example.com"}]""")
24+
});
25+
var factory = new Mock<IHttpClientFactory>();
26+
factory.Setup(x => x.CreateClient(It.IsAny<string>()))
27+
.Returns(new System.Net.Http.HttpClient(handler.Object));
28+
var client = new UsersApiClient(factory.Object);
29+
30+
var result = await client.GetUsersAsync();
31+
32+
Assert.Single(result);
33+
Assert.Equal("leanne@example.com", result[0].Email);
34+
}
35+
36+
[Fact]
37+
public async Task GetUsersAsync_WhenApiReturnsError_ThrowsHttpRequestException()
38+
{
39+
var handler = new Mock<HttpMessageHandler>();
40+
handler.Protected()
41+
.Setup<Task<HttpResponseMessage>>(
42+
"SendAsync",
43+
ItExpr.IsAny<HttpRequestMessage>(),
44+
ItExpr.IsAny<CancellationToken>())
45+
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.InternalServerError));
46+
var factory = new Mock<IHttpClientFactory>();
47+
factory.Setup(x => x.CreateClient(It.IsAny<string>()))
48+
.Returns(new System.Net.Http.HttpClient(handler.Object));
49+
var client = new UsersApiClient(factory.Object);
50+
51+
await Assert.ThrowsAsync<HttpRequestException>(() => client.GetUsersAsync());
52+
}
53+
}

‎DistributedCache.Tests/Usings.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
global using Xunit;
Lines changed: 30 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,30 @@
1-
using System.Linq;
2-
using System.Threading.Tasks;
3-
using Microsoft.AspNetCore.Mvc;
4-
using Microsoft.Extensions.Logging;
5-
using DistributedCache.Services;
6-
7-
namespace DistributedCache.Controllers
8-
{
9-
public class HomeController : Controller
10-
{
11-
private readonly ILogger<HomeController> _logger;
12-
private readonly IUserService _userService;
13-
private readonly ICacheUserService _cacheUserService;
14-
15-
public HomeController(ILogger<HomeController> logger, IUserService userService, ICacheUserService cacheUserService)
16-
{
17-
_logger = logger;
18-
_userService = userService;
19-
_cacheUserService = cacheUserService;
20-
}
21-
22-
public async Task<IActionResult> Index()
23-
{
24-
var users = (await _cacheUserService.GetCachedUser())?.FirstOrDefault();
25-
return View(users);
26-
}
27-
28-
public async Task<IActionResult> CacheUserAsync()
29-
{
30-
var users = await _userService.GetUsersAsync();
31-
var cachedEntry = users.First();
32-
return View(nameof(Index), cachedEntry);
33-
}
34-
35-
public IActionResult CacheRemoveAsync()
36-
{
37-
_cacheUserService.ClearCache();
38-
return RedirectToAction(nameof(Index));
39-
}
40-
}
41-
}
1+
using Microsoft.AspNetCore.Mvc;
2+
using DistributedCache.Services;
3+
4+
namespace DistributedCache.Controllers;
5+
6+
public class HomeController(
7+
IUserService userService,
8+
ICacheUserService cacheUserService) : Controller
9+
{
10+
private readonly IUserService _userService = userService;
11+
private readonly ICacheUserService _cacheUserService = cacheUserService;
12+
13+
public async Task<IActionResult> Index()
14+
{
15+
var user = (await _cacheUserService.GetCachedUserAsync()).FirstOrDefault();
16+
return View(user);
17+
}
18+
19+
public async Task<IActionResult> CacheUserAsync()
20+
{
21+
var users = await _userService.GetUsersAsync();
22+
return View(nameof(Index), users.FirstOrDefault());
23+
}
24+
25+
public async Task<IActionResult> CacheRemoveAsync()
26+
{
27+
await _cacheUserService.ClearCacheAsync();
28+
return RedirectToAction(nameof(Index));
29+
}
30+
}
Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

3-
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
5-
</PropertyGroup>
6-
7-
<ItemGroup>
8-
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="6.0.0" />
9-
</ItemGroup>
3+
<PropertyGroup>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.11" />
11+
</ItemGroup>
1012

1113
</Project>

0 commit comments

Comments
 (0)