1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 15:42:48 -05:00

[PM-18555] Main part of notifications refactor (#5757)

* More tests

* More  tests

* Add non-guid tests

* Introduce slimmer services

* Implement IPushEngine on services

* Implement IPushEngine

* Fix tests

* Format

* Switch to `Guid` on `PushSendRequestModel`

* Remove TODOs
This commit is contained in:
Justin Baur
2025-06-17 13:30:56 -04:00
committed by GitHub
parent 6dc26f4be6
commit 6800bc57f3
23 changed files with 1271 additions and 2408 deletions

View File

@ -5,12 +5,11 @@ using Bit.Core.Auth.Entities;
using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.Models;
using Bit.Core.Models.Data;
using Bit.Core.NotificationCenter.Entities;
using Bit.Core.NotificationCenter.Enums;
using Bit.Core.NotificationHub;
using Bit.Core.Platform.Push;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Bit.Core.Test.NotificationCenter.AutoFixture;
using Bit.Core.Tools.Entities;
using Bit.Core.Vault.Entities;
@ -33,483 +32,6 @@ public class NotificationHubPushNotificationServiceTests
private static readonly DateTime _now = DateTime.UtcNow;
private static readonly Guid _installationId = Guid.Parse("da73177b-513f-4444-b582-595c890e1022");
[Theory]
[BitAutoData]
[NotificationCustomize]
public async Task PushNotificationAsync_GlobalInstallationIdDefault_NotSent(
SutProvider<NotificationHubPushNotificationService> sutProvider, Notification notification)
{
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = default;
await sutProvider.Sut.PushNotificationAsync(notification);
await sutProvider.GetDependency<INotificationHubPool>()
.Received(0)
.AllClients
.Received(0)
.SendTemplateNotificationAsync(Arg.Any<IDictionary<string, string>>(), Arg.Any<string>());
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData]
[NotificationCustomize]
public async Task PushNotificationAsync_GlobalInstallationIdSetClientTypeAll_SentToInstallationId(
SutProvider<NotificationHubPushNotificationService> sutProvider, Notification notification, Guid installationId)
{
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = installationId;
notification.ClientType = ClientType.All;
var expectedNotification = ToNotificationPushNotification(notification, null, installationId);
await sutProvider.Sut.PushNotificationAsync(notification);
await AssertSendTemplateNotificationAsync(sutProvider, PushType.Notification,
expectedNotification,
$"(template:payload && installationId:{installationId})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData(ClientType.Browser)]
[BitAutoData(ClientType.Desktop)]
[BitAutoData(ClientType.Web)]
[BitAutoData(ClientType.Mobile)]
[NotificationCustomize]
public async Task PushNotificationAsync_GlobalInstallationIdSetClientTypeNotAll_SentToInstallationIdAndClientType(
ClientType clientType, SutProvider<NotificationHubPushNotificationService> sutProvider,
Notification notification, Guid installationId)
{
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = installationId;
notification.ClientType = clientType;
var expectedNotification = ToNotificationPushNotification(notification, null, installationId);
await sutProvider.Sut.PushNotificationAsync(notification);
await AssertSendTemplateNotificationAsync(sutProvider, PushType.Notification,
expectedNotification,
$"(template:payload && installationId:{installationId} && clientType:{clientType})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData(false)]
[BitAutoData(true)]
[NotificationCustomize(false)]
public async Task PushNotificationAsync_UserIdProvidedClientTypeAll_SentToUser(
bool organizationIdNull, SutProvider<NotificationHubPushNotificationService> sutProvider,
Notification notification)
{
if (organizationIdNull)
{
notification.OrganizationId = null;
}
notification.ClientType = ClientType.All;
var expectedNotification = ToNotificationPushNotification(notification, null, null);
await sutProvider.Sut.PushNotificationAsync(notification);
await AssertSendTemplateNotificationAsync(sutProvider, PushType.Notification,
expectedNotification,
$"(template:payload_userId:{notification.UserId})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData(ClientType.Browser)]
[BitAutoData(ClientType.Desktop)]
[BitAutoData(ClientType.Web)]
[BitAutoData(ClientType.Mobile)]
[NotificationCustomize(false)]
public async Task PushNotificationAsync_UserIdProvidedOrganizationIdNullClientTypeNotAll_SentToUser(
ClientType clientType, SutProvider<NotificationHubPushNotificationService> sutProvider,
Notification notification)
{
notification.OrganizationId = null;
notification.ClientType = clientType;
var expectedNotification = ToNotificationPushNotification(notification, null, null);
await sutProvider.Sut.PushNotificationAsync(notification);
await AssertSendTemplateNotificationAsync(sutProvider, PushType.Notification,
expectedNotification,
$"(template:payload_userId:{notification.UserId} && clientType:{clientType})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData(ClientType.Browser)]
[BitAutoData(ClientType.Desktop)]
[BitAutoData(ClientType.Web)]
[BitAutoData(ClientType.Mobile)]
[NotificationCustomize(false)]
public async Task PushNotificationAsync_UserIdProvidedOrganizationIdProvidedClientTypeNotAll_SentToUser(
ClientType clientType, SutProvider<NotificationHubPushNotificationService> sutProvider,
Notification notification)
{
notification.ClientType = clientType;
var expectedNotification = ToNotificationPushNotification(notification, null, null);
await sutProvider.Sut.PushNotificationAsync(notification);
await AssertSendTemplateNotificationAsync(sutProvider, PushType.Notification,
expectedNotification,
$"(template:payload_userId:{notification.UserId} && clientType:{clientType})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData]
[NotificationCustomize(false)]
public async Task PushNotificationAsync_UserIdNullOrganizationIdProvidedClientTypeAll_SentToOrganization(
SutProvider<NotificationHubPushNotificationService> sutProvider, Notification notification)
{
notification.UserId = null;
notification.ClientType = ClientType.All;
var expectedNotification = ToNotificationPushNotification(notification, null, null);
await sutProvider.Sut.PushNotificationAsync(notification);
await AssertSendTemplateNotificationAsync(sutProvider, PushType.Notification,
expectedNotification,
$"(template:payload && organizationId:{notification.OrganizationId})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData(ClientType.Browser)]
[BitAutoData(ClientType.Desktop)]
[BitAutoData(ClientType.Web)]
[BitAutoData(ClientType.Mobile)]
[NotificationCustomize(false)]
public async Task PushNotificationAsync_UserIdNullOrganizationIdProvidedClientTypeNotAll_SentToOrganization(
ClientType clientType, SutProvider<NotificationHubPushNotificationService> sutProvider,
Notification notification)
{
notification.UserId = null;
notification.ClientType = clientType;
var expectedNotification = ToNotificationPushNotification(notification, null, null);
await sutProvider.Sut.PushNotificationAsync(notification);
await AssertSendTemplateNotificationAsync(sutProvider, PushType.Notification,
expectedNotification,
$"(template:payload && organizationId:{notification.OrganizationId} && clientType:{clientType})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData]
[NotificationCustomize]
public async Task PushNotificationStatusAsync_GlobalInstallationIdDefault_NotSent(
SutProvider<NotificationHubPushNotificationService> sutProvider, Notification notification,
NotificationStatus notificationStatus)
{
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = default;
await sutProvider.Sut.PushNotificationStatusAsync(notification, notificationStatus);
await sutProvider.GetDependency<INotificationHubPool>()
.Received(0)
.AllClients
.Received(0)
.SendTemplateNotificationAsync(Arg.Any<IDictionary<string, string>>(), Arg.Any<string>());
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData]
[NotificationCustomize]
public async Task PushNotificationStatusAsync_GlobalInstallationIdSetClientTypeAll_SentToInstallationId(
SutProvider<NotificationHubPushNotificationService> sutProvider,
Notification notification, NotificationStatus notificationStatus, Guid installationId)
{
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = installationId;
notification.ClientType = ClientType.All;
var expectedNotification = ToNotificationPushNotification(notification, notificationStatus, installationId);
await sutProvider.Sut.PushNotificationStatusAsync(notification, notificationStatus);
await AssertSendTemplateNotificationAsync(sutProvider, PushType.NotificationStatus,
expectedNotification,
$"(template:payload && installationId:{installationId})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData(ClientType.Browser)]
[BitAutoData(ClientType.Desktop)]
[BitAutoData(ClientType.Web)]
[BitAutoData(ClientType.Mobile)]
[NotificationCustomize]
public async Task
PushNotificationStatusAsync_GlobalInstallationIdSetClientTypeNotAll_SentToInstallationIdAndClientType(
ClientType clientType, SutProvider<NotificationHubPushNotificationService> sutProvider,
Notification notification, NotificationStatus notificationStatus, Guid installationId)
{
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = installationId;
notification.ClientType = clientType;
var expectedNotification = ToNotificationPushNotification(notification, notificationStatus, installationId);
await sutProvider.Sut.PushNotificationStatusAsync(notification, notificationStatus);
await AssertSendTemplateNotificationAsync(sutProvider, PushType.NotificationStatus,
expectedNotification,
$"(template:payload && installationId:{installationId} && clientType:{clientType})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData(false)]
[BitAutoData(true)]
[NotificationCustomize(false)]
public async Task PushNotificationStatusAsync_UserIdProvidedClientTypeAll_SentToUser(
bool organizationIdNull, SutProvider<NotificationHubPushNotificationService> sutProvider,
Notification notification, NotificationStatus notificationStatus)
{
if (organizationIdNull)
{
notification.OrganizationId = null;
}
notification.ClientType = ClientType.All;
var expectedNotification = ToNotificationPushNotification(notification, notificationStatus, null);
await sutProvider.Sut.PushNotificationStatusAsync(notification, notificationStatus);
await AssertSendTemplateNotificationAsync(sutProvider, PushType.NotificationStatus,
expectedNotification,
$"(template:payload_userId:{notification.UserId})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData(ClientType.Browser)]
[BitAutoData(ClientType.Desktop)]
[BitAutoData(ClientType.Web)]
[BitAutoData(ClientType.Mobile)]
[NotificationCustomize(false)]
public async Task PushNotificationStatusAsync_UserIdProvidedOrganizationIdNullClientTypeNotAll_SentToUser(
ClientType clientType, SutProvider<NotificationHubPushNotificationService> sutProvider,
Notification notification, NotificationStatus notificationStatus)
{
notification.OrganizationId = null;
notification.ClientType = clientType;
var expectedNotification = ToNotificationPushNotification(notification, notificationStatus, null);
await sutProvider.Sut.PushNotificationStatusAsync(notification, notificationStatus);
await AssertSendTemplateNotificationAsync(sutProvider, PushType.NotificationStatus,
expectedNotification,
$"(template:payload_userId:{notification.UserId} && clientType:{clientType})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData(ClientType.Browser)]
[BitAutoData(ClientType.Desktop)]
[BitAutoData(ClientType.Web)]
[BitAutoData(ClientType.Mobile)]
[NotificationCustomize(false)]
public async Task PushNotificationStatusAsync_UserIdProvidedOrganizationIdProvidedClientTypeNotAll_SentToUser(
ClientType clientType, SutProvider<NotificationHubPushNotificationService> sutProvider,
Notification notification, NotificationStatus notificationStatus)
{
notification.ClientType = clientType;
var expectedNotification = ToNotificationPushNotification(notification, notificationStatus, null);
await sutProvider.Sut.PushNotificationStatusAsync(notification, notificationStatus);
await AssertSendTemplateNotificationAsync(sutProvider, PushType.NotificationStatus,
expectedNotification,
$"(template:payload_userId:{notification.UserId} && clientType:{clientType})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData]
[NotificationCustomize(false)]
public async Task PushNotificationStatusAsync_UserIdNullOrganizationIdProvidedClientTypeAll_SentToOrganization(
SutProvider<NotificationHubPushNotificationService> sutProvider, Notification notification,
NotificationStatus notificationStatus)
{
notification.UserId = null;
notification.ClientType = ClientType.All;
var expectedNotification = ToNotificationPushNotification(notification, notificationStatus, null);
await sutProvider.Sut.PushNotificationStatusAsync(notification, notificationStatus);
await AssertSendTemplateNotificationAsync(sutProvider, PushType.NotificationStatus,
expectedNotification,
$"(template:payload && organizationId:{notification.OrganizationId})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData(ClientType.Browser)]
[BitAutoData(ClientType.Desktop)]
[BitAutoData(ClientType.Web)]
[BitAutoData(ClientType.Mobile)]
[NotificationCustomize(false)]
public async Task
PushNotificationStatusAsync_UserIdNullOrganizationIdProvidedClientTypeNotAll_SentToOrganization(
ClientType clientType, SutProvider<NotificationHubPushNotificationService> sutProvider,
Notification notification, NotificationStatus notificationStatus)
{
notification.UserId = null;
notification.ClientType = clientType;
var expectedNotification = ToNotificationPushNotification(notification, notificationStatus, null);
await sutProvider.Sut.PushNotificationStatusAsync(notification, notificationStatus);
await AssertSendTemplateNotificationAsync(sutProvider, PushType.NotificationStatus,
expectedNotification,
$"(template:payload && organizationId:{notification.OrganizationId} && clientType:{clientType})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData([null])]
[BitAutoData(ClientType.All)]
public async Task SendPayloadToUserAsync_ClientTypeNullOrAll_SentToUser(ClientType? clientType,
SutProvider<NotificationHubPushNotificationService> sutProvider, Guid userId, PushType pushType, string payload,
string identifier)
{
await sutProvider.Sut.SendPayloadToUserAsync(userId.ToString(), pushType, payload, identifier, null,
clientType);
await AssertSendTemplateNotificationAsync(sutProvider, pushType, payload,
$"(template:payload_userId:{userId} && !deviceIdentifier:{identifier})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData(ClientType.Browser)]
[BitAutoData(ClientType.Desktop)]
[BitAutoData(ClientType.Mobile)]
[BitAutoData(ClientType.Web)]
public async Task SendPayloadToUserAsync_ClientTypeExplicit_SentToUserAndClientType(ClientType clientType,
SutProvider<NotificationHubPushNotificationService> sutProvider, Guid userId, PushType pushType, string payload,
string identifier)
{
await sutProvider.Sut.SendPayloadToUserAsync(userId.ToString(), pushType, payload, identifier, null,
clientType);
await AssertSendTemplateNotificationAsync(sutProvider, pushType, payload,
$"(template:payload_userId:{userId} && !deviceIdentifier:{identifier} && clientType:{clientType})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData([null])]
[BitAutoData(ClientType.All)]
public async Task SendPayloadToOrganizationAsync_ClientTypeNullOrAll_SentToOrganization(ClientType? clientType,
SutProvider<NotificationHubPushNotificationService> sutProvider, Guid organizationId, PushType pushType,
string payload, string identifier)
{
await sutProvider.Sut.SendPayloadToOrganizationAsync(organizationId.ToString(), pushType, payload, identifier,
null, clientType);
await AssertSendTemplateNotificationAsync(sutProvider, pushType, payload,
$"(template:payload && organizationId:{organizationId} && !deviceIdentifier:{identifier})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData(ClientType.Browser)]
[BitAutoData(ClientType.Desktop)]
[BitAutoData(ClientType.Mobile)]
[BitAutoData(ClientType.Web)]
public async Task SendPayloadToOrganizationAsync_ClientTypeExplicit_SentToOrganizationAndClientType(
ClientType clientType, SutProvider<NotificationHubPushNotificationService> sutProvider, Guid organizationId,
PushType pushType, string payload, string identifier)
{
await sutProvider.Sut.SendPayloadToOrganizationAsync(organizationId.ToString(), pushType, payload, identifier,
null, clientType);
await AssertSendTemplateNotificationAsync(sutProvider, pushType, payload,
$"(template:payload && organizationId:{organizationId} && !deviceIdentifier:{identifier} && clientType:{clientType})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData([null])]
[BitAutoData(ClientType.All)]
public async Task SendPayloadToInstallationAsync_ClientTypeNullOrAll_SentToInstallation(ClientType? clientType,
SutProvider<NotificationHubPushNotificationService> sutProvider, Guid installationId, PushType pushType,
string payload, string identifier)
{
await sutProvider.Sut.SendPayloadToInstallationAsync(installationId.ToString(), pushType, payload, identifier,
null, clientType);
await AssertSendTemplateNotificationAsync(sutProvider, pushType, payload,
$"(template:payload && installationId:{installationId} && !deviceIdentifier:{identifier})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Theory]
[BitAutoData(ClientType.Browser)]
[BitAutoData(ClientType.Desktop)]
[BitAutoData(ClientType.Mobile)]
[BitAutoData(ClientType.Web)]
public async Task SendPayloadToInstallationAsync_ClientTypeExplicit_SentToInstallationAndClientType(
ClientType clientType, SutProvider<NotificationHubPushNotificationService> sutProvider, Guid installationId,
PushType pushType, string payload, string identifier)
{
await sutProvider.Sut.SendPayloadToInstallationAsync(installationId.ToString(), pushType, payload, identifier,
null, clientType);
await AssertSendTemplateNotificationAsync(sutProvider, pushType, payload,
$"(template:payload && installationId:{installationId} && !deviceIdentifier:{identifier} && clientType:{clientType})");
await sutProvider.GetDependency<IInstallationDeviceRepository>()
.Received(0)
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
}
[Fact]
public async Task PushSyncCipherCreateAsync_SendExpectedData()
{
@ -1066,7 +588,7 @@ public class NotificationHubPushNotificationServiceTests
);
}
private async Task VerifyNotificationAsync(Func<NotificationHubPushNotificationService, Task> test,
private async Task VerifyNotificationAsync(Func<IPushNotificationService, Task> test,
PushType type, JsonNode expectedPayload, string tag)
{
var installationDeviceRepository = Substitute.For<IInstallationDeviceRepository>();
@ -1104,12 +626,11 @@ public class NotificationHubPushNotificationServiceTests
notificationHubPool,
httpContextAccessor,
NullLogger<NotificationHubPushNotificationService>.Instance,
globalSettings,
fakeTimeProvider
globalSettings
);
// Act
await test(sut);
await test(new EngineWrapper(sut, fakeTimeProvider, _installationId));
// Assert
var calls = notificationHubProxy.ReceivedCalls();