Added skorubauserservice tests
This commit is contained in:
@@ -147,7 +147,7 @@ public sealed class UserService(IDbContextFactory<ApplicationDbContext> contextF
|
||||
if (await context.Users.AnyAsync(u => u.Email == email, cancellationToken))
|
||||
return Result.Fail(ValidationError.Create("User with this email already exists."));
|
||||
|
||||
var createUserResult = await skorubaService.CreateUserAsync(request, userId, cancellationToken);
|
||||
var createUserResult = await skorubaService.CreateUserAsync(request, cancellationToken);
|
||||
|
||||
if (createUserResult.IsFailed)
|
||||
return Result.Fail(CreateError.Create("Failed to create user."));
|
||||
|
||||
@@ -7,7 +7,11 @@ public interface ISkorubaUserService
|
||||
{
|
||||
[Log]
|
||||
[Audit]
|
||||
Task<Result<string>> CreateUserAsync(CreateUserRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||
Task<Result<string>> CreateUserAsync(CreateUserRequest request, CancellationToken cancellationToken = default);
|
||||
|
||||
[Log]
|
||||
[Audit]
|
||||
Task<Result> RemoveUserAsync(Guid userId, CancellationToken cancellationToken = default);
|
||||
|
||||
[Log]
|
||||
Task<Result<UserResponse>> GetUserAsync(Guid userId, CancellationToken cancellationToken = default);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace PostFundManagement.Domain.Models.Security;
|
||||
|
||||
public sealed class UserLockOutRequest
|
||||
public abstract class SkorubaUser
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public string? UserId { get; set; }
|
||||
@@ -35,15 +35,15 @@ public sealed class UserLockOutRequest
|
||||
public DateTimeOffset? LockoutEnd { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UserLockOutRequest : SkorubaUser;
|
||||
public sealed class CreateUserRequest : SkorubaUser;
|
||||
public sealed class UpdateUserRequest : SkorubaUser;
|
||||
|
||||
public sealed record AddUserClaimRequest([Required] Guid UserId,
|
||||
[Required] int ClaimId,
|
||||
[Required][MaxLength(LabelLength)] string ClaimType,
|
||||
[Required][MaxLength(LabelLength)] string ClaimValue);
|
||||
|
||||
public sealed record CreateUserRequest(string UserName, string Email, string Password, bool EmailConfirmed = true, string? PhoneNumber = null, bool PhoneNumberConfirmed = false);
|
||||
|
||||
public sealed record UpdateUserRequest(string UserId, string UserName, string Email, bool EmailConfirmed, string? PhoneNumber, bool PhoneNumberConfirmed, bool LockoutEnabled, DateTimeOffset? LockoutEnd, int AccessFailedCount, bool TwoFactorEnabled);
|
||||
|
||||
public sealed record Claim(string ClaimType, string ClaimValue, int? ClaimId = null);
|
||||
|
||||
public sealed record ClaimsResponse(int TotalCount, int PageSize, ICollection<Claim> Claims);
|
||||
|
||||
@@ -6,14 +6,14 @@ namespace PostFundManagement.Infrastructure.Security;
|
||||
|
||||
public class SkorubaUserService(IHttpClientFactory httpClientFactory) : ISkorubaUserService
|
||||
{
|
||||
public async Task<Result<string>> CreateUserAsync(CreateUserRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
public async Task<Result<string>> CreateUserAsync(CreateUserRequest request, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<Task<Result<string>>>)(async () =>
|
||||
{
|
||||
var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient);
|
||||
var response = await httpClient.PostAsJsonAsync("api/users", request, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return Result.Fail(CreateError.Create("Failed to create user."));
|
||||
return Result.Fail(CreateError.Create(response.ReasonPhrase!));
|
||||
|
||||
var created = await response.Content.ReadFromJsonAsync<UserResponse>(cancellationToken: cancellationToken);
|
||||
|
||||
@@ -72,9 +72,9 @@ public class SkorubaUserService(IHttpClientFactory httpClientFactory) : ISkoruba
|
||||
await Result.Try((Func<Task<Result>>)(async () =>
|
||||
{
|
||||
var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient);
|
||||
var payload = new { UserId = userId, ClaimType = claimType, ClaimValue = claimValue };
|
||||
var payload = new { userId = userId, claimType = claimType, claimValue = claimValue };
|
||||
|
||||
var response = await httpClient.PostAsJsonAsync($"api/users/{userId}/claims", payload, cancellationToken);
|
||||
var response = await httpClient.PostAsJsonAsync($"api/users/claims", payload, cancellationToken);
|
||||
|
||||
return Result.OkIf(response.IsSuccessStatusCode, CreateError.Create("Failed to add user claim."));
|
||||
}));
|
||||
@@ -83,9 +83,12 @@ public class SkorubaUserService(IHttpClientFactory httpClientFactory) : ISkoruba
|
||||
await Result.Try((Func<Task<Result>>)(async () =>
|
||||
{
|
||||
var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient);
|
||||
var response = await httpClient.DeleteAsync($"api/users/{userId}/claims/{claimId}", cancellationToken);
|
||||
var response = await httpClient.DeleteAsync($"api/users/{userId}/Claims?claimId={claimId}", cancellationToken);
|
||||
|
||||
return Result.OkIf(response.IsSuccessStatusCode, CreateError.Create("Failed to remove user claim."));
|
||||
if(response.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||
return Result.Fail(NotFoundError.Create($"Claim {claimId} for user {userId} not found."));
|
||||
|
||||
return Result.OkIf(response.IsSuccessStatusCode, ValidationError.Create("Failed to remove user claim."));
|
||||
}));
|
||||
|
||||
public async Task<Result<Claim[]>> GetUserClaimsAsync(Guid userId, int page = 1, int pageSize = 50, CancellationToken cancellationToken = default) =>
|
||||
@@ -103,7 +106,7 @@ public class SkorubaUserService(IHttpClientFactory httpClientFactory) : ISkoruba
|
||||
await Result.Try((Func<Task<Result>>)(async () =>
|
||||
{
|
||||
var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient);
|
||||
var payload = new { UserId = userId, RoleId = roleId };
|
||||
var payload = new { userId = userId, roleId = roleId };
|
||||
|
||||
var response = await httpClient.PostAsJsonAsync($"api/users/roles", payload, cancellationToken);
|
||||
|
||||
@@ -183,4 +186,15 @@ public class SkorubaUserService(IHttpClientFactory httpClientFactory) : ISkoruba
|
||||
return Result.FailIf(result is null, NotFoundError.Create("Available roles could not be retrieved."))
|
||||
.ToResult(result?.Roles?.ToArray() ?? []);
|
||||
}));
|
||||
|
||||
public async Task<Result> RemoveUserAsync(Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<Task<Result>>)(async () =>
|
||||
{
|
||||
var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient);
|
||||
var result = await httpClient.DeleteAsync($"api/users/{userId}", cancellationToken);
|
||||
|
||||
return result.StatusCode == System.Net.HttpStatusCode.NotFound
|
||||
? Result.Fail(NotFoundError.Create($"User {userId} not found."))
|
||||
: Result.OkIf(result.IsSuccessStatusCode, UpdateError.Create("Failed to remove user."));
|
||||
}));
|
||||
}
|
||||
@@ -1,22 +1,11 @@
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Models.Security;
|
||||
using PostFundManagement.Infrastructure.Database.Services;
|
||||
using static PostFundManagement.Domain.Extensions.Constants;
|
||||
|
||||
namespace PostFundManagement.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests for SkorubaUserService.GetUserRolesAsync method.
|
||||
///
|
||||
/// These tests hit the actual Skoruba API environment.
|
||||
/// Requires the following configuration:
|
||||
/// - MachineIdentitySettings with valid ClientId and ClientSecret from user secrets
|
||||
/// - SkorubaApi connection string pointing to the API endpoint
|
||||
/// - Network access to the Skoruba API
|
||||
///
|
||||
/// To run these tests:
|
||||
/// dotnet test --filter "FullyQualifiedName~SkorubaUserServiceGetUserRolesIntegrationTests"
|
||||
/// </summary>
|
||||
public class SkorubaUserServiceGetUserRolesIntegrationTests
|
||||
{
|
||||
private readonly ISkorubaUserService _skorubaService;
|
||||
@@ -51,9 +40,6 @@ public class SkorubaUserServiceGetUserRolesIntegrationTests
|
||||
_httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Integration test: Verify HttpClient is properly configured with SkorubaHttpClient name.
|
||||
/// </summary>
|
||||
[DebugOnlyFact]
|
||||
public void HttpClientFactory_HasSkorubaHttpClientConfigured()
|
||||
{
|
||||
@@ -65,15 +51,72 @@ public class SkorubaUserServiceGetUserRolesIntegrationTests
|
||||
Assert.NotNull(httpClient.BaseAddress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Integration test: Verify that the service properly formats API requests.
|
||||
/// Uses a fake/invalid user ID to test the HTTP request flow.
|
||||
/// </summary>
|
||||
[DebugOnlyFact]
|
||||
public async Task CreateUserAsync_MustSucced_WithErrorMessage()
|
||||
{
|
||||
// Arrange
|
||||
var newUser = new CreateUserRequest
|
||||
{
|
||||
UserName = "testuser",
|
||||
Email = "testuser@example.com",
|
||||
EmailConfirmed = true,
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _skorubaService.CreateUserAsync(newUser);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsSuccess, result.Errors.FirstOrDefault()?.Message);
|
||||
Assert.Empty(result.Errors);
|
||||
}
|
||||
|
||||
[DebugOnlyFact]
|
||||
public async Task DeleteUserAsync_MustSucced_WithErrorMessage()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.Parse("3b001832-c78d-4705-9bc2-0f2f2ebb35b7");
|
||||
|
||||
// Act
|
||||
var result = await _skorubaService.RemoveUserAsync(userId);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsSuccess, result.Errors.FirstOrDefault()?.Message);
|
||||
Assert.Empty(result.Errors);
|
||||
}
|
||||
|
||||
[DebugOnlyFact]
|
||||
public async Task LockoutUserAsync_MustSucced_WithErrorMessage()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.Parse("285df6e4-c0f9-4c7c-8da7-b3867ffe1880");
|
||||
|
||||
// Act
|
||||
var result = await _skorubaService.SetUserLockoutAsync(userId, true);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsSuccess, result.Errors.FirstOrDefault()?.Message);
|
||||
Assert.Empty(result.Errors);
|
||||
}
|
||||
|
||||
[DebugOnlyFact]
|
||||
public async Task UnlockUserAsync_MustSucced_WithErrorMessage()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.Parse("285df6e4-c0f9-4c7c-8da7-b3867ffe1880");
|
||||
|
||||
// Act
|
||||
var result = await _skorubaService.SetUserLockoutAsync(userId, false);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsSuccess, result.Errors.FirstOrDefault()?.Message);
|
||||
Assert.Empty(result.Errors);
|
||||
}
|
||||
|
||||
[DebugOnlyFact]
|
||||
public async Task GetUserRolesAsync_SendsProperlyFormattedRequest()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.Parse("6059f1e4-6a41-4447-a546-86ece5102004");
|
||||
var userId = Guid.Parse("285df6e4-c0f9-4c7c-8da7-b3867ffe1880");
|
||||
var page = 1;
|
||||
var pageSize = 25;
|
||||
|
||||
@@ -85,9 +128,88 @@ public class SkorubaUserServiceGetUserRolesIntegrationTests
|
||||
Assert.Empty(result.Errors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Integration test: Verify cancellation token is respected.
|
||||
/// </summary>
|
||||
[DebugOnlyFact]
|
||||
public async Task AddUserRoleAsync_MustSucced_WithErrorMessage()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.Parse("285df6e4-c0f9-4c7c-8da7-b3867ffe1880");
|
||||
var roleId = Guid.Parse("34b03edc-cda4-4380-b7a5-3310933b75ab"); // Contributor role ID
|
||||
|
||||
// Act
|
||||
var result = await _skorubaService.AddRoleAsync(userId, roleId);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsSuccess, result.Errors.FirstOrDefault()?.Message);
|
||||
Assert.Empty(result.Errors);
|
||||
}
|
||||
|
||||
[DebugOnlyFact]
|
||||
public async Task RemoveUserRoleAsync_MustSucced_WithErrorMessage()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.Parse("285df6e4-c0f9-4c7c-8da7-b3867ffe1880");
|
||||
var roleId = Guid.Parse("34b03edc-cda4-4380-b7a5-3310933b75ab"); // Contributor role ID
|
||||
|
||||
// Act
|
||||
var result = await _skorubaService.RemoveRoleAsync(userId, roleId);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsSuccess, result.Errors.FirstOrDefault()?.Message);
|
||||
Assert.Empty(result.Errors);
|
||||
}
|
||||
|
||||
[DebugOnlyFact]
|
||||
public async Task GetUserClaimAsync_MustSucced_WithErrorMessage()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.Parse("285df6e4-c0f9-4c7c-8da7-b3867ffe1880");
|
||||
var page = 1;
|
||||
var pageSize = 25;
|
||||
|
||||
// Act
|
||||
var result = await _skorubaService.GetUserClaimsAsync(userId, page, pageSize);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsSuccess, result.Errors.FirstOrDefault()?.Message);
|
||||
Assert.Empty(result.Errors);
|
||||
}
|
||||
|
||||
[DebugOnlyFact]
|
||||
public async Task AddUserClaimAsync_MustSucced_WithErrorMessage()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.Parse("285df6e4-c0f9-4c7c-8da7-b3867ffe1880");
|
||||
var claimType = "test-claim";
|
||||
var claimValue = "test-value";
|
||||
|
||||
// Act
|
||||
var result = await _skorubaService.AddClaimAsync(userId, claimType, claimValue);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsSuccess, result.Errors.FirstOrDefault()?.Message);
|
||||
Assert.Empty(result.Errors);
|
||||
}
|
||||
|
||||
[DebugOnlyFact]
|
||||
public async Task RemoveUserClaimAsync_MustSucced_WithErrorMessage()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.Parse("285df6e4-c0f9-4c7c-8da7-b3867ffe1880");
|
||||
var claimType = "test-claim";
|
||||
|
||||
var userClaimsResult = await _skorubaService.GetUserClaimsAsync(userId);
|
||||
var matchingClaim = userClaimsResult.Value?.FirstOrDefault(c => c.ClaimType == claimType);
|
||||
|
||||
Assert.NotNull(matchingClaim);
|
||||
|
||||
// Act
|
||||
var result = await _skorubaService.RemoveClaimAsync(userId, matchingClaim!.ClaimId!.Value);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.IsSuccess, result.Errors.FirstOrDefault()?.Message);
|
||||
Assert.Empty(result.Errors);
|
||||
}
|
||||
|
||||
[DebugOnlyFact]
|
||||
public async Task GetUserRolesAsync_RespectsCancellationToken()
|
||||
{
|
||||
|
||||
@@ -7,11 +7,9 @@
|
||||
<File Path="PostFundManagement.snk" />
|
||||
<File Path="README.md" />
|
||||
</Folder>
|
||||
<Folder Name="/tests/">
|
||||
<Project Path="PostFundManagement.Tests/PostFundManagement.Tests.csproj" Id="ce650d27-8a68-436d-b5c1-db25f2286e85" />
|
||||
</Folder>
|
||||
<Project Path="PostFundManagement.Api/PostFundManagement.Api.csproj" />
|
||||
<Project Path="PostFundManagement.Application/PostFundManagement.Application.csproj" Id="750c7754-53a7-4573-abed-5c436246a04d" />
|
||||
<Project Path="PostFundManagement.Domain/PostFundManagement.Domain.csproj" />
|
||||
<Project Path="PostFundManagement.Infrastructure/PostFundManagement.Infrastructure.csproj" />
|
||||
<Project Path="PostFundManagement.Tests/PostFundManagement.Tests.csproj" Id="ce650d27-8a68-436d-b5c1-db25f2286e85" />
|
||||
</Solution>
|
||||
|
||||
Reference in New Issue
Block a user