2025-03-03 00:42:20 +01:00
|
|
|
using LightBDD.Framework;
|
|
|
|
|
using LightBDD.Framework.Scenarios;
|
|
|
|
|
using LightBDD.MsTest3;
|
|
|
|
|
using PlanTempus.X.Services;
|
|
|
|
|
using Shouldly;
|
|
|
|
|
|
|
|
|
|
namespace PlanTempus.X.BDD.FeatureFixtures;
|
2026-01-09 22:14:46 +01:00
|
|
|
|
|
|
|
|
[TestClass]
|
|
|
|
|
[FeatureDescription(@"As a system administrator
|
|
|
|
|
I want to ensure account security is maintained
|
|
|
|
|
So users' data remains protected")]
|
2025-03-03 00:42:20 +01:00
|
|
|
public partial class AccountSecuritySpecs : FeatureFixture
|
|
|
|
|
{
|
2026-01-09 22:14:46 +01:00
|
|
|
IAccountService _accountService;
|
2025-03-03 00:42:20 +01:00
|
|
|
IEmailService _emailService;
|
|
|
|
|
IOrganizationService _organizationService;
|
|
|
|
|
IAuthService _authService;
|
|
|
|
|
|
2026-01-09 22:14:46 +01:00
|
|
|
protected Account _currentAccount;
|
2025-03-03 00:42:20 +01:00
|
|
|
protected DateTime? _lockoutEnd;
|
|
|
|
|
protected bool _isLocked;
|
|
|
|
|
protected bool _loginSuccessful;
|
|
|
|
|
|
2026-01-09 22:14:46 +01:00
|
|
|
public async Task Given_account_exists(string email)
|
2025-03-03 00:42:20 +01:00
|
|
|
{
|
2026-01-09 22:14:46 +01:00
|
|
|
_currentAccount = await _accountService.GetAccountByEmailAsync(email);
|
|
|
|
|
if (_currentAccount == null)
|
2025-03-03 00:42:20 +01:00
|
|
|
{
|
2026-01-09 22:14:46 +01:00
|
|
|
_currentAccount = await _accountService.CreateAccountAsync(email, "TestPassword123!");
|
2025-03-03 00:42:20 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task When_I_attempt_5_failed_logins_within_5_minutes()
|
|
|
|
|
{
|
|
|
|
|
for (var i = 0; i < 5; i++)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2026-01-09 22:14:46 +01:00
|
|
|
await _authService.AttemptLoginAsync(_currentAccount.Email, "WrongPassword");
|
2025-03-03 00:42:20 +01:00
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
// Expected exception with wrong password
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task Then_the_account_should_be_locked()
|
|
|
|
|
{
|
2026-01-09 22:14:46 +01:00
|
|
|
_currentAccount = _accountService.GetAccountByEmail(_currentAccount.Email);
|
|
|
|
|
_isLocked = _currentAccount.IsLocked;
|
2025-03-03 00:42:20 +01:00
|
|
|
_isLocked.ShouldBeTrue();
|
|
|
|
|
|
|
|
|
|
await Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task And_lockout_end_should_be_set()
|
|
|
|
|
{
|
2026-01-09 22:14:46 +01:00
|
|
|
_lockoutEnd = _currentAccount.LockoutEnd;
|
2025-03-03 00:42:20 +01:00
|
|
|
_lockoutEnd.ShouldNotBeNull();
|
|
|
|
|
_lockoutEnd.Value.ShouldBeGreaterThan(DateTime.UtcNow);
|
|
|
|
|
|
|
|
|
|
await Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task And_subsequent_login_attempts_should_fail_until_lockout_end()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2026-01-09 22:14:46 +01:00
|
|
|
_loginSuccessful = await _authService.AttemptLoginAsync(_currentAccount.Email, _currentAccount.Password);
|
2025-03-03 00:42:20 +01:00
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
_loginSuccessful = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_loginSuccessful.ShouldBeFalse();
|
|
|
|
|
}
|
2026-01-09 22:14:46 +01:00
|
|
|
}
|