1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-22 12:15:36 +01:00

PM-10600: Unit tests for NotificationsApiPushNotificationService

Also added HttpClientCustomize fixture, that provides IHttpClientFactory, HttpClient and MockedHttpMessageHandler. The latter can be used to mock requests and provide expected responses.
Expanded HttpRequestMatcher to be able to respond with content.
This commit is contained in:
Maciej Zieniuk 2024-11-08 10:35:19 +00:00
parent 91567ed686
commit be7929c81d
No known key found for this signature in database
GPG Key ID: 9CACE59F1272ACD9
3 changed files with 124 additions and 27 deletions

View File

@ -74,11 +74,16 @@ public class HttpRequestMatcher : IHttpRequestMatcher
/// Note, after specifying a response, you can no longer further specify match criteria. /// Note, after specifying a response, you can no longer further specify match criteria.
/// </summary> /// </summary>
/// <param name="statusCode"></param> /// <param name="statusCode"></param>
/// <param name="content"></param>
/// <returns></returns> /// <returns></returns>
public MockedHttpResponse RespondWith(HttpStatusCode statusCode) public MockedHttpResponse RespondWith(HttpStatusCode statusCode, HttpContent? content = null)
{ {
_responseSpecified = true; _responseSpecified = true;
_mockedResponse = new MockedHttpResponse(statusCode); _mockedResponse = new MockedHttpResponse(statusCode);
if (content != null)
{
_mockedResponse.WithContent(content);
}
return _mockedResponse; return _mockedResponse;
} }

View File

@ -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<MockedHttpMessageHandler>();
var httpClientFactory = Substitute.For<IHttpClientFactory>();
httpClientFactory.CreateClient(Arg.Any<string>()).Returns(handler.ToHttpClient());
return httpClientFactory;
}
if (type == typeof(MockedHttpMessageHandler))
{
return _mockedHttpMessageHandler ??= new MockedHttpMessageHandler();
}
if (type == typeof(HttpClient))
{
var handler = context.Create<MockedHttpMessageHandler>();
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());
}
}

View File

@ -1,41 +1,82 @@
using Bit.Core.Services; #nullable enable
using Bit.Core.Settings; 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.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using NSubstitute; using NSubstitute;
using Xunit; using Xunit;
namespace Bit.Core.Test.Services; namespace Bit.Core.Test.Services;
[SutProviderCustomize]
[HttpClientCustomize]
public class NotificationsApiPushNotificationServiceTests public class NotificationsApiPushNotificationServiceTests
{ {
private readonly NotificationsApiPushNotificationService _sut; [Theory]
[BitAutoData]
private readonly IHttpClientFactory _httpFactory; [NotificationCustomize]
private readonly GlobalSettings _globalSettings; [CurrentContextCustomize]
private readonly IHttpContextAccessor _httpContextAccessor; public async void PushSyncNotificationAsync_Notification_Sent(
private readonly ILogger<NotificationsApiPushNotificationService> _logger; SutProvider<NotificationsApiPushNotificationService> sutProvider, Notification notification,
Guid deviceIdentifier, ICurrentContext currentContext, MockedHttpMessageHandler mockedHttpMessageHandler)
public NotificationsApiPushNotificationServiceTests()
{ {
_httpFactory = Substitute.For<IHttpClientFactory>(); var tokenResponse = mockedHttpMessageHandler
_globalSettings = new GlobalSettings(); .When(request =>
_httpContextAccessor = Substitute.For<IHttpContextAccessor>(); request.Method == HttpMethod.Post && request.RequestUri!.ToString().EndsWith("/connect/token"))
_logger = Substitute.For<ILogger<NotificationsApiPushNotificationService>>(); .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( var pushNotificationData = (PushNotificationData<SyncNotificationPushNotification>)jsonContent.Value;
_httpFactory, return MatchMessage(PushType.SyncNotification, pushNotificationData,
_globalSettings, new SyncNotificationEquals(notification), deviceIdentifier.ToString());
_httpContextAccessor, })
_logger .RespondWith(HttpStatusCode.OK);
);
currentContext.DeviceIdentifier.Returns(deviceIdentifier.ToString());
sutProvider.GetDependency<IHttpContextAccessor>().HttpContext!.RequestServices
.GetService(Arg.Any<Type>()).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 private static bool MatchMessage<T>(PushType pushType, PushNotificationData<T> pushNotificationData,
// we've properly constructed the system under test. IEquatable<T> expectedPayloadEquatable, string contextId)
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{ {
Assert.NotNull(_sut); return pushNotificationData.Type == pushType &&
expectedPayloadEquatable.Equals(pushNotificationData.Payload) &&
pushNotificationData.ContextId == contextId;
}
private class SyncNotificationEquals(Notification notification) : IEquatable<SyncNotificationPushNotification>
{
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;
}
} }
} }