1
0
mirror of https://github.com/bitwarden/server.git synced 2024-12-05 14:23:30 +01:00
bitwarden-server/test/Core.Test/Services/HandlebarsMailServiceTests.cs

172 lines
7.3 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Bit.Core.Models.Business;
using Bit.Core.Models.Table;
using Bit.Core.Models.Table.Provider;
using Bit.Core.Services;
using Bit.Core.Settings;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
{
public class HandlebarsMailServiceTests
{
private readonly HandlebarsMailService _sut;
private readonly GlobalSettings _globalSettings;
private readonly IMailDeliveryService _mailDeliveryService;
Support large organization sync (#1311) * Increase organization max seat size from 30k to 2b (#1274) * Increase organization max seat size from 30k to 2b * PR review. Do not modify unless state matches expected * Organization sync simultaneous event reporting (#1275) * Split up azure messages according to max size * Allow simultaneous login of organization user events * Early resolve small event lists * Clarify logic Co-authored-by: Chad Scharf <3904944+cscharf@users.noreply.github.com> * Improve readability This comes at the cost of multiple serializations, but the improvement in wire-time should more than make up for this on message where serialization time matters Co-authored-by: Chad Scharf <3904944+cscharf@users.noreply.github.com> * Queue emails (#1286) * Extract common Azure queue methods * Do not use internal entity framework namespace * Prefer IEnumerable to IList unless needed All of these implementations were just using `Count == 1`, which is easily replicated. This will be used when abstracting Azure queues * Add model for azure queue message * Abstract Azure queue for reuse * Creat service to enqueue mail messages for later processing Azure queue mail service uses Azure queues. Blocking just blocks until all the work is done -- This is how emailing works today * Provide mail queue service to DI * Queue organization invite emails for later processing All emails can later be added to this queue * Create Admin hosted service to process enqueued mail messages * Prefer constructors to static generators * Mass delete organization users (#1287) * Add delete many to Organization Users * Correct formatting * Remove erroneous migration * Clarify parameter name * Formatting fixes * Simplify bump account revision sproc * Formatting fixes * Match file names to objects * Indicate if large import is expected * Early pull all existing users we were planning on inviting (#1290) * Early pull all existing users we were planning on inviting * Improve sproc name * Batch upsert org users (#1289) * Add UpsertMany sprocs to OrganizationUser * Add method to create TVPs from any object. Uses DbOrder attribute to generate. Sproc will fail unless TVP column order matches that of the db type * Combine migrations * Correct formatting * Include sql objects in sql project * Keep consisten parameter names * Batch deletes for performance * Correct formatting * consolidate migrations * Use batch methods in OrganizationImport * Declare @BatchSize * Transaction names limited to 32 chars Drop sproc before creating it if it exists * Update import tests * Allow for more users in org upgrades * Fix formatting * Improve class hierarchy structure * Use name tuple types * Fix formatting * Front load all reflection * Format constructor * Simplify ToTvp as class-specific extension Co-authored-by: Chad Scharf <3904944+cscharf@users.noreply.github.com>
2021-05-17 16:43:02 +02:00
private readonly IMailEnqueuingService _mailEnqueuingService;
public HandlebarsMailServiceTests()
{
_globalSettings = new GlobalSettings();
_mailDeliveryService = Substitute.For<IMailDeliveryService>();
Support large organization sync (#1311) * Increase organization max seat size from 30k to 2b (#1274) * Increase organization max seat size from 30k to 2b * PR review. Do not modify unless state matches expected * Organization sync simultaneous event reporting (#1275) * Split up azure messages according to max size * Allow simultaneous login of organization user events * Early resolve small event lists * Clarify logic Co-authored-by: Chad Scharf <3904944+cscharf@users.noreply.github.com> * Improve readability This comes at the cost of multiple serializations, but the improvement in wire-time should more than make up for this on message where serialization time matters Co-authored-by: Chad Scharf <3904944+cscharf@users.noreply.github.com> * Queue emails (#1286) * Extract common Azure queue methods * Do not use internal entity framework namespace * Prefer IEnumerable to IList unless needed All of these implementations were just using `Count == 1`, which is easily replicated. This will be used when abstracting Azure queues * Add model for azure queue message * Abstract Azure queue for reuse * Creat service to enqueue mail messages for later processing Azure queue mail service uses Azure queues. Blocking just blocks until all the work is done -- This is how emailing works today * Provide mail queue service to DI * Queue organization invite emails for later processing All emails can later be added to this queue * Create Admin hosted service to process enqueued mail messages * Prefer constructors to static generators * Mass delete organization users (#1287) * Add delete many to Organization Users * Correct formatting * Remove erroneous migration * Clarify parameter name * Formatting fixes * Simplify bump account revision sproc * Formatting fixes * Match file names to objects * Indicate if large import is expected * Early pull all existing users we were planning on inviting (#1290) * Early pull all existing users we were planning on inviting * Improve sproc name * Batch upsert org users (#1289) * Add UpsertMany sprocs to OrganizationUser * Add method to create TVPs from any object. Uses DbOrder attribute to generate. Sproc will fail unless TVP column order matches that of the db type * Combine migrations * Correct formatting * Include sql objects in sql project * Keep consisten parameter names * Batch deletes for performance * Correct formatting * consolidate migrations * Use batch methods in OrganizationImport * Declare @BatchSize * Transaction names limited to 32 chars Drop sproc before creating it if it exists * Update import tests * Allow for more users in org upgrades * Fix formatting * Improve class hierarchy structure * Use name tuple types * Fix formatting * Front load all reflection * Format constructor * Simplify ToTvp as class-specific extension Co-authored-by: Chad Scharf <3904944+cscharf@users.noreply.github.com>
2021-05-17 16:43:02 +02:00
_mailEnqueuingService = Substitute.For<IMailEnqueuingService>();
_sut = new HandlebarsMailService(
_globalSettings,
Support large organization sync (#1311) * Increase organization max seat size from 30k to 2b (#1274) * Increase organization max seat size from 30k to 2b * PR review. Do not modify unless state matches expected * Organization sync simultaneous event reporting (#1275) * Split up azure messages according to max size * Allow simultaneous login of organization user events * Early resolve small event lists * Clarify logic Co-authored-by: Chad Scharf <3904944+cscharf@users.noreply.github.com> * Improve readability This comes at the cost of multiple serializations, but the improvement in wire-time should more than make up for this on message where serialization time matters Co-authored-by: Chad Scharf <3904944+cscharf@users.noreply.github.com> * Queue emails (#1286) * Extract common Azure queue methods * Do not use internal entity framework namespace * Prefer IEnumerable to IList unless needed All of these implementations were just using `Count == 1`, which is easily replicated. This will be used when abstracting Azure queues * Add model for azure queue message * Abstract Azure queue for reuse * Creat service to enqueue mail messages for later processing Azure queue mail service uses Azure queues. Blocking just blocks until all the work is done -- This is how emailing works today * Provide mail queue service to DI * Queue organization invite emails for later processing All emails can later be added to this queue * Create Admin hosted service to process enqueued mail messages * Prefer constructors to static generators * Mass delete organization users (#1287) * Add delete many to Organization Users * Correct formatting * Remove erroneous migration * Clarify parameter name * Formatting fixes * Simplify bump account revision sproc * Formatting fixes * Match file names to objects * Indicate if large import is expected * Early pull all existing users we were planning on inviting (#1290) * Early pull all existing users we were planning on inviting * Improve sproc name * Batch upsert org users (#1289) * Add UpsertMany sprocs to OrganizationUser * Add method to create TVPs from any object. Uses DbOrder attribute to generate. Sproc will fail unless TVP column order matches that of the db type * Combine migrations * Correct formatting * Include sql objects in sql project * Keep consisten parameter names * Batch deletes for performance * Correct formatting * consolidate migrations * Use batch methods in OrganizationImport * Declare @BatchSize * Transaction names limited to 32 chars Drop sproc before creating it if it exists * Update import tests * Allow for more users in org upgrades * Fix formatting * Improve class hierarchy structure * Use name tuple types * Fix formatting * Front load all reflection * Format constructor * Simplify ToTvp as class-specific extension Co-authored-by: Chad Scharf <3904944+cscharf@users.noreply.github.com>
2021-05-17 16:43:02 +02:00
_mailDeliveryService,
_mailEnqueuingService
);
}
[Fact(Skip = "Only for local development")]
public async Task SendAllEmails()
{
// This test is only opt in and is more for development purposes.
// This will send all emails to the test email address so that they can be viewed.
var namedParameters = new Dictionary<(string, Type), object>
{
// TODO: Swith to use env variable
{ ("email", typeof(string)), "test@bitwarden.com" },
{ ("user", typeof(User)), new User
{
Id = Guid.NewGuid(),
Email = "test@bitwarden.com",
}},
{ ("userId", typeof(Guid)), Guid.NewGuid() },
{ ("token", typeof(string)), "test_token" },
{ ("fromEmail", typeof(string)), "test@bitwarden.com" },
{ ("toEmail", typeof(string)), "test@bitwarden.com" },
{ ("newEmailAddress", typeof(string)), "test@bitwarden.com" },
{ ("hint", typeof(string)), "Test Hint" },
{ ("organizationName", typeof(string)), "Test Organization Name" },
{ ("orgUser", typeof(OrganizationUser)), new OrganizationUser
{
Id = Guid.NewGuid(),
Email = "test@bitwarden.com",
OrganizationId = Guid.NewGuid(),
}},
{ ("token", typeof(ExpiringToken)), new ExpiringToken("test_token", DateTime.UtcNow.AddDays(1))},
{ ("organization", typeof(Organization)), new Organization
{
Id = Guid.NewGuid(),
Name = "Test Organization Name",
Seats = 5
}},
{ ("initialSeatCount", typeof(int)), 5},
{ ("ownerEmails", typeof(IEnumerable<string>)), new [] { "test@bitwarden.com" }},
{ ("maxSeatCount", typeof(int)), 5 },
{ ("userIdentifier", typeof(string)), "test_user" },
{ ("adminEmails", typeof(IEnumerable<string>)), new [] { "test@bitwarden.com" }},
{ ("returnUrl", typeof(string)), "https://bitwarden.com/" },
{ ("amount", typeof(decimal)), 1.00M },
{ ("dueDate", typeof(DateTime)), DateTime.UtcNow.AddDays(1) },
{ ("items", typeof(List<string>)), new List<string> { "test@bitwarden.com" }},
{ ("mentionInvoices", typeof(bool)), true },
{ ("emails", typeof(IEnumerable<string>)), new [] { "test@bitwarden.com" }},
{ ("deviceType", typeof(string)), "Mobile" },
{ ("timestamp", typeof(DateTime)), DateTime.UtcNow.AddDays(1)},
{ ("ip", typeof(string)), "127.0.0.1" },
{ ("emergencyAccess", typeof(EmergencyAccess)), new EmergencyAccess
{
Id = Guid.NewGuid(),
Email = "test@bitwarden.com",
}},
{ ("granteeEmail", typeof(string)), "test@bitwarden.com" },
{ ("grantorName", typeof(string)), "Test User" },
{ ("initiatingName", typeof(string)), "Test" },
{ ("approvingName", typeof(string)), "Test Name" },
{ ("rejectingName", typeof(string)), "Test Name" },
{ ("provider", typeof(Provider)), new Provider
{
Id = Guid.NewGuid(),
}},
{ ("name", typeof(string)), "Test Name" },
{ ("ea", typeof(EmergencyAccess)), new EmergencyAccess
{
Id = Guid.NewGuid(),
Email = "test@bitwarden.com",
}},
{ ("userName", typeof(string)), "testUser" },
{ ("orgName", typeof(string)), "Test Org Name" },
{ ("providerName", typeof(string)), "testProvider" },
{ ("providerUser", typeof(ProviderUser)), new ProviderUser
{
ProviderId = Guid.NewGuid(),
Id = Guid.NewGuid(),
}},
};
var globalSettings = new GlobalSettings
{
Mail = new GlobalSettings.MailSettings
{
Smtp = new GlobalSettings.MailSettings.SmtpSettings
{
Host = "localhost",
TrustServer = true,
Port = 10250,
},
ReplyToEmail = "noreply@bitwarden.com",
},
SiteName = "Bitwarden",
};
var mailDeliveryService = new MailKitSmtpMailDeliveryService(globalSettings, Substitute.For<ILogger<MailKitSmtpMailDeliveryService>>());
var handlebarsService = new HandlebarsMailService(globalSettings, mailDeliveryService, new BlockingMailEnqueuingService());
var sendMethods = typeof(IMailService).GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Where(m => m.Name.StartsWith("Send") && m.Name != "SendEnqueuedMailMessageAsync");
foreach (var sendMethod in sendMethods)
{
await InvokeMethod(sendMethod);
}
async Task InvokeMethod(MethodInfo method)
{
var parameters = method.GetParameters();
var args = new object[parameters.Length];
for(var i = 0; i < parameters.Length; i++)
{
if (!namedParameters.TryGetValue((parameters[i].Name, parameters[i].ParameterType), out var value))
{
throw new InvalidOperationException($"Couldn't find a parameter for name '{parameters[i].Name}' and type '{parameters[i].ParameterType.FullName}'");
}
args[i] = value;
}
await (Task)method.Invoke(handlebarsService, args);
}
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}
}