From 469c223224fac9eaeaf5a01dc5f20b6914e0db42 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Tue, 6 Feb 2024 12:17:43 +0100 Subject: [PATCH 01/17] Core logic for supporting sms notification summaries --- .../Extensions/ServiceCollectionExtensions.cs | 3 +- .../Notification/SmsNotificationSummary.cs | 31 +++++++ .../Notification/SmsNotificationWithResult.cs | 45 +++++++++ .../INotificationSummaryRepository.cs | 6 ++ ....cs => EmailNotificationSummaryService.cs} | 10 +- ...cs => IEmailNotificationSummaryService.cs} | 2 +- .../ISmsNotificationSummaryService.cs | 18 ++++ .../Services/SmsNotificationSummaryService.cs | 92 +++++++++++++++++++ .../EmailNotificationsController.cs | 4 +- .../EmailNotificationsControllerTests.cs | 10 +- ...> EmailNotificationSummaryServiceTests.cs} | 12 +-- 11 files changed, 213 insertions(+), 20 deletions(-) create mode 100644 src/Altinn.Notifications.Core/Models/Notification/SmsNotificationSummary.cs create mode 100644 src/Altinn.Notifications.Core/Models/Notification/SmsNotificationWithResult.cs rename src/Altinn.Notifications.Core/Services/{NotificationSummaryService.cs => EmailNotificationSummaryService.cs} (91%) rename src/Altinn.Notifications.Core/Services/Interfaces/{INotificationSummaryService.cs => IEmailNotificationSummaryService.cs} (92%) create mode 100644 src/Altinn.Notifications.Core/Services/Interfaces/ISmsNotificationSummaryService.cs create mode 100644 src/Altinn.Notifications.Core/Services/SmsNotificationSummaryService.cs rename test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/{NotificationSummaryServiceTests.cs => EmailNotificationSummaryServiceTests.cs} (88%) diff --git a/src/Altinn.Notifications.Core/Extensions/ServiceCollectionExtensions.cs b/src/Altinn.Notifications.Core/Extensions/ServiceCollectionExtensions.cs index af20e0c9..c7fbb3a2 100644 --- a/src/Altinn.Notifications.Core/Extensions/ServiceCollectionExtensions.cs +++ b/src/Altinn.Notifications.Core/Extensions/ServiceCollectionExtensions.cs @@ -35,9 +35,10 @@ public static void AddCoreServices(this IServiceCollection services, IConfigurat .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .Configure(config.GetSection("KafkaSettings")) diff --git a/src/Altinn.Notifications.Core/Models/Notification/SmsNotificationSummary.cs b/src/Altinn.Notifications.Core/Models/Notification/SmsNotificationSummary.cs new file mode 100644 index 00000000..02c59b75 --- /dev/null +++ b/src/Altinn.Notifications.Core/Models/Notification/SmsNotificationSummary.cs @@ -0,0 +1,31 @@ +namespace Altinn.Notifications.Core.Models.Notification +{ + /// + /// An implementation of for email notifications"/> + /// + public class SmsNotificationSummary : INotificationSummary + { + /// + public Guid OrderId { get; set; } + + /// + public string? SendersReference { get; set; } + + /// + public int Generated { get; internal set; } + + /// + public int Succeeded { get; internal set; } + + /// + public List Notifications { get; set; } = new List(); + + /// + /// Initializes a new instance of the class. + /// + public SmsNotificationSummary(Guid orderId) + { + OrderId = orderId; + } + } +} diff --git a/src/Altinn.Notifications.Core/Models/Notification/SmsNotificationWithResult.cs b/src/Altinn.Notifications.Core/Models/Notification/SmsNotificationWithResult.cs new file mode 100644 index 00000000..3065b5fd --- /dev/null +++ b/src/Altinn.Notifications.Core/Models/Notification/SmsNotificationWithResult.cs @@ -0,0 +1,45 @@ +using Altinn.Notifications.Core.Enums; +using Altinn.Notifications.Core.Models.Recipients; + +namespace Altinn.Notifications.Core.Models.Notification +{ + /// + /// An implementation of for sms notifications"/> + /// Using the as recipient type and the as result type + /// + public class SmsNotificationWithResult : INotificationWithResult + { + /// + public Guid Id { get; } + + /// + public bool Succeeded { get; internal set; } + + /// + public SmsRecipient Recipient { get; } + + /// + public NotificationResult ResultStatus { get; } + + /// + /// Initializes a new instance of the class. + /// + public SmsNotificationWithResult(Guid id, SmsRecipient recipient, NotificationResult result) + { + Id = id; + Recipient = recipient; + ResultStatus = result; + } + + /// + /// Initializes a new instance of the class. + /// + internal SmsNotificationWithResult(Guid id, bool succeeded, SmsRecipient recipient, NotificationResult result) + { + Id = id; + Succeeded = succeeded; + Recipient = recipient; + ResultStatus = result; + } + } +} diff --git a/src/Altinn.Notifications.Core/Persistence/INotificationSummaryRepository.cs b/src/Altinn.Notifications.Core/Persistence/INotificationSummaryRepository.cs index 728dfa86..9ead654e 100644 --- a/src/Altinn.Notifications.Core/Persistence/INotificationSummaryRepository.cs +++ b/src/Altinn.Notifications.Core/Persistence/INotificationSummaryRepository.cs @@ -12,4 +12,10 @@ public interface INotificationSummaryRepository /// /// A partial email notification summary object public Task GetEmailSummary(Guid orderId, string creator); + + /// + /// Retrieves all sms notifications for the provided order id in an sms notification summary + /// + /// A partial sms notification summary object + public Task GetSmsSummary(Guid orderId, string creator); } diff --git a/src/Altinn.Notifications.Core/Services/NotificationSummaryService.cs b/src/Altinn.Notifications.Core/Services/EmailNotificationSummaryService.cs similarity index 91% rename from src/Altinn.Notifications.Core/Services/NotificationSummaryService.cs rename to src/Altinn.Notifications.Core/Services/EmailNotificationSummaryService.cs index d921af0a..fbd9f284 100644 --- a/src/Altinn.Notifications.Core/Services/NotificationSummaryService.cs +++ b/src/Altinn.Notifications.Core/Services/EmailNotificationSummaryService.cs @@ -7,9 +7,9 @@ namespace Altinn.Notifications.Core.Services { /// - /// Implementation of + /// Implementation of /// - public class NotificationSummaryService : INotificationSummaryService + public class EmailNotificationSummaryService : IEmailNotificationSummaryService { private readonly INotificationSummaryRepository _summaryRepository; private readonly static Dictionary _emailResultDescriptions = new() @@ -22,7 +22,7 @@ public class NotificationSummaryService : INotificationSummaryService { EmailNotificationResultType.Failed_RecipientNotIdentified, "The email was not sent because the recipient's email address was not found." }, { EmailNotificationResultType.Failed_InvalidEmailFormat, "The email was not sent because the recipient’s email address is in an invalid format." }, { EmailNotificationResultType.Failed_SupressedRecipient, "The email was not sent because the recipient’s email address is suppressed by the third party email service." }, - { EmailNotificationResultType.Failed_TransientError, "The email was not sent due to a transient error. We will retry sending the email." } + { EmailNotificationResultType.Failed_TransientError, "The email was not sent due to a transient error. We will retry sending the email." } }; private readonly static List _successResults = new() @@ -32,9 +32,9 @@ public class NotificationSummaryService : INotificationSummaryService }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public NotificationSummaryService(INotificationSummaryRepository summaryRepository) + public EmailNotificationSummaryService(INotificationSummaryRepository summaryRepository) { _summaryRepository = summaryRepository; } diff --git a/src/Altinn.Notifications.Core/Services/Interfaces/INotificationSummaryService.cs b/src/Altinn.Notifications.Core/Services/Interfaces/IEmailNotificationSummaryService.cs similarity index 92% rename from src/Altinn.Notifications.Core/Services/Interfaces/INotificationSummaryService.cs rename to src/Altinn.Notifications.Core/Services/Interfaces/IEmailNotificationSummaryService.cs index a58d65cd..99d7d1c1 100644 --- a/src/Altinn.Notifications.Core/Services/Interfaces/INotificationSummaryService.cs +++ b/src/Altinn.Notifications.Core/Services/Interfaces/IEmailNotificationSummaryService.cs @@ -6,7 +6,7 @@ namespace Altinn.Notifications.Core.Services.Interfaces /// /// Interface describing the notification summary service /// - public interface INotificationSummaryService + public interface IEmailNotificationSummaryService { /// /// Gets a summary of all the generated email notifications for the provided order id diff --git a/src/Altinn.Notifications.Core/Services/Interfaces/ISmsNotificationSummaryService.cs b/src/Altinn.Notifications.Core/Services/Interfaces/ISmsNotificationSummaryService.cs new file mode 100644 index 00000000..1a54cb74 --- /dev/null +++ b/src/Altinn.Notifications.Core/Services/Interfaces/ISmsNotificationSummaryService.cs @@ -0,0 +1,18 @@ +using Altinn.Notifications.Core.Models.Notification; +using Altinn.Notifications.Core.Shared; + +namespace Altinn.Notifications.Core.Services.Interfaces +{ + /// + /// Interface describing the notification summary service + /// + public interface ISmsNotificationSummaryService + { + /// + /// Gets a summary of all the generated sms notifications for the provided order id + /// + /// The order id to find notifications for + /// The creator of the order + public Task> GetSmsSummary(Guid orderId, string creator); + } +} diff --git a/src/Altinn.Notifications.Core/Services/SmsNotificationSummaryService.cs b/src/Altinn.Notifications.Core/Services/SmsNotificationSummaryService.cs new file mode 100644 index 00000000..6462cb81 --- /dev/null +++ b/src/Altinn.Notifications.Core/Services/SmsNotificationSummaryService.cs @@ -0,0 +1,92 @@ +using Altinn.Notifications.Core.Enums; +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 +{ + /// + /// Implementation of + /// + public class SmsNotificationSummaryService : ISmsNotificationSummaryService + { + private readonly INotificationSummaryRepository _summaryRepository; + private readonly static Dictionary _smsResultDescriptions = new() + { + { SmsNotificationResultType.New, "The sms has been created, but has not been picked up for processing yet." }, + { SmsNotificationResultType.Sending, "The sms is being processed and will be attempted sent shortly." }, + { SmsNotificationResultType.Accepted, "The sms has been accepted by the gateway service." }, + { SmsNotificationResultType.Failed, "The sms was not sent due to an unspecified failure." }, + { SmsNotificationResultType.Failed_RecipientNotIdentified, "The sms was not sent because the recipient's sms address was not found." }, + { SmsNotificationResultType.Failed_InvalidRecipient, "The sms was not sent because the recipient number was invalid." } + }; + + private readonly static List _successResults = new() + { + SmsNotificationResultType.Accepted + }; + + /// + /// Initializes a new instance of the class. + /// + public SmsNotificationSummaryService(INotificationSummaryRepository summaryRepository) + { + _summaryRepository = summaryRepository; + } + + /// + public async Task> GetSmsSummary(Guid orderId, string creator) + { + SmsNotificationSummary? summary = await _summaryRepository.GetSmsSummary(orderId, creator); + + if (summary == null) + { + return new ServiceError(404); + } + + if (summary.Notifications.Count != 0) + { + ProcessNotificationResults(summary); + } + + return summary; + } + + /// + /// Processes the notification results setting counts and descriptions + /// + internal static void ProcessNotificationResults(SmsNotificationSummary summary) + { + summary.Generated = summary.Notifications.Count; + + foreach (SmsNotificationWithResult notification in summary.Notifications) + { + NotificationResult resultStatus = notification.ResultStatus; + if (IsSuccessResult(resultStatus.Result)) + { + notification.Succeeded = true; + ++summary.Succeeded; + } + + resultStatus.SetResultDescription(GetResultDescription(resultStatus.Result)); + } + } + + /// + /// Checks if the is a success result + /// + internal static bool IsSuccessResult(SmsNotificationResultType result) + { + return _successResults.Contains(result); + } + + /// + /// Gets the English description of the " + /// + internal static string GetResultDescription(SmsNotificationResultType result) + { + return _smsResultDescriptions[result]; + } + } +} diff --git a/src/Altinn.Notifications/Controllers/EmailNotificationsController.cs b/src/Altinn.Notifications/Controllers/EmailNotificationsController.cs index 0d8d21f2..9e640651 100644 --- a/src/Altinn.Notifications/Controllers/EmailNotificationsController.cs +++ b/src/Altinn.Notifications/Controllers/EmailNotificationsController.cs @@ -22,13 +22,13 @@ namespace Altinn.Notifications.Controllers [SwaggerResponse(403, "Caller is not authorized to access the requested resource")] public class EmailNotificationsController : ControllerBase { - private readonly INotificationSummaryService _summaryService; + private readonly IEmailNotificationSummaryService _summaryService; /// /// Initializes a new instance of the class. /// /// The notifications summary service - public EmailNotificationsController(INotificationSummaryService summaryService) + public EmailNotificationsController(IEmailNotificationSummaryService summaryService) { _summaryService = summaryService; } diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/EmailNotificationsControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/EmailNotificationsControllerTests.cs index deacd17b..aecd6a01 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/EmailNotificationsControllerTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/EmailNotificationsControllerTests.cs @@ -100,7 +100,7 @@ public async Task Get_InvalidGuid_BadRequest() public async Task Get_ServiceReturnsError_ServerError() { // Arrange - Mock serviceMock = new(); + Mock serviceMock = new(); serviceMock.Setup(s => s.GetEmailSummary(It.IsAny(), It.IsAny())) .ReturnsAsync(new ServiceError(500)); @@ -130,7 +130,7 @@ public async Task Get_ValidScope_ServiceReturnsNotifications_Ok() Notifications = new List() }; - Mock serviceMock = new(); + Mock serviceMock = new(); serviceMock.Setup(s => s.GetEmailSummary(It.IsAny(), It.Is(s => s.Equals("ttd")))) .ReturnsAsync(output); @@ -166,7 +166,7 @@ public async Task Get_ValidAccessToken_ServiceReturnsOrder_Accepted() Notifications = new List() }; - Mock serviceMock = new(); + Mock serviceMock = new(); serviceMock.Setup(s => s.GetEmailSummary(It.IsAny(), It.Is(s => s.Equals("ttd")))) .ReturnsAsync(output); @@ -190,11 +190,11 @@ public async Task Get_ValidAccessToken_ServiceReturnsOrder_Accepted() serviceMock.VerifyAll(); } - private HttpClient GetTestClient(INotificationSummaryService? summaryService = null) + private HttpClient GetTestClient(IEmailNotificationSummaryService? summaryService = null) { if (summaryService == null) { - var summaryServiceMock = new Mock(); + var summaryServiceMock = new Mock(); summaryService = summaryServiceMock.Object; } diff --git a/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/NotificationSummaryServiceTests.cs b/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/EmailNotificationSummaryServiceTests.cs similarity index 88% rename from test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/NotificationSummaryServiceTests.cs rename to test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/EmailNotificationSummaryServiceTests.cs index 8c4923cf..8f0bc46b 100644 --- a/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/NotificationSummaryServiceTests.cs +++ b/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/EmailNotificationSummaryServiceTests.cs @@ -8,7 +8,7 @@ namespace Altinn.Notifications.Tests.Notifications.Core.TestingServices { - public class NotificationSummaryServiceTests + public class EmailNotificationSummaryServiceTests { [Theory] [InlineData(EmailNotificationResultType.New, false)] @@ -19,7 +19,7 @@ public class NotificationSummaryServiceTests [InlineData(EmailNotificationResultType.Failed_InvalidEmailFormat, false)] public void IsSuccessResult_CheckResultForAllEnums(EmailNotificationResultType result, bool expectedIsSuccess) { - bool actualIsSuccess = NotificationSummaryService.IsSuccessResult(result); + bool actualIsSuccess = EmailNotificationSummaryService.IsSuccessResult(result); Assert.Equal(expectedIsSuccess, actualIsSuccess); } @@ -33,7 +33,7 @@ public void IsSuccessResult_CheckResultForAllEnums(EmailNotificationResultType r [InlineData(EmailNotificationResultType.Failed_InvalidEmailFormat, "The email was not sent because the recipient’s email address is in an invalid format.")] public void GetResultDescription_ExpectedDescription(EmailNotificationResultType result, string expected) { - string actual = NotificationSummaryService.GetResultDescription(result); + string actual = EmailNotificationSummaryService.GetResultDescription(result); Assert.Equal(expected, actual); } @@ -42,7 +42,7 @@ public void GetResultDescription_AllResultTypesHaveDescriptions() { foreach (EmailNotificationResultType resultType in Enum.GetValues(typeof(EmailNotificationResultType))) { - string resultDescrption = NotificationSummaryService.GetResultDescription(resultType); + string resultDescrption = EmailNotificationSummaryService.GetResultDescription(resultType); Assert.NotEmpty(resultDescrption); } } @@ -63,7 +63,7 @@ public void ProcessNotificationResults_1_generated_1_successful() }; // Act - NotificationSummaryService.ProcessNotificationResults(summary); + EmailNotificationSummaryService.ProcessNotificationResults(summary); // Assert Assert.Equal(1, summary.Generated); @@ -86,7 +86,7 @@ public void ProcessNotificationResults_1_generated_0_successful() }; // Act - NotificationSummaryService.ProcessNotificationResults(summary); + EmailNotificationSummaryService.ProcessNotificationResults(summary); // Assert Assert.Equal(1, summary.Generated); From 07a1efd45bdf006f61801953e6a68cf08f6d1cda Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Tue, 6 Feb 2024 12:22:10 +0100 Subject: [PATCH 02/17] added unit test for service --- .../SmsNotificationSummaryServiceTests.cs | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/SmsNotificationSummaryServiceTests.cs diff --git a/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/SmsNotificationSummaryServiceTests.cs b/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/SmsNotificationSummaryServiceTests.cs new file mode 100644 index 00000000..f6ec614f --- /dev/null +++ b/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/SmsNotificationSummaryServiceTests.cs @@ -0,0 +1,95 @@ +using System; + +using Altinn.Notifications.Core.Enums; +using Altinn.Notifications.Core.Models.Notification; +using Altinn.Notifications.Core.Services; + +using Xunit; + +namespace Altinn.Notifications.Tests.Notifications.Core.TestingServices +{ + public class SmsNotificationSummaryServiceTests + { + [Theory] + [InlineData(SmsNotificationResultType.Accepted, true)] + [InlineData(SmsNotificationResultType.New, false)] + [InlineData(SmsNotificationResultType.Sending, false)] + [InlineData(SmsNotificationResultType.Failed_RecipientNotIdentified, false)] + [InlineData(SmsNotificationResultType.Failed_InvalidRecipient, false)] + [InlineData(SmsNotificationResultType.Failed, false)] + public void IsSuccessResult_CheckResultForAllEnums(SmsNotificationResultType result, bool expectedIsSuccess) + { + bool actualIsSuccess = SmsNotificationSummaryService.IsSuccessResult(result); + Assert.Equal(expectedIsSuccess, actualIsSuccess); + } + + [Theory] + [InlineData(SmsNotificationResultType.New, "The sms has been created, but has not been picked up for processing yet.")] + [InlineData(SmsNotificationResultType.Sending, "The sms is being processed and will be attempted sent shortly.")] + [InlineData(SmsNotificationResultType.Accepted, "The sms has been accepted by the gateway service.")] + [InlineData(SmsNotificationResultType.Failed, "The sms was not sent due to an unspecified failure.")] + [InlineData(SmsNotificationResultType.Failed_RecipientNotIdentified, "The sms was not sent because the recipient's sms address was not found.")] + [InlineData(SmsNotificationResultType.Failed_InvalidRecipient, "The sms was not sent because the recipient number was invalid.")] + public void GetResultDescription_ExpectedDescription(SmsNotificationResultType result, string expected) + { + string actual = SmsNotificationSummaryService.GetResultDescription(result); + Assert.Equal(expected, actual); + } + + [Fact] + public void GetResultDescription_AllResultTypesHaveDescriptions() + { + foreach (SmsNotificationResultType resultType in Enum.GetValues(typeof(SmsNotificationResultType))) + { + string resultDescrption = SmsNotificationSummaryService.GetResultDescription(resultType); + Assert.NotEmpty(resultDescrption); + } + } + + [Fact] + public void ProcessNotificationResults_1_generated_1_successful() + { + // Arrange + SmsNotificationSummary summary = new(Guid.NewGuid()) + { + Notifications = new() + { + new SmsNotificationWithResult( + Guid.NewGuid(), + new Altinn.Notifications.Core.Models.Recipients.SmsRecipient(), + new NotificationResult(SmsNotificationResultType.Accepted, DateTime.UtcNow)) + } + }; + + // Act + SmsNotificationSummaryService.ProcessNotificationResults(summary); + + // Assert + Assert.Equal(1, summary.Generated); + Assert.Equal(1, summary.Succeeded); + } + + [Fact] + public void ProcessNotificationResults_1_generated_0_successful() + { + // Arrange + SmsNotificationSummary summary = new(Guid.NewGuid()) + { + Notifications = new() + { + new SmsNotificationWithResult( + Guid.NewGuid(), + new Altinn.Notifications.Core.Models.Recipients.SmsRecipient(), + new NotificationResult(SmsNotificationResultType.Failed_RecipientNotIdentified, DateTime.UtcNow)) + } + }; + + // Act + SmsNotificationSummaryService.ProcessNotificationResults(summary); + + // Assert + Assert.Equal(1, summary.Generated); + Assert.Equal(0, summary.Succeeded); + } + } +} From 9a6cda3b74bfe909248fc0b3b1b5066653008e57 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Tue, 6 Feb 2024 12:29:48 +0100 Subject: [PATCH 03/17] initial implementation --- .../INotificationSummaryRepository.cs | 6 ++ .../Migration/v0.21/01-setup-functions.sql | 29 +++++++++ .../NotificationSummaryRepository.cs | 63 ++++++++++++++++++- 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 src/Altinn.Notifications.Persistence/Migration/v0.21/01-setup-functions.sql diff --git a/src/Altinn.Notifications.Core/Persistence/INotificationSummaryRepository.cs b/src/Altinn.Notifications.Core/Persistence/INotificationSummaryRepository.cs index 728dfa86..9ead654e 100644 --- a/src/Altinn.Notifications.Core/Persistence/INotificationSummaryRepository.cs +++ b/src/Altinn.Notifications.Core/Persistence/INotificationSummaryRepository.cs @@ -12,4 +12,10 @@ public interface INotificationSummaryRepository /// /// A partial email notification summary object public Task GetEmailSummary(Guid orderId, string creator); + + /// + /// Retrieves all sms notifications for the provided order id in an sms notification summary + /// + /// A partial sms notification summary object + public Task GetSmsSummary(Guid orderId, string creator); } diff --git a/src/Altinn.Notifications.Persistence/Migration/v0.21/01-setup-functions.sql b/src/Altinn.Notifications.Persistence/Migration/v0.21/01-setup-functions.sql new file mode 100644 index 00000000..912f7bbf --- /dev/null +++ b/src/Altinn.Notifications.Persistence/Migration/v0.21/01-setup-functions.sql @@ -0,0 +1,29 @@ +CREATE OR REPLACE FUNCTION notifications.getsmssummary( + _alternateorderid uuid, + _creatorname text) + RETURNS TABLE( + sendersreference text, + alternateid uuid, + recipientid text, + mobilenumber text, + result smsnotificationresulttype, + resulttime timestamptz) + LANGUAGE 'plpgsql' +AS $BODY$ + + BEGIN + RETURN QUERY + SELECT o.sendersreference, n.alternateid, n.recipientid, n.mobilenumber, n.result, n.resulttime + FROM notifications.smsnotifications n + LEFT JOIN notifications.orders o ON n._orderid = o._id + WHERE o.alternateid = _alternateorderid + and o.creatorname = _creatorname; + IF NOT FOUND THEN + RETURN QUERY + SELECT o.sendersreference, NULL::uuid, NULL::text, NULL::text, NULL::smsnotificationresulttype, NULL::timestamptz + FROM notifications.orders o + WHERE o.alternateid = _alternateorderid + and o.creatorname = _creatorname; + END IF; + END; +$BODY$; diff --git a/src/Altinn.Notifications.Persistence/Repository/NotificationSummaryRepository.cs b/src/Altinn.Notifications.Persistence/Repository/NotificationSummaryRepository.cs index 8bd24862..205d7f98 100644 --- a/src/Altinn.Notifications.Persistence/Repository/NotificationSummaryRepository.cs +++ b/src/Altinn.Notifications.Persistence/Repository/NotificationSummaryRepository.cs @@ -20,7 +20,8 @@ public class NotificationSummaryRepository : INotificationSummaryRepository private readonly NpgsqlDataSource _dataSource; private readonly TelemetryClient? _telemetryClient; - private const string _getNotificationsByOrderIdSql = "select * from notifications.getemailsummary($1, $2)"; // (_alternateorderid, creatorname) + private const string _getEmailNotificationsByOrderIdSql = "select * from notifications.getemailsummary($1, $2)"; // (_alternateorderid, creatorname) + private const string _getSmsNotificationsByOrderIdSql = "select * from notifications.getsmssummary($1, $2)"; // (_alternateorderid, creatorname) /// /// Initializes a new instance of the class. @@ -41,7 +42,7 @@ public NotificationSummaryRepository(NpgsqlDataSource dataSource, TelemetryClien List notificationList = new(); string sendersReference = string.Empty; - await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getNotificationsByOrderIdSql); + await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getEmailNotificationsByOrderIdSql); using TelemetryTracker tracker = new(_telemetryClient, pgcom); pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, orderId); pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, creator); @@ -90,4 +91,62 @@ public NotificationSummaryRepository(NpgsqlDataSource dataSource, TelemetryClien return emailSummary; } + + /// + public async Task GetSmsSummary(Guid orderId, string creator) + { + bool matchFound = false; + + List notificationList = new(); + string sendersReference = string.Empty; + + await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getSmsNotificationsByOrderIdSql); + using TelemetryTracker tracker = new(_telemetryClient, pgcom); + pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, orderId); + pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, creator); + + await using (NpgsqlDataReader reader = await pgcom.ExecuteReaderAsync()) + { + while (await reader.ReadAsync()) + { + if (!matchFound) + { + matchFound = true; + sendersReference = reader.GetValue("sendersreference"); + } + + Guid notificationId = reader.GetValue("alternateid"); + + if (notificationId != Guid.Empty) + { + notificationList.Add( + new SmsNotificationWithResult( + reader.GetValue("alternateid"), + new SmsRecipient() + { + RecipientId = reader.GetValue("recipientid"), + MobileNumber = reader.GetValue("mobilenumber") + }, + new NotificationResult( + reader.GetValue("result"), + reader.GetValue("resulttime")))); + } + } + } + + tracker.Track(); + + if (!matchFound) + { + return null; + } + + SmsNotificationSummary smsSummary = new(orderId) + { + SendersReference = sendersReference, + Notifications = notificationList + }; + + return smsSummary; + } } From b4f9af817e433ff966913d8752b4fa87fe627513 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Tue, 6 Feb 2024 12:30:27 +0100 Subject: [PATCH 04/17] undid persistence interface --- .../Persistence/INotificationSummaryRepository.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Altinn.Notifications.Core/Persistence/INotificationSummaryRepository.cs b/src/Altinn.Notifications.Core/Persistence/INotificationSummaryRepository.cs index 9ead654e..728dfa86 100644 --- a/src/Altinn.Notifications.Core/Persistence/INotificationSummaryRepository.cs +++ b/src/Altinn.Notifications.Core/Persistence/INotificationSummaryRepository.cs @@ -12,10 +12,4 @@ public interface INotificationSummaryRepository /// /// A partial email notification summary object public Task GetEmailSummary(Guid orderId, string creator); - - /// - /// Retrieves all sms notifications for the provided order id in an sms notification summary - /// - /// A partial sms notification summary object - public Task GetSmsSummary(Guid orderId, string creator); } From 7eb71295e2c74598c952118a99d35ac6ca999dc2 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Tue, 6 Feb 2024 16:00:55 +0100 Subject: [PATCH 05/17] added tests +Fixed code smells --- .../Notification/SmsNotificationSummary.cs | 2 +- .../IEmailNotificationSummaryService.cs | 2 +- .../ISmsNotificationSummaryService.cs | 2 +- .../SmsNotificationSummaryTests.cs | 72 +++++++++++++++++++ .../OrderRepositoryTests.cs | 19 ++--- .../SmsNotificationRepositoryTests.cs | 27 +++---- .../EmailNotificationsController/GetTests.cs | 12 ++-- .../Trigger_SendEmailNotificationsTests.cs | 2 +- .../Trigger_SendSmsNotificationsTests.cs | 2 +- .../EmailOrderProcessingServiceTests.cs | 3 - 10 files changed, 107 insertions(+), 36 deletions(-) create mode 100644 test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs diff --git a/src/Altinn.Notifications.Core/Models/Notification/SmsNotificationSummary.cs b/src/Altinn.Notifications.Core/Models/Notification/SmsNotificationSummary.cs index 02c59b75..50f1ccc2 100644 --- a/src/Altinn.Notifications.Core/Models/Notification/SmsNotificationSummary.cs +++ b/src/Altinn.Notifications.Core/Models/Notification/SmsNotificationSummary.cs @@ -1,7 +1,7 @@ namespace Altinn.Notifications.Core.Models.Notification { /// - /// An implementation of for email notifications"/> + /// An implementation of for sms notifications"/> /// public class SmsNotificationSummary : INotificationSummary { diff --git a/src/Altinn.Notifications.Core/Services/Interfaces/IEmailNotificationSummaryService.cs b/src/Altinn.Notifications.Core/Services/Interfaces/IEmailNotificationSummaryService.cs index 99d7d1c1..2221fbe5 100644 --- a/src/Altinn.Notifications.Core/Services/Interfaces/IEmailNotificationSummaryService.cs +++ b/src/Altinn.Notifications.Core/Services/Interfaces/IEmailNotificationSummaryService.cs @@ -4,7 +4,7 @@ namespace Altinn.Notifications.Core.Services.Interfaces { /// - /// Interface describing the notification summary service + /// Interface describing the email notification summary service /// public interface IEmailNotificationSummaryService { diff --git a/src/Altinn.Notifications.Core/Services/Interfaces/ISmsNotificationSummaryService.cs b/src/Altinn.Notifications.Core/Services/Interfaces/ISmsNotificationSummaryService.cs index 1a54cb74..1d799b19 100644 --- a/src/Altinn.Notifications.Core/Services/Interfaces/ISmsNotificationSummaryService.cs +++ b/src/Altinn.Notifications.Core/Services/Interfaces/ISmsNotificationSummaryService.cs @@ -4,7 +4,7 @@ namespace Altinn.Notifications.Core.Services.Interfaces { /// - /// Interface describing the notification summary service + /// Interface describing the sms notification summary service /// public interface ISmsNotificationSummaryService { diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs new file mode 100644 index 00000000..40f740fb --- /dev/null +++ b/test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Altinn.Notifications.Core.Enums; +using Altinn.Notifications.Core.Models.Notification; +using Altinn.Notifications.Core.Models.Orders; +using Altinn.Notifications.Core.Persistence; +using Altinn.Notifications.Core.Services; +using Altinn.Notifications.Core.Services.Interfaces; +using Altinn.Notifications.Core.Shared; +using Altinn.Notifications.IntegrationTests.Utils; +using Altinn.Notifications.Persistence.Repository; + +using Microsoft.AspNetCore.Http.HttpResults; + +using Xunit; + +namespace Altinn.Notifications.IntegrationTests.Notifications.Core +{ + public class SmsNotificationSummaryTests : IAsyncLifetime + { + private readonly List _orderIdsToDelete; + + public SmsNotificationSummaryTests() + { + _orderIdsToDelete = new List(); + } + + public async Task InitializeAsync() + { + await Task.CompletedTask; + } + + public async Task DisposeAsync() + { + string deleteSql = $@"DELETE from notifications.orders o where o.alternateid in ('{string.Join("','", _orderIdsToDelete)}')"; + await PostgreUtil.RunSql(deleteSql); + } + + [Fact] + public async Task GetSmsSummary_SingleNeweNotification_ReturnsSummary() + { + // Arrange + (NotificationOrder order, _) = await PostgreUtil.PopulateDBWithOrderAndSmsNotification(); + _orderIdsToDelete.Add(order.Id); + + SmsNotificationSummaryService service = (SmsNotificationSummaryService)ServiceUtil + .GetServices(new List() { typeof(ISmsNotificationSummaryService) }) + .First(i => i.GetType() == typeof(SmsNotificationSummaryService)); + + // Act + Result result = await service.GetSmsSummary(order.Id, "ttd"); + + // Assert + result.Match( + actualSummary => + { + Assert.Single(actualSummary.Notifications); + var notification = actualSummary.Notifications[0]; + Assert.Equal(SmsNotificationResultType.New, notification.ResultStatus.Result); + return true; + }, + error => + { + throw new Exception("Expected a summary, but got an error"); + }); + } + } +} diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications.Persistence/OrderRepositoryTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications.Persistence/OrderRepositoryTests.cs index 682c6d5a..048efa4c 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications.Persistence/OrderRepositoryTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications.Persistence/OrderRepositoryTests.cs @@ -1,4 +1,5 @@ -using Altinn.Notifications.Core.Models.NotificationTemplate; +using Altinn.Notifications.Core.Enums; +using Altinn.Notifications.Core.Models.NotificationTemplate; using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.Core.Persistence; using Altinn.Notifications.IntegrationTests.Utils; @@ -10,11 +11,11 @@ namespace Altinn.Notifications.IntegrationTests.Notifications.Persistence { public class OrderRepositoryTests : IAsyncLifetime { - private readonly List orderIdsToDelete; + private readonly List _orderIdsToDelete; public OrderRepositoryTests() { - orderIdsToDelete = new List(); + _orderIdsToDelete = new List(); } public async Task InitializeAsync() @@ -24,7 +25,7 @@ public async Task InitializeAsync() public async Task DisposeAsync() { - string deleteSql = $@"DELETE from notifications.orders o where o.alternateid in ('{string.Join("','", orderIdsToDelete)}')"; + string deleteSql = $@"DELETE from notifications.orders o where o.alternateid in ('{string.Join("','", _orderIdsToDelete)}')"; await PostgreUtil.RunSql(deleteSql); } @@ -48,7 +49,7 @@ public async Task Create_OrderWithSmsTemplate_SmsTextsPersisted() RequestedSendTime = DateTime.UtcNow }; - orderIdsToDelete.Add(order.Id); + _orderIdsToDelete.Add(order.Id); // Act await repo.Create(order); @@ -79,11 +80,11 @@ public async Task Create_OrderWithEmailTemplate_EmailTextsPersisted() Creator = new("test"), Templates = new List() { - new EmailTemplate("noreply@altinn.no", "Subject", "Body", Core.Enums.EmailContentType.Plain) + new EmailTemplate("noreply@altinn.no", "Subject", "Body", EmailContentType.Plain) } }; - orderIdsToDelete.Add(order.Id); + _orderIdsToDelete.Add(order.Id); // Act await repo.Create(order); @@ -114,12 +115,12 @@ public async Task Create_OrderWithEmailAndSmsTemplate_BothTextsPersisted() Creator = new("test"), Templates = new List() { - new EmailTemplate("noreply@altinn.no", "Subject", "Body", Core.Enums.EmailContentType.Plain), + new EmailTemplate("noreply@altinn.no", "Subject", "Body", EmailContentType.Plain), new SmsTemplate("Altinn", "This is the body") } }; - orderIdsToDelete.Add(order.Id); + _orderIdsToDelete.Add(order.Id); // Act await repo.Create(order); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications.Persistence/SmsNotificationRepositoryTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications.Persistence/SmsNotificationRepositoryTests.cs index 8249d0d7..8b3ff4dd 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications.Persistence/SmsNotificationRepositoryTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications.Persistence/SmsNotificationRepositoryTests.cs @@ -1,4 +1,5 @@ -using Altinn.Notifications.Core.Models; +using Altinn.Notifications.Core.Enums; +using Altinn.Notifications.Core.Models; using Altinn.Notifications.Core.Models.Notification; using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.Core.Models.Recipients; @@ -12,11 +13,11 @@ namespace Altinn.Notifications.IntegrationTests.Notifications.Persistence; public class SmsNotificationRepositoryTests : IAsyncLifetime { - private readonly List orderIdsToDelete; + private readonly List _orderIdsToDelete; public SmsNotificationRepositoryTests() { - orderIdsToDelete = []; + _orderIdsToDelete = []; } public async Task InitializeAsync() @@ -26,7 +27,7 @@ public async Task InitializeAsync() public async Task DisposeAsync() { - string deleteSql = $@"DELETE from notifications.orders o where o.alternateid in ('{string.Join("','", orderIdsToDelete)}')"; + string deleteSql = $@"DELETE from notifications.orders o where o.alternateid in ('{string.Join("','", _orderIdsToDelete)}')"; await PostgreUtil.RunSql(deleteSql); } @@ -35,7 +36,7 @@ public async Task AddNotification() { // Arrange Guid orderId = await PostgreUtil.PopulateDBWithEmailOrderAndReturnId(); - orderIdsToDelete.Add(orderId); + _orderIdsToDelete.Add(orderId); // Arrange SmsNotificationRepository repo = (SmsNotificationRepository)ServiceUtil @@ -69,7 +70,7 @@ public async Task GetNewNotifications() { // Arrange (NotificationOrder order, SmsNotification smsNotification) = await PostgreUtil.PopulateDBWithOrderAndSmsNotification(); - orderIdsToDelete.Add(order.Id); + _orderIdsToDelete.Add(order.Id); SmsNotificationRepository repo = (SmsNotificationRepository)ServiceUtil .GetServices(new List() { typeof(ISmsNotificationRepository) }) @@ -91,7 +92,7 @@ public async Task GetRecipients() .First(i => i.GetType() == typeof(SmsNotificationRepository)); (NotificationOrder order, SmsNotification smsNotification) = await PostgreUtil.PopulateDBWithOrderAndSmsNotification(); - orderIdsToDelete.Add(order.Id); + _orderIdsToDelete.Add(order.Id); string expectedNumber = smsNotification.RecipientNumber; string? expectedRecipientId = smsNotification.RecipientId; @@ -111,7 +112,7 @@ public async Task UpdateSendStatus_WithGatewayRef() { // Arrange (NotificationOrder order, SmsNotification smsNotification) = await PostgreUtil.PopulateDBWithOrderAndSmsNotification(); - orderIdsToDelete.Add(order.Id); + _orderIdsToDelete.Add(order.Id); SmsNotificationRepository repo = (SmsNotificationRepository)ServiceUtil .GetServices(new List() { typeof(ISmsNotificationRepository) }) @@ -120,13 +121,13 @@ public async Task UpdateSendStatus_WithGatewayRef() string gatewayReference = Guid.NewGuid().ToString(); // Act - await repo.UpdateSendStatus(smsNotification.Id, Core.Enums.SmsNotificationResultType.Accepted, gatewayReference); + await repo.UpdateSendStatus(smsNotification.Id, SmsNotificationResultType.Accepted, gatewayReference); // Assert string sql = $@"SELECT count(1) FROM notifications.smsnotifications sms WHERE sms.alternateid = '{smsNotification.Id}' - AND sms.result = '{Core.Enums.SmsNotificationResultType.Accepted}' + AND sms.result = '{SmsNotificationResultType.Accepted}' AND sms.gatewayreference = '{gatewayReference}'"; int actualCount = await PostgreUtil.RunSqlReturnOutput(sql); @@ -139,20 +140,20 @@ public async Task UpdateSendStatus_WithoutGatewayRef() { // Arrange (NotificationOrder order, SmsNotification smsNotification) = await PostgreUtil.PopulateDBWithOrderAndSmsNotification(); - orderIdsToDelete.Add(order.Id); + _orderIdsToDelete.Add(order.Id); SmsNotificationRepository repo = (SmsNotificationRepository)ServiceUtil .GetServices(new List() { typeof(ISmsNotificationRepository) }) .First(i => i.GetType() == typeof(SmsNotificationRepository)); // Act - await repo.UpdateSendStatus(smsNotification.Id, Core.Enums.SmsNotificationResultType.Accepted); + await repo.UpdateSendStatus(smsNotification.Id, SmsNotificationResultType.Accepted); // Assert string sql = $@"SELECT count(1) FROM notifications.smsnotifications sms WHERE sms.alternateid = '{smsNotification.Id}' - AND sms.result = '{Core.Enums.SmsNotificationResultType.Accepted}'"; + AND sms.result = '{SmsNotificationResultType.Accepted}'"; int actualCount = await PostgreUtil.RunSqlReturnOutput(sql); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/GetTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/GetTests.cs index 61a458cb..73c083c6 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/GetTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/GetTests.cs @@ -24,13 +24,13 @@ public class GetTests : IClassFixture _factory; - private readonly List orderIdsToDelete; + private readonly List _orderIdsToDelete; public GetTests(IntegrationTestWebApplicationFactory factory) { _basePath = $"/notifications/api/v1/orders"; _factory = factory; - orderIdsToDelete = new List(); + _orderIdsToDelete = new List(); } public async Task InitializeAsync() @@ -40,9 +40,9 @@ public async Task InitializeAsync() async Task IAsyncLifetime.DisposeAsync() { - if (orderIdsToDelete.Count != 0) + if (_orderIdsToDelete.Count != 0) { - string deleteSql = $@"DELETE from notifications.orders o where o.alternateid in ('{string.Join("','", orderIdsToDelete)}')"; + string deleteSql = $@"DELETE from notifications.orders o where o.alternateid in ('{string.Join("','", _orderIdsToDelete)}')"; await PostgreUtil.RunSql(deleteSql); } } @@ -70,7 +70,7 @@ public async Task Get_OrderIdForAnotherCreator_NotFound() { // Arrange NotificationOrder order = await PostgreUtil.PopulateDBWithEmailOrder(); - orderIdsToDelete.Add(order.Id); + _orderIdsToDelete.Add(order.Id); string uri = $"{_basePath}/{order.Id}/notifications/email"; @@ -92,7 +92,7 @@ public async Task Get_ValidOrderId_Ok() // Arrange (NotificationOrder order, EmailNotification notification) = await PostgreUtil.PopulateDBWithOrderAndEmailNotification(); string uri = $"{_basePath}/{order.Id}/notifications/email"; - orderIdsToDelete.Add(order.Id); + _orderIdsToDelete.Add(order.Id); HttpClient client = GetTestClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_SendEmailNotificationsTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_SendEmailNotificationsTests.cs index 6de36ff8..a672ed35 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_SendEmailNotificationsTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_SendEmailNotificationsTests.cs @@ -78,7 +78,7 @@ private HttpClient GetTestClient() { opts.Admin.TopicList = new List { _topicName }; }); - services.Configure(opts => + services.Configure(opts => { opts.EmailQueueTopicName = _topicName; }); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_SendSmsNotificationsTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_SendSmsNotificationsTests.cs index 916b5417..c575062a 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_SendSmsNotificationsTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/TriggerController/Trigger_SendSmsNotificationsTests.cs @@ -78,7 +78,7 @@ private HttpClient GetTestClient() { opts.Admin.TopicList = new List { _topicName }; }); - services.Configure(opts => + services.Configure(opts => { opts.SmsQueueTopicName = _topicName; }); diff --git a/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/EmailOrderProcessingServiceTests.cs b/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/EmailOrderProcessingServiceTests.cs index 591dda68..0a5364bf 100644 --- a/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/EmailOrderProcessingServiceTests.cs +++ b/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/EmailOrderProcessingServiceTests.cs @@ -156,9 +156,6 @@ private static EmailOrderProcessingService GetTestService( emailService = emailServiceMock.Object; } - var smsRepoMock = new Mock(); - var smsServiceMock = new Mock(); - return new EmailOrderProcessingService(emailRepo, emailService); } } From 0f9d773af694d7340e63e897341285e01c829ecd Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Tue, 6 Feb 2024 16:18:52 +0100 Subject: [PATCH 06/17] added more asserts to unit test --- .../SmsNotificationSummaryTests.cs | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs index 40f740fb..aaaead49 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs @@ -1,20 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Altinn.Notifications.Core.Enums; +using Altinn.Notifications.Core.Enums; using Altinn.Notifications.Core.Models.Notification; using Altinn.Notifications.Core.Models.Orders; -using Altinn.Notifications.Core.Persistence; using Altinn.Notifications.Core.Services; using Altinn.Notifications.Core.Services.Interfaces; using Altinn.Notifications.Core.Shared; using Altinn.Notifications.IntegrationTests.Utils; -using Altinn.Notifications.Persistence.Repository; - -using Microsoft.AspNetCore.Http.HttpResults; using Xunit; @@ -44,7 +34,7 @@ public async Task DisposeAsync() public async Task GetSmsSummary_SingleNeweNotification_ReturnsSummary() { // Arrange - (NotificationOrder order, _) = await PostgreUtil.PopulateDBWithOrderAndSmsNotification(); + (NotificationOrder order, SmsNotification notification) = await PostgreUtil.PopulateDBWithOrderAndSmsNotification(); _orderIdsToDelete.Add(order.Id); SmsNotificationSummaryService service = (SmsNotificationSummaryService)ServiceUtil @@ -59,8 +49,11 @@ public async Task GetSmsSummary_SingleNeweNotification_ReturnsSummary() actualSummary => { Assert.Single(actualSummary.Notifications); - var notification = actualSummary.Notifications[0]; - Assert.Equal(SmsNotificationResultType.New, notification.ResultStatus.Result); + var actualNotification = actualSummary.Notifications[0]; + Assert.Equal(SmsNotificationResultType.New, actualNotification.ResultStatus.Result); + Assert.NotEmpty(actualNotification.Recipient.MobileNumber); + Assert.Equal(notification.Id, actualNotification.Id); + Assert.Equivalent(notification.RecipientNumber, actualNotification.Recipient.MobileNumber); return true; }, error => From b1e102f07cdd902de9978b7ec482507a0f4efb7b Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Tue, 6 Feb 2024 16:21:26 +0100 Subject: [PATCH 07/17] attempt to avoid code duplication --- .../NotificationSummaryRepository.cs | 113 +++++++----------- 1 file changed, 46 insertions(+), 67 deletions(-) diff --git a/src/Altinn.Notifications.Persistence/Repository/NotificationSummaryRepository.cs b/src/Altinn.Notifications.Persistence/Repository/NotificationSummaryRepository.cs index 205d7f98..13f2320d 100644 --- a/src/Altinn.Notifications.Persistence/Repository/NotificationSummaryRepository.cs +++ b/src/Altinn.Notifications.Persistence/Repository/NotificationSummaryRepository.cs @@ -37,70 +37,70 @@ public NotificationSummaryRepository(NpgsqlDataSource dataSource, TelemetryClien /// public async Task GetEmailSummary(Guid orderId, string creator) { - bool matchFound = false; - - List notificationList = new(); - string sendersReference = string.Empty; - - await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getEmailNotificationsByOrderIdSql); - using TelemetryTracker tracker = new(_telemetryClient, pgcom); - pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, orderId); - pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, creator); + var (matchFound, sendersReference, notificationList) = await GetSummary( + orderId, + creator, + _getEmailNotificationsByOrderIdSql, + reader => new EmailNotificationWithResult( + reader.GetValue("alternateid"), + new EmailRecipient() + { + RecipientId = reader.GetValue("recipientid"), + ToAddress = reader.GetValue("toaddress") + }, + new NotificationResult( + reader.GetValue("result"), + reader.GetValue("resulttime")))); - await using (NpgsqlDataReader reader = await pgcom.ExecuteReaderAsync()) + if (!matchFound) { - while (await reader.ReadAsync()) - { - if (!matchFound) - { - matchFound = true; - sendersReference = reader.GetValue("sendersreference"); - } + return null; + } - Guid notificationId = reader.GetValue("alternateid"); + return new EmailNotificationSummary(orderId) + { + SendersReference = sendersReference, + Notifications = notificationList.Cast().ToList() + }; + } - if (notificationId != Guid.Empty) + /// + public async Task GetSmsSummary(Guid orderId, string creator) + { + var (matchFound, sendersReference, notificationList) = await GetSummary( + orderId, + creator, + _getSmsNotificationsByOrderIdSql, + reader => new SmsNotificationWithResult( + reader.GetValue("alternateid"), + new SmsRecipient() { - notificationList.Add( - new EmailNotificationWithResult( - reader.GetValue("alternateid"), - new EmailRecipient() - { - RecipientId = reader.GetValue("recipientid"), - ToAddress = reader.GetValue("toaddress") - }, - new NotificationResult( - reader.GetValue("result"), - reader.GetValue("resulttime")))); - } - } - } - - tracker.Track(); + RecipientId = reader.GetValue("recipientid"), + MobileNumber = reader.GetValue("mobilenumber") + }, + new NotificationResult( + reader.GetValue("result"), + reader.GetValue("resulttime")))); if (!matchFound) { return null; } - EmailNotificationSummary emailSummary = new(orderId) + return new SmsNotificationSummary(orderId) { SendersReference = sendersReference, - Notifications = notificationList + Notifications = notificationList.Cast().ToList() }; - - return emailSummary; } - /// - public async Task GetSmsSummary(Guid orderId, string creator) + private async Task<(bool MatchFound, string SendersReference, List NotificationList)> GetSummary(Guid orderId, string creator, string sqlCommand, Func createNotification) { bool matchFound = false; - - List notificationList = new(); + List notificationList = new(); string sendersReference = string.Empty; - await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getSmsNotificationsByOrderIdSql); + await using NpgsqlCommand pgcom = _dataSource.CreateCommand(sqlCommand); using TelemetryTracker tracker = new(_telemetryClient, pgcom); pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, orderId); pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, creator); @@ -119,34 +119,13 @@ public NotificationSummaryRepository(NpgsqlDataSource dataSource, TelemetryClien if (notificationId != Guid.Empty) { - notificationList.Add( - new SmsNotificationWithResult( - reader.GetValue("alternateid"), - new SmsRecipient() - { - RecipientId = reader.GetValue("recipientid"), - MobileNumber = reader.GetValue("mobilenumber") - }, - new NotificationResult( - reader.GetValue("result"), - reader.GetValue("resulttime")))); + notificationList.Add(createNotification(reader)); } } } tracker.Track(); - if (!matchFound) - { - return null; - } - - SmsNotificationSummary smsSummary = new(orderId) - { - SendersReference = sendersReference, - Notifications = notificationList - }; - - return smsSummary; + return (matchFound, sendersReference, notificationList); } } From 81d418bf3d12e6b3782c338c284747a8b2dea6cb Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Tue, 6 Feb 2024 16:35:58 +0100 Subject: [PATCH 08/17] added unit test --- .../SmsNotificationSummaryServiceTests.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/SmsNotificationSummaryServiceTests.cs b/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/SmsNotificationSummaryServiceTests.cs index f6ec614f..ed725d59 100644 --- a/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/SmsNotificationSummaryServiceTests.cs +++ b/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/SmsNotificationSummaryServiceTests.cs @@ -1,8 +1,13 @@ using System; +using System.Threading.Tasks; using Altinn.Notifications.Core.Enums; using Altinn.Notifications.Core.Models.Notification; +using Altinn.Notifications.Core.Persistence; using Altinn.Notifications.Core.Services; +using Altinn.Notifications.Core.Shared; + +using Moq; using Xunit; @@ -91,5 +96,29 @@ public void ProcessNotificationResults_1_generated_0_successful() Assert.Equal(1, summary.Generated); Assert.Equal(0, summary.Succeeded); } + + [Fact] + public async Task GetSmsSummary_NoMatchInDBForOrder() + { + // Arrange + Mock repoMock = new(); + repoMock.Setup(r => r.GetSmsSummary(It.IsAny(), It.IsAny())) + .ReturnsAsync((SmsNotificationSummary?)null); + + var service = new SmsNotificationSummaryService(repoMock.Object); + + // Act + var result = await service.GetSmsSummary(Guid.NewGuid(), "ttd"); + + // Assert + result.Match( + success => throw new Exception("No success value should be returned if db returns null"), + actuallError => + { + Assert.IsType(actuallError); + Assert.Equal(404, actuallError.ErrorCode); + return true; + }); + } } } From 75c600275441d7d521929fb05a650605b8163238 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Tue, 6 Feb 2024 16:47:45 +0100 Subject: [PATCH 09/17] added integration test --- .../SmsNotificationSummaryTests.cs | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs index aaaead49..6931e5bc 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs @@ -26,8 +26,11 @@ public async Task InitializeAsync() public async Task DisposeAsync() { - string deleteSql = $@"DELETE from notifications.orders o where o.alternateid in ('{string.Join("','", _orderIdsToDelete)}')"; - await PostgreUtil.RunSql(deleteSql); + if (_orderIdsToDelete.Count != 0) + { + string deleteSql = $@"DELETE from notifications.orders o where o.alternateid in ('{string.Join("','", _orderIdsToDelete)}')"; + await PostgreUtil.RunSql(deleteSql); + } } [Fact] @@ -61,5 +64,27 @@ public async Task GetSmsSummary_SingleNeweNotification_ReturnsSummary() throw new Exception("Expected a summary, but got an error"); }); } + + [Fact] + public async Task GetSmsSummary_NoOrderIdMatchInDb_ReturnsNull() + { + SmsNotificationSummaryService service = (SmsNotificationSummaryService)ServiceUtil + .GetServices(new List() { typeof(ISmsNotificationSummaryService) }) + .First(i => i.GetType() == typeof(SmsNotificationSummaryService)); + + // Act + Result result = await service.GetSmsSummary(Guid.NewGuid(), "ttd"); + + // Assert + result.Match( + success => throw new Exception("No success value should be returned if db returns null"), + actuallError => + { + Assert.IsType(actuallError); + Assert.Equal(404, actuallError.ErrorCode); + return true; + }); + } + } } From 6c0834941bd27ccbf90358c95b838f1e211678e2 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Wed, 7 Feb 2024 11:26:39 +0100 Subject: [PATCH 10/17] implemented api endpoint and tests --- .../Controllers/SmsNotificationsController.cs | 61 +++++ ...r.cs => EmailNotificationSummaryMapper.cs} | 2 +- .../Mappers/SmsNotificationSummaryMapper.cs | 57 +++++ .../Models/SmsNotificationSummaryExt.cs | 43 ++++ .../Models/SmsNotificationWithResultExt.cs | 39 ++++ .../SmsNotificationSummaryTests.cs | 1 - .../EmailNotificationsController/GetTests.cs | 20 +- .../SmsNotificationsController/GetTests.cs | 133 +++++++++++ .../SmsNotificationsControllerTests.cs | 215 ++++++++++++++++++ 9 files changed, 559 insertions(+), 12 deletions(-) create mode 100644 src/Altinn.Notifications/Controllers/SmsNotificationsController.cs rename src/Altinn.Notifications/Mappers/{NotificationSummaryMapper.cs => EmailNotificationSummaryMapper.cs} (97%) create mode 100644 src/Altinn.Notifications/Mappers/SmsNotificationSummaryMapper.cs create mode 100644 src/Altinn.Notifications/Models/SmsNotificationSummaryExt.cs create mode 100644 src/Altinn.Notifications/Models/SmsNotificationWithResultExt.cs create mode 100644 test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/GetTests.cs create mode 100644 test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/SmsNotificationsControllerTests.cs diff --git a/src/Altinn.Notifications/Controllers/SmsNotificationsController.cs b/src/Altinn.Notifications/Controllers/SmsNotificationsController.cs new file mode 100644 index 00000000..b33866e7 --- /dev/null +++ b/src/Altinn.Notifications/Controllers/SmsNotificationsController.cs @@ -0,0 +1,61 @@ +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; + +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +using Swashbuckle.AspNetCore.Annotations; + +namespace Altinn.Notifications.Controllers +{ + /// + /// Controller for all operations related to sms notifications + /// + [Authorize(Policy = AuthorizationConstants.POLICY_CREATE_SCOPE_OR_PLATFORM_ACCESS)] + [Route("notifications/api/v1/orders/{id}/notifications/sms")] + [ApiController] + [SwaggerResponse(401, "Caller is unauthorized")] + [SwaggerResponse(403, "Caller is not authorized to access the requested resource")] + public class SmsNotificationsController : ControllerBase + { + private readonly ISmsNotificationSummaryService _summaryService; + + /// + /// Initializes a new instance of the class. + /// + /// The notifications summary service + public SmsNotificationsController(ISmsNotificationSummaryService summaryService) + { + _summaryService = summaryService; + } + + /// + /// Endpoint for retrieving a summary of all sms notifications related to an order + /// + /// The order id + /// Sumarized order details and a list containing all sms notifications and their send status + [HttpGet] + [Produces("application/json")] + [SwaggerResponse(200, "The notification order was accepted", typeof(SmsNotificationSummaryExt))] + [SwaggerResponse(404, "No notification order mathching the id was found")] + public async Task> Get([FromRoute] Guid id) + { + string? expectedCreator = HttpContext.GetOrg(); + + if (expectedCreator == null) + { + return Forbid(); + } + + Result result = await _summaryService.GetSmsSummary(id, expectedCreator); + + return result.Match( + summary => Ok(summary.MapToSmsNotificationSummaryExt()), + error => StatusCode(error.ErrorCode, error.ErrorMessage)); + } + } +} diff --git a/src/Altinn.Notifications/Mappers/NotificationSummaryMapper.cs b/src/Altinn.Notifications/Mappers/EmailNotificationSummaryMapper.cs similarity index 97% rename from src/Altinn.Notifications/Mappers/NotificationSummaryMapper.cs rename to src/Altinn.Notifications/Mappers/EmailNotificationSummaryMapper.cs index 4f6a11a7..c93f2f2a 100644 --- a/src/Altinn.Notifications/Mappers/NotificationSummaryMapper.cs +++ b/src/Altinn.Notifications/Mappers/EmailNotificationSummaryMapper.cs @@ -5,7 +5,7 @@ namespace Altinn.Notifications.Mappers /// /// Mapper for /// - public static class NotificationSummaryMapper + public static class EmailNotificationSummaryMapper { /// /// Maps a to a diff --git a/src/Altinn.Notifications/Mappers/SmsNotificationSummaryMapper.cs b/src/Altinn.Notifications/Mappers/SmsNotificationSummaryMapper.cs new file mode 100644 index 00000000..9104fb05 --- /dev/null +++ b/src/Altinn.Notifications/Mappers/SmsNotificationSummaryMapper.cs @@ -0,0 +1,57 @@ +using Altinn.Notifications.Core.Models.Notification; + +namespace Altinn.Notifications.Mappers +{ + /// + /// Mapper for + /// + public static class SmsNotificationSummaryMapper + { + /// + /// Maps a to a + /// + public static SmsNotificationSummaryExt MapToSmsNotificationSummaryExt(this SmsNotificationSummary summary) + { + return new SmsNotificationSummaryExt() + { + OrderId = summary.OrderId, + SendersReference = summary.SendersReference, + Generated = summary.Generated, + Succeeded = summary.Succeeded, + Notifications = summary.Notifications.MapToSmsNotificationWithResultExt(), + }; + } + + /// + /// Maps a list of to a list of + /// + public static List MapToSmsNotificationWithResultExt(this List notifications) + { + List result = notifications.Select(n => n.MapToSmsNotificationWithResultExt()).ToList(); + + return result; + } + + /// + /// Maps a to a + /// + public static SmsNotificationWithResultExt MapToSmsNotificationWithResultExt(this SmsNotificationWithResult notification) + { + return new SmsNotificationWithResultExt() + { + Id = notification.Id, + Succeeded = notification.Succeeded, + Recipient = new() + { + MobileNumber = notification.Recipient.MobileNumber + }, + SendStatus = new() + { + Status = notification.ResultStatus.Result.ToString(), + StatusDescription = notification.ResultStatus.ResultDescription, + LastUpdate = notification.ResultStatus.ResultTime + } + }; + } + } +} diff --git a/src/Altinn.Notifications/Models/SmsNotificationSummaryExt.cs b/src/Altinn.Notifications/Models/SmsNotificationSummaryExt.cs new file mode 100644 index 00000000..abfe0f20 --- /dev/null +++ b/src/Altinn.Notifications/Models/SmsNotificationSummaryExt.cs @@ -0,0 +1,43 @@ +using System.Text.Json.Serialization; + +namespace Altinn.Notifications.Core.Models.Notification +{ + /// + /// A class representing an sms notification summary + /// + /// + /// External representaion to be used in the API. + /// + public class SmsNotificationSummaryExt + { + /// + /// The order id + /// + [JsonPropertyName("orderId")] + public Guid OrderId { get; set; } + + /// + /// The senders reference + /// + [JsonPropertyName("sendersReference")] + public string? SendersReference { get; set; } + + /// + /// The number of generated email notifications + /// + [JsonPropertyName("generated")] + public int Generated { get; set; } + + /// + /// The number of email notifications that were sent successfully + /// + [JsonPropertyName("succeeded")] + public int Succeeded { get; set; } + + /// + /// A list of notifications with send result + /// + [JsonPropertyName("notifications")] + public List Notifications { get; set; } = []; + } +} diff --git a/src/Altinn.Notifications/Models/SmsNotificationWithResultExt.cs b/src/Altinn.Notifications/Models/SmsNotificationWithResultExt.cs new file mode 100644 index 00000000..822fc037 --- /dev/null +++ b/src/Altinn.Notifications/Models/SmsNotificationWithResultExt.cs @@ -0,0 +1,39 @@ +using System.Text.Json.Serialization; + +using Altinn.Notifications.Models; + +namespace Altinn.Notifications.Core.Models.Notification +{ + /// + /// A class representing an sms notification with result + /// + /// + /// External representaion to be used in the API. + /// + public class SmsNotificationWithResultExt + { + /// + /// The notification id + /// + [JsonPropertyName("id")] + public Guid Id { get; set; } + + /// + /// Boolean indicating if the sending of the notification was successful + /// + [JsonPropertyName("succeeded")] + public bool Succeeded { get; set; } + + /// + /// The recipient of the notification + /// + [JsonPropertyName("recipient")] + public RecipientExt Recipient { get; set; } = new(); + + /// + /// The result status of the notification + /// + [JsonPropertyName("sendStatus")] + public StatusExt SendStatus { get; set; } = new(); + } +} diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs index 6931e5bc..b697d477 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications.Core/SmsNotificationSummaryTests.cs @@ -85,6 +85,5 @@ public async Task GetSmsSummary_NoOrderIdMatchInDb_ReturnsNull() return true; }); } - } } diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/GetTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/GetTests.cs index 73c083c6..1b842d82 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/GetTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/GetTests.cs @@ -18,15 +18,15 @@ using Xunit; -namespace Altinn.Notifications.IntegrationTests.Notifications.EmailNotificationsController +namespace Altinn.Notifications.IntegrationTests.Notifications.SmsNotificationsController { - public class GetTests : IClassFixture>, IAsyncLifetime + public class GetTests : IClassFixture>, IAsyncLifetime { private readonly string _basePath; - private readonly IntegrationTestWebApplicationFactory _factory; + private readonly IntegrationTestWebApplicationFactory _factory; private readonly List _orderIdsToDelete; - public GetTests(IntegrationTestWebApplicationFactory factory) + public GetTests(IntegrationTestWebApplicationFactory factory) { _basePath = $"/notifications/api/v1/orders"; _factory = factory; @@ -51,7 +51,7 @@ async Task IAsyncLifetime.DisposeAsync() public async Task Get_NonExistingOrder_NotFound() { // Arrange - string uri = $"{_basePath}/{Guid.NewGuid()}/notifications/email"; + string uri = $"{_basePath}/{Guid.NewGuid()}/notifications/sms"; HttpClient client = GetTestClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); @@ -69,10 +69,10 @@ public async Task Get_NonExistingOrder_NotFound() public async Task Get_OrderIdForAnotherCreator_NotFound() { // Arrange - NotificationOrder order = await PostgreUtil.PopulateDBWithEmailOrder(); + NotificationOrder order = await PostgreUtil.PopulateDBWithSmsOrder(); _orderIdsToDelete.Add(order.Id); - string uri = $"{_basePath}/{order.Id}/notifications/email"; + string uri = $"{_basePath}/{order.Id}/notifications/sms"; HttpClient client = GetTestClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("nav", scope: "altinn:serviceowner/notifications.create")); @@ -90,8 +90,8 @@ public async Task Get_OrderIdForAnotherCreator_NotFound() public async Task Get_ValidOrderId_Ok() { // Arrange - (NotificationOrder order, EmailNotification notification) = await PostgreUtil.PopulateDBWithOrderAndEmailNotification(); - string uri = $"{_basePath}/{order.Id}/notifications/email"; + (NotificationOrder order, SmsNotification notification) = await PostgreUtil.PopulateDBWithOrderAndSmsNotification(); + string uri = $"{_basePath}/{order.Id}/notifications/sms"; _orderIdsToDelete.Add(order.Id); HttpClient client = GetTestClient(); @@ -105,7 +105,7 @@ public async Task Get_ValidOrderId_Ok() // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - EmailNotificationSummaryExt? summary = JsonSerializer.Deserialize(responseString); + SmsNotificationSummaryExt? summary = JsonSerializer.Deserialize(responseString); Assert.True(summary?.Notifications.Count > 0); Assert.Equal(order.Id, summary?.OrderId); Assert.Equal(notification.Id, summary?.Notifications[0].Id); diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/GetTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/GetTests.cs new file mode 100644 index 00000000..73c083c6 --- /dev/null +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/GetTests.cs @@ -0,0 +1,133 @@ +using System.Net; +using System.Net.Http.Headers; +using System.Text.Json; + +using Altinn.Common.AccessToken.Services; +using Altinn.Notifications.Core.Models.Notification; +using Altinn.Notifications.Core.Models.Orders; +using Altinn.Notifications.IntegrationTests.Utils; +using Altinn.Notifications.Tests.Notifications.Mocks.Authentication; +using Altinn.Notifications.Tests.Notifications.Utils; + +using AltinnCore.Authentication.JwtCookie; + +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Logging; + +using Xunit; + +namespace Altinn.Notifications.IntegrationTests.Notifications.EmailNotificationsController +{ + public class GetTests : IClassFixture>, IAsyncLifetime + { + private readonly string _basePath; + private readonly IntegrationTestWebApplicationFactory _factory; + private readonly List _orderIdsToDelete; + + public GetTests(IntegrationTestWebApplicationFactory factory) + { + _basePath = $"/notifications/api/v1/orders"; + _factory = factory; + _orderIdsToDelete = new List(); + } + + public async Task InitializeAsync() + { + await Task.CompletedTask; + } + + async Task IAsyncLifetime.DisposeAsync() + { + if (_orderIdsToDelete.Count != 0) + { + string deleteSql = $@"DELETE from notifications.orders o where o.alternateid in ('{string.Join("','", _orderIdsToDelete)}')"; + await PostgreUtil.RunSql(deleteSql); + } + } + + [Fact] + public async Task Get_NonExistingOrder_NotFound() + { + // Arrange + string uri = $"{_basePath}/{Guid.NewGuid()}/notifications/email"; + + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); + + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, uri); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task Get_OrderIdForAnotherCreator_NotFound() + { + // Arrange + NotificationOrder order = await PostgreUtil.PopulateDBWithEmailOrder(); + _orderIdsToDelete.Add(order.Id); + + string uri = $"{_basePath}/{order.Id}/notifications/email"; + + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("nav", scope: "altinn:serviceowner/notifications.create")); + + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, uri); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task Get_ValidOrderId_Ok() + { + // Arrange + (NotificationOrder order, EmailNotification notification) = await PostgreUtil.PopulateDBWithOrderAndEmailNotification(); + string uri = $"{_basePath}/{order.Id}/notifications/email"; + _orderIdsToDelete.Add(order.Id); + + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); + + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, uri); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + string responseString = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + EmailNotificationSummaryExt? summary = JsonSerializer.Deserialize(responseString); + Assert.True(summary?.Notifications.Count > 0); + Assert.Equal(order.Id, summary?.OrderId); + Assert.Equal(notification.Id, summary?.Notifications[0].Id); + Assert.Equal(1, summary?.Generated); + Assert.Equal(0, summary?.Succeeded); + } + + private HttpClient GetTestClient() + { + HttpClient client = _factory.WithWebHostBuilder(builder => + { + IdentityModelEventSource.ShowPII = true; + + builder.ConfigureTestServices(services => + { + // Set up mock authentication and authorization + services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); + services.AddSingleton(); + }); + }).CreateClient(); + + return client; + } + } +} diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/SmsNotificationsControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/SmsNotificationsControllerTests.cs new file mode 100644 index 00000000..453974e2 --- /dev/null +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/SmsNotificationsControllerTests.cs @@ -0,0 +1,215 @@ +using System.Net; +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; + +using Altinn.Common.AccessToken.Services; +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; + +using AltinnCore.Authentication.JwtCookie; + +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Logging; + +using Moq; + +using Xunit; + +namespace Altinn.Notifications.IntegrationTests.Notifications.SmsNotificationsController; + +public class SmsNotificationsControllerTests : IClassFixture> +{ + private readonly string _basePath; + private readonly string _invalidGuidBase; + private readonly IntegrationTestWebApplicationFactory _factory; + + private readonly JsonSerializerOptions _options; + + public SmsNotificationsControllerTests(IntegrationTestWebApplicationFactory factory) + { + _basePath = $"/notifications/api/v1/orders/{Guid.NewGuid()}/notifications/sms"; + _invalidGuidBase = "/notifications/api/v1/orders/1337;1=1/notifications/sms"; + _factory = factory; + _options = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; + } + + [Fact] + public async Task Get_MissingBearerToken_Unauthorized() + { + // Arrange + HttpClient client = GetTestClient(); + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, _basePath); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task Get_InvalidScopeInToken_Forbidden() + { + // Arrange + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:dummmy.scope")); + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, _basePath); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + [Fact] + public async Task Get_InvalidGuid_BadRequest() + { + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); + + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, _invalidGuidBase) + { + Content = new StringContent(string.Empty, Encoding.UTF8, "application/json") + }; + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + string content = await response.Content.ReadAsStringAsync(); + ProblemDetails? actual = JsonSerializer.Deserialize(content, _options); + + // Assert + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + Assert.Equal("One or more validation errors occurred.", actual?.Title); + } + + [Fact] + public async Task Get_ServiceReturnsError_ServerError() + { + // Arrange + Mock serviceMock = new(); + serviceMock.Setup(s => s.GetSmsSummary(It.IsAny(), It.IsAny())) + .ReturnsAsync(new ServiceError(500)); + + HttpClient client = GetTestClient(summaryService: serviceMock.Object); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); + + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, _basePath); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + serviceMock.VerifyAll(); + } + + [Fact] + public async Task Get_ValidScope_ServiceReturnsNotifications_Ok() + { + // Arrange + Guid id = Guid.NewGuid(); + SmsNotificationSummary output = new(id) + { + SendersReference = "senders-ref", + Generated = 1, + Succeeded = 1, + Notifications = new List() + }; + + Mock serviceMock = new(); + serviceMock.Setup(s => s.GetSmsSummary(It.IsAny(), It.Is(s => s.Equals("ttd")))) + .ReturnsAsync(output); + + HttpClient client = GetTestClient(summaryService: serviceMock.Object); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); + + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, _basePath); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + string respoonseString = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + SmsNotificationSummaryExt? summaryExt = JsonSerializer.Deserialize(respoonseString); + Assert.NotNull(summaryExt); + Assert.Equal(id, summaryExt.OrderId); + + serviceMock.VerifyAll(); + } + + [Fact] + public async Task Get_ValidAccessToken_ServiceReturnsOrder_Accepted() + { + // Arrange + Guid id = Guid.NewGuid(); + SmsNotificationSummary output = new(id) + { + SendersReference = "senders-ref", + Generated = 1, + Succeeded = 1, + Notifications = new List() + }; + + Mock serviceMock = new(); + serviceMock.Setup(s => s.GetSmsSummary(It.IsAny(), It.Is(s => s.Equals("ttd")))) + .ReturnsAsync(output); + + HttpClient client = GetTestClient(summaryService: serviceMock.Object); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); + + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, _basePath); + httpRequestMessage.Headers.Add("PlatformAccessToken", PrincipalUtil.GetAccessToken("ttd", "apps-test")); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + string respoonseString = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + SmsNotificationSummaryExt? summaryExt = JsonSerializer.Deserialize(respoonseString); + Assert.NotNull(summaryExt); + Assert.Equal(id, summaryExt.OrderId); + + serviceMock.VerifyAll(); + } + + private HttpClient GetTestClient(ISmsNotificationSummaryService? summaryService = null) + { + if (summaryService == null) + { + var summaryServiceMock = new Mock(); + summaryService = summaryServiceMock.Object; + } + + HttpClient client = _factory.WithWebHostBuilder(builder => + { + IdentityModelEventSource.ShowPII = true; + + builder.ConfigureTestServices(services => + { + services.AddSingleton(summaryService); + + // Set up mock authentication and authorization + services.AddSingleton, JwtCookiePostConfigureOptionsStub>(); + services.AddSingleton(); + }); + }).CreateClient(); + + return client; + } +} From 546d4288ca3939ef8b96a8a83755d882b05189e3 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Wed, 7 Feb 2024 12:18:39 +0100 Subject: [PATCH 11/17] added unit test --- ...=> EmailNotificationSummaryMapperTests.cs} | 2 +- .../SmsNotificationSummaryMapperTests.cs | 114 ++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) rename test/Altinn.Notifications.Tests/Notifications/TestingMappers/{NotificationSummaryMapperTests.cs => EmailNotificationSummaryMapperTests.cs} (98%) create mode 100644 test/Altinn.Notifications.Tests/Notifications/TestingMappers/SmsNotificationSummaryMapperTests.cs diff --git a/test/Altinn.Notifications.Tests/Notifications/TestingMappers/NotificationSummaryMapperTests.cs b/test/Altinn.Notifications.Tests/Notifications/TestingMappers/EmailNotificationSummaryMapperTests.cs similarity index 98% rename from test/Altinn.Notifications.Tests/Notifications/TestingMappers/NotificationSummaryMapperTests.cs rename to test/Altinn.Notifications.Tests/Notifications/TestingMappers/EmailNotificationSummaryMapperTests.cs index c58f5332..6ef64a39 100644 --- a/test/Altinn.Notifications.Tests/Notifications/TestingMappers/NotificationSummaryMapperTests.cs +++ b/test/Altinn.Notifications.Tests/Notifications/TestingMappers/EmailNotificationSummaryMapperTests.cs @@ -10,7 +10,7 @@ namespace Altinn.Notifications.Tests.Notifications.TestingMappers { - public class NotificationSummaryMapperTests + public class EmailNotificationSummaryMapperTests { [Fact] public void MapToEmailNotificationWithResultExt_EmptyList_AreEquivalent() diff --git a/test/Altinn.Notifications.Tests/Notifications/TestingMappers/SmsNotificationSummaryMapperTests.cs b/test/Altinn.Notifications.Tests/Notifications/TestingMappers/SmsNotificationSummaryMapperTests.cs new file mode 100644 index 00000000..0d248d16 --- /dev/null +++ b/test/Altinn.Notifications.Tests/Notifications/TestingMappers/SmsNotificationSummaryMapperTests.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; + +using Altinn.Notifications.Core.Enums; +using Altinn.Notifications.Core.Models.Notification; +using Altinn.Notifications.Core.Models.Recipients; +using Altinn.Notifications.Mappers; + +using Xunit; + +namespace Altinn.Notifications.Tests.Notifications.TestingMappers +{ + public class SmsNotificationSummaryMapperTests + { + [Fact] + public void MapToSmsNotificationWithResultExt_EmptyList_AreEquivalent() + { + // Arrange + List input = new(); + + // Act + var actual = input.MapToSmsNotificationWithResultExt(); + + // Assert + Assert.Empty(actual); + } + + [Fact] + public void MapToSmsNotificationWithResultExt_NotificationWithFailedResult_AreEquivalent() + { + // Arrange + Guid id = Guid.NewGuid(); + DateTime timestamp = DateTime.UtcNow; + SmsNotificationWithResultExt expected = new() + { + Id = id, + Succeeded = false, + Recipient = new() + { + MobileNumber = "+4799999999" + }, + SendStatus = new() + { + LastUpdate = timestamp, + Status = "Failed_RecipientNotIdentified", + StatusDescription = "Failed to send. Could not identify recipient." + } + }; + + SmsNotificationWithResult input = new( + id, + false, + new SmsRecipient() + { + RecipientId = "12345678910", + MobileNumber = "+4799999999" + }, + new NotificationResult( + SmsNotificationResultType.Failed_RecipientNotIdentified, + timestamp)); + + input.ResultStatus.SetResultDescription("Failed to send. Could not identify recipient."); + + // Act + var actual = input.MapToSmsNotificationWithResultExt(); + + // Assert + Assert.Equivalent(expected, actual, false); + } + + [Fact] + public void MapToSmsNotificationWithResultExt_NotificationWithSuccessResult_AreEquivalent() + { + // Arrange + Guid id = Guid.NewGuid(); + DateTime timestamp = DateTime.UtcNow; + SmsNotificationWithResultExt expected = new() + { + Id = id, + Succeeded = true, + Recipient = new() + { + MobileNumber = "+4799999999" + }, + SendStatus = new() + { + LastUpdate = timestamp, + Status = "Accepted", + StatusDescription = "This is the description" + } + }; + + SmsNotificationWithResult input = new( + id, + true, + new SmsRecipient() + { + RecipientId = "12345678910", + MobileNumber = "+4799999999" + }, + new NotificationResult( + SmsNotificationResultType.Accepted, + timestamp)); + + input.ResultStatus.SetResultDescription("This is the description"); + + // Act + var actual = input.MapToSmsNotificationWithResultExt(); + + // Assert + Assert.Equivalent(expected, actual, false); + } + } +} From 28a220725736fe2160bcfc889714df8c7e259950 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Thu, 8 Feb 2024 11:05:06 +0100 Subject: [PATCH 12/17] fixed bug --- .../TestingServices/SmsNotificationSummaryServiceTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/SmsNotificationSummaryServiceTests.cs b/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/SmsNotificationSummaryServiceTests.cs index 953572b8..62337c0d 100644 --- a/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/SmsNotificationSummaryServiceTests.cs +++ b/test/Altinn.Notifications.Tests/Notifications.Core/TestingServices/SmsNotificationSummaryServiceTests.cs @@ -108,7 +108,7 @@ public async Task GetSmsSummary_NoMatchInDBForOrder() var service = new SmsNotificationSummaryService(repoMock.Object); // Act - var result = await service.GetSmsSummary(Guid.NewGuid(), "ttd"); + var result = await service.GetSummary(Guid.NewGuid(), "ttd"); // Assert result.Match( From afd44fcddb042b839245a9e8693f91dedb56dabe Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Thu, 8 Feb 2024 11:06:10 +0100 Subject: [PATCH 13/17] got test from main --- .../EmailNotificationsController/GetTests.cs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/GetTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/GetTests.cs index b1e393ba..73c083c6 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/GetTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/EmailNotificationsController/GetTests.cs @@ -18,17 +18,15 @@ using Xunit; -namespace Altinn.Notifications.IntegrationTests.Notifications.SmsNotificationsController +namespace Altinn.Notifications.IntegrationTests.Notifications.EmailNotificationsController { - public class GetTests : IClassFixture>, IAsyncLifetime + public class GetTests : IClassFixture>, IAsyncLifetime { private readonly string _basePath; private readonly IntegrationTestWebApplicationFactory _factory; private readonly List _orderIdsToDelete; - private readonly IntegrationTestWebApplicationFactory _factory; - private readonly List _orderIdsToDelete; - public GetTests(IntegrationTestWebApplicationFactory factory) + public GetTests(IntegrationTestWebApplicationFactory factory) { _basePath = $"/notifications/api/v1/orders"; _factory = factory; @@ -53,7 +51,7 @@ async Task IAsyncLifetime.DisposeAsync() public async Task Get_NonExistingOrder_NotFound() { // Arrange - string uri = $"{_basePath}/{Guid.NewGuid()}/notifications/sms"; + string uri = $"{_basePath}/{Guid.NewGuid()}/notifications/email"; HttpClient client = GetTestClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); @@ -71,10 +69,10 @@ public async Task Get_NonExistingOrder_NotFound() public async Task Get_OrderIdForAnotherCreator_NotFound() { // Arrange - NotificationOrder order = await PostgreUtil.PopulateDBWithSmsOrder(); + NotificationOrder order = await PostgreUtil.PopulateDBWithEmailOrder(); _orderIdsToDelete.Add(order.Id); - string uri = $"{_basePath}/{order.Id}/notifications/sms"; + string uri = $"{_basePath}/{order.Id}/notifications/email"; HttpClient client = GetTestClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("nav", scope: "altinn:serviceowner/notifications.create")); @@ -92,8 +90,8 @@ public async Task Get_OrderIdForAnotherCreator_NotFound() public async Task Get_ValidOrderId_Ok() { // Arrange - (NotificationOrder order, SmsNotification notification) = await PostgreUtil.PopulateDBWithOrderAndSmsNotification(); - string uri = $"{_basePath}/{order.Id}/notifications/sms"; + (NotificationOrder order, EmailNotification notification) = await PostgreUtil.PopulateDBWithOrderAndEmailNotification(); + string uri = $"{_basePath}/{order.Id}/notifications/email"; _orderIdsToDelete.Add(order.Id); HttpClient client = GetTestClient(); @@ -107,7 +105,7 @@ public async Task Get_ValidOrderId_Ok() // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - SmsNotificationSummaryExt? summary = JsonSerializer.Deserialize(responseString); + EmailNotificationSummaryExt? summary = JsonSerializer.Deserialize(responseString); Assert.True(summary?.Notifications.Count > 0); Assert.Equal(order.Id, summary?.OrderId); Assert.Equal(notification.Id, summary?.Notifications[0].Id); From d4565bc49125f4a4c582f2bd1ee489f2165c9f8d Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Thu, 8 Feb 2024 11:07:35 +0100 Subject: [PATCH 14/17] reinstated file removed by merge --- .../SmsNotificationsController/GetTests.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/GetTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/GetTests.cs index 73c083c6..1b842d82 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/GetTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/GetTests.cs @@ -18,15 +18,15 @@ using Xunit; -namespace Altinn.Notifications.IntegrationTests.Notifications.EmailNotificationsController +namespace Altinn.Notifications.IntegrationTests.Notifications.SmsNotificationsController { - public class GetTests : IClassFixture>, IAsyncLifetime + public class GetTests : IClassFixture>, IAsyncLifetime { private readonly string _basePath; - private readonly IntegrationTestWebApplicationFactory _factory; + private readonly IntegrationTestWebApplicationFactory _factory; private readonly List _orderIdsToDelete; - public GetTests(IntegrationTestWebApplicationFactory factory) + public GetTests(IntegrationTestWebApplicationFactory factory) { _basePath = $"/notifications/api/v1/orders"; _factory = factory; @@ -51,7 +51,7 @@ async Task IAsyncLifetime.DisposeAsync() public async Task Get_NonExistingOrder_NotFound() { // Arrange - string uri = $"{_basePath}/{Guid.NewGuid()}/notifications/email"; + string uri = $"{_basePath}/{Guid.NewGuid()}/notifications/sms"; HttpClient client = GetTestClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); @@ -69,10 +69,10 @@ public async Task Get_NonExistingOrder_NotFound() public async Task Get_OrderIdForAnotherCreator_NotFound() { // Arrange - NotificationOrder order = await PostgreUtil.PopulateDBWithEmailOrder(); + NotificationOrder order = await PostgreUtil.PopulateDBWithSmsOrder(); _orderIdsToDelete.Add(order.Id); - string uri = $"{_basePath}/{order.Id}/notifications/email"; + string uri = $"{_basePath}/{order.Id}/notifications/sms"; HttpClient client = GetTestClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("nav", scope: "altinn:serviceowner/notifications.create")); @@ -90,8 +90,8 @@ public async Task Get_OrderIdForAnotherCreator_NotFound() public async Task Get_ValidOrderId_Ok() { // Arrange - (NotificationOrder order, EmailNotification notification) = await PostgreUtil.PopulateDBWithOrderAndEmailNotification(); - string uri = $"{_basePath}/{order.Id}/notifications/email"; + (NotificationOrder order, SmsNotification notification) = await PostgreUtil.PopulateDBWithOrderAndSmsNotification(); + string uri = $"{_basePath}/{order.Id}/notifications/sms"; _orderIdsToDelete.Add(order.Id); HttpClient client = GetTestClient(); @@ -105,7 +105,7 @@ public async Task Get_ValidOrderId_Ok() // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - EmailNotificationSummaryExt? summary = JsonSerializer.Deserialize(responseString); + SmsNotificationSummaryExt? summary = JsonSerializer.Deserialize(responseString); Assert.True(summary?.Notifications.Count > 0); Assert.Equal(order.Id, summary?.OrderId); Assert.Equal(notification.Id, summary?.Notifications[0].Id); From 8da06d3b94e6182a02ceabbb5467b8221d9114b1 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Thu, 8 Feb 2024 11:08:37 +0100 Subject: [PATCH 15/17] fixing merge conflict --- .../SmsNotificationsControllerTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/SmsNotificationsControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/SmsNotificationsControllerTests.cs index 453974e2..2754dd3c 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/SmsNotificationsControllerTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/SmsNotificationsControllerTests.cs @@ -99,7 +99,7 @@ public async Task Get_ServiceReturnsError_ServerError() { // Arrange Mock serviceMock = new(); - serviceMock.Setup(s => s.GetSmsSummary(It.IsAny(), It.IsAny())) + serviceMock.Setup(s => s.GetSummary(It.IsAny(), It.IsAny())) .ReturnsAsync(new ServiceError(500)); HttpClient client = GetTestClient(summaryService: serviceMock.Object); @@ -129,7 +129,7 @@ public async Task Get_ValidScope_ServiceReturnsNotifications_Ok() }; Mock serviceMock = new(); - serviceMock.Setup(s => s.GetSmsSummary(It.IsAny(), It.Is(s => s.Equals("ttd")))) + serviceMock.Setup(s => s.GetSummary(It.IsAny(), It.Is(s => s.Equals("ttd")))) .ReturnsAsync(output); HttpClient client = GetTestClient(summaryService: serviceMock.Object); @@ -165,7 +165,7 @@ public async Task Get_ValidAccessToken_ServiceReturnsOrder_Accepted() }; Mock serviceMock = new(); - serviceMock.Setup(s => s.GetSmsSummary(It.IsAny(), It.Is(s => s.Equals("ttd")))) + serviceMock.Setup(s => s.GetSummary(It.IsAny(), It.Is(s => s.Equals("ttd")))) .ReturnsAsync(output); HttpClient client = GetTestClient(summaryService: serviceMock.Object); From 9545005e20b91c786ccc1bcea0acd34345f0cb91 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Thu, 8 Feb 2024 11:38:25 +0100 Subject: [PATCH 16/17] added test --- .../SmsNotificationsControllerTests.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/SmsNotificationsControllerTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/SmsNotificationsControllerTests.cs index 2754dd3c..c9c76482 100644 --- a/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/SmsNotificationsControllerTests.cs +++ b/test/Altinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/SmsNotificationsControllerTests.cs @@ -72,6 +72,21 @@ public async Task Get_InvalidScopeInToken_Forbidden() Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } + [Fact] + public async Task Get_EndUserTokenWithCalidScope_Forbidden() + { + // Arrange + HttpClient client = GetTestClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetUserToken(12345, scope: "altinn:serviceowner/notifications.create")); + HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, _basePath); + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + [Fact] public async Task Get_InvalidGuid_BadRequest() { From 016823d8006006b8c2d244220815468e82142249 Mon Sep 17 00:00:00 2001 From: acn-sbuad Date: Fri, 9 Feb 2024 12:33:40 +0100 Subject: [PATCH 17/17] fixed some comments --- src/Altinn.Notifications/Models/SmsNotificationSummaryExt.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Altinn.Notifications/Models/SmsNotificationSummaryExt.cs b/src/Altinn.Notifications/Models/SmsNotificationSummaryExt.cs index abfe0f20..c7383dfe 100644 --- a/src/Altinn.Notifications/Models/SmsNotificationSummaryExt.cs +++ b/src/Altinn.Notifications/Models/SmsNotificationSummaryExt.cs @@ -23,13 +23,13 @@ public class SmsNotificationSummaryExt public string? SendersReference { get; set; } /// - /// The number of generated email notifications + /// The number of generated sms notifications /// [JsonPropertyName("generated")] public int Generated { get; set; } /// - /// The number of email notifications that were sent successfully + /// The number of sms notifications that were sent successfully /// [JsonPropertyName("succeeded")] public int Succeeded { get; set; }