From 8eca793f5f6c61acf0c553e8b63e59d892f951bd Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Sat, 10 Oct 2026 15:31:36 +0200 Subject: [PATCH] Added skoruba user service integration test --- .../Endpoints/Users/AddUserRoleEndpoint.cs | 2 +- .../Endpoints/Users/DeleteUserRoleEndpoint.cs | 2 +- .../Abstractions/Services/IUserService.cs | 4 +- .../Extensions/Security.cs | 7 +- .../Services/Users/UserCacheService.cs | 4 +- .../Services/Users/UserService.cs | 10 +- .../Abstractions/ISkorubaUserService.cs | 7 +- .../Security/SkorubaUserService.cs | 57 +++++---- .../PostFundManagement.Tests.csproj | 39 +++++++ .../SkorubaUserServiceIntegrationTests.cs | 109 ++++++++++++++++++ PostFundManagement.Tests/appsettings.json | 45 ++++++++ PostFundManagement.slnx | 3 + 12 files changed, 248 insertions(+), 41 deletions(-) create mode 100644 PostFundManagement.Tests/PostFundManagement.Tests.csproj create mode 100644 PostFundManagement.Tests/SkorubaUserServiceIntegrationTests.cs create mode 100644 PostFundManagement.Tests/appsettings.json diff --git a/PostFundManagement.Api/Endpoints/Users/AddUserRoleEndpoint.cs b/PostFundManagement.Api/Endpoints/Users/AddUserRoleEndpoint.cs index 31109ec..359bb42 100644 --- a/PostFundManagement.Api/Endpoints/Users/AddUserRoleEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Users/AddUserRoleEndpoint.cs @@ -11,7 +11,7 @@ public sealed class AddUserRoleEndpoint : IEndpoint { public void Map(IEndpointRouteBuilder builder) { - builder.MapPatch("api/users/{userId:Guid}/roles/{roleId}", async (Guid userId, string roleId, HttpContext context, + builder.MapPatch("api/users/{userId:Guid}/roles/{roleId:Guid}", async (Guid userId, Guid roleId, HttpContext context, IUserService userService, CancellationToken cancellationToken = default) => await userService.AddUserRoleAsync(userId, roleId, context.GetUserId(), cancellationToken) switch { diff --git a/PostFundManagement.Api/Endpoints/Users/DeleteUserRoleEndpoint.cs b/PostFundManagement.Api/Endpoints/Users/DeleteUserRoleEndpoint.cs index 4977002..5938916 100644 --- a/PostFundManagement.Api/Endpoints/Users/DeleteUserRoleEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Users/DeleteUserRoleEndpoint.cs @@ -11,7 +11,7 @@ public sealed class DeleteUserRoleEndpoint : IEndpoint { public void Map(IEndpointRouteBuilder builder) { - builder.MapDelete("api/users/{userId:Guid}/roles/{roleId}", async (Guid userId, string roleId, HttpContext context, + builder.MapDelete("api/users/{userId:Guid}/roles/{roleId:Guid}", async (Guid userId, Guid roleId, HttpContext context, IUserService service, CancellationToken cancellationToken = default) => await service.DeleteUserRoleAsync(userId, roleId, context.GetUserId(), cancellationToken) switch { diff --git a/PostFundManagement.Application/Abstractions/Services/IUserService.cs b/PostFundManagement.Application/Abstractions/Services/IUserService.cs index a6e7974..537c8b4 100644 --- a/PostFundManagement.Application/Abstractions/Services/IUserService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IUserService.cs @@ -32,7 +32,7 @@ public interface IUserService [Log] [Audit] - Task AddUserRoleAsync(Guid targetUserId, string roleId, Guid userId, CancellationToken cancellationToken = default); + Task AddUserRoleAsync(Guid targetUserId, Guid roleId, Guid userId, CancellationToken cancellationToken = default); [Log] [Audit] @@ -57,5 +57,5 @@ public interface IUserService [Log] [Audit] - Task DeleteUserRoleAsync(Guid targetUserId, string roleId, Guid userId, CancellationToken cancellationToken = default); + Task DeleteUserRoleAsync(Guid targetUserId, Guid roleId, Guid userId, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Extensions/Security.cs b/PostFundManagement.Application/Extensions/Security.cs index 029c3dc..e107b62 100644 --- a/PostFundManagement.Application/Extensions/Security.cs +++ b/PostFundManagement.Application/Extensions/Security.cs @@ -24,23 +24,20 @@ public static class Security if (!authOptions.Authority.EndsWith('/')) authOptions.Authority += "/"; - services.AddClientCredentialsTokenManagement() + services.AddClientCredentialsTokenManagement() .AddClient(SkorubaCredentialClient, client => { client.TokenEndpoint = new Uri($"{authOptions.Authority}connect/token"); client.ClientId = ClientId.Parse(authOptions.ClientId!); client.ClientSecret = ClientSecret.Parse(authOptions.ClientSecret!); client.Scope = Duende.AccessTokenManagement.Scope.ParseOrDefault(authOptions.Scope); - }); + }); services.AddHttpClient(SkorubaHttpClient, client => { client.BaseAddress = new Uri(configuration.GetConnectionString("SkorubaApi")!); }).AddClientCredentialsTokenHandler(ClientCredentialsClientName.Parse(SkorubaCredentialClient)); - services.AddKeyedScoped(SkorubaHttpClient, (provider, key) => - provider.GetRequiredService()); - services.AddScoped(); services.AddScoped(); services.AddSingleton(); diff --git a/PostFundManagement.Application/Services/Users/UserCacheService.cs b/PostFundManagement.Application/Services/Users/UserCacheService.cs index f4e5022..94605b7 100644 --- a/PostFundManagement.Application/Services/Users/UserCacheService.cs +++ b/PostFundManagement.Application/Services/Users/UserCacheService.cs @@ -14,7 +14,7 @@ public sealed class UserCacheService(UserService service, IDistributedCache cach public async Task AddUserClaimAsync(AddUserClaimRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.AddUserClaimAsync(request, userId, cancellationToken); - public async Task AddUserRoleAsync(Guid targetUserId, string roleId, Guid userId, CancellationToken cancellationToken = default) => + public async Task AddUserRoleAsync(Guid targetUserId, Guid roleId, Guid userId, CancellationToken cancellationToken = default) => await service.AddUserRoleAsync(targetUserId, roleId, userId, cancellationToken); public async Task CreateUserAsync(string tenantIdHash, string idNumberHash, CreateUserRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -23,7 +23,7 @@ public sealed class UserCacheService(UserService service, IDistributedCache cach public async Task DeleteUserClaimAsync(string claimIdHash, Guid targetUserId, Guid userId, CancellationToken cancellationToken = default) => await service.DeleteUserClaimAsync(claimIdHash, targetUserId, userId, cancellationToken); - public async Task DeleteUserRoleAsync(Guid targetUserId, string roleId, Guid userId, CancellationToken cancellationToken = default) => + public async Task DeleteUserRoleAsync(Guid targetUserId, Guid roleId, Guid userId, CancellationToken cancellationToken = default) => await service.DeleteUserRoleAsync(targetUserId, roleId, userId, cancellationToken); public async Task> GetSystemRolesAsync(Guid userId, CancellationToken cancellationToken = default) => diff --git a/PostFundManagement.Application/Services/Users/UserService.cs b/PostFundManagement.Application/Services/Users/UserService.cs index 76527f9..6eb981f 100644 --- a/PostFundManagement.Application/Services/Users/UserService.cs +++ b/PostFundManagement.Application/Services/Users/UserService.cs @@ -164,7 +164,7 @@ public sealed class UserService(IDbContextFactory contextF if (defaultRole == null) return Result.Fail(NotFoundError.Create("Role 'Contributor' not found.")); - var roleAssignmentResult = await skorubaService.AddRoleAsync(newUserId, defaultRole.Id, cancellationToken); + var roleAssignmentResult = await skorubaService.AddRoleAsync(newUserId, Guid.Parse(defaultRole.Id), cancellationToken); if (roleAssignmentResult.IsFailed) return Result.Fail(UpdateError.Create("Failed to assign role to user.")); @@ -255,7 +255,7 @@ public sealed class UserService(IDbContextFactory contextF await Result.Try((Func>>)(async () => await skorubaService.GetAvailableRolesAsync(cancellationToken: cancellationToken))); - public async Task DeleteUserRoleAsync(Guid targetUserId, string roleId, Guid userId, CancellationToken cancellationToken = default) => + public async Task DeleteUserRoleAsync(Guid targetUserId, Guid roleId, Guid userId, CancellationToken cancellationToken = default) => await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -278,7 +278,7 @@ public sealed class UserService(IDbContextFactory contextF .CausedBy(addClaimResult.Errors)); })); - public async Task AddUserRoleAsync(Guid targetUserId, string roleId, Guid userId, CancellationToken cancellationToken = default) => + public async Task AddUserRoleAsync(Guid targetUserId, Guid roleId, Guid userId, CancellationToken cancellationToken = default) => await Result.Try((Func>)(async () => { var userRolesResult = await skorubaService.GetUserRolesAsync(targetUserId, cancellationToken: cancellationToken); @@ -289,7 +289,7 @@ public sealed class UserService(IDbContextFactory contextF foreach (var role in userRolesResult.Value) { - var deleteRoleResult = await skorubaService.RemoveRoleAsync(targetUserId, role.Id, cancellationToken); + var deleteRoleResult = await skorubaService.RemoveRoleAsync(targetUserId, Guid.Parse(role.Id), cancellationToken); if (deleteRoleResult.IsFailed) return Result.Fail(UpdateError.Create("Failed to remove existing user role.") @@ -308,7 +308,7 @@ public sealed class UserService(IDbContextFactory contextF return Result.Fail(NotFoundError.Create("Failed to retrieve system roles.") .CausedBy(systemRoles.Errors)); - var targetRole = systemRoles.Value.FirstOrDefault(r => r.Id == roleId); + var targetRole = systemRoles.Value.FirstOrDefault(r => r.Id == roleId.ToString()); if (targetRole == null) return Result.Fail(NotFoundError.Create("Role not found.")); diff --git a/PostFundManagement.Domain/Abstractions/ISkorubaUserService.cs b/PostFundManagement.Domain/Abstractions/ISkorubaUserService.cs index 3294d03..83a354e 100644 --- a/PostFundManagement.Domain/Abstractions/ISkorubaUserService.cs +++ b/PostFundManagement.Domain/Abstractions/ISkorubaUserService.cs @@ -30,11 +30,11 @@ public interface ISkorubaUserService [Log] [Audit] - Task AddRoleAsync(Guid userId, string roleId, CancellationToken cancellationToken = default); + Task AddRoleAsync(Guid userId, Guid roleId, CancellationToken cancellationToken = default); [Log] [Audit] - Task RemoveRoleAsync(Guid userId, string roleId, CancellationToken cancellationToken = default); + Task RemoveRoleAsync(Guid userId, Guid roleId, CancellationToken cancellationToken = default); [Log] Task> GetAvailableRolesAsync(string? searchText = null, int page = 1, int pageSize = 50, CancellationToken cancellationToken = default); @@ -45,6 +45,9 @@ public interface ISkorubaUserService [Log] Task> GetUserClaimsAsync(Guid userId, int page = 1, int pageSize = 50, CancellationToken cancellationToken = default); + [Log] + Task> GetRoleClaimsAsync(Guid roleId, int page = 1, int pageSize = 50, CancellationToken cancellationToken = default); + [Log] Task InvalidateUserSessionAsync(Guid userId, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Infrastructure/Security/SkorubaUserService.cs b/PostFundManagement.Infrastructure/Security/SkorubaUserService.cs index f070cfe..333a886 100644 --- a/PostFundManagement.Infrastructure/Security/SkorubaUserService.cs +++ b/PostFundManagement.Infrastructure/Security/SkorubaUserService.cs @@ -4,12 +4,12 @@ using PostFundManagement.Domain.Models.Security; namespace PostFundManagement.Infrastructure.Security; -public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClientFactory httpClientFactory) : ISkorubaUserService +public class SkorubaUserService(IHttpClientFactory httpClientFactory) : ISkorubaUserService { public async Task> CreateUserAsync(CreateUserRequest request, Guid userId, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - var httpClient = httpClientFactory.CreateClient(); + var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient); var response = await httpClient.PostAsJsonAsync("api/users", request, cancellationToken); if (!response.IsSuccessStatusCode) @@ -24,7 +24,7 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie public async Task> GetUserAsync(Guid userId, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - var httpClient = httpClientFactory.CreateClient(); + var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient); var result = await httpClient.GetFromJsonAsync($"api/users/{userId}", cancellationToken); return Result.FailIf(result is null, NotFoundError.Create($"User {userId} not found.")) @@ -34,7 +34,7 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie public async Task UpdateUserAsync(UpdateUserRequest request, CancellationToken cancellationToken = default) => await Result.Try((Func>)(async () => { - var httpClient = httpClientFactory.CreateClient(); + var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient); var response = await httpClient.PutAsJsonAsync("api/users", request, cancellationToken); return Result.OkIf(response.IsSuccessStatusCode, UpdateError.Create("Failed to update user.")); @@ -43,7 +43,7 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie public async Task SetUserLockoutAsync(Guid userId, bool disabled, CancellationToken cancellationToken = default) => await Result.Try((Func>)(async () => { - var httpClient = httpClientFactory.CreateClient(); + var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient); var user = await httpClient.GetFromJsonAsync($"api/users/{userId}", cancellationToken); if (user is null) @@ -63,7 +63,7 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie public async Task AddClaimAsync(Guid userId, string claimType, string claimValue, CancellationToken cancellationToken = default) => await Result.Try((Func>)(async () => { - var httpClient = httpClientFactory.CreateClient(); + var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient); var payload = new { UserId = userId, ClaimType = claimType, ClaimValue = claimValue }; var response = await httpClient.PostAsJsonAsync($"api/users/{userId}/claims", payload, cancellationToken); @@ -74,7 +74,7 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie public async Task RemoveClaimAsync(Guid userId, int claimId, CancellationToken cancellationToken = default) => await Result.Try((Func>)(async () => { - var httpClient = httpClientFactory.CreateClient(); + var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient); var response = await httpClient.DeleteAsync($"api/users/{userId}/claims/{claimId}", cancellationToken); return Result.OkIf(response.IsSuccessStatusCode, CreateError.Create("Failed to remove user claim.")); @@ -83,7 +83,7 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie public async Task> GetUserClaimsAsync(Guid userId, int page = 1, int pageSize = 50, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - var httpClient = httpClientFactory.CreateClient(); + var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient); var result = await httpClient.GetFromJsonAsync($"api/users/{userId}/claims?page={page}&pageSize={pageSize}", cancellationToken); @@ -91,10 +91,10 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie .ToResult(result?.Claims.ToArray() ?? []); })); - public async Task AddRoleAsync(Guid userId, string roleId, CancellationToken cancellationToken = default) => + public async Task AddRoleAsync(Guid userId, Guid roleId, CancellationToken cancellationToken = default) => await Result.Try((Func>)(async () => { - var httpClient = httpClientFactory.CreateClient(); + var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient); var payload = new { UserId = userId, RoleId = roleId }; var response = await httpClient.PostAsJsonAsync($"api/users/{userId}/roles", payload, cancellationToken); @@ -102,10 +102,10 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie return Result.OkIf(response.IsSuccessStatusCode, CreateError.Create("Failed to add user role.")); })); - public async Task RemoveRoleAsync(Guid userId, string roleId, CancellationToken cancellationToken = default) => + public async Task RemoveRoleAsync(Guid userId, Guid roleId, CancellationToken cancellationToken = default) => await Result.Try((Func>)(async () => { - var httpClient = httpClientFactory.CreateClient(); + var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient); var response = await httpClient.DeleteAsync($"api/users/{userId}/roles/{roleId}", cancellationToken); return Result.OkIf(response.IsSuccessStatusCode, CreateError.Create("Failed to remove user role.")); @@ -114,7 +114,7 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie public async Task> GetUserRolesAsync(Guid userId, int page = 1, int pageSize = 50, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => { - var httpClient = httpClientFactory.CreateClient(); + var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient); var result = await httpClient.GetFromJsonAsync($"api/users/{userId}/roles?page={page}&pageSize={pageSize}", cancellationToken); @@ -122,10 +122,21 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie .ToResult(result?.Roles?.ToArray() ?? []); })); + public async Task> GetRoleClaimsAsync(Guid roleId, int page = 1, int pageSize = 50, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => + { + var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient); + + var result = await httpClient.GetFromJsonAsync($"api/roles/{roleId}/claims?page={page}&pageSize={pageSize}", cancellationToken); + + return Result.FailIf(result is null, NotFoundError.Create($"Claims for role {roleId} could not be retrieved.")) + .ToResult(result?.Claims.ToArray() ?? []); + })); + public async Task InvalidateUserSessionAsync(Guid userId, CancellationToken cancellationToken = default) => await Result.Try((Func>)(async () => { - var httpClient = httpClientFactory.CreateClient(); + var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient); var user = await httpClient.GetFromJsonAsync($"api/users/{userId}", cancellationToken); if (user is null) @@ -138,17 +149,17 @@ public class SkorubaUserService([FromKeyedServices(SkorubaHttpClient)] IHttpClie public async Task> GetAvailableRolesAsync(string? searchText = null, int page = 1, int pageSize = 50, CancellationToken cancellationToken = default) => await Result.Try((Func>>)(async () => - { - var httpClient = httpClientFactory.CreateClient(); + { + var httpClient = httpClientFactory.CreateClient(SkorubaHttpClient); - var query = $"api/roles?page={page}&pageSize={pageSize}"; + var query = $"api/roles?page={page}&pageSize={pageSize}"; - if (!string.IsNullOrWhiteSpace(searchText)) - query += $"&searchText={Uri.EscapeDataString(searchText)}"; + if (!string.IsNullOrWhiteSpace(searchText)) + query += $"&searchText={Uri.EscapeDataString(searchText)}"; - var result = await httpClient.GetFromJsonAsync(query, cancellationToken); + var result = await httpClient.GetFromJsonAsync(query, cancellationToken); - return Result.FailIf(result is null, NotFoundError.Create("Available roles could not be retrieved.")) - .ToResult(result?.Roles?.ToArray() ?? []); - })); + return Result.FailIf(result is null, NotFoundError.Create("Available roles could not be retrieved.")) + .ToResult(result?.Roles?.ToArray() ?? []); + })); } \ No newline at end of file diff --git a/PostFundManagement.Tests/PostFundManagement.Tests.csproj b/PostFundManagement.Tests/PostFundManagement.Tests.csproj new file mode 100644 index 0000000..497b59a --- /dev/null +++ b/PostFundManagement.Tests/PostFundManagement.Tests.csproj @@ -0,0 +1,39 @@ + + + + net10.0 + enable + enable + false + 079fcde1-9b10-4402-9624-5f0827b4dbfd + + + + + PreserveNewest + true + PreserveNewest + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PostFundManagement.Tests/SkorubaUserServiceIntegrationTests.cs b/PostFundManagement.Tests/SkorubaUserServiceIntegrationTests.cs new file mode 100644 index 0000000..ad0316c --- /dev/null +++ b/PostFundManagement.Tests/SkorubaUserServiceIntegrationTests.cs @@ -0,0 +1,109 @@ +using PostFundManagement.Application.Extensions; +using PostFundManagement.Domain.Abstractions; +using PostFundManagement.Infrastructure.Database.Services; +using System.Diagnostics; +using static PostFundManagement.Domain.Extensions.Constants; + +namespace PostFundManagement.Tests; + +/// +/// 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" +/// +public class SkorubaUserServiceGetUserRolesIntegrationTests +{ + private readonly ISkorubaUserService _skorubaService; + private readonly IHttpClientFactory _httpClientFactory; + + public SkorubaUserServiceGetUserRolesIntegrationTests() + { + var services = new ServiceCollection(); + + // Load configuration from environment and secrets + var configuration = new ConfigurationBuilder() + .AddJsonFile("appsettings.json", optional: true) + .AddUserSecrets(Assembly.GetExecutingAssembly(), optional: true) + .AddEnvironmentVariables() + .Build(); + + services.AddSingleton(configuration); + services.AddHttpClient(); + + // Register the Skoruba HTTP client with the actual DI configuration + services.AddSkorubaApiService(configuration) + .AddRegisterServicesWithCaching(configuration) + .AddEndpointAndServiceCaching(configuration) + .AddSharedServices() + .AddHashServices(configuration) + .AddApplicationDbContext(configuration) + .AddHttpContextAccessor() + .AddTransient(); + + var serviceProvider = services.BuildServiceProvider(); + _skorubaService = serviceProvider.GetRequiredService(); + _httpClientFactory = serviceProvider.GetRequiredService(); + } + + /// + /// Integration test: Verify HttpClient is properly configured with SkorubaHttpClient name. + /// + //[Fact(Skip = "Skipping integration test")] + [Fact] + public void HttpClientFactory_HasSkorubaHttpClientConfigured() + { + // Act + var httpClient = _httpClientFactory.CreateClient(SkorubaHttpClient); + + // Assert + Assert.NotNull(httpClient); + Assert.NotNull(httpClient.BaseAddress); + } + + /// + /// Integration test: Verify that the service properly formats API requests. + /// Uses a fake/invalid user ID to test the HTTP request flow. + /// + //[Fact(Skip = "Skipping integration test")] + [Fact] + public async Task GetUserRolesAsync_SendsProperlyFormattedRequest() + { + // Arrange + var userId = Guid.NewGuid(); + var page = 2; + var pageSize = 25; + + // Act - This will likely fail with 401 or 404 + var result = await _skorubaService.GetUserRolesAsync(userId, page, pageSize); + + // Assert - Request was processed (even if it failed) + Assert.False(result.IsSuccess); + Assert.NotNull(result.Errors); + } + + /// + /// Integration test: Verify cancellation token is respected. + /// + //[Fact(Skip = "Skipping integration test")] + [Fact] + public async Task GetUserRolesAsync_RespectsCancellationToken() + { + // Arrange + var userId = Guid.NewGuid(); + var cts = new CancellationTokenSource(); + cts.Cancel(); + + // Act & Assert + await Assert.ThrowsAsync(async () => + { + await _skorubaService.GetUserRolesAsync(userId, cancellationToken: cts.Token); + }); + } +} diff --git a/PostFundManagement.Tests/appsettings.json b/PostFundManagement.Tests/appsettings.json new file mode 100644 index 0000000..58a2660 --- /dev/null +++ b/PostFundManagement.Tests/appsettings.json @@ -0,0 +1,45 @@ +{ + "EnableServiceCaching": true, + "CorsOrigins": [ + "https://console.ifri.khongisa.co.za", + "https://localhost:7208" + ], + "GeneralS3Settings": { + "ServiceUrl": "https://s3.garage.khongisa.co.za", + "Region": "garage", + "BucketName": "pfm", + "CdnBaseUrl": "https://pfm.cdn.khongisa.co.za" + }, + "Monitoring": { + "Address": "http://aspire-dashboard-service.aspire.svc.cluster.local:18889", + "ServiceName": "Pfm.DEV" + }, + "SecuritySettings": { + "Authority": "https://sts.security.khongisa.co.za", + "Audience": "pfm-api-dev" + }, + "MachineIdentitySettings": { + "Authority": "https://sts.security.khongisa.co.za", + "Scope": "skoruba_identity_admin_api" + }, + "Logging": { + "LogLevel": { + "Default": "Warning", + "Quartz": "Error", + "Microsoft.AspNetCore": "Warning", + "Microsoft.EntityFrameworkCore.Database": "Error", + "Microsoft.EntityFrameworkCore.Update": "Error" + } + }, + "EmailProcessorCron": "0 */5 * * * ?", + "Email": { + "SmtpServer": "smtp.example.com", + "SmtpPort": 587, + "SenderEmail": "noreply@example.com", + "SenderName": "Example" + }, + "HasherSettings": { + "MinHashLength": 11 + }, + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/PostFundManagement.slnx b/PostFundManagement.slnx index 9982c4f..c829b6c 100644 --- a/PostFundManagement.slnx +++ b/PostFundManagement.slnx @@ -7,6 +7,9 @@ + + +