Adds transactional outbox and email verification
Implements outbox pattern for reliable message delivery Adds email verification flow with Postmark integration Enhances account registration with secure token generation Introduces background processing for asynchronous email sending Implements database-level notification mechanism for message processing
This commit is contained in:
parent
88812177a9
commit
54b057886c
35 changed files with 1174 additions and 358 deletions
|
|
@ -1,89 +1,119 @@
|
|||
using LightBDD.Framework;
|
||||
using LightBDD.Framework.Scenarios;
|
||||
using LightBDD.MsTest3;
|
||||
using PlanTempus.X.Services;
|
||||
using PlanTempus.Components.Accounts.Create;
|
||||
using PlanTempus.Components.Accounts.Exceptions;
|
||||
using PlanTempus.Core.CommandQueries;
|
||||
using Shouldly;
|
||||
|
||||
namespace PlanTempus.X.BDD.FeatureFixtures;
|
||||
|
||||
[TestClass]
|
||||
[FeatureDescription(@"As a new user
|
||||
I want to register with my email
|
||||
So I can start using the system")]
|
||||
public partial class AccountRegistrationSpecs : FeatureFixture
|
||||
public partial class AccountRegistrationSpecs : BddTestFixture
|
||||
{
|
||||
protected Account _currentAccount;
|
||||
protected string _currentEmail;
|
||||
protected Exception _registrationError;
|
||||
|
||||
IAccountService _accountService;
|
||||
IEmailService _emailService;
|
||||
IOrganizationService _organizationService;
|
||||
|
||||
public async Task Given_no_account_exists_with_email(string email)
|
||||
{
|
||||
// Ensure account doesn't exist with email
|
||||
var account = await _accountService.GetAccountByEmailAsync(email);
|
||||
account.ShouldBeNull();
|
||||
_currentEmail = email;
|
||||
}
|
||||
|
||||
public async Task When_I_submit_registration_with_email_and_password(string email, string password)
|
||||
{
|
||||
try
|
||||
{
|
||||
_currentAccount = await _accountService.CreateAccountAsync(email, password);
|
||||
_currentEmail = email;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_registrationError = ex;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task When_I_submit_registration_with_email(string email)
|
||||
{
|
||||
try
|
||||
{
|
||||
_currentAccount = await _accountService.CreateAccountAsync(email, "TestPassword123!");
|
||||
_currentEmail = email;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_registrationError = ex;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Then_a_new_account_should_be_created_with_email_and_confirmation_status(string email, bool confirmationStatus)
|
||||
{
|
||||
_currentAccount.ShouldNotBeNull();
|
||||
_currentAccount.Email.ShouldBe(email);
|
||||
_currentAccount.EmailConfirmed.ShouldBe(confirmationStatus);
|
||||
protected CommandResponse _commandResponse;
|
||||
protected string _currentEmail;
|
||||
protected Exception _registrationError;
|
||||
|
||||
public async Task Given_a_unique_email_address()
|
||||
{
|
||||
// Generate a unique email to ensure no account exists
|
||||
_currentEmail = $"{GetRandomWord()}_{Guid.NewGuid():N}@test.example.com";
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task Then_a_confirmation_email_should_be_sent()
|
||||
{
|
||||
var emailSent = _emailService.WasConfirmationEmailSent(_currentEmail);
|
||||
emailSent.ShouldBeTrue();
|
||||
public async Task When_I_submit_registration_with_valid_credentials()
|
||||
{
|
||||
try
|
||||
{
|
||||
var command = new CreateAccountCommand
|
||||
{
|
||||
Email = _currentEmail,
|
||||
Password = "TestPassword123!",
|
||||
IsActive = true,
|
||||
CorrelationId = Guid.NewGuid()
|
||||
};
|
||||
|
||||
_commandResponse = await CommandHandler.Handle(command);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_registrationError = ex;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task When_I_submit_registration_with_email(string email, string password)
|
||||
{
|
||||
try
|
||||
{
|
||||
var command = new CreateAccountCommand
|
||||
{
|
||||
Email = email,
|
||||
Password = password,
|
||||
IsActive = true,
|
||||
CorrelationId = Guid.NewGuid()
|
||||
};
|
||||
|
||||
_commandResponse = await CommandHandler.Handle(command);
|
||||
_currentEmail = email;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_registrationError = ex;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Then_the_account_should_be_created_successfully()
|
||||
{
|
||||
_registrationError.ShouldBeNull();
|
||||
_commandResponse.ShouldNotBeNull();
|
||||
_commandResponse.RequestId.ShouldNotBe(Guid.Empty);
|
||||
_commandResponse.CommandName.ShouldBe(nameof(CreateAccountCommand));
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task Given_an_account_already_exists_with_email(string email)
|
||||
{
|
||||
// Create an account first to ensure it exists
|
||||
_currentAccount = await _accountService.CreateAccountAsync(email, "ExistingPassword123!");
|
||||
_currentAccount.ShouldNotBeNull();
|
||||
_currentEmail = email;
|
||||
{
|
||||
// Create an account first to ensure it exists
|
||||
var command = new CreateAccountCommand
|
||||
{
|
||||
Email = email,
|
||||
Password = "ExistingPassword123!",
|
||||
IsActive = true,
|
||||
CorrelationId = Guid.NewGuid()
|
||||
};
|
||||
|
||||
await Task.CompletedTask;
|
||||
await CommandHandler.Handle(command);
|
||||
_currentEmail = email;
|
||||
}
|
||||
|
||||
public async Task Then_registration_should_fail_with_error(string expectedErrorMessage)
|
||||
{
|
||||
_registrationError.ShouldNotBeNull();
|
||||
_registrationError.Message.ShouldBe(expectedErrorMessage);
|
||||
public async Task When_I_try_to_register_with_the_same_email()
|
||||
{
|
||||
try
|
||||
{
|
||||
var command = new CreateAccountCommand
|
||||
{
|
||||
Email = _currentEmail,
|
||||
Password = "AnotherPassword123!",
|
||||
IsActive = true,
|
||||
CorrelationId = Guid.NewGuid()
|
||||
};
|
||||
|
||||
_commandResponse = await CommandHandler.Handle(command);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_registrationError = ex;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Then_registration_should_fail_with_duplicate_email_error()
|
||||
{
|
||||
_registrationError.ShouldNotBeNull();
|
||||
_registrationError.ShouldBeOfType<EmailAlreadyRegistreredException>();
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue