Adds more BDDs
This commit is contained in:
parent
104187fcac
commit
e79f48e027
3 changed files with 563 additions and 50 deletions
155
PlanTempus.X.BDD/Class1.cs
Normal file
155
PlanTempus.X.BDD/Class1.cs
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
// PlanTempus.X.Services.cs
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PlanTempus.X.Services
|
||||||
|
{
|
||||||
|
// Models
|
||||||
|
public class User
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public bool EmailConfirmed { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
public bool IsLocked { get; set; }
|
||||||
|
public DateTime? LockoutEnd { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Organization
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string CreatedBy { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UserOrganization
|
||||||
|
{
|
||||||
|
public string UserId { get; set; }
|
||||||
|
public string OrganizationId { get; set; }
|
||||||
|
public string Role { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service interfaces
|
||||||
|
public interface IUserService
|
||||||
|
{
|
||||||
|
Task<User> CreateUserAsync(string email, string name);
|
||||||
|
Task<User> GetUserByEmailAsync(string email);
|
||||||
|
User GetUserByEmail(string email);
|
||||||
|
Task ConfirmEmailAsync(string confirmationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IEmailService
|
||||||
|
{
|
||||||
|
Task<string> GetConfirmationLinkForEmail(string email);
|
||||||
|
bool WasConfirmationEmailSent(string email);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IOrganizationService
|
||||||
|
{
|
||||||
|
Task<Organization> SetupOrganizationAsync(string userId, string orgName, string password);
|
||||||
|
Task<Organization> CreateOrganizationAsync(string userId, string orgName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IUserOrganizationService
|
||||||
|
{
|
||||||
|
UserOrganization GetUserOrganization(string userId, string organizationId);
|
||||||
|
List<UserOrganization> GetUserOrganizations(string userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ITenantService
|
||||||
|
{
|
||||||
|
bool ValidateTenantTablesExist(string organizationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IAuthService
|
||||||
|
{
|
||||||
|
bool IsUserAuthenticated(string userId);
|
||||||
|
Task<bool> AttemptLoginAsync(string email, string password);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service implementations
|
||||||
|
public class UserService : IUserService
|
||||||
|
{
|
||||||
|
public async Task<User> CreateUserAsync(string email, string name)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("CreateUserAsync not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User> GetUserByEmailAsync(string email)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("GetUserByEmailAsync not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
public User GetUserByEmail(string email)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("GetUserByEmail not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ConfirmEmailAsync(string confirmationToken)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("ConfirmEmailAsync not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EmailService : IEmailService
|
||||||
|
{
|
||||||
|
public async Task<string> GetConfirmationLinkForEmail(string email)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("GetConfirmationLinkForEmail not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool WasConfirmationEmailSent(string email)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("WasConfirmationEmailSent not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrganizationService : IOrganizationService
|
||||||
|
{
|
||||||
|
public async Task<Organization> SetupOrganizationAsync(string userId, string orgName, string password)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("SetupOrganizationAsync not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Organization> CreateOrganizationAsync(string userId, string orgName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("CreateOrganizationAsync not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UserOrganizationService : IUserOrganizationService
|
||||||
|
{
|
||||||
|
public UserOrganization GetUserOrganization(string userId, string organizationId)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("GetUserOrganization not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UserOrganization> GetUserOrganizations(string userId)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("GetUserOrganizations not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TenantService : ITenantService
|
||||||
|
{
|
||||||
|
public bool ValidateTenantTablesExist(string organizationId)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("ValidateTenantTablesExist not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AuthService : IAuthService
|
||||||
|
{
|
||||||
|
public bool IsUserAuthenticated(string userId)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("IsUserAuthenticated not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> AttemptLoginAsync(string email, string password)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("AttemptLoginAsync not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,73 +1,333 @@
|
||||||
using LightBDD.Framework;
|
using LightBDD.Framework;
|
||||||
using LightBDD.Framework.Scenarios;
|
using LightBDD.Framework.Scenarios;
|
||||||
using LightBDD.MsTest3;
|
using LightBDD.MsTest3;
|
||||||
|
using PlanTempus.X.Services;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
|
|
||||||
namespace PlanTempus.X.BDD.FeatureFixtures;
|
namespace PlanTempus.X.BDD.FeatureFixtures;
|
||||||
|
|
||||||
[TestClass]
|
[TestClass]
|
||||||
|
[FeatureDescription(@"As a new user
|
||||||
|
I want to register with my email
|
||||||
|
So I can start using the system")]
|
||||||
public partial class UserRegistrationSpecs : FeatureFixture
|
public partial class UserRegistrationSpecs : FeatureFixture
|
||||||
{
|
{
|
||||||
//private CalculatorContext _context;
|
protected User _currentUser;
|
||||||
|
protected string _currentEmail;
|
||||||
|
protected Exception _registrationError;
|
||||||
|
|
||||||
public UserRegistrationSpecs()
|
IUserService _userService;
|
||||||
|
IEmailService _emailService;
|
||||||
|
IOrganizationService _organizationService;
|
||||||
|
|
||||||
|
public async Task Given_no_user_exists_with_email(string email)
|
||||||
{
|
{
|
||||||
//_context = new CalculatorContext();
|
// Ensure user doesn't exist with email
|
||||||
}
|
var user = await _userService.GetUserByEmailAsync(email);
|
||||||
//[Scenario]
|
user.ShouldBeNull();
|
||||||
//[TestMethod]
|
_currentEmail = email;
|
||||||
//public void Successful_user_registration_with_valid_email()
|
|
||||||
//{
|
|
||||||
// // Runner.RunScenario(
|
|
||||||
// // _ => Given_no_user_exists_with_email("test@example.com")
|
|
||||||
|
|
||||||
// //);
|
|
||||||
// //await Runner.RunScenarioAsync(
|
|
||||||
// // _ => Given_no_user_exists_with_email("test@example.com"),
|
|
||||||
// // _ => When_I_submit_registration_with_name_and_email("Test User", "test@example.com"),
|
|
||||||
// // _ => Then_a_new_user_should_be_created_with_email_and_confirmation_status("test@example.com", false),
|
|
||||||
// // _ => Then_a_confirmation_email_should_be_sent()
|
|
||||||
// //);
|
|
||||||
//}
|
|
||||||
protected async Task Given_no_user_exists_with_email(string email)
|
|
||||||
{
|
|
||||||
await Task.Run(() =>
|
|
||||||
{
|
|
||||||
|
|
||||||
});
|
|
||||||
//Assert.IsFalse(await _context.UserExistsAsync(email), $"User with email {email} should not exist");
|
|
||||||
true.ShouldBe(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task When_I_submit_registration_with_name_and_email(string name, string email)
|
public async Task When_I_submit_registration_with_name_and_email(string name, string email)
|
||||||
{
|
{
|
||||||
//await _context.RegisterUserAsync(name, email);
|
try
|
||||||
await Task.Run(() =>
|
|
||||||
{
|
{
|
||||||
|
_currentUser = await _userService.CreateUserAsync(email, name);
|
||||||
});
|
_currentEmail = email;
|
||||||
true.ShouldBe(true);
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_registrationError = ex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task Then_a_new_user_should_be_created_with_email_and_confirmation_status(string email, bool emailConfirmed)
|
public async Task When_I_submit_registration_with_email(string email)
|
||||||
{
|
{
|
||||||
//var user = await _context.GetUserByEmailAsync(email);
|
try
|
||||||
//Assert.IsNotNull(user);
|
|
||||||
//Assert.AreEqual(email, user.Email);
|
|
||||||
//Assert.AreEqual(emailConfirmed, user.EmailConfirmed);
|
|
||||||
await Task.Run(() =>
|
|
||||||
{
|
{
|
||||||
|
_currentUser = await _userService.CreateUserAsync(email, "Test User");
|
||||||
});
|
_currentEmail = email;
|
||||||
true.ShouldBe(true);
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_registrationError = ex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task Then_a_confirmation_email_should_be_sent()
|
public void Then_a_new_user_should_be_created_with_email_and_confirmation_status(string email, bool confirmationStatus)
|
||||||
{
|
{
|
||||||
//Assert.IsTrue(await _context.WasConfirmationEmailSentAsync(), "Confirmation email should be sent");
|
_currentUser.ShouldNotBeNull();
|
||||||
await Task.Run(() =>
|
_currentUser.Email.ShouldBe(email);
|
||||||
{
|
_currentUser.EmailConfirmed.ShouldBe(confirmationStatus);
|
||||||
|
}
|
||||||
|
|
||||||
});
|
public void Then_a_confirmation_email_should_be_sent()
|
||||||
true.ShouldBe(true);
|
{
|
||||||
|
var emailSent = _emailService.WasConfirmationEmailSent(_currentEmail);
|
||||||
|
emailSent.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Given_a_user_already_exists_with_email(string email)
|
||||||
|
{
|
||||||
|
// Create a user first to ensure it exists
|
||||||
|
_currentUser = await _userService.CreateUserAsync(email, "Existing User");
|
||||||
|
_currentUser.ShouldNotBeNull();
|
||||||
|
_currentEmail = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Then_registration_should_fail_with_error(string expectedErrorMessage)
|
||||||
|
{
|
||||||
|
_registrationError.ShouldNotBeNull();
|
||||||
|
_registrationError.Message.ShouldBe(expectedErrorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
[FeatureDescription(@"As a registered user
|
||||||
|
I want to confirm my email
|
||||||
|
So I can activate my account")]
|
||||||
|
public partial class EmailConfirmationSpecs : FeatureFixture
|
||||||
|
{
|
||||||
|
IUserService _userService;
|
||||||
|
IEmailService _emailService;
|
||||||
|
IOrganizationService _organizationService;
|
||||||
|
|
||||||
|
protected User _currentUser;
|
||||||
|
protected string _currentEmail;
|
||||||
|
protected string _confirmationLink;
|
||||||
|
protected bool _redirectedToWelcome;
|
||||||
|
protected string _errorMessage;
|
||||||
|
|
||||||
|
public async Task Given_a_user_exists_with_unconfirmed_email(string email)
|
||||||
|
{
|
||||||
|
_currentUser = await _userService.CreateUserAsync(email, "Test User");
|
||||||
|
_currentUser.EmailConfirmed.ShouldBeFalse();
|
||||||
|
_currentEmail = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task When_I_click_the_valid_confirmation_link_for(string email)
|
||||||
|
{
|
||||||
|
_confirmationLink = await _emailService.GetConfirmationLinkForEmail(email);
|
||||||
|
await _userService.ConfirmEmailAsync(_confirmationLink);
|
||||||
|
_redirectedToWelcome = true; // Simulate redirect
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Then_the_users_email_confirmed_should_be_true()
|
||||||
|
{
|
||||||
|
_currentUser = _userService.GetUserByEmail(_currentEmail);
|
||||||
|
_currentUser.EmailConfirmed.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void And_I_should_be_redirected_to_the_welcome_page()
|
||||||
|
{
|
||||||
|
_redirectedToWelcome.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task When_I_click_an_invalid_confirmation_link()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _userService.ConfirmEmailAsync("invalid-confirmation-token");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_errorMessage = ex.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Then_I_should_see_an_error_message(string expectedErrorMessage)
|
||||||
|
{
|
||||||
|
_errorMessage.ShouldBe(expectedErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void And_my_email_remains_unconfirmed()
|
||||||
|
{
|
||||||
|
if (_currentUser != null)
|
||||||
|
{
|
||||||
|
_currentUser.EmailConfirmed.ShouldBeFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
[FeatureDescription(@"As a user with confirmed email
|
||||||
|
I want to set up my organization
|
||||||
|
So I can start using the system with my team")]
|
||||||
|
public partial class OrganizationSetupSpecs : FeatureFixture
|
||||||
|
{
|
||||||
|
IUserService _userService;
|
||||||
|
IEmailService _emailService;
|
||||||
|
IOrganizationService _organizationService;
|
||||||
|
IUserOrganizationService _userOrganizationService;
|
||||||
|
ITenantService _tenantService;
|
||||||
|
IAuthService _authService;
|
||||||
|
|
||||||
|
protected User _currentUser;
|
||||||
|
protected Organization _organization;
|
||||||
|
protected Exception _setupError;
|
||||||
|
protected List<Organization> _userOrganizations;
|
||||||
|
|
||||||
|
public async Task Given_user_has_confirmed_their_email(string email)
|
||||||
|
{
|
||||||
|
// Create a user with confirmed email
|
||||||
|
_currentUser = await _userService.CreateUserAsync(email, "Test User");
|
||||||
|
var confirmationLink = await _emailService.GetConfirmationLinkForEmail(email);
|
||||||
|
await _userService.ConfirmEmailAsync(confirmationLink);
|
||||||
|
_currentUser.EmailConfirmed.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task When_I_submit_organization_name_and_valid_password(string orgName, string password)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_organization = await _organizationService.SetupOrganizationAsync(_currentUser.Id, orgName, password);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_setupError = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Then_a_new_organization_should_be_created_with_expected_properties()
|
||||||
|
{
|
||||||
|
_organization.ShouldNotBeNull();
|
||||||
|
_organization.Name.ShouldBe("Acme Corp");
|
||||||
|
_organization.CreatedBy.ShouldBe(_currentUser.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void And_the_user_should_be_linked_to_the_organization_in_user_organizations()
|
||||||
|
{
|
||||||
|
var userOrg = _userOrganizationService.GetUserOrganization(_currentUser.Id, _organization.Id);
|
||||||
|
userOrg.ShouldNotBeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void And_tenant_tables_should_be_created_for_the_organization()
|
||||||
|
{
|
||||||
|
var tenantTablesExist = _tenantService.ValidateTenantTablesExist(_organization.Id);
|
||||||
|
tenantTablesExist.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void And_I_should_be_logged_into_the_system()
|
||||||
|
{
|
||||||
|
var isAuthenticated = _authService.IsUserAuthenticated(_currentUser.Id);
|
||||||
|
isAuthenticated.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task When_I_submit_organization_name_without_password(string orgName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _organizationService.SetupOrganizationAsync(_currentUser.Id, orgName, "");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_setupError = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Then_organization_setup_should_fail_with_error(string expectedErrorMessage)
|
||||||
|
{
|
||||||
|
_setupError.ShouldNotBeNull();
|
||||||
|
_setupError.Message.ShouldBe(expectedErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Given_user_has_completed_initial_setup(string email)
|
||||||
|
{
|
||||||
|
await Given_user_has_confirmed_their_email(email);
|
||||||
|
await When_I_submit_organization_name_and_valid_password("First Org", "ValidP@ssw0rd");
|
||||||
|
_userOrganizations = new List<Organization> { _organization };
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task When_I_create_a_new_organization(string orgName)
|
||||||
|
{
|
||||||
|
var newOrg = await _organizationService.CreateOrganizationAsync(_currentUser.Id, orgName);
|
||||||
|
_userOrganizations.Add(newOrg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Then_a_new_organization_entry_should_be_created()
|
||||||
|
{
|
||||||
|
_userOrganizations.Count.ShouldBe(2);
|
||||||
|
_userOrganizations[1].Name.ShouldBe("Second Org");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void And_the_user_should_be_linked_to_both_organizations()
|
||||||
|
{
|
||||||
|
var userOrgs = _userOrganizationService.GetUserOrganizations(_currentUser.Id);
|
||||||
|
userOrgs.Count.ShouldBe(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
[FeatureDescription(@"As a system administrator
|
||||||
|
I want to ensure account security is maintained
|
||||||
|
So users' data remains protected")]
|
||||||
|
public partial class AccountSecuritySpecs : FeatureFixture
|
||||||
|
{
|
||||||
|
IUserService _userService;
|
||||||
|
IEmailService _emailService;
|
||||||
|
IOrganizationService _organizationService;
|
||||||
|
IAuthService _authService;
|
||||||
|
|
||||||
|
protected User _currentUser;
|
||||||
|
protected DateTime? _lockoutEnd;
|
||||||
|
protected bool _isLocked;
|
||||||
|
protected bool _loginSuccessful;
|
||||||
|
|
||||||
|
public async Task Given_user_exists(string email)
|
||||||
|
{
|
||||||
|
_currentUser = await _userService.GetUserByEmailAsync(email);
|
||||||
|
if (_currentUser == null)
|
||||||
|
{
|
||||||
|
_currentUser = await _userService.CreateUserAsync(email, "Test User");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task When_I_attempt_5_failed_logins_within_5_minutes()
|
||||||
|
{
|
||||||
|
for (var i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _authService.AttemptLoginAsync(_currentUser.Email, "WrongPassword");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Expected exception with wrong password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Then_the_account_should_be_locked()
|
||||||
|
{
|
||||||
|
_currentUser = _userService.GetUserByEmail(_currentUser.Email);
|
||||||
|
_isLocked = _currentUser.IsLocked;
|
||||||
|
_isLocked.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void And_lockout_end_should_be_set()
|
||||||
|
{
|
||||||
|
_lockoutEnd = _currentUser.LockoutEnd;
|
||||||
|
_lockoutEnd.ShouldNotBeNull();
|
||||||
|
_lockoutEnd.Value.ShouldBeGreaterThan(DateTime.UtcNow);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task And_subsequent_login_attempts_should_fail_until_lockout_end()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_loginSuccessful = await _authService.AttemptLoginAsync(_currentUser.Email, _currentUser.Password);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_loginSuccessful = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_loginSuccessful.ShouldBeFalse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
using LightBDD.Framework;
|
using LightBDD.Framework;
|
||||||
using LightBDD.Framework.Scenarios;
|
using LightBDD.Framework.Scenarios;
|
||||||
using LightBDD.MsTest3;
|
using LightBDD.MsTest3;
|
||||||
|
|
||||||
namespace PlanTempus.X.BDD.Scenarios;
|
namespace PlanTempus.X.BDD.Scenarios;
|
||||||
|
|
||||||
[TestClass]
|
[TestClass]
|
||||||
|
|
@ -18,4 +17,103 @@ public partial class UserRegistrationSpecs : FeatureFixtures.UserRegistrationSpe
|
||||||
_ => Then_a_confirmation_email_should_be_sent()
|
_ => Then_a_confirmation_email_should_be_sent()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Scenario]
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Reject_duplicate_email_registration()
|
||||||
|
{
|
||||||
|
await Runner.RunScenarioAsync(
|
||||||
|
_ => Given_a_user_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")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public partial class EmailConfirmationSpecs : FeatureFixtures.EmailConfirmationSpecs
|
||||||
|
{
|
||||||
|
[Scenario]
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Confirm_valid_email_address()
|
||||||
|
{
|
||||||
|
await Runner.RunScenarioAsync(
|
||||||
|
_ => Given_a_user_exists_with_unconfirmed_email("test@example.com"),
|
||||||
|
_ => When_I_click_the_valid_confirmation_link_for("test@example.com"),
|
||||||
|
_ => Then_the_users_email_confirmed_should_be_true(),
|
||||||
|
_ => And_I_should_be_redirected_to_the_welcome_page()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Scenario]
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Handle_invalid_confirmation_link()
|
||||||
|
{
|
||||||
|
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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public partial class OrganizationSetupSpecs : FeatureFixtures.OrganizationSetupSpecs
|
||||||
|
{
|
||||||
|
[Scenario]
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Complete_organization_setup_after_confirmation()
|
||||||
|
{
|
||||||
|
await Runner.RunScenarioAsync(
|
||||||
|
_ => Given_user_has_confirmed_their_email("test@example.com"),
|
||||||
|
_ => When_I_submit_organization_name_and_valid_password("Acme Corp", "ValidP@ssw0rd"),
|
||||||
|
_ => Then_a_new_organization_should_be_created_with_expected_properties(),
|
||||||
|
_ => And_the_user_should_be_linked_to_the_organization_in_user_organizations(),
|
||||||
|
_ => And_tenant_tables_should_be_created_for_the_organization(),
|
||||||
|
_ => And_I_should_be_logged_into_the_system()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Scenario]
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Prevent_organization_setup_without_password()
|
||||||
|
{
|
||||||
|
await Runner.RunScenarioAsync(
|
||||||
|
_ => Given_user_has_confirmed_their_email("test@example.com"),
|
||||||
|
_ => When_I_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_user_has_completed_initial_setup("test@example.com"),
|
||||||
|
_ => When_I_create_a_new_organization("Second Org"),
|
||||||
|
_ => Then_a_new_organization_entry_should_be_created(),
|
||||||
|
_ => And_the_user_should_be_linked_to_both_organizations()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public partial class AccountSecuritySpecs : FeatureFixtures.AccountSecuritySpecs
|
||||||
|
{
|
||||||
|
[Scenario]
|
||||||
|
[TestMethod]
|
||||||
|
public async Task User_lockout_after_multiple_failed_attempts()
|
||||||
|
{
|
||||||
|
await Runner.RunScenarioAsync(
|
||||||
|
_ => Given_user_exists("test@example.com"),
|
||||||
|
_ => When_I_attempt_5_failed_logins_within_5_minutes(),
|
||||||
|
_ => Then_the_account_should_be_locked(),
|
||||||
|
_ => And_lockout_end_should_be_set(),
|
||||||
|
_ => And_subsequent_login_attempts_should_fail_until_lockout_end()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue