using LightBDD.Framework; using LightBDD.Framework.Scenarios; using LightBDD.MsTest3; using PlanTempus.X.Services; 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 UserRegistrationSpecs : FeatureFixture { protected User _currentUser; protected string _currentEmail; protected Exception _registrationError; IUserService _userService; IEmailService _emailService; IOrganizationService _organizationService; public async Task Given_no_user_exists_with_email(string email) { // Ensure user doesn't exist with email var user = await _userService.GetUserByEmailAsync(email); user.ShouldBeNull(); _currentEmail = email; } public async Task When_I_submit_registration_with_name_and_email(string name, string email) { try { _currentUser = await _userService.CreateUserAsync(email, name); _currentEmail = email; } catch (Exception ex) { _registrationError = ex; } } public async Task When_I_submit_registration_with_email(string email) { try { _currentUser = await _userService.CreateUserAsync(email, "Test User"); _currentEmail = email; } catch (Exception ex) { _registrationError = ex; } } public void Then_a_new_user_should_be_created_with_email_and_confirmation_status(string email, bool confirmationStatus) { _currentUser.ShouldNotBeNull(); _currentUser.Email.ShouldBe(email); _currentUser.EmailConfirmed.ShouldBe(confirmationStatus); } public void Then_a_confirmation_email_should_be_sent() { 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 _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 }; } 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(); } }