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,31 +1,34 @@
|
|||
using LightBDD.Framework;
|
||||
using LightBDD.Framework.Scenarios;
|
||||
using LightBDD.MsTest3;
|
||||
|
||||
namespace PlanTempus.X.BDD.Scenarios;
|
||||
|
||||
[TestClass]
|
||||
public partial class AccountRegistrationSpecs : FeatureFixtures.AccountRegistrationSpecs
|
||||
{
|
||||
[Scenario]
|
||||
[TestMethod]
|
||||
public async Task Successful_account_registration_with_valid_email()
|
||||
{
|
||||
await Runner.RunScenarioAsync(
|
||||
_ => Given_no_account_exists_with_email("test@example.com"),
|
||||
_ => When_I_submit_registration_with_email_and_password("test@example.com", "TestPassword123!"),
|
||||
_ => Then_a_new_account_should_be_created_with_email_and_confirmation_status("test@example.com", false),
|
||||
_ => Then_a_confirmation_email_should_be_sent()
|
||||
);
|
||||
}
|
||||
[Scenario]
|
||||
[TestMethod]
|
||||
public async Task Successful_account_registration_with_valid_email()
|
||||
{
|
||||
await Runner.RunScenarioAsync(
|
||||
_ => Given_a_unique_email_address(),
|
||||
_ => When_I_submit_registration_with_valid_credentials(),
|
||||
_ => Then_the_account_should_be_created_successfully()
|
||||
);
|
||||
}
|
||||
|
||||
[Scenario]
|
||||
[TestMethod]
|
||||
public async Task Reject_duplicate_email_registration()
|
||||
{
|
||||
await Runner.RunScenarioAsync(
|
||||
_ => Given_an_account_already_exists_with_email("existing@example.com"),
|
||||
_ => When_I_submit_registration_with_email("existing@example.com"),
|
||||
_ => Then_registration_should_fail_with_error("Email already exists")
|
||||
);
|
||||
}
|
||||
[Scenario]
|
||||
[TestMethod]
|
||||
public async Task Reject_duplicate_email_registration()
|
||||
{
|
||||
// Use a unique email for this test to avoid conflicts with other test runs
|
||||
var uniqueEmail = $"duplicate_{Guid.NewGuid():N}@test.example.com";
|
||||
|
||||
await Runner.RunScenarioAsync(
|
||||
_ => Given_an_account_already_exists_with_email(uniqueEmail),
|
||||
_ => When_I_try_to_register_with_the_same_email(),
|
||||
_ => Then_registration_should_fail_with_duplicate_email_error()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,21 +12,23 @@ public partial class EmailConfirmationSpecs : FeatureFixtures.EmailConfirmationS
|
|||
public async Task Confirm_valid_email_address()
|
||||
{
|
||||
await Runner.RunScenarioAsync(
|
||||
_ => Given_an_account_exists_with_unconfirmed_email("test@example.com"),
|
||||
_ => When_I_click_the_valid_confirmation_link_for("test@example.com"),
|
||||
_ => Then_the_accounts_email_confirmed_should_be_true(),
|
||||
_ => And_I_should_be_redirected_to_the_welcome_page()
|
||||
_ => Given_an_account_exists_with_unconfirmed_email($"test-{Guid.NewGuid():N}@example.com"),
|
||||
_ => And_a_verification_email_is_queued_in_outbox(),
|
||||
_ => And_the_outbox_message_is_processed(),
|
||||
_ => When_I_confirm_email_with_valid_token(),
|
||||
_ => Then_the_accounts_email_should_be_confirmed()
|
||||
);
|
||||
}
|
||||
|
||||
[Scenario]
|
||||
[TestMethod]
|
||||
public async Task Handle_invalid_confirmation_link()
|
||||
public async Task Handle_invalid_confirmation_token()
|
||||
{
|
||||
await Runner.RunScenarioAsync(
|
||||
_ => When_I_click_an_invalid_confirmation_link(),
|
||||
_ => Then_I_should_see_an_error_message("Invalid confirmation link"),
|
||||
_ => And_my_email_remains_unconfirmed()
|
||||
_ => Given_an_account_exists_with_unconfirmed_email($"test-{Guid.NewGuid():N}@example.com"),
|
||||
_ => When_I_confirm_email_with_invalid_token(),
|
||||
_ => Then_I_should_see_an_error_message("Invalid"),
|
||||
_ => And_the_email_remains_unconfirmed()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,40 +7,14 @@ namespace PlanTempus.X.BDD.Scenarios;
|
|||
[TestClass]
|
||||
public partial class OrganizationSetupSpecs : FeatureFixtures.OrganizationSetupSpecs
|
||||
{
|
||||
[Scenario]
|
||||
[TestMethod]
|
||||
public async Task Complete_organization_setup_after_confirmation()
|
||||
{
|
||||
await Runner.RunScenarioAsync(
|
||||
_ => Given_account_has_confirmed_their_email("test@example.com"),
|
||||
_ => When_account_submit_organization_name_and_valid_password("Acme Corp", "ValidP@ssw0rd"),
|
||||
_ => Then_a_new_organization_should_be_created_with_expected_properties(),
|
||||
_ => And_the_account_should_be_linked_to_the_organization_in_account_organizations(),
|
||||
_ => And_tenant_tables_should_be_created_for_the_organization(),
|
||||
_ => And_account_should_be_logged_into_the_system()
|
||||
);
|
||||
}
|
||||
|
||||
[Scenario]
|
||||
[TestMethod]
|
||||
public async Task Prevent_organization_setup_without_password()
|
||||
{
|
||||
await Runner.RunScenarioAsync(
|
||||
_ => Given_account_has_confirmed_their_email("test@example.com"),
|
||||
_ => When_account_submit_organization_name_without_password("Acme Corp"),
|
||||
_ => Then_organization_setup_should_fail_with_error("Password required")
|
||||
);
|
||||
}
|
||||
|
||||
[Scenario]
|
||||
[TestMethod]
|
||||
public async Task Handle_multiple_organization_creations()
|
||||
{
|
||||
await Runner.RunScenarioAsync(
|
||||
_ => Given_account_has_completed_initial_setup("test@example.com"),
|
||||
_ => When_account_create_a_new_organization("Second Org"),
|
||||
_ => Then_a_new_organization_entry_should_be_created(),
|
||||
_ => And_the_account_should_be_linked_to_both_organizations()
|
||||
);
|
||||
}
|
||||
[Scenario]
|
||||
[TestMethod]
|
||||
public async Task Create_organization_for_registered_account()
|
||||
{
|
||||
await Runner.RunScenarioAsync(
|
||||
_ => Given_a_registered_account(),
|
||||
_ => When_I_create_an_organization_with_connection_string("Host=localhost;Database=tenant_db;"),
|
||||
_ => Then_the_organization_should_be_created_successfully()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue