-
Notifications
You must be signed in to change notification settings - Fork 3
Introduce Azure Event Grid Notification Service #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
6319ae4
22a5e3c
fc3a5fd
4554ef8
5778769
c2c50c0
0d10321
8967200
121c67b
4c9cc1a
108857f
6c7d9ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- created eventgrid wrapper - added additional payload tests
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| using Azure; | ||
| using Azure.Messaging.EventGrid; | ||
| using System; | ||
| using System.Diagnostics.CodeAnalysis; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace sama.Services | ||
| { | ||
| /// <summary> | ||
| /// This is a wrapper around EventGridPublisherClient functionality that cannot be (easily) tested. | ||
| /// </summary> | ||
| [ExcludeFromCodeCoverage] | ||
| public class EventGridPublisherClientWrapper | ||
| { | ||
| public virtual async Task SendEventAsync(Uri topicEndpoint, string accessKey, EventGridEvent eventGridEvent) | ||
| { | ||
| if (topicEndpoint == null) | ||
| { | ||
| throw new ArgumentNullException(nameof(topicEndpoint), "Topic endpoint is not configured"); | ||
| } | ||
|
|
||
| if (string.IsNullOrWhiteSpace(accessKey)) | ||
| { | ||
| throw new ArgumentNullException(nameof(accessKey), "Access key is not configured"); | ||
| } | ||
|
|
||
| var credential = new AzureKeyCredential(accessKey); | ||
| var client = new EventGridPublisherClient(topicEndpoint, credential); | ||
|
|
||
| await client.SendEventAsync(eventGridEvent); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,11 @@ | ||
| using Azure.Messaging.EventGrid; | ||
| using Microsoft.Extensions.Logging; | ||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
| using NSubstitute; | ||
| using sama.Models; | ||
| using sama.Services; | ||
| using System; | ||
| using System.Text.Json; | ||
|
|
||
| namespace TestSama.Services | ||
| { | ||
|
|
@@ -13,6 +15,7 @@ public class EventGridNotificationServiceTests | |
| private ILogger<EventGridNotificationService> _logger; | ||
| private SettingsService _settings; | ||
| private BackgroundExecutionWrapper _bgExec; | ||
| private EventGridPublisherClientWrapper _eventGridWrapper; | ||
| private EventGridNotificationService _service; | ||
|
|
||
| [TestInitialize] | ||
|
|
@@ -21,8 +24,9 @@ public void Setup() | |
| _logger = Substitute.For<ILogger<EventGridNotificationService>>(); | ||
| _settings = Substitute.For<SettingsService>((IServiceProvider)null); | ||
| _bgExec = Substitute.For<BackgroundExecutionWrapper>(); | ||
| _eventGridWrapper = Substitute.For<EventGridPublisherClientWrapper>(); | ||
|
|
||
| _service = new EventGridNotificationService(_logger, _settings, _bgExec); | ||
| _service = new EventGridNotificationService(_logger, _settings, _bgExec, _eventGridWrapper); | ||
|
|
||
| // Configure the service with mock settings | ||
| _settings.Notifications_EventGrid_TopicEndpoint.Returns("https://test-topic.eastus-1.eventgrid.azure.net/api/events"); | ||
|
|
@@ -176,9 +180,209 @@ private Endpoint CreateTestHttpEndpoint() | |
| return TestUtility.CreateHttpEndpoint("test-endpoint", true, 1, "https://example.com"); | ||
| } | ||
|
|
||
| private Endpoint CreateTestIcmpEndpoint() | ||
| [TestMethod] | ||
| public void NotifySingleResultShouldSendCorrectEventPayload() | ||
| { | ||
| var endpoint = CreateTestHttpEndpoint(); | ||
| var startTime = DateTimeOffset.UtcNow; | ||
| var stopTime = startTime.AddMilliseconds(250); | ||
| var result = new EndpointCheckResult | ||
| { | ||
| Start = startTime, | ||
| Stop = stopTime, | ||
| Success = true, | ||
| ResponseTime = TimeSpan.FromMilliseconds(250), | ||
| Error = null | ||
| }; | ||
|
|
||
| // Capture the action passed to background execution | ||
| Action capturedAction = null; | ||
| _bgExec.When(x => x.Execute(Arg.Any<Action>())).Do(call => capturedAction = call.Arg<Action>()); | ||
|
|
||
| _service.NotifySingleResult(endpoint, result); | ||
|
|
||
| // Execute the captured action synchronously to trigger the wrapper call | ||
| capturedAction?.Invoke(); | ||
|
|
||
| var result1 = _eventGridWrapper.Received(1).SendEventAsync( | ||
| Arg.Is<Uri>(uri => uri.ToString() == "https://test-topic.eastus-1.eventgrid.azure.net/api/events"), | ||
| Arg.Is<string>(key => key == "test-access-key"), | ||
| Arg.Is<EventGridEvent>(evt => | ||
| evt.EventType == "sama.endpoint.check.completed" && | ||
| evt.Subject == "Sama/1" && | ||
| evt.DataVersion == "1.0" && | ||
| VerifyCheckCompletedEventData(evt.Data, endpoint, result) | ||
| ) | ||
| ); | ||
|
|
||
| // Avoid async warning by not awaiting the result since we're testing the call was made | ||
|
||
| _ = result1; | ||
jonfuller marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| [TestMethod] | ||
| public void NotifyUpShouldSendCorrectEventPayload() | ||
| { | ||
| var endpoint = CreateTestHttpEndpoint(); | ||
| var downAsOf = DateTimeOffset.UtcNow.AddMinutes(-10); | ||
|
|
||
| // Capture the action passed to background execution | ||
| Action capturedAction = null; | ||
| _bgExec.When(x => x.Execute(Arg.Any<Action>())).Do(call => capturedAction = call.Arg<Action>()); | ||
|
|
||
| _service.NotifyUp(endpoint, downAsOf); | ||
|
|
||
| // Execute the captured action synchronously to trigger the wrapper call | ||
| capturedAction?.Invoke(); | ||
|
|
||
| var result2 = _eventGridWrapper.Received(1).SendEventAsync( | ||
| Arg.Any<Uri>(), | ||
| Arg.Any<string>(), | ||
| Arg.Is<EventGridEvent>(evt => | ||
| evt.EventType == "sama.endpoint.status.up" && | ||
| evt.Subject == "Sama/endpoints/1" && | ||
| evt.DataVersion == "1.0" && | ||
| VerifyUpEventData(evt.Data, endpoint, downAsOf) | ||
| ) | ||
| ); | ||
|
|
||
| // Avoid async warning by not awaiting the result since we're testing the call was made | ||
| _ = result2; | ||
|
||
| } | ||
|
|
||
| [TestMethod] | ||
| public void NotifyDownShouldSendCorrectEventPayload() | ||
| { | ||
| var endpoint = CreateTestHttpEndpoint(); | ||
| var downAsOf = DateTimeOffset.UtcNow; | ||
| var exception = new InvalidOperationException("Connection timeout"); | ||
|
|
||
| // Capture the action passed to background execution | ||
| Action capturedAction = null; | ||
| _bgExec.When(x => x.Execute(Arg.Any<Action>())).Do(call => capturedAction = call.Arg<Action>()); | ||
|
|
||
| _service.NotifyDown(endpoint, downAsOf, exception); | ||
|
|
||
| // Execute the captured action synchronously to trigger the wrapper call | ||
| capturedAction?.Invoke(); | ||
|
|
||
| var result3 = _eventGridWrapper.Received(1).SendEventAsync( | ||
| Arg.Any<Uri>(), | ||
| Arg.Any<string>(), | ||
| Arg.Is<EventGridEvent>(evt => | ||
| evt.EventType == "sama.endpoint.status.down" && | ||
| evt.Subject == "Sama/endpoints/1" && | ||
| evt.DataVersion == "1.0" && | ||
| VerifyDownEventData(evt.Data, endpoint, downAsOf, exception) | ||
| ) | ||
| ); | ||
|
|
||
| // Avoid async warning by not awaiting the result since we're testing the call was made | ||
| _ = result3; | ||
jonfuller marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| [TestMethod] | ||
| public void NotifyMiscShouldSendCorrectEventPayloadForEndpointAdded() | ||
| { | ||
| var endpoint = CreateTestHttpEndpoint(); | ||
|
|
||
| // Capture the action passed to background execution | ||
| Action capturedAction = null; | ||
| _bgExec.When(x => x.Execute(Arg.Any<Action>())).Do(call => capturedAction = call.Arg<Action>()); | ||
|
|
||
| _service.NotifyMisc(endpoint, NotificationType.EndpointAdded); | ||
|
|
||
| // Execute the captured action synchronously to trigger the wrapper call | ||
| capturedAction?.Invoke(); | ||
|
|
||
| var result4 = _eventGridWrapper.Received(1).SendEventAsync( | ||
| Arg.Any<Uri>(), | ||
| Arg.Any<string>(), | ||
| Arg.Is<EventGridEvent>(evt => | ||
| evt.EventType == "sama.endpoint.management.added" && | ||
| evt.Subject == "Sama/endpoints/1" && | ||
| evt.DataVersion == "1.0" && | ||
| VerifyManagementEventData(evt.Data, endpoint, NotificationType.EndpointAdded) | ||
| ) | ||
| ); | ||
|
|
||
| // Avoid async warning by not awaiting the result since we're testing the call was made | ||
| _ = result4; | ||
jonfuller marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| [TestMethod] | ||
| public void NotifyMiscShouldSendCorrectEventPayloadForEndpointRemoved() | ||
| { | ||
| var endpoint = CreateTestHttpEndpoint(); | ||
|
|
||
| // Capture the action passed to background execution | ||
| Action capturedAction = null; | ||
| _bgExec.When(x => x.Execute(Arg.Any<Action>())).Do(call => capturedAction = call.Arg<Action>()); | ||
|
|
||
| _service.NotifyMisc(endpoint, NotificationType.EndpointRemoved); | ||
|
|
||
| // Execute the captured action synchronously to trigger the wrapper call | ||
| capturedAction?.Invoke(); | ||
|
|
||
| var result = _eventGridWrapper.Received(1).SendEventAsync( | ||
| Arg.Any<Uri>(), | ||
| Arg.Any<string>(), | ||
| Arg.Is<EventGridEvent>(evt => | ||
| evt.EventType == "sama.endpoint.management.removed" && | ||
| evt.Subject == "Sama/endpoints/1" && | ||
| VerifyManagementEventData(evt.Data, endpoint, NotificationType.EndpointRemoved) | ||
| ) | ||
| ); | ||
|
|
||
| // Avoid async warning by not awaiting the result since we're testing the call was made | ||
| _ = result; | ||
jonfuller marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| private bool VerifyCheckCompletedEventData(BinaryData data, Endpoint endpoint, EndpointCheckResult result) | ||
| { | ||
| var json = JsonSerializer.Deserialize<JsonElement>(data.ToString()); | ||
|
|
||
| return json.GetProperty("endpointId").GetInt32() == endpoint.Id && | ||
| json.GetProperty("endpointName").GetString() == endpoint.Name && | ||
| json.GetProperty("success").GetBoolean() == result.Success && | ||
| json.GetProperty("responseTime").GetDouble() == result.ResponseTime?.TotalMilliseconds && | ||
| json.GetProperty("startTime").GetDateTimeOffset() == result.Start && | ||
| json.GetProperty("stopTime").GetDateTimeOffset() == result.Stop && | ||
| json.GetProperty("error").ValueKind == JsonValueKind.Null; | ||
| } | ||
|
|
||
| private bool VerifyUpEventData(BinaryData data, Endpoint endpoint, DateTimeOffset? downAsOf) | ||
| { | ||
| var json = JsonSerializer.Deserialize<JsonElement>(data.ToString()); | ||
| var expectedDowntimeMinutes = downAsOf.HasValue | ||
| ? (int)DateTimeOffset.UtcNow.Subtract(downAsOf.Value).TotalMinutes | ||
| : 0; | ||
|
|
||
| return json.GetProperty("endpointId").GetInt32() == endpoint.Id && | ||
| json.GetProperty("endpointName").GetString() == endpoint.Name && | ||
| json.GetProperty("downAsOf").GetDateTimeOffset() == downAsOf && | ||
| json.GetProperty("downtimeMinutes").GetInt32() == expectedDowntimeMinutes && | ||
| json.TryGetProperty("recoveredAt", out var recoveredAt) && recoveredAt.ValueKind == JsonValueKind.String; | ||
| } | ||
|
|
||
| private bool VerifyDownEventData(BinaryData data, Endpoint endpoint, DateTimeOffset downAsOf, Exception exception) | ||
| { | ||
| var json = JsonSerializer.Deserialize<JsonElement>(data.ToString()); | ||
|
|
||
| return json.GetProperty("endpointId").GetInt32() == endpoint.Id && | ||
| json.GetProperty("endpointName").GetString() == endpoint.Name && | ||
| json.GetProperty("downAsOf").GetDateTimeOffset() == downAsOf && | ||
| json.GetProperty("reason").GetString() == exception.Message && | ||
| json.GetProperty("reasonType").GetString() == exception.GetType().Name; | ||
| } | ||
|
|
||
| private bool VerifyManagementEventData(BinaryData data, Endpoint endpoint, NotificationType notificationType) | ||
| { | ||
| return TestUtility.CreateIcmpEndpoint("test-ping", true, 2, "192.168.1.1"); | ||
| var json = JsonSerializer.Deserialize<JsonElement>(data.ToString()); | ||
|
|
||
| return json.GetProperty("endpointId").GetInt32() == endpoint.Id && | ||
| json.GetProperty("endpointName").GetString() == endpoint.Name && | ||
| json.GetProperty("notificationType").GetString() == notificationType.ToString() && | ||
| json.TryGetProperty("timestamp", out var timestamp) && timestamp.ValueKind == JsonValueKind.String; | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.