1
0
mirror of https://github.com/bitwarden/server synced 2025-12-11 05:43:35 +00:00

Notifications service unit test coverage with small refactor (#6126)

This commit is contained in:
Maciej Zieniuk
2025-10-23 14:40:57 +02:00
committed by GitHub
parent 69f0464e05
commit dd1f0a120a
6 changed files with 380 additions and 69 deletions

View File

@@ -1,34 +1,26 @@
// FIXME: Update this file to be null safe and then delete the line below using Azure.Storage.Queues;
#nullable disable
using Azure.Storage.Queues;
using Bit.Core.Settings; using Bit.Core.Settings;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.AspNetCore.SignalR;
namespace Bit.Notifications; namespace Bit.Notifications;
public class AzureQueueHostedService : IHostedService, IDisposable public class AzureQueueHostedService : IHostedService, IDisposable
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IHubContext<NotificationsHub> _hubContext; private readonly HubHelpers _hubHelpers;
private readonly IHubContext<AnonymousNotificationsHub> _anonymousHubContext;
private readonly GlobalSettings _globalSettings; private readonly GlobalSettings _globalSettings;
private Task _executingTask; private Task? _executingTask;
private CancellationTokenSource _cts; private CancellationTokenSource? _cts;
private QueueClient _queueClient;
public AzureQueueHostedService( public AzureQueueHostedService(
ILogger<AzureQueueHostedService> logger, ILogger<AzureQueueHostedService> logger,
IHubContext<NotificationsHub> hubContext, HubHelpers hubHelpers,
IHubContext<AnonymousNotificationsHub> anonymousHubContext,
GlobalSettings globalSettings) GlobalSettings globalSettings)
{ {
_logger = logger; _logger = logger;
_hubContext = hubContext; _hubHelpers = hubHelpers;
_globalSettings = globalSettings; _globalSettings = globalSettings;
_anonymousHubContext = anonymousHubContext;
} }
public Task StartAsync(CancellationToken cancellationToken) public Task StartAsync(CancellationToken cancellationToken)
@@ -44,32 +36,39 @@ public class AzureQueueHostedService : IHostedService, IDisposable
{ {
return; return;
} }
_logger.LogWarning("Stopping service."); _logger.LogWarning("Stopping service.");
_cts.Cancel(); _cts?.Cancel();
await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken)); await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken));
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
} }
public void Dispose() public void Dispose()
{ } {
}
private async Task ExecuteAsync(CancellationToken cancellationToken) private async Task ExecuteAsync(CancellationToken cancellationToken)
{ {
_queueClient = new QueueClient(_globalSettings.Notifications.ConnectionString, "notifications"); var queueClient = new QueueClient(_globalSettings.Notifications.ConnectionString, "notifications");
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
try try
{ {
var messages = await _queueClient.ReceiveMessagesAsync(32); var messages = await queueClient.ReceiveMessagesAsync(32, cancellationToken: cancellationToken);
if (messages.Value?.Any() ?? false) if (messages.Value?.Any() ?? false)
{ {
foreach (var message in messages.Value) foreach (var message in messages.Value)
{ {
try try
{ {
await HubHelpers.SendNotificationToHubAsync( var decodedMessage = message.DecodeMessageText();
message.DecodeMessageText(), _hubContext, _anonymousHubContext, _logger, cancellationToken); if (!string.IsNullOrWhiteSpace(decodedMessage))
await _queueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt); {
await _hubHelpers.SendNotificationToHubAsync(decodedMessage, cancellationToken);
}
await queueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt,
cancellationToken);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -77,7 +76,8 @@ public class AzureQueueHostedService : IHostedService, IDisposable
message.MessageId, message.DequeueCount); message.MessageId, message.DequeueCount);
if (message.DequeueCount > 2) if (message.DequeueCount > 2)
{ {
await _queueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt); await queueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt,
cancellationToken);
} }
} }
} }

View File

@@ -1,36 +1,30 @@
using System.Text; #nullable enable
using System.Text;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
namespace Bit.Notifications; namespace Bit.Notifications.Controllers;
[Authorize("Internal")] [Authorize("Internal")]
public class SendController : Controller public class SendController : Controller
{ {
private readonly IHubContext<NotificationsHub> _hubContext; private readonly HubHelpers _hubHelpers;
private readonly IHubContext<AnonymousNotificationsHub> _anonymousHubContext;
private readonly ILogger<SendController> _logger;
public SendController(IHubContext<NotificationsHub> hubContext, IHubContext<AnonymousNotificationsHub> anonymousHubContext, ILogger<SendController> logger) public SendController(HubHelpers hubHelpers)
{ {
_hubContext = hubContext; _hubHelpers = hubHelpers;
_anonymousHubContext = anonymousHubContext;
_logger = logger;
} }
[HttpPost("~/send")] [HttpPost("~/send")]
[SelfHosted(SelfHostedOnly = true)] [SelfHosted(SelfHostedOnly = true)]
public async Task PostSend() public async Task PostSendAsync()
{ {
using (var reader = new StreamReader(Request.Body, Encoding.UTF8)) using var reader = new StreamReader(Request.Body, Encoding.UTF8);
var notificationJson = await reader.ReadToEndAsync();
if (!string.IsNullOrWhiteSpace(notificationJson))
{ {
var notificationJson = await reader.ReadToEndAsync(); await _hubHelpers.SendNotificationToHubAsync(notificationJson);
if (!string.IsNullOrWhiteSpace(notificationJson))
{
await HubHelpers.SendNotificationToHubAsync(notificationJson, _hubContext, _anonymousHubContext, _logger);
}
} }
} }
} }

View File

@@ -1,31 +1,39 @@
// FIXME: Update this file to be null safe and then delete the line below using System.Text.Json;
#nullable disable
using System.Text.Json;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models; using Bit.Core.Models;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
namespace Bit.Notifications; namespace Bit.Notifications;
public static class HubHelpers public class HubHelpers
{ {
private static JsonSerializerOptions _deserializerOptions = private static readonly JsonSerializerOptions _deserializerOptions = new() { PropertyNameCaseInsensitive = true };
new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
private static readonly string _receiveMessageMethod = "ReceiveMessage"; private static readonly string _receiveMessageMethod = "ReceiveMessage";
public static async Task SendNotificationToHubAsync( private readonly IHubContext<NotificationsHub> _hubContext;
string notificationJson, private readonly IHubContext<AnonymousNotificationsHub> _anonymousHubContext;
IHubContext<NotificationsHub> hubContext, private readonly ILogger<HubHelpers> _logger;
public HubHelpers(IHubContext<NotificationsHub> hubContext,
IHubContext<AnonymousNotificationsHub> anonymousHubContext, IHubContext<AnonymousNotificationsHub> anonymousHubContext,
ILogger logger, ILogger<HubHelpers> logger)
CancellationToken cancellationToken = default(CancellationToken) {
) _hubContext = hubContext;
_anonymousHubContext = anonymousHubContext;
_logger = logger;
}
public async Task SendNotificationToHubAsync(string notificationJson, CancellationToken cancellationToken = default)
{ {
var notification = var notification =
JsonSerializer.Deserialize<PushNotificationData<object>>(notificationJson, _deserializerOptions); JsonSerializer.Deserialize<PushNotificationData<object>>(notificationJson, _deserializerOptions);
logger.LogInformation("Sending notification: {NotificationType}", notification.Type); if (notification is null)
{
return;
}
_logger.LogInformation("Sending notification: {NotificationType}", notification.Type);
switch (notification.Type) switch (notification.Type)
{ {
case PushType.SyncCipherUpdate: case PushType.SyncCipherUpdate:
@@ -35,14 +43,19 @@ public static class HubHelpers
var cipherNotification = var cipherNotification =
JsonSerializer.Deserialize<PushNotificationData<SyncCipherPushNotification>>( JsonSerializer.Deserialize<PushNotificationData<SyncCipherPushNotification>>(
notificationJson, _deserializerOptions); notificationJson, _deserializerOptions);
if (cipherNotification is null)
{
break;
}
if (cipherNotification.Payload.UserId.HasValue) if (cipherNotification.Payload.UserId.HasValue)
{ {
await hubContext.Clients.User(cipherNotification.Payload.UserId.ToString()) await _hubContext.Clients.User(cipherNotification.Payload.UserId.Value.ToString())
.SendAsync(_receiveMessageMethod, cipherNotification, cancellationToken); .SendAsync(_receiveMessageMethod, cipherNotification, cancellationToken);
} }
else if (cipherNotification.Payload.OrganizationId.HasValue) else if (cipherNotification.Payload.OrganizationId.HasValue)
{ {
await hubContext.Clients await _hubContext.Clients
.Group(NotificationsHub.GetOrganizationGroup(cipherNotification.Payload.OrganizationId.Value)) .Group(NotificationsHub.GetOrganizationGroup(cipherNotification.Payload.OrganizationId.Value))
.SendAsync(_receiveMessageMethod, cipherNotification, cancellationToken); .SendAsync(_receiveMessageMethod, cipherNotification, cancellationToken);
} }
@@ -54,7 +67,12 @@ public static class HubHelpers
var folderNotification = var folderNotification =
JsonSerializer.Deserialize<PushNotificationData<SyncFolderPushNotification>>( JsonSerializer.Deserialize<PushNotificationData<SyncFolderPushNotification>>(
notificationJson, _deserializerOptions); notificationJson, _deserializerOptions);
await hubContext.Clients.User(folderNotification.Payload.UserId.ToString()) if (folderNotification is null)
{
break;
}
await _hubContext.Clients.User(folderNotification.Payload.UserId.ToString())
.SendAsync(_receiveMessageMethod, folderNotification, cancellationToken); .SendAsync(_receiveMessageMethod, folderNotification, cancellationToken);
break; break;
case PushType.SyncCiphers: case PushType.SyncCiphers:
@@ -66,7 +84,12 @@ public static class HubHelpers
var userNotification = var userNotification =
JsonSerializer.Deserialize<PushNotificationData<LogOutPushNotification>>( JsonSerializer.Deserialize<PushNotificationData<LogOutPushNotification>>(
notificationJson, _deserializerOptions); notificationJson, _deserializerOptions);
await hubContext.Clients.User(userNotification.Payload.UserId.ToString()) if (userNotification is null)
{
break;
}
await _hubContext.Clients.User(userNotification.Payload.UserId.ToString())
.SendAsync(_receiveMessageMethod, userNotification, cancellationToken); .SendAsync(_receiveMessageMethod, userNotification, cancellationToken);
break; break;
case PushType.SyncSendCreate: case PushType.SyncSendCreate:
@@ -75,36 +98,65 @@ public static class HubHelpers
var sendNotification = var sendNotification =
JsonSerializer.Deserialize<PushNotificationData<SyncSendPushNotification>>( JsonSerializer.Deserialize<PushNotificationData<SyncSendPushNotification>>(
notificationJson, _deserializerOptions); notificationJson, _deserializerOptions);
await hubContext.Clients.User(sendNotification.Payload.UserId.ToString()) if (sendNotification is null)
{
break;
}
await _hubContext.Clients.User(sendNotification.Payload.UserId.ToString())
.SendAsync(_receiveMessageMethod, sendNotification, cancellationToken); .SendAsync(_receiveMessageMethod, sendNotification, cancellationToken);
break; break;
case PushType.AuthRequestResponse: case PushType.AuthRequestResponse:
var authRequestResponseNotification = var authRequestResponseNotification =
JsonSerializer.Deserialize<PushNotificationData<AuthRequestPushNotification>>( JsonSerializer.Deserialize<PushNotificationData<AuthRequestPushNotification>>(
notificationJson, _deserializerOptions); notificationJson, _deserializerOptions);
await anonymousHubContext.Clients.Group(authRequestResponseNotification.Payload.Id.ToString()) if (authRequestResponseNotification is null)
{
break;
}
await _anonymousHubContext.Clients.Group(authRequestResponseNotification.Payload.Id.ToString())
.SendAsync("AuthRequestResponseRecieved", authRequestResponseNotification, cancellationToken); .SendAsync("AuthRequestResponseRecieved", authRequestResponseNotification, cancellationToken);
break; break;
case PushType.AuthRequest: case PushType.AuthRequest:
var authRequestNotification = var authRequestNotification =
JsonSerializer.Deserialize<PushNotificationData<AuthRequestPushNotification>>( JsonSerializer.Deserialize<PushNotificationData<AuthRequestPushNotification>>(
notificationJson, _deserializerOptions); notificationJson, _deserializerOptions);
await hubContext.Clients.User(authRequestNotification.Payload.UserId.ToString()) if (authRequestNotification is null)
{
break;
}
await _hubContext.Clients.User(authRequestNotification.Payload.UserId.ToString())
.SendAsync(_receiveMessageMethod, authRequestNotification, cancellationToken); .SendAsync(_receiveMessageMethod, authRequestNotification, cancellationToken);
break; break;
case PushType.SyncOrganizationStatusChanged: case PushType.SyncOrganizationStatusChanged:
var orgStatusNotification = var orgStatusNotification =
JsonSerializer.Deserialize<PushNotificationData<OrganizationStatusPushNotification>>( JsonSerializer.Deserialize<PushNotificationData<OrganizationStatusPushNotification>>(
notificationJson, _deserializerOptions); notificationJson, _deserializerOptions);
await hubContext.Clients.Group(NotificationsHub.GetOrganizationGroup(orgStatusNotification.Payload.OrganizationId)) if (orgStatusNotification is null)
{
break;
}
await _hubContext.Clients
.Group(NotificationsHub.GetOrganizationGroup(orgStatusNotification.Payload.OrganizationId))
.SendAsync(_receiveMessageMethod, orgStatusNotification, cancellationToken); .SendAsync(_receiveMessageMethod, orgStatusNotification, cancellationToken);
break; break;
case PushType.SyncOrganizationCollectionSettingChanged: case PushType.SyncOrganizationCollectionSettingChanged:
var organizationCollectionSettingsChangedNotification = var organizationCollectionSettingsChangedNotification =
JsonSerializer.Deserialize<PushNotificationData<OrganizationStatusPushNotification>>( JsonSerializer.Deserialize<PushNotificationData<OrganizationStatusPushNotification>>(
notificationJson, _deserializerOptions); notificationJson, _deserializerOptions);
await hubContext.Clients.Group(NotificationsHub.GetOrganizationGroup(organizationCollectionSettingsChangedNotification.Payload.OrganizationId)) if (organizationCollectionSettingsChangedNotification is null)
.SendAsync(_receiveMessageMethod, organizationCollectionSettingsChangedNotification, cancellationToken); {
break;
}
await _hubContext.Clients
.Group(NotificationsHub.GetOrganizationGroup(organizationCollectionSettingsChangedNotification
.Payload.OrganizationId))
.SendAsync(_receiveMessageMethod, organizationCollectionSettingsChangedNotification,
cancellationToken);
break; break;
case PushType.OrganizationBankAccountVerified: case PushType.OrganizationBankAccountVerified:
var organizationBankAccountVerifiedNotification = var organizationBankAccountVerifiedNotification =
@@ -124,9 +176,14 @@ public static class HubHelpers
case PushType.NotificationStatus: case PushType.NotificationStatus:
var notificationData = JsonSerializer.Deserialize<PushNotificationData<NotificationPushNotification>>( var notificationData = JsonSerializer.Deserialize<PushNotificationData<NotificationPushNotification>>(
notificationJson, _deserializerOptions); notificationJson, _deserializerOptions);
if (notificationData is null)
{
break;
}
if (notificationData.Payload.InstallationId.HasValue) if (notificationData.Payload.InstallationId.HasValue)
{ {
await hubContext.Clients.Group(NotificationsHub.GetInstallationGroup( await _hubContext.Clients.Group(NotificationsHub.GetInstallationGroup(
notificationData.Payload.InstallationId.Value, notificationData.Payload.ClientType)) notificationData.Payload.InstallationId.Value, notificationData.Payload.ClientType))
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken); .SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
} }
@@ -134,27 +191,34 @@ public static class HubHelpers
{ {
if (notificationData.Payload.ClientType == ClientType.All) if (notificationData.Payload.ClientType == ClientType.All)
{ {
await hubContext.Clients.User(notificationData.Payload.UserId.ToString()) await _hubContext.Clients.User(notificationData.Payload.UserId.Value.ToString())
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken); .SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
} }
else else
{ {
await hubContext.Clients.Group(NotificationsHub.GetUserGroup( await _hubContext.Clients.Group(NotificationsHub.GetUserGroup(
notificationData.Payload.UserId.Value, notificationData.Payload.ClientType)) notificationData.Payload.UserId.Value, notificationData.Payload.ClientType))
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken); .SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
} }
} }
else if (notificationData.Payload.OrganizationId.HasValue) else if (notificationData.Payload.OrganizationId.HasValue)
{ {
await hubContext.Clients.Group(NotificationsHub.GetOrganizationGroup( await _hubContext.Clients.Group(NotificationsHub.GetOrganizationGroup(
notificationData.Payload.OrganizationId.Value, notificationData.Payload.ClientType)) notificationData.Payload.OrganizationId.Value, notificationData.Payload.ClientType))
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken); .SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
} }
break; break;
case PushType.RefreshSecurityTasks: case PushType.RefreshSecurityTasks:
var pendingTasksData = JsonSerializer.Deserialize<PushNotificationData<UserPushNotification>>(notificationJson, _deserializerOptions); var pendingTasksData =
await hubContext.Clients.User(pendingTasksData.Payload.UserId.ToString()) JsonSerializer.Deserialize<PushNotificationData<UserPushNotification>>(notificationJson,
_deserializerOptions);
if (pendingTasksData is null)
{
break;
}
await _hubContext.Clients.User(pendingTasksData.Payload.UserId.ToString())
.SendAsync(_receiveMessageMethod, pendingTasksData, cancellationToken); .SendAsync(_receiveMessageMethod, pendingTasksData, cancellationToken);
break; break;
default: default:

View File

@@ -61,6 +61,7 @@ public class Startup
} }
services.AddSingleton<IUserIdProvider, SubjectUserIdProvider>(); services.AddSingleton<IUserIdProvider, SubjectUserIdProvider>();
services.AddSingleton<ConnectionCounter>(); services.AddSingleton<ConnectionCounter>();
services.AddSingleton<HubHelpers>();
// Mvc // Mvc
services.AddMvc(); services.AddMvc();

View File

@@ -0,0 +1,250 @@
#nullable enable
using System.Text.Json;
using Bit.Core.Enums;
using Bit.Core.Models;
using Bit.Core.Test.NotificationCenter.AutoFixture;
using Bit.Core.Utilities;
using Bit.Notifications;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Microsoft.AspNetCore.SignalR;
using NSubstitute;
namespace Notifications.Test;
[SutProviderCustomize]
[NotificationCustomize(false)]
public class HubHelpersTest
{
[Theory]
[BitAutoData]
public async Task SendNotificationToHubAsync_NotificationPushNotificationGlobal_NothingSent(
SutProvider<HubHelpers> sutProvider,
NotificationPushNotification notification,
string contextId, CancellationToken cancellationToke)
{
notification.Global = true;
notification.InstallationId = null;
notification.UserId = null;
notification.OrganizationId = null;
var json = ToNotificationJson(notification, PushType.Notification, contextId);
await sutProvider.Sut.SendNotificationToHubAsync(json, cancellationToke);
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).Group(Arg.Any<string>());
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0)
.Group(Arg.Any<string>());
}
[Theory]
[BitAutoData]
public async Task
SendNotificationToHubAsync_NotificationPushNotificationInstallationIdProvidedClientTypeAll_SentToGroupInstallation(
SutProvider<HubHelpers> sutProvider,
NotificationPushNotification notification,
string contextId, CancellationToken cancellationToken)
{
notification.UserId = null;
notification.OrganizationId = null;
notification.ClientType = ClientType.All;
var json = ToNotificationJson(notification, PushType.Notification, contextId);
await sutProvider.Sut.SendNotificationToHubAsync(json, cancellationToken);
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
await sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(1)
.Group($"Installation_{notification.InstallationId!.Value.ToString()}")
.Received(1)
.SendCoreAsync("ReceiveMessage", Arg.Is<object?[]>(objects =>
objects.Length == 1 && IsNotificationPushNotificationEqual(notification, objects[0],
PushType.Notification, contextId)),
cancellationToken);
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0)
.Group(Arg.Any<string>());
}
[Theory]
[BitAutoData(ClientType.Browser)]
[BitAutoData(ClientType.Desktop)]
[BitAutoData(ClientType.Mobile)]
[BitAutoData(ClientType.Web)]
public async Task
SendNotificationToHubAsync_NotificationPushNotificationInstallationIdProvidedClientTypeNotAll_SentToGroupInstallationClientType(
ClientType clientType, SutProvider<HubHelpers> sutProvider,
NotificationPushNotification notification,
string contextId, CancellationToken cancellationToken)
{
notification.UserId = null;
notification.OrganizationId = null;
notification.ClientType = clientType;
var json = ToNotificationJson(notification, PushType.Notification, contextId);
await sutProvider.Sut.SendNotificationToHubAsync(json, cancellationToken);
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
await sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(1)
.Group($"Installation_ClientType_{notification.InstallationId!.Value}_{clientType}")
.Received(1)
.SendCoreAsync("ReceiveMessage", Arg.Is<object?[]>(objects =>
objects.Length == 1 && IsNotificationPushNotificationEqual(notification, objects[0],
PushType.Notification, contextId)),
cancellationToken);
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0)
.Group(Arg.Any<string>());
}
[Theory]
[BitAutoData(false)]
[BitAutoData(true)]
public async Task SendNotificationToHubAsync_NotificationPushNotificationUserIdProvidedClientTypeAll_SentToUser(
bool organizationIdProvided, SutProvider<HubHelpers> sutProvider,
NotificationPushNotification notification,
string contextId, CancellationToken cancellationToken)
{
notification.InstallationId = null;
notification.ClientType = ClientType.All;
if (!organizationIdProvided)
{
notification.OrganizationId = null;
}
var json = ToNotificationJson(notification, PushType.Notification, contextId);
await sutProvider.Sut.SendNotificationToHubAsync(json, cancellationToken);
await sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(1)
.User(notification.UserId!.Value.ToString())
.Received(1)
.SendCoreAsync("ReceiveMessage", Arg.Is<object?[]>(objects =>
objects.Length == 1 && IsNotificationPushNotificationEqual(notification, objects[0],
PushType.Notification, contextId)),
cancellationToken);
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).Group(Arg.Any<string>());
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0)
.Group(Arg.Any<string>());
}
[Theory]
[BitAutoData(false, ClientType.Browser)]
[BitAutoData(false, ClientType.Desktop)]
[BitAutoData(false, ClientType.Mobile)]
[BitAutoData(false, ClientType.Web)]
[BitAutoData(true, ClientType.Browser)]
[BitAutoData(true, ClientType.Desktop)]
[BitAutoData(true, ClientType.Mobile)]
[BitAutoData(true, ClientType.Web)]
public async Task
SendNotificationToHubAsync_NotificationPushNotificationUserIdProvidedClientTypeNotAll_SentToGroupUserClientType(
bool organizationIdProvided, ClientType clientType, SutProvider<HubHelpers> sutProvider,
NotificationPushNotification notification,
string contextId, CancellationToken cancellationToken)
{
notification.InstallationId = null;
notification.ClientType = clientType;
if (!organizationIdProvided)
{
notification.OrganizationId = null;
}
var json = ToNotificationJson(notification, PushType.Notification, contextId);
await sutProvider.Sut.SendNotificationToHubAsync(json, cancellationToken);
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
await sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(1)
.Group($"UserClientType_{notification.UserId!.Value}_{clientType}")
.Received(1)
.SendCoreAsync("ReceiveMessage", Arg.Is<object?[]>(objects =>
objects.Length == 1 && IsNotificationPushNotificationEqual(notification, objects[0],
PushType.Notification, contextId)),
cancellationToken);
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0)
.Group(Arg.Any<string>());
}
[Theory]
[BitAutoData]
public async Task
SendNotificationToHubAsync_NotificationPushNotificationOrganizationIdProvidedClientTypeAll_SentToGroupOrganization(
SutProvider<HubHelpers> sutProvider, string contextId,
NotificationPushNotification notification,
CancellationToken cancellationToken)
{
notification.UserId = null;
notification.InstallationId = null;
notification.ClientType = ClientType.All;
var json = ToNotificationJson(notification, PushType.Notification, contextId);
await sutProvider.Sut.SendNotificationToHubAsync(json, cancellationToken);
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
await sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(1)
.Group($"Organization_{notification.OrganizationId!.Value}")
.Received(1)
.SendCoreAsync("ReceiveMessage", Arg.Is<object?[]>(objects =>
objects.Length == 1 && IsNotificationPushNotificationEqual(notification, objects[0],
PushType.Notification, contextId)),
cancellationToken);
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0)
.Group(Arg.Any<string>());
}
[Theory]
[BitAutoData(ClientType.Browser)]
[BitAutoData(ClientType.Desktop)]
[BitAutoData(ClientType.Mobile)]
[BitAutoData(ClientType.Web)]
public async Task
SendNotificationToHubAsync_NotificationPushNotificationOrganizationIdProvidedClientTypeNotAll_SentToGroupOrganizationClientType(
ClientType clientType, SutProvider<HubHelpers> sutProvider, string contextId,
NotificationPushNotification notification,
CancellationToken cancellationToken)
{
notification.UserId = null;
notification.InstallationId = null;
notification.ClientType = clientType;
var json = ToNotificationJson(notification, PushType.Notification, contextId);
await sutProvider.Sut.SendNotificationToHubAsync(json, cancellationToken);
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
await sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(1)
.Group($"OrganizationClientType_{notification.OrganizationId!.Value}_{clientType}")
.Received(1)
.SendCoreAsync("ReceiveMessage", Arg.Is<object?[]>(objects =>
objects.Length == 1 && IsNotificationPushNotificationEqual(notification, objects[0],
PushType.Notification, contextId)),
cancellationToken);
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0)
.Group(Arg.Any<string>());
}
private static string ToNotificationJson(object payload, PushType type, string contextId)
{
var notification = new PushNotificationData<object>(type, payload, contextId);
return JsonSerializer.Serialize(notification, JsonHelpers.IgnoreWritingNull);
}
private static bool IsNotificationPushNotificationEqual(NotificationPushNotification expected, object? actual,
PushType type, string contextId)
{
if (actual is not PushNotificationData<NotificationPushNotification> pushNotificationData)
{
return false;
}
return pushNotificationData.Type == type &&
pushNotificationData.ContextId == contextId &&
expected.Id == pushNotificationData.Payload.Id &&
expected.UserId == pushNotificationData.Payload.UserId &&
expected.OrganizationId == pushNotificationData.Payload.OrganizationId &&
expected.ClientType == pushNotificationData.Payload.ClientType &&
expected.RevisionDate == pushNotificationData.Payload.RevisionDate;
}
}

View File

@@ -18,5 +18,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Notifications\Notifications.csproj" /> <ProjectReference Include="..\..\src\Notifications\Notifications.csproj" />
<ProjectReference Include="..\Common\Common.csproj" />
<ProjectReference Include="..\Core.Test\Core.Test.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>