tests: add remaining register integration tests
This commit is contained in:
@@ -16,11 +16,7 @@ public class LoginTests(TestApi testApi) : IntegrationTest(testApi)
|
||||
|
||||
var res = await Client.PostAsJsonAsync(LoginUri, req, TestContext.Current.CancellationToken);
|
||||
|
||||
res.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
|
||||
var problem = await res.Content.ReadFromJsonAsync<ValidationProblemDetails>(TestContext.Current.CancellationToken);
|
||||
|
||||
problem!.Errors.Should().BeEquivalentTo(tc.ExpectedErrors);
|
||||
await res.Should().BeValidationProblemDetails(tc.ExpectedErrors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -34,7 +30,7 @@ public class LoginTests(TestApi testApi) : IntegrationTest(testApi)
|
||||
|
||||
var res = await Client.PostAsJsonAsync(LoginUri, req, TestContext.Current.CancellationToken);
|
||||
|
||||
res.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
|
||||
await res.Should().BeProblemDetails(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -57,7 +53,7 @@ public class LoginTests(TestApi testApi) : IntegrationTest(testApi)
|
||||
|
||||
var res = await Client.PostAsJsonAsync(LoginUri, req, TestContext.Current.CancellationToken);
|
||||
|
||||
res.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
|
||||
await res.Should().BeProblemDetails(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -80,18 +76,8 @@ public class LoginTests(TestApi testApi) : IntegrationTest(testApi)
|
||||
|
||||
var res = await Client.PostAsJsonAsync(LoginUri, req, TestContext.Current.CancellationToken);
|
||||
|
||||
res.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var content = await res.Content.ReadFromJsonAsync<Login.Response>(TestContext.Current.CancellationToken);
|
||||
|
||||
content!.AccessToken.Should().NotBeNullOrEmpty();
|
||||
|
||||
res.Headers.TryGetValues("Set-Cookie", out var cookies).Should().BeTrue();
|
||||
|
||||
var hasRefreshTokenCookie = cookies!
|
||||
.Any(static c => c.StartsWith("fiscalos_refresh_cookie=", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
hasRefreshTokenCookie.Should().BeTrue();
|
||||
res.Should().HaveSetCookieHeader("fiscalos_refresh_cookie");
|
||||
await res.Should().BeJsonContentOfType<Login.Response>(HttpStatusCode.OK);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
namespace FiscalOS.API.Tests.Integration;
|
||||
|
||||
public class RefreshTests(TestApi testApi) : IntegrationTest(testApi)
|
||||
@@ -6,10 +5,189 @@ public class RefreshTests(TestApi testApi) : IntegrationTest(testApi)
|
||||
private static readonly Uri RefreshUri = new("/refresh", UriKind.Relative);
|
||||
|
||||
[Fact]
|
||||
public async Task Refresh_WhenCalledWithNoAccessToken_ItShouldReturn401Unauthorized()
|
||||
public async Task Refresh_WhenCalledWithNoAccessToken_ItShouldReturn401WithProblemDetails()
|
||||
{
|
||||
var response = await Client.PostAsync(RefreshUri, null, TestContext.Current.CancellationToken); ;
|
||||
var response = await Client.PostAsync(RefreshUri, null, TestContext.Current.CancellationToken);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
|
||||
await response.Should().BeProblemDetails(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Refresh_WhenCalledWithNonExistentRefreshToken_ItShouldReturn400WithProblemDetails()
|
||||
{
|
||||
var jwt = JwtTokenBuilder.New()
|
||||
.WithClaim(JwtRegisteredClaimNames.Sub, Guid.NewGuid().ToString())
|
||||
.Build();
|
||||
|
||||
using var request = new HttpRequestMessage(HttpMethod.Post, RefreshUri);
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
|
||||
request.Headers.Add("Cookie", "fiscalos_refresh_cookie=nonexistenttoken");
|
||||
|
||||
var response = await Client.SendAsync(request, TestContext.Current.CancellationToken);
|
||||
|
||||
await response.Should().BeProblemDetails(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Refresh_WhenCalledWithTokenBelongingToDifferentUser_ItShouldReturn403WithProblemDetails()
|
||||
{
|
||||
var (users, refreshToken) = await ExecuteDbContextAsync(static async (context, sp) =>
|
||||
{
|
||||
var passwordHasher = sp.GetRequiredService<IPasswordHasher>();
|
||||
var tokenGenerator = sp.GetRequiredService<ITokenGenerator>();
|
||||
|
||||
var user1 = User.From("User1", passwordHasher.Hash("@Password1"));
|
||||
var user2 = User.From("User2", passwordHasher.Hash("@Password2"));
|
||||
var refreshToken1 = tokenGenerator.GenerateRefreshToken(user2);
|
||||
var refreshToken2 = tokenGenerator.GenerateRefreshToken(user2);
|
||||
user2.AddRefreshToken(refreshToken1);
|
||||
user2.AddRefreshToken(refreshToken2);
|
||||
|
||||
context.Add(user1);
|
||||
context.Add(user2);
|
||||
|
||||
await context.SaveChangesAsync(TestContext.Current.CancellationToken);
|
||||
return (new User[] { user1, user2 }, refreshToken1);
|
||||
});
|
||||
|
||||
var jwt = JwtTokenBuilder.New()
|
||||
.WithClaim(JwtRegisteredClaimNames.Sub, users[0].Id.ToString())
|
||||
.Build();
|
||||
|
||||
using var request = new HttpRequestMessage(HttpMethod.Post, RefreshUri);
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
|
||||
request.Headers.Add("Cookie", $"fiscalos_refresh_cookie={refreshToken.Token}");
|
||||
|
||||
var response = await Client.SendAsync(request, TestContext.Current.CancellationToken);
|
||||
|
||||
await response.Should().BeProblemDetails(HttpStatusCode.Forbidden);
|
||||
|
||||
var unrevokedTokensCountForUser2 = await ExecuteDbContextAsync(
|
||||
async context => await context.Set<RefreshToken>()
|
||||
.Include(t => t.User)
|
||||
.Where(t => t.UserId == users[1].Id && t.Revoked == false)
|
||||
.CountAsync()
|
||||
);
|
||||
|
||||
unrevokedTokensCountForUser2.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Refresh_WhenCalledWithRevokedRefreshToken_ItShouldReturn400WithProblemDetails()
|
||||
{
|
||||
var (user, refreshToken) = await ExecuteDbContextAsync(static async (context, sp) =>
|
||||
{
|
||||
var passwordHasher = sp.GetRequiredService<IPasswordHasher>();
|
||||
var tokenGenerator = sp.GetRequiredService<ITokenGenerator>();
|
||||
|
||||
var user = User.From("User1", passwordHasher.Hash("@Password1"));
|
||||
var refreshToken = tokenGenerator.GenerateRefreshToken(user);
|
||||
refreshToken.Revoke();
|
||||
|
||||
user.AddRefreshToken(refreshToken);
|
||||
|
||||
context.Add(user);
|
||||
|
||||
await context.SaveChangesAsync(TestContext.Current.CancellationToken);
|
||||
return (user, refreshToken);
|
||||
});
|
||||
|
||||
var jwt = JwtTokenBuilder.New()
|
||||
.WithClaim(JwtRegisteredClaimNames.Sub, user.Id.ToString())
|
||||
.Build();
|
||||
|
||||
using var request = new HttpRequestMessage(HttpMethod.Post, RefreshUri);
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
|
||||
request.Headers.Add("Cookie", $"fiscalos_refresh_cookie={refreshToken.Token}");
|
||||
|
||||
var response = await Client.SendAsync(request, TestContext.Current.CancellationToken);
|
||||
|
||||
await response.Should().BeProblemDetails(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Refresh_WhenCalledWithExpiredRefreshToken_ItShouldReturn400WithProblemDetails()
|
||||
{
|
||||
var (user, refreshToken) = await ExecuteDbContextAsync(static async (context, sp) =>
|
||||
{
|
||||
var passwordHasher = sp.GetRequiredService<IPasswordHasher>();
|
||||
var tokenGenerator = sp.GetRequiredService<ITokenGenerator>();
|
||||
var timeProvider = sp.GetRequiredService<TimeProvider>();
|
||||
|
||||
var user = User.From("User1", passwordHasher.Hash("@Password1"));
|
||||
var refreshToken = RefreshToken.From(user.Id, "expiredtoken", timeProvider.GetUtcNow().AddHours(-1));
|
||||
user.AddRefreshToken(refreshToken);
|
||||
|
||||
context.Add(user);
|
||||
|
||||
await context.SaveChangesAsync(TestContext.Current.CancellationToken);
|
||||
return (user, refreshToken);
|
||||
});
|
||||
|
||||
var jwt = JwtTokenBuilder.New()
|
||||
.WithClaim(JwtRegisteredClaimNames.Sub, user.Id.ToString())
|
||||
.Build();
|
||||
|
||||
using var request = new HttpRequestMessage(HttpMethod.Post, RefreshUri);
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
|
||||
request.Headers.Add("Cookie", $"fiscalos_refresh_cookie={refreshToken.Token}");
|
||||
|
||||
var response = await Client.SendAsync(request, TestContext.Current.CancellationToken);
|
||||
|
||||
await response.Should().BeProblemDetails(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(5)]
|
||||
[InlineData(-5)]
|
||||
public async Task Refresh_WhenCalledWithValidRefreshTokenAndExpiredOrNotExpiredAccessToken_ItShouldReturn200WithNewTokensAndSetRefreshCookie(int accessTokenExpiresAtOffset)
|
||||
{
|
||||
var (user, refreshToken) = await ExecuteDbContextAsync(static async (context, sp) =>
|
||||
{
|
||||
var passwordHasher = sp.GetRequiredService<IPasswordHasher>();
|
||||
var tokenGenerator = sp.GetRequiredService<ITokenGenerator>();
|
||||
var timeProvider = sp.GetRequiredService<TimeProvider>();
|
||||
|
||||
var user = User.From("User1", passwordHasher.Hash("@Password1"));
|
||||
var refreshToken = tokenGenerator.GenerateRefreshToken(user);
|
||||
user.AddRefreshToken(refreshToken);
|
||||
|
||||
context.Add(user);
|
||||
|
||||
await context.SaveChangesAsync(TestContext.Current.CancellationToken);
|
||||
return (user, refreshToken);
|
||||
});
|
||||
|
||||
var jwt = JwtTokenBuilder.New()
|
||||
.WithClaim(JwtRegisteredClaimNames.Sub, user.Id.ToString())
|
||||
.WithExpiresAt(DateTime.UtcNow.AddMinutes(accessTokenExpiresAtOffset))
|
||||
.Build();
|
||||
|
||||
using var request = new HttpRequestMessage(HttpMethod.Post, RefreshUri);
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
|
||||
request.Headers.Add("Cookie", $"fiscalos_refresh_cookie={refreshToken.Token}");
|
||||
|
||||
var response = await Client.SendAsync(request, TestContext.Current.CancellationToken);
|
||||
|
||||
response.Should().HaveSetCookieHeader("fiscalos_refresh_cookie");
|
||||
await response.Should().BeJsonContentOfType<Refresh.Response>(HttpStatusCode.OK);
|
||||
|
||||
var oldRefreshTokenInDb = await ExecuteDbContextAsync(
|
||||
async context => await context.Set<RefreshToken>()
|
||||
.Include(t => t.User)
|
||||
.Where(t => t.UserId == user.Id && t.Token == refreshToken.Token && t.Revoked == true)
|
||||
.SingleOrDefaultAsync()
|
||||
);
|
||||
|
||||
oldRefreshTokenInDb.Should().NotBeNull();
|
||||
|
||||
var newRefreshTokenInDb = await ExecuteDbContextAsync(
|
||||
async context => await context.Set<RefreshToken>()
|
||||
.Include(t => t.User)
|
||||
.Where(t => t.UserId == user.Id && t.Revoked == false && t.Token != refreshToken.Token)
|
||||
.SingleOrDefaultAsync()
|
||||
);
|
||||
|
||||
newRefreshTokenInDb.Should().NotBeNull();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user