Skip to content

Commit b6ee595

Browse files
authored
Merge pull request #10 from ProjectVG/test
Test
2 parents 962aabb + 7e9e924 commit b6ee595

13 files changed

+4559
-0
lines changed

ProjectVG.Tests/Api/Configuration/ConfigurationExtensionsTests.cs

Lines changed: 444 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
using FluentAssertions;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.AspNetCore.Mvc;
4+
using Microsoft.AspNetCore.Mvc.Abstractions;
5+
using Microsoft.AspNetCore.Mvc.Filters;
6+
using Microsoft.AspNetCore.Routing;
7+
using Microsoft.Extensions.DependencyInjection;
8+
using Microsoft.Extensions.Logging;
9+
using Moq;
10+
using ProjectVG.Api.Filters;
11+
using ProjectVG.Common.Constants;
12+
using ProjectVG.Common.Exceptions;
13+
using ProjectVG.Infrastructure.Auth;
14+
using System.Security.Claims;
15+
using Xunit;
16+
17+
namespace ProjectVG.Tests.Api.Filters
18+
{
19+
public class JwtAuthenticationFilterTests
20+
{
21+
private readonly Mock<ITokenService> _mockTokenService;
22+
private readonly Mock<ILogger<JwtAuthenticationAttribute>> _mockLogger;
23+
private readonly JwtAuthenticationAttribute _filter;
24+
private readonly ServiceProvider _serviceProvider;
25+
26+
public JwtAuthenticationFilterTests()
27+
{
28+
_mockTokenService = new Mock<ITokenService>();
29+
_mockLogger = new Mock<ILogger<JwtAuthenticationAttribute>>();
30+
31+
var services = new ServiceCollection();
32+
services.AddSingleton(_mockTokenService.Object);
33+
services.AddSingleton(_mockLogger.Object);
34+
_serviceProvider = services.BuildServiceProvider();
35+
36+
_filter = new JwtAuthenticationAttribute();
37+
}
38+
39+
private AuthorizationFilterContext CreateFilterContext(string? headerName = null, string? headerValue = null)
40+
{
41+
var httpContext = new DefaultHttpContext
42+
{
43+
RequestServices = _serviceProvider
44+
};
45+
46+
if (!string.IsNullOrEmpty(headerName) && !string.IsNullOrEmpty(headerValue))
47+
{
48+
httpContext.Request.Headers[headerName] = headerValue;
49+
}
50+
51+
var actionContext = new ActionContext(
52+
httpContext,
53+
new RouteData(),
54+
new ActionDescriptor()
55+
);
56+
57+
return new AuthorizationFilterContext(actionContext, new List<IFilterMetadata>());
58+
}
59+
60+
#region Token Extraction Tests
61+
62+
[Theory]
63+
[InlineData("Authorization", "Bearer valid-token-123")]
64+
[InlineData("X-Forwarded-Authorization", "Bearer forwarded-token-456")]
65+
[InlineData("X-Original-Authorization", "Bearer original-token-789")]
66+
[InlineData("HTTP_AUTHORIZATION", "Bearer http-token-abc")]
67+
public async Task OnAuthorizationAsync_ValidTokenInDifferentHeaders_ShouldAuthenticate(string headerName, string headerValue)
68+
{
69+
// Arrange
70+
var userId = Guid.NewGuid();
71+
var expectedToken = headerValue.Substring("Bearer ".Length);
72+
var filterContext = CreateFilterContext(headerName, headerValue);
73+
74+
_mockTokenService.Setup(x => x.ValidateAccessTokenAsync(expectedToken))
75+
.ReturnsAsync(true);
76+
_mockTokenService.Setup(x => x.GetUserIdFromTokenAsync(expectedToken))
77+
.ReturnsAsync(userId);
78+
79+
// Act
80+
await _filter.OnAuthorizationAsync(filterContext);
81+
82+
// Assert
83+
filterContext.HttpContext.User.Should().NotBeNull();
84+
filterContext.HttpContext.User.Identity!.IsAuthenticated.Should().BeTrue();
85+
filterContext.HttpContext.User.Identity.AuthenticationType.Should().Be("Bearer");
86+
87+
var userIdClaim = filterContext.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier);
88+
userIdClaim.Should().NotBeNull();
89+
userIdClaim!.Value.Should().Be(userId.ToString());
90+
91+
var customUserIdClaim = filterContext.HttpContext.User.FindFirst("user_id");
92+
customUserIdClaim.Should().NotBeNull();
93+
customUserIdClaim!.Value.Should().Be(userId.ToString());
94+
}
95+
96+
[Fact]
97+
public async Task OnAuthorizationAsync_TokenWithExtraSpaces_ShouldTrimAndUse()
98+
{
99+
// Arrange
100+
var userId = Guid.NewGuid();
101+
var token = "token-with-spaces";
102+
var filterContext = CreateFilterContext("Authorization", $"Bearer {token} ");
103+
104+
_mockTokenService.Setup(x => x.ValidateAccessTokenAsync(token))
105+
.ReturnsAsync(true);
106+
_mockTokenService.Setup(x => x.GetUserIdFromTokenAsync(token))
107+
.ReturnsAsync(userId);
108+
109+
// Act
110+
await _filter.OnAuthorizationAsync(filterContext);
111+
112+
// Assert
113+
_mockTokenService.Verify(x => x.ValidateAccessTokenAsync(token), Times.Once);
114+
filterContext.HttpContext.User.Identity!.IsAuthenticated.Should().BeTrue();
115+
}
116+
117+
#endregion
118+
119+
#region Token Missing Tests
120+
121+
[Fact]
122+
public async Task OnAuthorizationAsync_NoAuthorizationHeader_ShouldThrowTokenMissingException()
123+
{
124+
// Arrange
125+
var filterContext = CreateFilterContext();
126+
127+
// Act & Assert
128+
var exception = await Assert.ThrowsAsync<AuthenticationException>(
129+
() => _filter.OnAuthorizationAsync(filterContext)
130+
);
131+
132+
exception.ErrorCode.Should().Be(ErrorCode.TOKEN_MISSING);
133+
}
134+
135+
[Fact]
136+
public async Task OnAuthorizationAsync_EmptyAuthorizationHeader_ShouldThrowTokenMissingException()
137+
{
138+
// Arrange
139+
var filterContext = CreateFilterContext("Authorization", "");
140+
141+
// Act & Assert
142+
var exception = await Assert.ThrowsAsync<AuthenticationException>(
143+
() => _filter.OnAuthorizationAsync(filterContext)
144+
);
145+
146+
exception.ErrorCode.Should().Be(ErrorCode.TOKEN_MISSING);
147+
}
148+
149+
[Fact]
150+
public async Task OnAuthorizationAsync_BearerWithoutToken_ShouldThrowTokenMissingException()
151+
{
152+
// Arrange
153+
var filterContext = CreateFilterContext("Authorization", "Bearer");
154+
155+
// Act & Assert
156+
var exception = await Assert.ThrowsAsync<AuthenticationException>(
157+
() => _filter.OnAuthorizationAsync(filterContext)
158+
);
159+
160+
exception.ErrorCode.Should().Be(ErrorCode.TOKEN_MISSING);
161+
}
162+
163+
[Fact]
164+
public async Task OnAuthorizationAsync_BearerWithEmptyToken_ShouldThrowTokenMissingException()
165+
{
166+
// Arrange
167+
var filterContext = CreateFilterContext("Authorization", "Bearer ");
168+
169+
// Act & Assert
170+
var exception = await Assert.ThrowsAsync<AuthenticationException>(
171+
() => _filter.OnAuthorizationAsync(filterContext)
172+
);
173+
174+
exception.ErrorCode.Should().Be(ErrorCode.TOKEN_MISSING);
175+
}
176+
177+
#endregion
178+
179+
#region Token Validation Tests
180+
181+
[Fact]
182+
public async Task OnAuthorizationAsync_InvalidToken_ShouldThrowTokenInvalidException()
183+
{
184+
// Arrange
185+
var invalidToken = "invalid-token";
186+
var filterContext = CreateFilterContext("Authorization", $"Bearer {invalidToken}");
187+
188+
_mockTokenService.Setup(x => x.ValidateAccessTokenAsync(invalidToken))
189+
.ReturnsAsync(false);
190+
191+
// Act & Assert
192+
var exception = await Assert.ThrowsAsync<AuthenticationException>(
193+
() => _filter.OnAuthorizationAsync(filterContext)
194+
);
195+
196+
exception.ErrorCode.Should().Be(ErrorCode.TOKEN_INVALID);
197+
}
198+
199+
#endregion
200+
201+
#region User ID Extraction Tests
202+
203+
[Fact]
204+
public async Task OnAuthorizationAsync_ValidTokenButNoUserId_ShouldThrowAuthenticationFailedException()
205+
{
206+
// Arrange
207+
var validToken = "valid-token-no-user";
208+
var filterContext = CreateFilterContext("Authorization", $"Bearer {validToken}");
209+
210+
_mockTokenService.Setup(x => x.ValidateAccessTokenAsync(validToken))
211+
.ReturnsAsync(true);
212+
_mockTokenService.Setup(x => x.GetUserIdFromTokenAsync(validToken))
213+
.ReturnsAsync((Guid?)null);
214+
215+
// Act & Assert
216+
var exception = await Assert.ThrowsAsync<AuthenticationException>(
217+
() => _filter.OnAuthorizationAsync(filterContext)
218+
);
219+
220+
exception.ErrorCode.Should().Be(ErrorCode.AUTHENTICATION_FAILED);
221+
}
222+
223+
#endregion
224+
225+
#region Successful Authentication Tests
226+
227+
[Fact]
228+
public async Task OnAuthorizationAsync_ValidTokenAndUserId_ShouldSetUserWithCorrectClaims()
229+
{
230+
// Arrange
231+
var userId = Guid.NewGuid();
232+
var validToken = "valid-token-123";
233+
var filterContext = CreateFilterContext("Authorization", $"Bearer {validToken}");
234+
235+
_mockTokenService.Setup(x => x.ValidateAccessTokenAsync(validToken))
236+
.ReturnsAsync(true);
237+
_mockTokenService.Setup(x => x.GetUserIdFromTokenAsync(validToken))
238+
.ReturnsAsync(userId);
239+
240+
// Act
241+
await _filter.OnAuthorizationAsync(filterContext);
242+
243+
// Assert
244+
var user = filterContext.HttpContext.User;
245+
user.Should().NotBeNull();
246+
user.Identity!.IsAuthenticated.Should().BeTrue();
247+
user.Identity.AuthenticationType.Should().Be("Bearer");
248+
249+
var nameIdentifierClaim = user.FindFirst(ClaimTypes.NameIdentifier);
250+
nameIdentifierClaim.Should().NotBeNull();
251+
nameIdentifierClaim!.Value.Should().Be(userId.ToString());
252+
253+
var userIdClaim = user.FindFirst("user_id");
254+
userIdClaim.Should().NotBeNull();
255+
userIdClaim!.Value.Should().Be(userId.ToString());
256+
257+
user.Claims.Should().HaveCount(2);
258+
}
259+
260+
[Fact]
261+
public async Task OnAuthorizationAsync_CompleteValidFlow_ShouldNotSetResult()
262+
{
263+
// Arrange
264+
var userId = Guid.NewGuid();
265+
var validToken = "complete-valid-token";
266+
var filterContext = CreateFilterContext("Authorization", $"Bearer {validToken}");
267+
268+
_mockTokenService.Setup(x => x.ValidateAccessTokenAsync(validToken))
269+
.ReturnsAsync(true);
270+
_mockTokenService.Setup(x => x.GetUserIdFromTokenAsync(validToken))
271+
.ReturnsAsync(userId);
272+
273+
// Act
274+
await _filter.OnAuthorizationAsync(filterContext);
275+
276+
// Assert
277+
filterContext.Result.Should().BeNull(); // No result means continue with request
278+
filterContext.HttpContext.User.Identity!.IsAuthenticated.Should().BeTrue();
279+
}
280+
281+
#endregion
282+
}
283+
}

0 commit comments

Comments
 (0)