diff --git a/src/Altinn.Profile/Altinn.Profile.csproj b/src/Altinn.Profile/Altinn.Profile.csproj index 2eced9a..e112844 100644 --- a/src/Altinn.Profile/Altinn.Profile.csproj +++ b/src/Altinn.Profile/Altinn.Profile.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Altinn.Profile/Controllers/UserProfileInternalController.cs b/src/Altinn.Profile/Controllers/UserProfileInternalController.cs index 2cc92c7..27e3cb1 100644 --- a/src/Altinn.Profile/Controllers/UserProfileInternalController.cs +++ b/src/Altinn.Profile/Controllers/UserProfileInternalController.cs @@ -30,9 +30,9 @@ public UserProfileInternalController(IUserProfiles userProfilesWrapper) /// /// Gets the user profile for a given user identified by one of the available types of user identifiers: /// UserId (from Altinn 2 Authn UserProfile) + /// UserUuid (from Altinn 2 Authn UserProfile) /// Username (from Altinn 2 Authn UserProfile) /// SSN/Dnr (from Freg) - /// Uuid (from Altinn 2 Party/UserProfile implementation will be added later) /// /// Input model for providing one of the supported lookup parameters /// User profile of the given user @@ -47,6 +47,10 @@ public async Task> Get([FromBody] UserProfileLookup us { result = await _userProfilesWrapper.GetUser(userProfileLookup.UserId); } + else if (userProfileLookup?.UserUuid != null) + { + result = await _userProfilesWrapper.GetUserByUuid(userProfileLookup.UserUuid.Value); + } else if (!string.IsNullOrWhiteSpace(userProfileLookup?.Username)) { result = await _userProfilesWrapper.GetUserByUsername(userProfileLookup.Username); diff --git a/src/Altinn.Profile/Controllers/UsersController.cs b/src/Altinn.Profile/Controllers/UsersController.cs index 9d99c62..b4b7478 100644 --- a/src/Altinn.Profile/Controllers/UsersController.cs +++ b/src/Altinn.Profile/Controllers/UsersController.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Threading.Tasks; @@ -37,7 +38,7 @@ public UsersController(IUserProfiles userProfilesWrapper) /// /// The user id /// The information about a given user - [HttpGet("{userID}")] + [HttpGet("{userID:int}")] [Authorize(Policy = "PlatformAccess")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] @@ -52,6 +53,26 @@ public async Task> Get(int userID) return Ok(result); } + /// + /// Gets the user profile for a given user uuid + /// + /// The user uuid + /// The information about a given user + [HttpGet("{userUuid:Guid}")] + [Authorize(Policy = "PlatformAccess")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task> Get(Guid userUuid) + { + UserProfile result = await _userProfilesWrapper.GetUserByUuid(userUuid); + if (result == null) + { + return NotFound(); + } + + return Ok(result); + } + /// /// Gets the current user based on the request context /// diff --git a/src/Altinn.Profile/Models/UserProfileLookup.cs b/src/Altinn.Profile/Models/UserProfileLookup.cs index 75fab04..9e5d3e3 100644 --- a/src/Altinn.Profile/Models/UserProfileLookup.cs +++ b/src/Altinn.Profile/Models/UserProfileLookup.cs @@ -1,4 +1,6 @@ -namespace Altinn.Profile.Models +using System; + +namespace Altinn.Profile.Models { /// /// Input model for internal UserProfile lookup requests, where one of the lookup identifiers available must be set for performing the lookup request: @@ -13,6 +15,11 @@ public class UserProfileLookup /// Gets or sets the users UserId if the lookup is to be performed based on this identifier /// public int UserId { get; set; } + + /// + /// Gets or sets the users UserUuid if the lookup is to be performed based on this identifier + /// + public Guid? UserUuid { get; set; } /// /// Gets or sets the users Username if the lookup is to be performed based on this identifier diff --git a/src/Altinn.Profile/Services/Decorators/UserProfileCachingDecorator.cs b/src/Altinn.Profile/Services/Decorators/UserProfileCachingDecorator.cs index d900245..ce0d165 100644 --- a/src/Altinn.Profile/Services/Decorators/UserProfileCachingDecorator.cs +++ b/src/Altinn.Profile/Services/Decorators/UserProfileCachingDecorator.cs @@ -79,6 +79,26 @@ public async Task GetUser(string ssn) return user; } + /// + public async Task GetUserByUuid(Guid userUuid) + { + string uniqueCacheKey = $"User:UserUuid:{userUuid}"; + + if (_memoryCache.TryGetValue(uniqueCacheKey, out UserProfile user)) + { + return user; + } + + user = await _decoratedService.GetUserByUuid(userUuid); + + if (user != null) + { + _memoryCache.Set(uniqueCacheKey, user, _cacheOptions); + } + + return user; + } + /// public async Task GetUserByUsername(string username) { diff --git a/src/Altinn.Profile/Services/Implementation/UserProfilesWrapper.cs b/src/Altinn.Profile/Services/Implementation/UserProfilesWrapper.cs index 9695b2b..fd886e4 100644 --- a/src/Altinn.Profile/Services/Implementation/UserProfilesWrapper.cs +++ b/src/Altinn.Profile/Services/Implementation/UserProfilesWrapper.cs @@ -88,6 +88,25 @@ public async Task GetUser(string ssn) return user; } + /// + public async Task GetUserByUuid(Guid userUuid) + { + Uri endpointUrl = new Uri($"{_generalSettings.BridgeApiEndpoint}users?useruuid={userUuid}"); + + HttpResponseMessage response = await _client.GetAsync(endpointUrl); + + if (!response.IsSuccessStatusCode) + { + _logger.LogError("Getting user {userUuid} failed with {statusCode}", userUuid, response.StatusCode); + return null; + } + + string content = await response.Content.ReadAsStringAsync(); + UserProfile user = JsonSerializer.Deserialize(content, _serializerOptions); + + return user; + } + /// public async Task GetUserByUsername(string username) { diff --git a/src/Altinn.Profile/Services/Interfaces/IUserProfiles.cs b/src/Altinn.Profile/Services/Interfaces/IUserProfiles.cs index 420d811..a925ac3 100644 --- a/src/Altinn.Profile/Services/Interfaces/IUserProfiles.cs +++ b/src/Altinn.Profile/Services/Interfaces/IUserProfiles.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; using Altinn.Platform.Profile.Models; @@ -23,6 +24,13 @@ public interface IUserProfiles /// User profile connected to given ssn. Task GetUser(string ssn); + /// + /// Method that fetches a user based on a user uuid + /// + /// The user uuid + /// User profile with given user id. + Task GetUserByUuid(Guid userUuid); + /// /// Method that fetches a user based on username. /// diff --git a/test/Altinn.Profile.Tests/IntegrationTests/UserProfileInternalTests.cs b/test/Altinn.Profile.Tests/IntegrationTests/UserProfileInternalTests.cs index 90b4b26..192d342 100644 --- a/test/Altinn.Profile.Tests/IntegrationTests/UserProfileInternalTests.cs +++ b/test/Altinn.Profile.Tests/IntegrationTests/UserProfileInternalTests.cs @@ -1,3 +1,4 @@ +using System; using System.Net; using System.Net.Http; using System.Net.Http.Json; @@ -76,6 +77,46 @@ public async Task GetUserById_SblBridgeFindsProfile_ResponseOk_ReturnsUserProfil Assert.Equal("nb", actualUser.ProfileSettingPreference.Language); } + [Fact] + public async Task GetUserByUuid_SblBridgeFindsProfile_ResponseOk_ReturnsUserProfile() + { + // Arrange + Guid userUuid = new("cc86d2c7-1695-44b0-8e82-e633243fdf31"); + + HttpRequestMessage sblRequest = null; + DelegatingHandlerStub messageHandler = new(async (request, token) => + { + sblRequest = request; + + UserProfile userProfile = await TestDataLoader.Load(userUuid.ToString()); + return new HttpResponseMessage() { Content = JsonContent.Create(userProfile) }; + }); + _webApplicationFactorySetup.SblBridgeHttpMessageHandler = messageHandler; + + HttpRequestMessage httpRequestMessage = CreatePostRequest($"/profile/api/v1/internal/user/", new UserProfileLookup { UserUuid = userUuid }); + + HttpClient client = _webApplicationFactorySetup.GetTestServerClient(); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.NotNull(sblRequest); + Assert.Equal(HttpMethod.Get, sblRequest.Method); + Assert.EndsWith($"sblbridge/profile/api/users?useruuid={userUuid}", sblRequest.RequestUri.ToString()); + + string responseContent = await response.Content.ReadAsStringAsync(); + + UserProfile actualUser = JsonSerializer.Deserialize( + responseContent, serializerOptionsCamelCase); + + // These asserts check that deserializing with camel casing was successful. + Assert.Equal(userUuid, actualUser.UserUuid); + Assert.Equal("LEO WILHELMSEN", actualUser.Party.Name); + Assert.Equal("LEO", actualUser.Party.Person.FirstName); + Assert.Equal("nb", actualUser.ProfileSettingPreference.Language); + } + [Fact] public async Task GetUserById_SblBridgeReturnsNotFound_ResponseNotFound() { @@ -106,6 +147,36 @@ public async Task GetUserById_SblBridgeReturnsNotFound_ResponseNotFound() Assert.EndsWith($"sblbridge/profile/api/users/{UserId}", sblRequest.RequestUri.ToString()); } + [Fact] + public async Task GetUserByUuid_SblBridgeReturnsNotFound_ResponseNotFound() + { + // Arrange + Guid userUuid = new("cc86d2c7-1695-44b0-8e82-e633243fdf31"); + + HttpRequestMessage sblRequest = null; + DelegatingHandlerStub messageHandler = new(async (request, token) => + { + sblRequest = request; + + return await Task.FromResult(new HttpResponseMessage() { StatusCode = HttpStatusCode.NotFound }); + }); + _webApplicationFactorySetup.SblBridgeHttpMessageHandler = messageHandler; + + HttpRequestMessage httpRequestMessage = CreatePostRequest($"/profile/api/v1/internal/user/", new UserProfileLookup { UserUuid = userUuid }); + + HttpClient client = _webApplicationFactorySetup.GetTestServerClient(); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + + Assert.NotNull(sblRequest); + Assert.Equal(HttpMethod.Get, sblRequest.Method); + Assert.EndsWith($"sblbridge/profile/api/users?useruuid={userUuid}", sblRequest.RequestUri.ToString()); + } + [Fact] public async Task GetUserById_SblBridgeReturnsUnavailable_ResponseNotFound() { @@ -136,6 +207,36 @@ public async Task GetUserById_SblBridgeReturnsUnavailable_ResponseNotFound() Assert.EndsWith($"sblbridge/profile/api/users/{UserId}", sblRequest.RequestUri.ToString()); } + [Fact] + public async Task GetUserByUuid_SblBridgeReturnsUnavailable_ResponseNotFound() + { + // Arrange + Guid userUuid = new("cc86d2c7-1695-44b0-8e82-e633243fdf31"); + + HttpRequestMessage sblRequest = null; + DelegatingHandlerStub messageHandler = new(async (request, token) => + { + sblRequest = request; + + return await Task.FromResult(new HttpResponseMessage() { StatusCode = HttpStatusCode.ServiceUnavailable }); + }); + _webApplicationFactorySetup.SblBridgeHttpMessageHandler = messageHandler; + + HttpRequestMessage httpRequestMessage = CreatePostRequest($"/profile/api/v1/internal/user/", new UserProfileLookup { UserUuid = userUuid }); + + HttpClient client = _webApplicationFactorySetup.GetTestServerClient(); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + + Assert.NotNull(sblRequest); + Assert.Equal(HttpMethod.Get, sblRequest.Method); + Assert.EndsWith($"sblbridge/profile/api/users?useruuid={userUuid}", sblRequest.RequestUri.ToString()); + } + [Fact] public async Task GetUserBySsn_SblBridgeFindsProfile_ReturnsUserProfile() { diff --git a/test/Altinn.Profile.Tests/IntegrationTests/UserProfileTests.cs b/test/Altinn.Profile.Tests/IntegrationTests/UserProfileTests.cs index 4708e2e..692fd23 100644 --- a/test/Altinn.Profile.Tests/IntegrationTests/UserProfileTests.cs +++ b/test/Altinn.Profile.Tests/IntegrationTests/UserProfileTests.cs @@ -1,3 +1,4 @@ +using System; using System.Net; using System.Net.Http; using System.Net.Http.Headers; @@ -15,7 +16,6 @@ using Altinn.Profile.Tests.Testdata; using Microsoft.AspNetCore.Mvc.Testing; - using Xunit; namespace Altinn.Profile.Tests.IntegrationTests @@ -144,6 +144,121 @@ public async Task GetUsersById_SblBridgeFindsProfile_ResponseOk_ReturnsUserProfi Assert.Equal("nb", actualUser.ProfileSettingPreference.Language); } + [Fact] + public async Task GetUsersByUuid_SblBridgeFindsProfile_ResponseOk_ReturnsUserProfile() + { + // Arrange + const int userId = 20000009; + Guid userUuid = new("cc86d2c7-1695-44b0-8e82-e633243fdf31"); + + HttpRequestMessage sblRequest = null; + DelegatingHandlerStub messageHandler = new(async (request, token) => + { + sblRequest = request; + + UserProfile userProfile = await TestDataLoader.Load(userUuid.ToString()); + return new HttpResponseMessage() { Content = JsonContent.Create(userProfile) }; + }); + _webApplicationFactorySetup.SblBridgeHttpMessageHandler = messageHandler; + + HttpRequestMessage httpRequestMessage = CreateGetRequest(userId, $"/profile/api/v1/users/{userUuid}"); + + httpRequestMessage.Headers.Add("PlatformAccessToken", PrincipalUtil.GetAccessToken("ttd", "unittest")); + + HttpClient client = _webApplicationFactorySetup.GetTestServerClient(); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.NotNull(sblRequest); + Assert.Equal(HttpMethod.Get, sblRequest.Method); + Assert.EndsWith($"users?useruuid={userUuid}", sblRequest.RequestUri.ToString()); + + string responseContent = await response.Content.ReadAsStringAsync(); + + UserProfile actualUser = JsonSerializer.Deserialize( + responseContent, serializerOptionsCamelCase); + + // These asserts check that deserializing with camel casing was successful. + Assert.Equal(userId, actualUser.UserId); + Assert.Equal(userUuid, actualUser.UserUuid); + Assert.Equal("LEO WILHELMSEN", actualUser.Party.Name); + Assert.Equal(userUuid, actualUser.Party.PartyUuid); + Assert.Equal("LEO", actualUser.Party.Person.FirstName); + Assert.Equal("nb", actualUser.ProfileSettingPreference.Language); + } + + [Fact] + public async Task GetUsersByUuid_UserAuthenticatedMissingPlatformAccesToken_ReturnsForbidden() + { + // Arrange + const int userId = 20000009; + Guid userUuid = new("cc86d2c7-1695-44b0-8e82-e633243fdf31"); + + HttpRequestMessage httpRequestMessage = CreateGetRequest(userId, $"/profile/api/v1/users/{userUuid}"); + + HttpClient client = _webApplicationFactorySetup.GetTestServerClient(); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + [Fact] + public async Task GetUsersByUuid_SblBridgeReturnsNotFound_ResponseNotFound() + { + // Arrange + const int userId = 20000009; + Guid userUuid = new("cc86d2c7-1695-44b0-8e82-e633243fdf31"); + + HttpRequestMessage sblRequest = null; + DelegatingHandlerStub messageHandler = new(async (request, token) => + { + sblRequest = request; + + return await Task.FromResult(new HttpResponseMessage() { StatusCode = HttpStatusCode.NotFound }); + }); + _webApplicationFactorySetup.SblBridgeHttpMessageHandler = messageHandler; + + HttpRequestMessage httpRequestMessage = CreateGetRequest(userId, $"/profile/api/v1/users/{userUuid}"); + + httpRequestMessage.Headers.Add("PlatformAccessToken", PrincipalUtil.GetAccessToken("ttd", "unittest")); + + HttpClient client = _webApplicationFactorySetup.GetTestServerClient(); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + + Assert.NotNull(sblRequest); + Assert.Equal(HttpMethod.Get, sblRequest.Method); + Assert.EndsWith($"users?useruuid={userUuid}", sblRequest.RequestUri.ToString()); + } + + [Fact] + public async Task GetUsersByUuid_MissingAuthentication_NotAuthorized() + { + // Arrange + Guid userUuid = new("cc86d2c7-1695-44b0-8e82-e633243fdf31"); + + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, $"/profile/api/v1/users/{userUuid}"); + + httpRequestMessage.Headers.Add("PlatformAccessToken", PrincipalUtil.GetAccessToken("ttd", "unittest")); + + HttpClient client = _webApplicationFactorySetup.GetTestServerClient(); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + [Fact] public async Task GetUsersById_SblBridgeReturnsNotFound_ResponseNotFound() { @@ -331,7 +446,7 @@ private static HttpRequestMessage CreateGetRequest(int userId, string requestUri httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); return httpRequestMessage; } - + private static HttpRequestMessage CreatePostRequest(int userId, string requestUri, StringContent content) { HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, requestUri); diff --git a/test/Altinn.Profile.Tests/Testdata/TestDataLoader.cs b/test/Altinn.Profile.Tests/Testdata/TestDataLoader.cs index 9d77e84..bf95ec4 100644 --- a/test/Altinn.Profile.Tests/Testdata/TestDataLoader.cs +++ b/test/Altinn.Profile.Tests/Testdata/TestDataLoader.cs @@ -6,11 +6,17 @@ namespace Altinn.Profile.Tests.Testdata { public static class TestDataLoader { + private static readonly JsonSerializerOptions _options = new() + { + PropertyNameCaseInsensitive = true + }; + public static async Task Load(string id) { string path = $"../../../Testdata/{typeof(T).Name}/{id}.json"; string fileContent = await File.ReadAllTextAsync(path); - T data = JsonSerializer.Deserialize(fileContent); + + T data = JsonSerializer.Deserialize(fileContent, _options); return data; } } diff --git a/test/Altinn.Profile.Tests/Testdata/UserProfile/2001606.json b/test/Altinn.Profile.Tests/Testdata/UserProfile/2001606.json index 74e7364..d4cb0a4 100644 --- a/test/Altinn.Profile.Tests/Testdata/UserProfile/2001606.json +++ b/test/Altinn.Profile.Tests/Testdata/UserProfile/2001606.json @@ -1,5 +1,6 @@ { "UserId": 2001606, + "UserUUID": "1a131a3b-c6c9-4572-86fd-dfe36c3de06a", "UserType": 1, "UserName": "", "PhoneNumber": null, @@ -30,6 +31,7 @@ }, "Organization": null, "PartyId": 50002113, + "PartyUUID": "1a131a3b-c6c9-4572-86fd-dfe36c3de06a", "UnitType": null, "Name": "TUVA LANDRO", "IsDeleted": false, diff --git a/test/Altinn.Profile.Tests/Testdata/UserProfile/2001607.json b/test/Altinn.Profile.Tests/Testdata/UserProfile/2001607.json index fb33d2f..2e4913f 100644 --- a/test/Altinn.Profile.Tests/Testdata/UserProfile/2001607.json +++ b/test/Altinn.Profile.Tests/Testdata/UserProfile/2001607.json @@ -1,5 +1,6 @@ { "UserId": 2001607, + "UserUUID": "db8eafc0-6056-43b5-b047-4adcd84f659c", "UserType": 1, "UserName": "", "PhoneNumber": null, @@ -30,6 +31,7 @@ }, "Organization": null, "PartyId": 50002113, + "PartyUUID": "db8eafc0-6056-43b5-b047-4adcd84f659c", "UnitType": null, "Name": "TUVA LANDRO", "IsDeleted": false, diff --git a/test/Altinn.Profile.Tests/Testdata/UserProfile/2516356.json b/test/Altinn.Profile.Tests/Testdata/UserProfile/2516356.json index f49abad..df20193 100644 --- a/test/Altinn.Profile.Tests/Testdata/UserProfile/2516356.json +++ b/test/Altinn.Profile.Tests/Testdata/UserProfile/2516356.json @@ -1,11 +1,13 @@ { "UserId": 2516356, + "UserUUID": "4f4e31b5-0e4f-400c-b89d-551fe2385d9f", "UserName": "sophie", "PhoneNumber": "90001337", "Email": "2516356@altinnstudiotestusers.com", "PartyId": 5780927, "Party": { "PartyId": 5780927, + "PartyUUID": "4f4e31b5-0e4f-400c-b89d-551fe2385d9f", "Name": "Sophie Salt", "SSN": "01017512345", "Person": { diff --git a/test/Altinn.Profile.Tests/Testdata/UserProfile/OrstaECUser.json b/test/Altinn.Profile.Tests/Testdata/UserProfile/OrstaECUser.json index bb5173a..35925eb 100644 --- a/test/Altinn.Profile.Tests/Testdata/UserProfile/OrstaECUser.json +++ b/test/Altinn.Profile.Tests/Testdata/UserProfile/OrstaECUser.json @@ -1,5 +1,6 @@ { "UserId": 2001072, + "UserUUID": "34b62493-84c6-4794-bf91-6cd23ead761c", "UserType": 3, "UserName": "OrstaECUser", "ExternalIdentity": "", @@ -29,6 +30,7 @@ "UnitStatus": "N" }, "PartyId": 50005545, + "PartyUUID": "ec061efa-4c2a-4dbd-87f5-bcb59cdeaf91", "UnitType": "AS", "Name": "ORSTA OG HEGGEDAL ", "IsDeleted": false, diff --git a/test/Altinn.Profile.Tests/Testdata/UserProfile/cc86d2c7-1695-44b0-8e82-e633243fdf31.json b/test/Altinn.Profile.Tests/Testdata/UserProfile/cc86d2c7-1695-44b0-8e82-e633243fdf31.json new file mode 100644 index 0000000..6ea264e --- /dev/null +++ b/test/Altinn.Profile.Tests/Testdata/UserProfile/cc86d2c7-1695-44b0-8e82-e633243fdf31.json @@ -0,0 +1,48 @@ +{ + "UserId": 20000009, + "UserUUID": "cc86d2c7-1695-44b0-8e82-e633243fdf31", + "UserType": 1, + "UserName": "", + "ExternalIdentity": "", + "PhoneNumber": null, + "Email": null, + "PartyId": 50002117, + "Party": { + "PartyTypeName": 1, + "SSN": "01032730090", + "OrgNumber": "", + "Person": { + "SSN": "01032730090", + "Name": "LEO WILHELMSEN", + "FirstName": "LEO", + "MiddleName": "", + "LastName": "WILHELMSEN", + "TelephoneNumber": "", + "MobileNumber": "", + "MailingAddress": " Farrisvegen 13 3948 PORSGRUNN", + "MailingPostalCode": "3948", + "MailingPostalCity": "PORSGRUNN", + "AddressMunicipalNumber": "", + "AddressMunicipalName": "", + "AddressStreetName": "", + "AddressHouseNumber": "", + "AddressHouseLetter": "", + "AddressPostalCode": "3948", + "AddressCity": "PORSGRUNN", + "DateOfDeath": null + }, + "Organization": null, + "PartyId": 50002117, + "PartyUUID": "cc86d2c7-1695-44b0-8e82-e633243fdf31", + "UnitType": null, + "Name": "LEO WILHELMSEN", + "IsDeleted": false, + "OnlyHierarchyElementWithNoAccess": false, + "ChildParties": null + }, + "ProfileSettingPreference": { + "Language": "nb", + "PreSelectedPartyId": 0, + "DoNotPromptForParty": false + } +} \ No newline at end of file diff --git a/test/Altinn.Profile.Tests/UnitTests/UserProfileCachingDecoratorTest.cs b/test/Altinn.Profile.Tests/UnitTests/UserProfileCachingDecoratorTest.cs index ef15cdb..de44e7b 100644 --- a/test/Altinn.Profile.Tests/UnitTests/UserProfileCachingDecoratorTest.cs +++ b/test/Altinn.Profile.Tests/UnitTests/UserProfileCachingDecoratorTest.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; using Altinn.Platform.Profile.Models; @@ -53,6 +54,29 @@ public async Task GetUserUserId_UserInCache_decoratedServiceNotCalled() /// Tests that the userprofile available in the cache is returned to the caller without forwarding request to decorated service. /// [Fact] + public async Task GetUserUserUuid_UserInCache_decoratedServiceNotCalled() + { + // Arrange + Guid userUuid = new("cc86d2c7-1695-44b0-8e82-e633243fdf31"); + MemoryCache memoryCache = new(new MemoryCacheOptions()); + + var userProfile = await TestDataLoader.Load(userUuid.ToString()); + memoryCache.Set($"User:UserUuid:{userUuid}", userProfile); + var target = new UserProfileCachingDecorator(_decoratedServiceMock.Object, memoryCache, generalSettingsOptions.Object); + + // Act + UserProfile actual = await target.GetUserByUuid(userUuid); + + // Assert + _decoratedServiceMock.Verify(service => service.GetUser(It.IsAny()), Times.Never()); + Assert.NotNull(actual); + Assert.Equal(userUuid, actual.UserUuid); + } + + /// + /// Tests that the userprofile is not available in the cache call is forwarded to decorated service and cache is populated result returned to caller. + /// + [Fact] public async Task GetUserUserId_UserNotInCache_decoratedServiceCalledMockPopulated() { // Arrange @@ -74,6 +98,31 @@ public async Task GetUserUserId_UserNotInCache_decoratedServiceCalledMockPopulat Assert.True(memoryCache.TryGetValue("User_UserId_2001607", out UserProfile _)); } + /// + /// Tests that the userprofile is not available in the cache call is forwarded to decorated service and cache is populated result returned to caller. + /// + [Fact] + public async Task GetUserUserUuid_UserNotInCache_decoratedServiceCalledMockPopulated() + { + // Arrange + Guid userUuid = new("cc86d2c7-1695-44b0-8e82-e633243fdf31"); + MemoryCache memoryCache = new(new MemoryCacheOptions()); + + var userProfile = await TestDataLoader.Load(userUuid.ToString()); + _decoratedServiceMock.Setup(service => service.GetUserByUuid(It.IsAny())).ReturnsAsync(userProfile); + var target = new UserProfileCachingDecorator(_decoratedServiceMock.Object, memoryCache, generalSettingsOptions.Object); + + // Act + UserProfile actual = await target.GetUserByUuid(userUuid); + + // Assert + _decoratedServiceMock.Verify(service => service.GetUserByUuid(It.IsAny()), Times.Once()); + + Assert.NotNull(actual); + Assert.Equal(userUuid, actual.UserUuid); + Assert.True(memoryCache.TryGetValue($"User:UserUuid:{userUuid}", out UserProfile _)); + } + /// /// Tests that if the result from decorated service is null, nothing is stored in cache and the null object returned to caller. /// @@ -96,6 +145,28 @@ public async Task GetUserUserUserId_NullFromDecoratedService_CacheNotPopulated() Assert.False(memoryCache.TryGetValue("User_UserId_2001607", out UserProfile _)); } + /// + /// Tests that if the result from decorated service is null, nothing is stored in cache and the null object returned to caller. + /// + [Fact] + public async Task GetUserUserUserUuid_NullFromDecoratedService_CacheNotPopulated() + { + // Arrange + Guid userUuid = new("cc86d2c7-1695-44b0-8e82-e633243fdf31"); + MemoryCache memoryCache = new(new MemoryCacheOptions()); + + _decoratedServiceMock.Setup(service => service.GetUserByUuid(It.IsAny())).ReturnsAsync((UserProfile)null); + var target = new UserProfileCachingDecorator(_decoratedServiceMock.Object, memoryCache, generalSettingsOptions.Object); + + // Act + UserProfile actual = await target.GetUserByUuid(userUuid); + + // Assert + _decoratedServiceMock.Verify(service => service.GetUserByUuid(It.IsAny()), Times.Once()); + Assert.Null(actual); + Assert.False(memoryCache.TryGetValue($"User:UserUuid:{userUuid}", out UserProfile _)); + } + /// /// Tests that the userprofile available in the cache is returned to the caller without forwarding request to decorated service. ///