diff --git a/test/Common/MockedHttpClient/HttpRequestMatcher.cs b/test/Common/MockedHttpClient/HttpRequestMatcher.cs
index 7e4d0d2da..2457b0f3f 100644
--- a/test/Common/MockedHttpClient/HttpRequestMatcher.cs
+++ b/test/Common/MockedHttpClient/HttpRequestMatcher.cs
@@ -74,11 +74,16 @@ public class HttpRequestMatcher : IHttpRequestMatcher
/// Note, after specifying a response, you can no longer further specify match criteria.
///
///
+ ///
///
- public MockedHttpResponse RespondWith(HttpStatusCode statusCode)
+ public MockedHttpResponse RespondWith(HttpStatusCode statusCode, HttpContent? content = null)
{
_responseSpecified = true;
_mockedResponse = new MockedHttpResponse(statusCode);
+ if (content != null)
+ {
+ _mockedResponse.WithContent(content);
+ }
return _mockedResponse;
}
diff --git a/test/Core.Test/AutoFixture/HttpClientFixtures.cs b/test/Core.Test/AutoFixture/HttpClientFixtures.cs
new file mode 100644
index 000000000..55dd5c6e6
--- /dev/null
+++ b/test/Core.Test/AutoFixture/HttpClientFixtures.cs
@@ -0,0 +1,51 @@
+#nullable enable
+using AutoFixture;
+using AutoFixture.Kernel;
+using Bit.Test.Common.AutoFixture.Attributes;
+using Bit.Test.Common.MockedHttpClient;
+using NSubstitute;
+
+namespace Bit.Core.Test.AutoFixture;
+
+public class HttpClientFactoryBuilder : ISpecimenBuilder
+{
+ private MockedHttpMessageHandler? _mockedHttpMessageHandler;
+
+ public object Create(object request, ISpecimenContext context)
+ {
+ var type = request as Type;
+ if (type == typeof(IHttpClientFactory))
+ {
+ var handler = context.Create();
+ var httpClientFactory = Substitute.For();
+ httpClientFactory.CreateClient(Arg.Any()).Returns(handler.ToHttpClient());
+ return httpClientFactory;
+ }
+
+ if (type == typeof(MockedHttpMessageHandler))
+ {
+ return _mockedHttpMessageHandler ??= new MockedHttpMessageHandler();
+ }
+
+ if (type == typeof(HttpClient))
+ {
+ var handler = context.Create();
+ return handler.ToHttpClient();
+ }
+
+ return new NoSpecimen();
+ }
+}
+
+public class HttpClientCustomizeAttribute : BitCustomizeAttribute
+{
+ public override ICustomization GetCustomization() => new HttpClientFixtures();
+}
+
+public class HttpClientFixtures : ICustomization
+{
+ public void Customize(IFixture fixture)
+ {
+ fixture.Customizations.Add(new HttpClientFactoryBuilder());
+ }
+}
diff --git a/test/Core.Test/Services/NotificationsApiPushNotificationServiceTests.cs b/test/Core.Test/Services/NotificationsApiPushNotificationServiceTests.cs
index d1ba15d6a..9c10a055a 100644
--- a/test/Core.Test/Services/NotificationsApiPushNotificationServiceTests.cs
+++ b/test/Core.Test/Services/NotificationsApiPushNotificationServiceTests.cs
@@ -1,41 +1,82 @@
-using Bit.Core.Services;
-using Bit.Core.Settings;
+#nullable enable
+using System.Net;
+using System.Net.Http.Json;
+using Bit.Core.Context;
+using Bit.Core.Enums;
+using Bit.Core.Models;
+using Bit.Core.NotificationCenter.Entities;
+using Bit.Core.Services;
+using Bit.Core.Test.AutoFixture;
+using Bit.Core.Test.AutoFixture.CurrentContextFixtures;
+using Bit.Core.Test.NotificationCenter.AutoFixture;
+using Bit.Test.Common.AutoFixture;
+using Bit.Test.Common.AutoFixture.Attributes;
+using Bit.Test.Common.MockedHttpClient;
using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services;
+[SutProviderCustomize]
+[HttpClientCustomize]
public class NotificationsApiPushNotificationServiceTests
{
- private readonly NotificationsApiPushNotificationService _sut;
-
- private readonly IHttpClientFactory _httpFactory;
- private readonly GlobalSettings _globalSettings;
- private readonly IHttpContextAccessor _httpContextAccessor;
- private readonly ILogger _logger;
-
- public NotificationsApiPushNotificationServiceTests()
+ [Theory]
+ [BitAutoData]
+ [NotificationCustomize]
+ [CurrentContextCustomize]
+ public async void PushSyncNotificationAsync_Notification_Sent(
+ SutProvider sutProvider, Notification notification,
+ Guid deviceIdentifier, ICurrentContext currentContext, MockedHttpMessageHandler mockedHttpMessageHandler)
{
- _httpFactory = Substitute.For();
- _globalSettings = new GlobalSettings();
- _httpContextAccessor = Substitute.For();
- _logger = Substitute.For>();
+ var tokenResponse = mockedHttpMessageHandler
+ .When(request =>
+ request.Method == HttpMethod.Post && request.RequestUri!.ToString().EndsWith("/connect/token"))
+ .RespondWith(HttpStatusCode.OK, new StringContent("{\"access_token\":\"token\"}"));
+ var sendResponse = mockedHttpMessageHandler
+ .When(request =>
+ {
+ if (request.Method != HttpMethod.Post || !request.RequestUri!.ToString().EndsWith("/send") ||
+ request.Content is not JsonContent jsonContent || jsonContent.Value == null)
+ {
+ return false;
+ }
- _sut = new NotificationsApiPushNotificationService(
- _httpFactory,
- _globalSettings,
- _httpContextAccessor,
- _logger
- );
+ var pushNotificationData = (PushNotificationData)jsonContent.Value;
+ return MatchMessage(PushType.SyncNotification, pushNotificationData,
+ new SyncNotificationEquals(notification), deviceIdentifier.ToString());
+ })
+ .RespondWith(HttpStatusCode.OK);
+
+ currentContext.DeviceIdentifier.Returns(deviceIdentifier.ToString());
+ sutProvider.GetDependency().HttpContext!.RequestServices
+ .GetService(Arg.Any()).Returns(currentContext);
+
+ await sutProvider.Sut.PushSyncNotificationAsync(notification);
+
+ Assert.Equal(1, tokenResponse.NumberOfResponses);
+ Assert.Equal(1, sendResponse.NumberOfResponses);
}
- // Remove this test when we add actual tests. It only proves that
- // we've properly constructed the system under test.
- [Fact(Skip = "Needs additional work")]
- public void ServiceExists()
+ private static bool MatchMessage(PushType pushType, PushNotificationData pushNotificationData,
+ IEquatable expectedPayloadEquatable, string contextId)
{
- Assert.NotNull(_sut);
+ return pushNotificationData.Type == pushType &&
+ expectedPayloadEquatable.Equals(pushNotificationData.Payload) &&
+ pushNotificationData.ContextId == contextId;
+ }
+
+ private class SyncNotificationEquals(Notification notification) : IEquatable
+ {
+ public bool Equals(SyncNotificationPushNotification? other)
+ {
+ return other != null &&
+ other.Id == notification.Id &&
+ other.UserId == notification.UserId &&
+ other.OrganizationId == notification.OrganizationId &&
+ other.ClientType == notification.ClientType &&
+ other.RevisionDate == notification.RevisionDate;
+ }
}
}