diff --git a/src/Altinn.Notifications.Core/Services/GetOrderService.cs b/src/Altinn.Notifications.Core/Services/GetOrderService.cs index 3fc65754..87bea858 100644 --- a/src/Altinn.Notifications.Core/Services/GetOrderService.cs +++ b/src/Altinn.Notifications.Core/Services/GetOrderService.cs @@ -1,8 +1,8 @@ using Altinn.Notifications.Core.Enums; -using Altinn.Notifications.Core.Models; using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.Core.Persistence; using Altinn.Notifications.Core.Services.Interfaces; +using Altinn.Notifications.Core.Shared; namespace Altinn.Notifications.Core.Services; @@ -28,38 +28,38 @@ public GetOrderService(IOrderRepository repo) } /// - public async Task<(NotificationOrder? Order, ServiceError? Error)> GetOrderById(Guid id, string creator) + public async Task> GetOrderById(Guid id, string creator) { NotificationOrder? order = await _repo.GetOrderById(id, creator); if (order == null) { - return (null, new ServiceError(404)); + return new ServiceError(404); } - return (order, null); + return order; } /// - public async Task<(List? Orders, ServiceError? Error)> GetOrdersBySendersReference(string senderRef, string creator) + public async Task, ServiceError>> GetOrdersBySendersReference(string senderRef, string creator) { List orders = await _repo.GetOrdersBySendersReference(senderRef, creator); - return (orders, null); + return orders; } /// - public async Task<(NotificationOrderWithStatus? Order, ServiceError? Error)> GetOrderWithStatuById(Guid id, string creator) + public async Task> GetOrderWithStatuById(Guid id, string creator) { NotificationOrderWithStatus? order = await _repo.GetOrderWithStatusById(id, creator); if (order == null) { - return (null, new ServiceError(404)); + return new ServiceError(404); } order.ProcessingStatus.StatusDescription = GetStatusDescription(order.ProcessingStatus.Status); - return (order, null); + return order; } /// diff --git a/src/Altinn.Notifications.Core/Services/Interfaces/IGetOrderService.cs b/src/Altinn.Notifications.Core/Services/Interfaces/IGetOrderService.cs index 715899a9..7240089b 100644 --- a/src/Altinn.Notifications.Core/Services/Interfaces/IGetOrderService.cs +++ b/src/Altinn.Notifications.Core/Services/Interfaces/IGetOrderService.cs @@ -1,5 +1,5 @@ -using Altinn.Notifications.Core.Models; -using Altinn.Notifications.Core.Models.Orders; +using Altinn.Notifications.Core.Models.Orders; +using Altinn.Notifications.Core.Shared; namespace Altinn.Notifications.Core.Services.Interfaces; @@ -13,19 +13,19 @@ public interface IGetOrderService /// /// The order id /// The creator of the orders - public Task<(NotificationOrder? Order, ServiceError? Error)> GetOrderById(Guid id, string creator); + public Task> GetOrderById(Guid id, string creator); /// /// Retrieves a notification order by senders reference /// /// The senders reference /// The creator of the orders - public Task<(List? Orders, ServiceError? Error)> GetOrdersBySendersReference(string senderRef, string creator); + public Task, ServiceError>> GetOrdersBySendersReference(string senderRef, string creator); /// /// Retrieves a notification order with process and notification status by id /// /// The order id /// The creator of the orders - public Task<(NotificationOrderWithStatus? Order, ServiceError? Error)> GetOrderWithStatuById(Guid id, string creator); + public Task> GetOrderWithStatuById(Guid id, string creator); } diff --git a/src/Altinn.Notifications.Core/Services/Interfaces/INotificationSummaryService.cs b/src/Altinn.Notifications.Core/Services/Interfaces/INotificationSummaryService.cs index a99c679e..a58d65cd 100644 --- a/src/Altinn.Notifications.Core/Services/Interfaces/INotificationSummaryService.cs +++ b/src/Altinn.Notifications.Core/Services/Interfaces/INotificationSummaryService.cs @@ -1,5 +1,5 @@ -using Altinn.Notifications.Core.Models; -using Altinn.Notifications.Core.Models.Notification; +using Altinn.Notifications.Core.Models.Notification; +using Altinn.Notifications.Core.Shared; namespace Altinn.Notifications.Core.Services.Interfaces { @@ -13,6 +13,6 @@ public interface INotificationSummaryService /// /// The order id to find notifications for /// The creator of the order - public Task<(EmailNotificationSummary? Summary, ServiceError? Error)> GetEmailSummary(Guid orderId, string creator); + public Task> GetEmailSummary(Guid orderId, string creator); } } diff --git a/src/Altinn.Notifications.Core/Services/Interfaces/IOrderRequestService.cs b/src/Altinn.Notifications.Core/Services/Interfaces/IOrderRequestService.cs index b9edf362..cc67fe4b 100644 --- a/src/Altinn.Notifications.Core/Services/Interfaces/IOrderRequestService.cs +++ b/src/Altinn.Notifications.Core/Services/Interfaces/IOrderRequestService.cs @@ -1,5 +1,5 @@ -using Altinn.Notifications.Core.Models; -using Altinn.Notifications.Core.Models.Orders; +using Altinn.Notifications.Core.Models.Orders; +using Altinn.Notifications.Core.Shared; namespace Altinn.Notifications.Core.Services.Interfaces; @@ -13,5 +13,5 @@ public interface IOrderRequestService /// /// The notification order request /// The registered notification order - public Task<(NotificationOrder? Order, ServiceError? Error)> RegisterNotificationOrder(NotificationOrderRequest orderRequest); + public Task RegisterNotificationOrder(NotificationOrderRequest orderRequest); } diff --git a/src/Altinn.Notifications.Core/Services/NotificationSummaryService.cs b/src/Altinn.Notifications.Core/Services/NotificationSummaryService.cs index 82dec78a..d921af0a 100644 --- a/src/Altinn.Notifications.Core/Services/NotificationSummaryService.cs +++ b/src/Altinn.Notifications.Core/Services/NotificationSummaryService.cs @@ -1,8 +1,8 @@ using Altinn.Notifications.Core.Enums; -using Altinn.Notifications.Core.Models; using Altinn.Notifications.Core.Models.Notification; using Altinn.Notifications.Core.Persistence; using Altinn.Notifications.Core.Services.Interfaces; +using Altinn.Notifications.Core.Shared; namespace Altinn.Notifications.Core.Services { @@ -40,13 +40,13 @@ public NotificationSummaryService(INotificationSummaryRepository summaryReposito } /// - public async Task<(EmailNotificationSummary? Summary, ServiceError? Error)> GetEmailSummary(Guid orderId, string creator) + public async Task> GetEmailSummary(Guid orderId, string creator) { EmailNotificationSummary? summary = await _summaryRepository.GetEmailSummary(orderId, creator); if (summary == null) { - return (null, new ServiceError(404)); + return new ServiceError(404); } if (summary.Notifications.Count != 0) @@ -54,7 +54,7 @@ public NotificationSummaryService(INotificationSummaryRepository summaryReposito ProcessNotificationResults(summary); } - return (summary, null); + return summary; } /// diff --git a/src/Altinn.Notifications.Core/Services/OrderRequestService.cs b/src/Altinn.Notifications.Core/Services/OrderRequestService.cs index 12ebf309..702b6e47 100644 --- a/src/Altinn.Notifications.Core/Services/OrderRequestService.cs +++ b/src/Altinn.Notifications.Core/Services/OrderRequestService.cs @@ -1,9 +1,9 @@ using Altinn.Notifications.Core.Configuration; -using Altinn.Notifications.Core.Models; using Altinn.Notifications.Core.Models.NotificationTemplate; using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.Core.Persistence; using Altinn.Notifications.Core.Services.Interfaces; +using Altinn.Notifications.Core.Shared; using Microsoft.Extensions.Options; @@ -33,7 +33,7 @@ public OrderRequestService(IOrderRepository repository, IGuidService guid, IDate } /// - public async Task<(NotificationOrder? Order, ServiceError? Error)> RegisterNotificationOrder(NotificationOrderRequest orderRequest) + public async Task RegisterNotificationOrder(NotificationOrderRequest orderRequest) { Guid orderId = _guid.NewGuid(); DateTime created = _dateTime.UtcNow(); @@ -52,7 +52,7 @@ public OrderRequestService(IOrderRepository repository, IGuidService guid, IDate NotificationOrder savedOrder = await _repository.Create(order); - return (savedOrder, null); + return savedOrder; } private List SetSenderIfNotDefined(List templates) diff --git a/src/Altinn.Notifications.Core/Shared/Result.cs b/src/Altinn.Notifications.Core/Shared/Result.cs new file mode 100644 index 00000000..8727ade1 --- /dev/null +++ b/src/Altinn.Notifications.Core/Shared/Result.cs @@ -0,0 +1,61 @@ +namespace Altinn.Notifications.Core.Shared; + +/// +/// A simple implementation of the Result class to handle success XOR failure as separate return +/// values in a type safe way. +/// +/// The type to be assigned to indicate success. +/// The type to be assigned to indicate failure. +public readonly struct Result +{ + private readonly TValue? _value; + private readonly TError? _error; + + private Result(TValue value) + { + IsError = false; + _value = value; + _error = default; + } + + private Result(TError error) + { + IsError = true; + _value = default; + _error = error; + } + + /// + /// Gets a value indicating whether the Result contains an error value. + /// + public bool IsError { get; } + + /// + /// Gets a value indicating whether the Result contains a success value. + /// + public bool IsSuccess => !IsError; + + /// + /// Implicit operator used when creating an instance of Result when assigning a success value. + /// + /// An object of the type indicating success. + public static implicit operator Result(TValue value) => new(value); + + /// + /// Implicit operator used when creating an instance of Result when assigning an error value. + /// + /// An object of the type indicating failure. + public static implicit operator Result(TError error) => new(error); + + /// + /// This method will call either the success OR the failure function based on it's error state. + /// + /// The type to be returned by the given functions. + /// The function to call if Result holds a success value. + /// The function to call if Result holds an error value. + /// An instance of the defined type. + public TResult Match( + Func success, + Func failure) => + !IsError ? success(_value!) : failure(_error!); +} diff --git a/src/Altinn.Notifications.Core/Models/ServiceError.cs b/src/Altinn.Notifications.Core/Shared/ServiceError.cs similarity index 94% rename from src/Altinn.Notifications.Core/Models/ServiceError.cs rename to src/Altinn.Notifications.Core/Shared/ServiceError.cs index 08c9138d..1ba18dcf 100644 --- a/src/Altinn.Notifications.Core/Models/ServiceError.cs +++ b/src/Altinn.Notifications.Core/Shared/ServiceError.cs @@ -1,4 +1,4 @@ -namespace Altinn.Notifications.Core.Models; +namespace Altinn.Notifications.Core.Shared; /// /// A class representing a service error object used to transfere error information from service to controller. diff --git a/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs b/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs index ebc63eb6..00685a61 100644 --- a/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs +++ b/src/Altinn.Notifications/Controllers/EmailNotificationOrdersController.cs @@ -1,7 +1,7 @@ using Altinn.Notifications.Configuration; -using Altinn.Notifications.Core.Models; using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.Core.Services.Interfaces; +using Altinn.Notifications.Core.Shared; using Altinn.Notifications.Extensions; using Altinn.Notifications.Mappers; using Altinn.Notifications.Models; @@ -71,14 +71,14 @@ public async Task> Post(EmailNotificationOrderRequestEx } var orderRequest = emailNotificationOrderRequest.MapToOrderRequest(creator); - (NotificationOrder? registeredOrder, ServiceError? error) = await _orderRequestService.RegisterNotificationOrder(orderRequest); + Result result = await _orderRequestService.RegisterNotificationOrder(orderRequest); - if (error != null) - { - return StatusCode(error.ErrorCode, error.ErrorMessage); - } - - string selfLink = registeredOrder!.GetSelfLink(); - return Accepted(selfLink, new OrderIdExt(registeredOrder!.Id)); + return result.Match( + order => + { + string selfLink = order.GetSelfLink(); + return Accepted(selfLink, new OrderIdExt(order.Id)); + }, + error => StatusCode(error.ErrorCode, error.ErrorMessage)); } } diff --git a/src/Altinn.Notifications/Controllers/EmailNotificationsController.cs b/src/Altinn.Notifications/Controllers/EmailNotificationsController.cs index 9005b58d..0d8d21f2 100644 --- a/src/Altinn.Notifications/Controllers/EmailNotificationsController.cs +++ b/src/Altinn.Notifications/Controllers/EmailNotificationsController.cs @@ -1,6 +1,7 @@ using Altinn.Notifications.Configuration; using Altinn.Notifications.Core.Models.Notification; using Altinn.Notifications.Core.Services.Interfaces; +using Altinn.Notifications.Core.Shared; using Altinn.Notifications.Extensions; using Altinn.Notifications.Mappers; @@ -50,14 +51,11 @@ public async Task> Get([FromRoute] Gui return Forbid(); } - var (emailSummary, error) = await _summaryService.GetEmailSummary(id, expectedCreator); + Result result = await _summaryService.GetEmailSummary(id, expectedCreator); - if (error != null) - { - return StatusCode(error.ErrorCode, error.ErrorMessage); - } - - return Ok(emailSummary?.MapToEmailNotificationSummaryExt()); + return result.Match( + summary => Ok(summary.MapToEmailNotificationSummaryExt()), + error => StatusCode(error.ErrorCode, error.ErrorMessage)); } } } diff --git a/src/Altinn.Notifications/Controllers/OrdersController.cs b/src/Altinn.Notifications/Controllers/OrdersController.cs index 5702dec2..202a090b 100644 --- a/src/Altinn.Notifications/Controllers/OrdersController.cs +++ b/src/Altinn.Notifications/Controllers/OrdersController.cs @@ -1,5 +1,7 @@ using Altinn.Notifications.Configuration; +using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.Core.Services.Interfaces; +using Altinn.Notifications.Core.Shared; using Altinn.Notifications.Extensions; using Altinn.Notifications.Mappers; using Altinn.Notifications.Models; @@ -51,14 +53,14 @@ public async Task> GetById([FromRoute] Guid i return Forbid(); } - var (order, error) = await _getOrderService.GetOrderById(id, expectedCreator); + Result result = await _getOrderService.GetOrderById(id, expectedCreator); - if (error != null) - { - return StatusCode(error.ErrorCode, error.ErrorMessage); - } - - return order!.MapToNotificationOrderExt(); + return result.Match>( + order => + { + return order.MapToNotificationOrderExt(); + }, + error => StatusCode(error.ErrorCode, error.ErrorMessage)); } /// @@ -77,14 +79,14 @@ public async Task> GetBySendersRef([FromQ return Forbid(); } - var (orders, error) = await _getOrderService.GetOrdersBySendersReference(sendersReference, expectedCreator); + Result, ServiceError> result = await _getOrderService.GetOrdersBySendersReference(sendersReference, expectedCreator); - if (error != null) - { - return StatusCode(error.ErrorCode, error.ErrorMessage); - } - - return orders!.MapToNotificationOrderListExt(); + return result.Match>( + orders => + { + return orders.MapToNotificationOrderListExt(); + }, + error => StatusCode(error.ErrorCode, error.ErrorMessage)); } /// @@ -105,13 +107,13 @@ public async Task> GetWithStatusByI return Forbid(); } - var (order, error) = await _getOrderService.GetOrderWithStatuById(id, expectedCreator); - - if (error != null) - { - return StatusCode(error.ErrorCode, error.ErrorMessage); - } + Result result = await _getOrderService.GetOrderWithStatuById(id, expectedCreator); - return order!.MapToNotificationOrderWithStatusExt(); + return result.Match>( + order => + { + return order.MapToNotificationOrderWithStatusExt(); + }, + error => StatusCode(error.ErrorCode, error.ErrorMessage)); } } diff --git a/src/Altinn.Notifications/Controllers/SmsNotificationOrdersController.cs b/src/Altinn.Notifications/Controllers/SmsNotificationOrdersController.cs index 55dbf679..fe9c2c30 100644 --- a/src/Altinn.Notifications/Controllers/SmsNotificationOrdersController.cs +++ b/src/Altinn.Notifications/Controllers/SmsNotificationOrdersController.cs @@ -1,8 +1,7 @@ -using System.Collections; -using Altinn.Notifications.Configuration; -using Altinn.Notifications.Core.Models; +using Altinn.Notifications.Configuration; using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.Core.Services.Interfaces; +using Altinn.Notifications.Core.Shared; using Altinn.Notifications.Extensions; using Altinn.Notifications.Mappers; using Altinn.Notifications.Models; @@ -72,14 +71,14 @@ public async Task> Post(SmsNotificationOrderRequestExt } NotificationOrderRequest orderRequest = smsNotificationOrderRequest.MapToOrderRequest(creator); - (NotificationOrder? registeredOrder, ServiceError? error) = await _orderRequestService.RegisterNotificationOrder(orderRequest); + Result result = await _orderRequestService.RegisterNotificationOrder(orderRequest); - if (error != null) - { - return StatusCode(error.ErrorCode, error.ErrorMessage); - } - - string selfLink = registeredOrder!.GetSelfLink(); - return Accepted(selfLink, new OrderIdExt(registeredOrder!.Id)); + return result.Match( + registeredOrder => + { + string selfLink = registeredOrder!.GetSelfLink(); + return Accepted(selfLink, new OrderIdExt(registeredOrder!.Id)); + }, + error => StatusCode(error.ErrorCode, error.ErrorMessage)); } } diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/EmailNotificationsControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/EmailNotificationsControllerTests.cs index 68429eb3..deacd17b 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/EmailNotificationsControllerTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/EmailNotificationsControllerTests.cs @@ -8,6 +8,7 @@ using Altinn.Notifications.Core.Models; using Altinn.Notifications.Core.Models.Notification; using Altinn.Notifications.Core.Services.Interfaces; +using Altinn.Notifications.Core.Shared; using Altinn.Notifications.Tests.Notifications.Mocks.Authentication; using Altinn.Notifications.Tests.Notifications.Utils; @@ -101,7 +102,7 @@ public async Task Get_ServiceReturnsError_ServerError() // Arrange Mock serviceMock = new(); serviceMock.Setup(s => s.GetEmailSummary(It.IsAny(), It.IsAny())) - .ReturnsAsync((null, new ServiceError(500))); + .ReturnsAsync(new ServiceError(500)); HttpClient client = GetTestClient(summaryService: serviceMock.Object); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); @@ -131,7 +132,7 @@ public async Task Get_ValidScope_ServiceReturnsNotifications_Ok() Mock serviceMock = new(); serviceMock.Setup(s => s.GetEmailSummary(It.IsAny(), It.Is(s => s.Equals("ttd")))) - .ReturnsAsync((output, null)); + .ReturnsAsync(output); HttpClient client = GetTestClient(summaryService: serviceMock.Object); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); @@ -167,7 +168,7 @@ public async Task Get_ValidAccessToken_ServiceReturnsOrder_Accepted() Mock serviceMock = new(); serviceMock.Setup(s => s.GetEmailSummary(It.IsAny(), It.Is(s => s.Equals("ttd")))) - .ReturnsAsync((output, null)); + .ReturnsAsync(output); HttpClient client = GetTestClient(summaryService: serviceMock.Object); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs index cfe44357..71f360bc 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsOrderController/EmailNotificationOrdersControllerTests.cs @@ -10,6 +10,7 @@ using Altinn.Notifications.Core.Models.NotificationTemplate; using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.Core.Services.Interfaces; +using Altinn.Notifications.Core.Shared; using Altinn.Notifications.Models; using Altinn.Notifications.Tests.Notifications.Mocks.Authentication; using Altinn.Notifications.Tests.Notifications.Utils; @@ -167,31 +168,7 @@ public async Task Post_UserClaimsPrincipal_Forbidden() // Assert Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } - - [Fact] - public async Task Post_ServiceReturnsError_ServerError() - { - // Arrange - Mock serviceMock = new(); - serviceMock.Setup(s => s.RegisterNotificationOrder(It.IsAny())) - .ReturnsAsync((null, new ServiceError(500))); - - HttpClient client = GetTestClient(orderService: serviceMock.Object); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); - - HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, _basePath) - { - Content = new StringContent(_orderRequestExt.Serialize(), Encoding.UTF8, "application/json") - }; - - // Act - HttpResponseMessage response = await client.SendAsync(httpRequestMessage); - - // Assert - Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); - serviceMock.VerifyAll(); - } - + [Fact] public async Task Post_ValidScope_ServiceReturnsOrder_Accepted() { @@ -207,7 +184,7 @@ public async Task Post_ValidScope_ServiceReturnsOrder_Accepted() Assert.NotNull(emailTemplate); Assert.Equal(string.Empty, emailTemplate.FromAddress); }) - .ReturnsAsync((_order, null)); + .ReturnsAsync(_order); HttpClient client = GetTestClient(orderService: serviceMock.Object); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); @@ -246,7 +223,7 @@ public async Task Post_ValidAccessToken_ServiceReturnsOrder_Accepted() Assert.NotNull(emailTemplate); Assert.Empty(emailTemplate.FromAddress); }) - .ReturnsAsync((_order, null)); + .ReturnsAsync(_order); HttpClient client = GetTestClient(orderService: serviceMock.Object); @@ -286,7 +263,7 @@ public async Task Post_OrderWithoutFromAddress_StringEmptyUsedAsServiceInput_Acc Assert.NotNull(emailTemplate); Assert.Empty(emailTemplate.FromAddress); }) - .ReturnsAsync((_order, null)); + .ReturnsAsync(_order); HttpClient client = GetTestClient(orderService: serviceMock.Object); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs index 7d00ded5..da822e9d 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/OrdersController/OrdersControllerTests.cs @@ -7,6 +7,7 @@ using Altinn.Notifications.Core.Models.NotificationTemplate; using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.Core.Services.Interfaces; +using Altinn.Notifications.Core.Shared; using Altinn.Notifications.Tests.Notifications.Mocks.Authentication; using Altinn.Notifications.Tests.Notifications.Utils; @@ -112,7 +113,7 @@ public async Task GetBySendersRef_ValidBearerToken_CorrespondingServiceMethodCal var orderService = new Mock(); orderService .Setup(o => o.GetOrdersBySendersReference(It.Is(s => s.Equals("internal-ref")), It.Is(s => s.Equals("ttd")))) - .ReturnsAsync((new List() { _order }, null)); + .ReturnsAsync(new List() { _order }); HttpClient client = GetTestClient(orderService.Object); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); @@ -135,7 +136,7 @@ public async Task GetBySendersRef_ValidPlatformAccessToken_CorrespondingServiceM var orderService = new Mock(); orderService .Setup(o => o.GetOrdersBySendersReference(It.Is(s => s.Equals("internal-ref")), It.Is(s => s.Equals("ttd")))) - .ReturnsAsync((new List() { _order }, null)); + .ReturnsAsync(new List() { _order }); HttpClient client = GetTestClient(orderService.Object); @@ -209,7 +210,7 @@ public async Task GetById_ValidBearerToken_CorrespondingServiceMethodCalled() var orderService = new Mock(); orderService .Setup(o => o.GetOrderById(It.Is(g => g.Equals(orderId)), It.Is(s => s.Equals("ttd")))) - .ReturnsAsync((_order, null)); + .ReturnsAsync(_order); HttpClient client = GetTestClient(orderService.Object); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); @@ -234,7 +235,7 @@ public async Task GetById_ValidPlatformAccessToken_CorrespondingServiceMethodCal var orderService = new Mock(); orderService .Setup(o => o.GetOrderById(It.Is(g => g.Equals(orderId)), It.Is(s => s.Equals("ttd")))) - .ReturnsAsync((_order, null)); + .ReturnsAsync(_order); HttpClient client = GetTestClient(orderService.Object); @@ -259,7 +260,7 @@ public async Task GetById_ServiceReturnsError_StatusCodeMatchesError() var orderService = new Mock(); orderService .Setup(o => o.GetOrderById(It.Is(g => g.Equals(orderId)), It.Is(s => s.Equals("ttd")))) - .ReturnsAsync((null, new ServiceError(404))); + .ReturnsAsync(new ServiceError(404)); HttpClient client = GetTestClient(orderService.Object); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); @@ -333,7 +334,7 @@ public async Task GetWithStatusById_ValidBearerToken_CorrespondingServiceMethodC var orderService = new Mock(); orderService .Setup(o => o.GetOrderWithStatuById(It.Is(g => g.Equals(orderId)), It.Is(s => s.Equals("ttd")))) - .ReturnsAsync((_orderWithStatus, null)); + .ReturnsAsync(_orderWithStatus); HttpClient client = GetTestClient(orderService.Object); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); @@ -358,7 +359,7 @@ public async Task GetWithStatusById_ValidPlatformAccessToken_CorrespondingServic var orderService = new Mock(); orderService .Setup(o => o.GetOrderWithStatuById(It.Is(g => g.Equals(orderId)), It.Is(s => s.Equals("ttd")))) - .ReturnsAsync((_orderWithStatus, null)); + .ReturnsAsync(_orderWithStatus); HttpClient client = GetTestClient(orderService.Object); @@ -384,7 +385,7 @@ public async Task GetWithStatusById_ServiceReturnsError_StatusCodeMatchesError() var orderService = new Mock(); orderService .Setup(o => o.GetOrderWithStatuById(It.Is(g => g.Equals(orderId)), It.Is(s => s.Equals("ttd")))) - .ReturnsAsync((null, new ServiceError(404))); + .ReturnsAsync(new ServiceError(404)); HttpClient client = GetTestClient(orderService.Object); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); @@ -407,11 +408,11 @@ private HttpClient GetTestClient(IGetOrderService? orderService = null) var orderServiceMock = new Mock(); orderServiceMock .Setup(o => o.GetOrderById(It.IsAny(), It.IsAny())) - .ReturnsAsync((_order, null)); + .ReturnsAsync(_order); orderServiceMock .Setup(o => o.GetOrdersBySendersReference(It.IsAny(), It.IsAny())) - .ReturnsAsync((new List() { _order }, null)); + .ReturnsAsync(new List() { _order }); orderService = orderServiceMock.Object; } diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsOrdersController/SmsNotificationOrdersControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsOrdersController/SmsNotificationOrdersControllerTests.cs index 5a130d7b..84944d0f 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsOrdersController/SmsNotificationOrdersControllerTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsOrdersController/SmsNotificationOrdersControllerTests.cs @@ -10,6 +10,7 @@ using Altinn.Notifications.Core.Models.NotificationTemplate; using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.Core.Services.Interfaces; +using Altinn.Notifications.Core.Shared; using Altinn.Notifications.Models; using Altinn.Notifications.Tests.Notifications.Mocks.Authentication; using Altinn.Notifications.Tests.Notifications.Utils; @@ -166,30 +167,6 @@ public async Task Post_UserClaimsPrincipal_Forbidden() Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } - [Fact] - public async Task Post_ServiceReturnsError_ServerError() - { - // Arrange - Mock serviceMock = new(); - serviceMock.Setup(s => s.RegisterNotificationOrder(It.IsAny())) - .ReturnsAsync((null, new ServiceError(500))); - - HttpClient client = GetTestClient(orderService: serviceMock.Object); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); - - HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, _basePath) - { - Content = new StringContent(_orderRequestExt.Serialize(), Encoding.UTF8, "application/json") - }; - - // Act - HttpResponseMessage response = await client.SendAsync(httpRequestMessage); - - // Assert - Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); - serviceMock.VerifyAll(); - } - [Fact] public async Task Post_ValidScope_ServiceReturnsOrder_Accepted() { @@ -205,7 +182,7 @@ public async Task Post_ValidScope_ServiceReturnsOrder_Accepted() Assert.NotNull(smsTemplate); Assert.Equal(string.Empty, smsTemplate.SenderNumber); }) - .ReturnsAsync((_order, null)); + .ReturnsAsync(_order); HttpClient client = GetTestClient(orderService: serviceMock.Object); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); @@ -244,7 +221,7 @@ public async Task Post_ValidAccessToken_ServiceReturnsOrder_Accepted() Assert.NotNull(smsTemplate); Assert.Empty(smsTemplate.SenderNumber); }) - .ReturnsAsync((_order, null)); + .ReturnsAsync(_order); HttpClient client = GetTestClient(orderService: serviceMock.Object); @@ -284,7 +261,7 @@ public async Task Post_OrderWithoutFromAddress_StringEmptyUsedAsServiceInput_Acc Assert.NotNull(smsTemplate); Assert.Empty(smsTemplate.SenderNumber); }) - .ReturnsAsync((_order, null)); + .ReturnsAsync(_order); HttpClient client = GetTestClient(orderService: serviceMock.Object); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); diff --git a/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/GetOrderServiceTests.cs b/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/GetOrderServiceTests.cs index 6e9a248a..703b6172 100644 --- a/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/GetOrderServiceTests.cs +++ b/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/GetOrderServiceTests.cs @@ -6,6 +6,7 @@ using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.Core.Persistence; using Altinn.Notifications.Core.Services; +using Altinn.Notifications.Core.Shared; using Moq; @@ -31,12 +32,18 @@ public async Task GetOrderById_RepoReturnsNull_ServiceErrorReturned() var service = GetTestService(repoMock.Object); // Act - var (actualOrder, actuallError) = await service.GetOrderById(_id, _creator); + Result result = await service.GetOrderById(_id, _creator); // Assert - Assert.Null(actualOrder); - Assert.NotNull(actuallError); - Assert.Equal(404, actuallError.ErrorCode); + Assert.True(result.IsError); + await result.Match( + async actualOrder => await Task.CompletedTask, + async actuallError => + { + await Task.CompletedTask; + Assert.NotNull(actuallError); + Assert.Equal(404, actuallError.ErrorCode); + }); } [Fact] @@ -66,13 +73,19 @@ public async Task GetOrderById_GetOrderByIdCalledInRepo_OrderReturned() var service = GetTestService(repoMock.Object); // Act - var (actualOrder, actuallError) = await service.GetOrderById(_id, _creator); + Result result = await service.GetOrderById(_id, _creator); // Assert - repoMock.Verify(r => r.GetOrderById(It.Is(g => g == _id), It.Is(s => s.Equals("ttd"))), Times.Once); - Assert.NotNull(actualOrder); - Assert.Null(actuallError); - Assert.Equal(_id, actualOrder.Id); + Assert.True(result.IsSuccess); + await result.Match( + async actualOrder => + { + await Task.CompletedTask; + repoMock.Verify(r => r.GetOrderById(It.Is(g => g == _id), It.Is(s => s.Equals("ttd"))), Times.Once); + Assert.NotNull(actualOrder); + Assert.Equal(_id, actualOrder.Id); + }, + async actuallError => await Task.CompletedTask); } [Fact] @@ -102,13 +115,20 @@ public async Task GetOrdersBySendersReference_GetOrdersBySendersReferenceCalledI var service = GetTestService(repoMock.Object); // Act - var (actualOrders, actuallError) = await service.GetOrdersBySendersReference(_sendersRef, _creator); + Result, ServiceError> result = await service.GetOrdersBySendersReference(_sendersRef, _creator); // Assert - repoMock.Verify(r => r.GetOrdersBySendersReference(It.Is(s => s.Equals("sendersRef")), It.Is(s => s.Equals("ttd"))), Times.Once); - Assert.NotNull(actualOrders); - Assert.Equal(3, actualOrders.Count); - Assert.Null(actuallError); + Assert.True(result.IsSuccess); + await result.Match( + async actualOrders => + { + await Task.CompletedTask; + + repoMock.Verify(r => r.GetOrdersBySendersReference(It.Is(s => s.Equals("sendersRef")), It.Is(s => s.Equals("ttd"))), Times.Once); + Assert.NotNull(actualOrders); + Assert.Equal(3, actualOrders.Count); + }, + async actuallError => await Task.CompletedTask); } [Theory] diff --git a/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/OrderRequestServiceTests.cs b/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/OrderRequestServiceTests.cs index 5fa0be7a..d72ead41 100644 --- a/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/OrderRequestServiceTests.cs +++ b/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/OrderRequestServiceTests.cs @@ -9,6 +9,7 @@ using Altinn.Notifications.Core.Persistence; using Altinn.Notifications.Core.Services; using Altinn.Notifications.Core.Services.Interfaces; +using Altinn.Notifications.Core.Shared; using Microsoft.Extensions.Options; @@ -59,11 +60,18 @@ public async Task RegisterNotificationOrder_ForEmail_ExpectedInputToRepository() var service = GetTestService(repoMock.Object, id, createdTime); // Act - (NotificationOrder? actual, ServiceError? _) = await service.RegisterNotificationOrder(input); + Result result = await service.RegisterNotificationOrder(input); // Assert - Assert.Equivalent(expected, actual, true); - repoMock.VerifyAll(); + Assert.True(result.IsSuccess); + await result.Match( + async actual => + { + await Task.CompletedTask; + Assert.Equivalent(expected, actual, true); + repoMock.VerifyAll(); + }, + async actuallError => await Task.CompletedTask); } [Fact] @@ -105,11 +113,18 @@ public async Task RegisterNotificationOrder_ForEmail_NoFromAddressDefaultInserte var service = GetTestService(repoMock.Object, id, createdTime); // Act - (NotificationOrder? actual, ServiceError? _) = await service.RegisterNotificationOrder(input); + Result result = await service.RegisterNotificationOrder(input); // Assert - Assert.Equivalent(expected, actual, true); - repoMock.VerifyAll(); + Assert.True(result.IsSuccess); + await result.Match( + async actual => + { + await Task.CompletedTask; + Assert.Equivalent(expected, actual, true); + repoMock.VerifyAll(); + }, + async actuallError => await Task.CompletedTask); } [Fact] @@ -151,11 +166,18 @@ public async Task RegisterNotificationOrder_ForSms_ExpectedInputToRepository() var service = GetTestService(repoMock.Object, id, createdTime); // Act - (NotificationOrder? actual, ServiceError? _) = await service.RegisterNotificationOrder(input); + Result result = await service.RegisterNotificationOrder(input); // Assert - Assert.Equivalent(expected, actual, true); - repoMock.VerifyAll(); + Assert.True(result.IsSuccess); + await result.Match( + async actual => + { + await Task.CompletedTask; + Assert.Equivalent(expected, actual, true); + repoMock.VerifyAll(); + }, + async actuallError => await Task.CompletedTask); } [Fact] @@ -197,11 +219,18 @@ public async Task RegisterNotificationOrder_ForSms_NoSenderNumberDefaultInserted var service = GetTestService(repoMock.Object, id, createdTime); // Act - (NotificationOrder? actual, ServiceError? _) = await service.RegisterNotificationOrder(input); + Result result = await service.RegisterNotificationOrder(input); // Assert - Assert.Equivalent(expected, actual, true); - repoMock.VerifyAll(); + Assert.True(result.IsSuccess); + await result.Match( + async actual => + { + await Task.CompletedTask; + Assert.Equivalent(expected, actual, true); + repoMock.VerifyAll(); + }, + async actuallError => await Task.CompletedTask); } public static OrderRequestService GetTestService(IOrderRepository? repository = null, Guid? guid = null, DateTime? dateTime = null)