diff --git a/.gitignore b/.gitignore index 1ee5385..8c05519 100644 --- a/.gitignore +++ b/.gitignore @@ -360,3 +360,5 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd + +nul diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4798424 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} \ No newline at end of file diff --git a/Core/Entities/Users/User.cs b/Core/Entities/Accounts/Account.cs similarity index 73% rename from Core/Entities/Users/User.cs rename to Core/Entities/Accounts/Account.cs index 805c028..b991f6b 100644 --- a/Core/Entities/Users/User.cs +++ b/Core/Entities/Accounts/Account.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PlanTempus.Core.Entities.Users +namespace PlanTempus.Core.Entities.Accounts { - public class User + public class Account { public int Id { get; set; } public string Email { get; set; } @@ -26,9 +20,9 @@ namespace PlanTempus.Core.Entities.Users public bool IsActive { get; set; } } - public class UserOrganization + public class AccountOrganization { - public int UserId { get; set; } + public int AccountId { get; set; } public int OrganizationId { get; set; } public DateTime CreatedDate { get; set; } } diff --git a/Database/Core/UserService.cs b/Database/Core/AccountService.cs similarity index 61% rename from Database/Core/UserService.cs rename to Database/Core/AccountService.cs index e74d291..0ed59ba 100644 --- a/Database/Core/UserService.cs +++ b/Database/Core/AccountService.cs @@ -1,24 +1,24 @@ -using Insight.Database; +using Insight.Database; using PlanTempus.Core; -using PlanTempus.Core.Entities.Users; +using PlanTempus.Core.Entities.Accounts; using System.Data; namespace PlanTempus.Database.Core { - public class UserService + public class AccountService { - public record UserCreateCommand(string CorrelationId, string Email, string Password); + public record AccountCreateCommand(string CorrelationId, string Email, string Password); private readonly IDbConnection _db; - public UserService(IDbConnection db) + public AccountService(IDbConnection db) { _db = db; } - public async Task CreateUser(UserCreateCommand command) + public async Task CreateAccount(AccountCreateCommand command) { - var user = new User + var account = new Account { Email = command.Email, PasswordHash = new SecureTokenizer().TokenizeText(command.Password), @@ -27,13 +27,13 @@ namespace PlanTempus.Database.Core CreatedDate = DateTime.UtcNow }; - var userId = await _db.ExecuteScalarAsync(@$" - INSERT INTO users (email, password_hash, security_stamp, email_confirmed, created_at) + var accountId = await _db.ExecuteScalarAsync(@$" + INSERT INTO accounts (email, password_hash, security_stamp, email_confirmed, created_at) VALUES (@Email, @PasswordHash, @SecurityStamp, @EmailConfirmed, @CreatedDate) - RETURNING id", user); + RETURNING id", account); } - public async Task CreateOrganization(int userId, string organizationConnectionString) + public async Task CreateOrganization(int accountId, string organizationConnectionString) { var schema = "dev"; @@ -46,7 +46,7 @@ namespace PlanTempus.Database.Core { ConnectionString = organizationConnectionString, CreatedDate = DateTime.UtcNow, - CreatedBy = userId, + CreatedBy = accountId, IsActive = true }; @@ -55,17 +55,17 @@ namespace PlanTempus.Database.Core VALUES (@ConnectionString, @CreatedDate, @IsActive) RETURNING id", organization); - // Link user to organization - var userOrganization = new UserOrganization + // Link account to organization + var accountOrganization = new AccountOrganization { - UserId = userId, + AccountId = accountId, OrganizationId = organizationId, CreatedDate = DateTime.UtcNow }; await _db.ExecuteAsync(@$" - INSERT INTO {schema}.user_organizations (user_id, organization_id, created_date) - VALUES (@UserId, @OrganizationId, @CreatedDate)", userOrganization); + INSERT INTO {schema}.account_organizations (account_id, organization_id, created_date) + VALUES (@AccountId, @OrganizationId, @CreatedDate)", accountOrganization); transaction.Commit(); } @@ -76,4 +76,4 @@ namespace PlanTempus.Database.Core } } } -} \ No newline at end of file +} diff --git a/Database/Core/DDL/SetupIdentitySystem.cs b/Database/Core/DDL/SetupIdentitySystem.cs index 01a883f..3547ff2 100644 --- a/Database/Core/DDL/SetupIdentitySystem.cs +++ b/Database/Core/DDL/SetupIdentitySystem.cs @@ -35,9 +35,9 @@ namespace PlanTempus.Database.Core.DDL using var transaction = conn.OpenWithTransaction(); try { - CreateUsersTable(conn); + CreateAccountsTable(conn); CreateOrganizationsTable(conn); - CreateUserOrganizationsTable(conn); + CreateAccountOrganizationsTable(conn); SetupRLS(conn); transaction.Commit(); @@ -51,12 +51,12 @@ namespace PlanTempus.Database.Core.DDL /// - /// Creates the users table + /// Creates the accounts table /// - void CreateUsersTable(IDbConnection db) + void CreateAccountsTable(IDbConnection db) { var sql = @$" - CREATE TABLE IF NOT EXISTS {_command.Schema}.users ( + CREATE TABLE IF NOT EXISTS {_command.Schema}.accounts ( id SERIAL PRIMARY KEY, email VARCHAR(256) NOT NULL UNIQUE, password_hash VARCHAR(256) NOT NULL, @@ -91,17 +91,17 @@ namespace PlanTempus.Database.Core.DDL } /// - /// Creates the user_organizations table + /// Creates the account_organizations table /// - void CreateUserOrganizationsTable(IDbConnection db) + void CreateAccountOrganizationsTable(IDbConnection db) { var sql = @$" - CREATE TABLE IF NOT EXISTS {_command.Schema}.user_organizations ( - user_id INTEGER NOT NULL REFERENCES {_command.Schema}.users(id), + CREATE TABLE IF NOT EXISTS {_command.Schema}.account_organizations ( + account_id INTEGER NOT NULL REFERENCES {_command.Schema}.accounts(id), organization_id INTEGER NOT NULL REFERENCES {_command.Schema}.organizations(id), pin_code VARCHAR(10) NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (user_id, organization_id) + PRIMARY KEY (account_id, organization_id) );"; db.ExecuteSql(sql); @@ -109,24 +109,24 @@ namespace PlanTempus.Database.Core.DDL } /// - /// Sets up Row Level Security (RLS) for the organizations and user_organizations tables. + /// Sets up Row Level Security (RLS) for the organizations and account_organizations tables. /// void SetupRLS(IDbConnection db) { var sql = new[] { $"ALTER TABLE {_command.Schema}.organizations ENABLE ROW LEVEL SECURITY;", - $"ALTER TABLE {_command.Schema}.user_organizations ENABLE ROW LEVEL SECURITY;", + $"ALTER TABLE {_command.Schema}.account_organizations ENABLE ROW LEVEL SECURITY;", $"DROP POLICY IF EXISTS organization_access ON {_command.Schema}.organizations;", @$"CREATE POLICY organization_access ON {_command.Schema}.organizations USING (id IN ( - SELECT organization_id - FROM {_command.Schema}.user_organizations - WHERE user_id = current_setting('app.user_id', TRUE)::INTEGER + SELECT organization_id + FROM {_command.Schema}.account_organizations + WHERE account_id = current_setting('app.account_id', TRUE)::INTEGER )) WITH CHECK (true);", - $"DROP POLICY IF EXISTS user_organization_access ON {_command.Schema}.user_organizations;", - @$"CREATE POLICY user_organization_access ON {_command.Schema}.user_organizations - USING (user_id = current_setting('app.user_id', TRUE)::INTEGER) WITH CHECK (true);" + $"DROP POLICY IF EXISTS account_organization_access ON {_command.Schema}.account_organizations;", + @$"CREATE POLICY account_organization_access ON {_command.Schema}.account_organizations + USING (account_id = current_setting('app.account_id', TRUE)::INTEGER) WITH CHECK (true);" }; foreach (var statement in sql) diff --git a/PlanTempus.Components/Users/Create/CommandHandlerDecorator.cs b/PlanTempus.Components/Accounts/Create/CommandHandlerDecorator.cs similarity index 93% rename from PlanTempus.Components/Users/Create/CommandHandlerDecorator.cs rename to PlanTempus.Components/Accounts/Create/CommandHandlerDecorator.cs index 6da5c8a..5e4d015 100644 --- a/PlanTempus.Components/Users/Create/CommandHandlerDecorator.cs +++ b/PlanTempus.Components/Accounts/Create/CommandHandlerDecorator.cs @@ -1,9 +1,9 @@ -using System.Diagnostics; +using System.Diagnostics; using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.DataContracts; using PlanTempus.Core.CommandQueries; -namespace PlanTempus.Components.Users.Create; +namespace PlanTempus.Components.Accounts.Create; public class CommandHandlerDecorator( ICommandHandler decoratedHandler, @@ -14,30 +14,30 @@ public class CommandHandlerDecorator( command.TransactionId = Guid.NewGuid(); using var operation = telemetryClient.StartOperation($"Handle {decoratedHandler.GetType().FullName}", - command.CorrelationId.ToString()); - try - { - operation.Telemetry.Properties["CorrelationId"] = command.CorrelationId.ToString(); - operation.Telemetry.Properties["TransactionId"] = command.TransactionId.ToString(); - - var result = await decoratedHandler.Handle(command); - - operation.Telemetry.Properties["RequestId"] = result.RequestId.ToString(); - operation.Telemetry.Success = true; - - return result; - } - catch (Exception ex) - { - operation.Telemetry.Success = false; - - telemetryClient.TrackException(ex, new Dictionary - { - ["CorrelationId"] = command.CorrelationId.ToString(), - ["Command"] = command.GetType().Name, - ["CommandHandler"] = decoratedHandler.GetType().FullName - }); - throw; + command.CorrelationId.ToString()); + try + { + operation.Telemetry.Properties["CorrelationId"] = command.CorrelationId.ToString(); + operation.Telemetry.Properties["TransactionId"] = command.TransactionId.ToString(); + + var result = await decoratedHandler.Handle(command); + + operation.Telemetry.Properties["RequestId"] = result.RequestId.ToString(); + operation.Telemetry.Success = true; + + return result; + } + catch (Exception ex) + { + operation.Telemetry.Success = false; + + telemetryClient.TrackException(ex, new Dictionary + { + ["CorrelationId"] = command.CorrelationId.ToString(), + ["Command"] = command.GetType().Name, + ["CommandHandler"] = decoratedHandler.GetType().FullName + }); + throw; } } -} \ No newline at end of file +} diff --git a/PlanTempus.Components/Users/Create/CreateUserCommand.cs b/PlanTempus.Components/Accounts/Create/CreateAccountCommand.cs similarity index 68% rename from PlanTempus.Components/Users/Create/CreateUserCommand.cs rename to PlanTempus.Components/Accounts/Create/CreateAccountCommand.cs index 5d0ef11..99fda2a 100644 --- a/PlanTempus.Components/Users/Create/CreateUserCommand.cs +++ b/PlanTempus.Components/Accounts/Create/CreateAccountCommand.cs @@ -1,11 +1,11 @@ using PlanTempus.Core.CommandQueries; -namespace PlanTempus.Components.Users.Create +namespace PlanTempus.Components.Accounts.Create { - public class CreateUserCommand : Command + public class CreateAccountCommand : Command { public required string Email { get; set; } public required string Password { get; set; } public bool IsActive { get; set; } = true; } -} \ No newline at end of file +} diff --git a/PlanTempus.Components/Users/Create/CreateUserController.cs b/PlanTempus.Components/Accounts/Create/CreateAccountController.cs similarity index 75% rename from PlanTempus.Components/Users/Create/CreateUserController.cs rename to PlanTempus.Components/Accounts/Create/CreateAccountController.cs index 487b0dc..db8238b 100644 --- a/PlanTempus.Components/Users/Create/CreateUserController.cs +++ b/PlanTempus.Components/Accounts/Create/CreateAccountController.cs @@ -1,14 +1,14 @@ using Microsoft.AspNetCore.Mvc; using PlanTempus.Core.CommandQueries; -namespace PlanTempus.Components.Users.Create +namespace PlanTempus.Components.Accounts.Create { [ApiController] - [Route("api/users")] - public class CreateUserController(CreateUserHandler handler, CreateUserValidator validator) : ControllerBase + [Route("api/accounts")] + public class CreateAccountController(CreateAccountHandler handler, CreateAccountValidator validator) : ControllerBase { [HttpPost] - public async Task> Create([FromBody] CreateUserCommand command) + public async Task> Create([FromBody] CreateAccountCommand command) { try { @@ -28,4 +28,4 @@ namespace PlanTempus.Components.Users.Create } } } -} \ No newline at end of file +} diff --git a/PlanTempus.Components/Users/Create/CreateUserHandler.cs b/PlanTempus.Components/Accounts/Create/CreateAccountHandler.cs similarity index 72% rename from PlanTempus.Components/Users/Create/CreateUserHandler.cs rename to PlanTempus.Components/Accounts/Create/CreateAccountHandler.cs index 42bb8dc..4264c4f 100644 --- a/PlanTempus.Components/Users/Create/CreateUserHandler.cs +++ b/PlanTempus.Components/Accounts/Create/CreateAccountHandler.cs @@ -1,25 +1,25 @@ using Insight.Database; using Microsoft.ApplicationInsights; using Npgsql; -using PlanTempus.Components.Users.Exceptions; +using PlanTempus.Components.Accounts.Exceptions; using PlanTempus.Core; using PlanTempus.Core.CommandQueries; using PlanTempus.Core.Database; -namespace PlanTempus.Components.Users.Create +namespace PlanTempus.Components.Accounts.Create { - public class CreateUserHandler( + public class CreateAccountHandler( IDatabaseOperations databaseOperations, - ISecureTokenizer secureTokenizer) : ICommandHandler + ISecureTokenizer secureTokenizer) : ICommandHandler { - public async Task Handle(CreateUserCommand command) + public async Task Handle(CreateAccountCommand command) { - using var db = databaseOperations.CreateScope(nameof(CreateUserHandler)); + using var db = databaseOperations.CreateScope(nameof(CreateAccountHandler)); try { var sql = @" - INSERT INTO system.users(email , password_hash, security_stamp, email_confirmed, - access_failed_count, lockout_enabled, + INSERT INTO system.accounts(email , password_hash, security_stamp, email_confirmed, + access_failed_count, lockout_enabled, is_active) VALUES(@Email, @PasswordHash, @SecurityStamp, @EmailConfirmed, @AccessFailedCount, @LockoutEnabled, @IsActive) @@ -35,11 +35,10 @@ namespace PlanTempus.Components.Users.Create LockoutEnabled = false, command.IsActive, }); - //lav en mapping mellem requestid og userid - + return new CommandResponse(command.CorrelationId, command.GetType().Name, command.TransactionId); } - catch (PostgresException ex) when (ex.SqlState == "23505" && ex.ConstraintName.Equals("users_email_key", StringComparison.InvariantCultureIgnoreCase)) + catch (PostgresException ex) when (ex.SqlState == "23505" && ex.ConstraintName.Equals("accounts_email_key", StringComparison.InvariantCultureIgnoreCase)) { db.Error(ex); throw new EmailAlreadyRegistreredException(); @@ -51,4 +50,4 @@ namespace PlanTempus.Components.Users.Create } } } -} \ No newline at end of file +} diff --git a/PlanTempus.Components/Users/Create/CreateUserResult.cs b/PlanTempus.Components/Accounts/Create/CreateAccountResult.cs similarity index 64% rename from PlanTempus.Components/Users/Create/CreateUserResult.cs rename to PlanTempus.Components/Accounts/Create/CreateAccountResult.cs index 24c92c8..a2eea82 100644 --- a/PlanTempus.Components/Users/Create/CreateUserResult.cs +++ b/PlanTempus.Components/Accounts/Create/CreateAccountResult.cs @@ -1,11 +1,11 @@ -namespace PlanTempus.Components.Users.Create +namespace PlanTempus.Components.Accounts.Create { - public class CreateUserResult + public class CreateAccountResult { public long Id { get; set; } public string Email { get; set; } public bool IsActive { get; set; } public DateTime CreatedAt { get; set; } } -} \ No newline at end of file +} diff --git a/PlanTempus.Components/Users/Create/CreateUserValidator.cs b/PlanTempus.Components/Accounts/Create/CreateAccountValidator.cs similarity index 81% rename from PlanTempus.Components/Users/Create/CreateUserValidator.cs rename to PlanTempus.Components/Accounts/Create/CreateAccountValidator.cs index 6007115..1a894fc 100644 --- a/PlanTempus.Components/Users/Create/CreateUserValidator.cs +++ b/PlanTempus.Components/Accounts/Create/CreateAccountValidator.cs @@ -1,10 +1,10 @@ using FluentValidation; -namespace PlanTempus.Components.Users.Create +namespace PlanTempus.Components.Accounts.Create { - public class CreateUserValidator : AbstractValidator + public class CreateAccountValidator : AbstractValidator { - public CreateUserValidator() + public CreateAccountValidator() { RuleFor(x => x.Email) .NotEmpty().WithMessage("Email skal angives.") @@ -20,4 +20,4 @@ namespace PlanTempus.Components.Users.Create .Matches("[^a-zA-Z0-9]").WithMessage("Password skal indeholde mindst ét specialtegn."); } } -} \ No newline at end of file +} diff --git a/PlanTempus.Components/Users/Exceptions/EmailAlreadyRegistreredException.cs b/PlanTempus.Components/Accounts/Exceptions/EmailAlreadyRegistreredException.cs similarity index 50% rename from PlanTempus.Components/Users/Exceptions/EmailAlreadyRegistreredException.cs rename to PlanTempus.Components/Accounts/Exceptions/EmailAlreadyRegistreredException.cs index c098df4..3376cd5 100644 --- a/PlanTempus.Components/Users/Exceptions/EmailAlreadyRegistreredException.cs +++ b/PlanTempus.Components/Accounts/Exceptions/EmailAlreadyRegistreredException.cs @@ -1,6 +1,6 @@ -namespace PlanTempus.Components.Users.Exceptions; +namespace PlanTempus.Components.Accounts.Exceptions; public class EmailAlreadyRegistreredException: Exception { - -} \ No newline at end of file + +} diff --git a/PlanTempus.Components/CommandHandler.cs b/PlanTempus.Components/CommandHandler.cs index 93c55cb..f503920 100644 --- a/PlanTempus.Components/CommandHandler.cs +++ b/PlanTempus.Components/CommandHandler.cs @@ -1,5 +1,4 @@ using Autofac; -using PlanTempus.Components.Users.Create; using PlanTempus.Core.CommandQueries; namespace PlanTempus.Components; diff --git a/PlanTempus.Components/ModuleRegistry/CommandModule.cs b/PlanTempus.Components/ModuleRegistry/CommandModule.cs index 23a1579..8f6c890 100644 --- a/PlanTempus.Components/ModuleRegistry/CommandModule.cs +++ b/PlanTempus.Components/ModuleRegistry/CommandModule.cs @@ -1,5 +1,5 @@ using Autofac; -using PlanTempus.Components.Users.Create; +using PlanTempus.Components.Accounts.Create; using PlanTempus.Core.CommandQueries; using PlanTempus.Core.SeqLogging; diff --git a/PlanTempus.Components/Organizations/Create/CreateOrganizationCommand.cs b/PlanTempus.Components/Organizations/Create/CreateOrganizationCommand.cs index 57234c1..0d7cc81 100644 --- a/PlanTempus.Components/Organizations/Create/CreateOrganizationCommand.cs +++ b/PlanTempus.Components/Organizations/Create/CreateOrganizationCommand.cs @@ -3,6 +3,6 @@ public class CreateOrganizationCommand { public string ConnectionString { get; set; } - public Guid UserId { get; set; } + public Guid AccountId { get; set; } } } diff --git a/PlanTempus.Components/Organizations/Create/CreateOrganizationHandler.cs b/PlanTempus.Components/Organizations/Create/CreateOrganizationHandler.cs index 06517f2..abd91b0 100644 --- a/PlanTempus.Components/Organizations/Create/CreateOrganizationHandler.cs +++ b/PlanTempus.Components/Organizations/Create/CreateOrganizationHandler.cs @@ -26,9 +26,9 @@ namespace PlanTempus.Components.Organizations.Create var orgResult = data.First(); await db.Connection.ExecuteAsync(@$" - INSERT INTO user_organizations (user_id, organization_id) - VALUES (@UserId, @OrganizationId)", - new { command.UserId, OrganizationId = orgResult.Id }); + INSERT INTO account_organizations (account_id, organization_id) + VALUES (@AccountId, @OrganizationId)", + new { command.AccountId, OrganizationId = orgResult.Id }); transaction.Commit(); diff --git a/PlanTempus.X.BDD/Class1.cs b/PlanTempus.X.BDD/Class1.cs index 6a7a5b6..b10a77b 100644 --- a/PlanTempus.X.BDD/Class1.cs +++ b/PlanTempus.X.BDD/Class1.cs @@ -1,4 +1,4 @@ -// PlanTempus.X.Services.cs +// PlanTempus.X.Services.cs using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -6,11 +6,10 @@ using System.Threading.Tasks; namespace PlanTempus.X.Services { // Models - public class User + public class Account { 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; } @@ -24,19 +23,19 @@ namespace PlanTempus.X.Services public string CreatedBy { get; set; } } - public class UserOrganization + public class AccountOrganization { - public string UserId { get; set; } + public string AccountId { get; set; } public string OrganizationId { get; set; } public string Role { get; set; } } // Service interfaces - public interface IUserService + public interface IAccountService { - Task CreateUserAsync(string email, string name); - Task GetUserByEmailAsync(string email); - User GetUserByEmail(string email); + Task CreateAccountAsync(string email, string password); + Task GetAccountByEmailAsync(string email); + Account GetAccountByEmail(string email); Task ConfirmEmailAsync(string confirmationToken); } @@ -48,14 +47,14 @@ namespace PlanTempus.X.Services public interface IOrganizationService { - Task SetupOrganizationAsync(string userId, string orgName, string password); - Task CreateOrganizationAsync(string userId, string orgName); + Task SetupOrganizationAsync(string accountId, string orgName, string password); + Task CreateOrganizationAsync(string accountId, string orgName); } - public interface IUserOrganizationService + public interface IAccountOrganizationService { - UserOrganization GetUserOrganization(string userId, string organizationId); - List GetUserOrganizations(string userId); + AccountOrganization GetAccountOrganization(string accountId, string organizationId); + List GetAccountOrganizations(string accountId); } public interface ITenantService @@ -65,26 +64,26 @@ namespace PlanTempus.X.Services public interface IAuthService { - bool IsUserAuthenticated(string userId); + bool IsAccountAuthenticated(string accountId); Task AttemptLoginAsync(string email, string password); } // Service implementations - public class UserService : IUserService + public class AccountService : IAccountService { - public async Task CreateUserAsync(string email, string name) + public async Task CreateAccountAsync(string email, string password) { - throw new NotImplementedException("CreateUserAsync not implemented"); + throw new NotImplementedException("CreateAccountAsync not implemented"); } - public async Task GetUserByEmailAsync(string email) + public async Task GetAccountByEmailAsync(string email) { - throw new NotImplementedException("GetUserByEmailAsync not implemented"); + throw new NotImplementedException("GetAccountByEmailAsync not implemented"); } - public User GetUserByEmail(string email) + public Account GetAccountByEmail(string email) { - throw new NotImplementedException("GetUserByEmail not implemented"); + throw new NotImplementedException("GetAccountByEmail not implemented"); } public async Task ConfirmEmailAsync(string confirmationToken) @@ -108,27 +107,27 @@ namespace PlanTempus.X.Services public class OrganizationService : IOrganizationService { - public async Task SetupOrganizationAsync(string userId, string orgName, string password) + public async Task SetupOrganizationAsync(string accountId, string orgName, string password) { throw new NotImplementedException("SetupOrganizationAsync not implemented"); } - public async Task CreateOrganizationAsync(string userId, string orgName) + public async Task CreateOrganizationAsync(string accountId, string orgName) { throw new NotImplementedException("CreateOrganizationAsync not implemented"); } } - public class UserOrganizationService : IUserOrganizationService + public class AccountOrganizationService : IAccountOrganizationService { - public UserOrganization GetUserOrganization(string userId, string organizationId) + public AccountOrganization GetAccountOrganization(string accountId, string organizationId) { - throw new NotImplementedException("GetUserOrganization not implemented"); + throw new NotImplementedException("GetAccountOrganization not implemented"); } - public List GetUserOrganizations(string userId) + public List GetAccountOrganizations(string accountId) { - throw new NotImplementedException("GetUserOrganizations not implemented"); + throw new NotImplementedException("GetAccountOrganizations not implemented"); } } @@ -142,9 +141,9 @@ namespace PlanTempus.X.Services public class AuthService : IAuthService { - public bool IsUserAuthenticated(string userId) + public bool IsAccountAuthenticated(string accountId) { - throw new NotImplementedException("IsUserAuthenticated not implemented"); + throw new NotImplementedException("IsAccountAuthenticated not implemented"); } public async Task AttemptLoginAsync(string email, string password) @@ -152,4 +151,4 @@ namespace PlanTempus.X.Services throw new NotImplementedException("AttemptLoginAsync not implemented"); } } -} \ No newline at end of file +} diff --git a/PlanTempus.X.BDD/FeatureFixtures/UserRegistrationSpecs.cs b/PlanTempus.X.BDD/FeatureFixtures/AccountRegistrationSpecs.cs similarity index 52% rename from PlanTempus.X.BDD/FeatureFixtures/UserRegistrationSpecs.cs rename to PlanTempus.X.BDD/FeatureFixtures/AccountRegistrationSpecs.cs index 2c822db..dc5ab12 100644 --- a/PlanTempus.X.BDD/FeatureFixtures/UserRegistrationSpecs.cs +++ b/PlanTempus.X.BDD/FeatureFixtures/AccountRegistrationSpecs.cs @@ -1,4 +1,4 @@ -using LightBDD.Framework; +using LightBDD.Framework; using LightBDD.Framework.Scenarios; using LightBDD.MsTest3; using PlanTempus.X.Services; @@ -9,29 +9,29 @@ namespace PlanTempus.X.BDD.FeatureFixtures; [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 AccountRegistrationSpecs : FeatureFixture { - protected User _currentUser; + protected Account _currentAccount; protected string _currentEmail; protected Exception _registrationError; - IUserService _userService; + IAccountService _accountService; IEmailService _emailService; IOrganizationService _organizationService; - public async Task Given_no_user_exists_with_email(string email) + public async Task Given_no_account_exists_with_email(string email) { - // Ensure user doesn't exist with email - var user = await _userService.GetUserByEmailAsync(email); - user.ShouldBeNull(); + // 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_name_and_email(string name, string email) + public async Task When_I_submit_registration_with_email_and_password(string email, string password) { try { - _currentUser = await _userService.CreateUserAsync(email, name); + _currentAccount = await _accountService.CreateAccountAsync(email, password); _currentEmail = email; } catch (Exception ex) @@ -44,7 +44,7 @@ public partial class UserRegistrationSpecs : FeatureFixture { try { - _currentUser = await _userService.CreateUserAsync(email, "Test User"); + _currentAccount = await _accountService.CreateAccountAsync(email, "TestPassword123!"); _currentEmail = email; } catch (Exception ex) @@ -53,14 +53,13 @@ public partial class UserRegistrationSpecs : FeatureFixture } } - public async Task Then_a_new_user_should_be_created_with_email_and_confirmation_status(string email, bool confirmationStatus) + public async Task Then_a_new_account_should_be_created_with_email_and_confirmation_status(string email, bool confirmationStatus) { - _currentUser.ShouldNotBeNull(); - _currentUser.Email.ShouldBe(email); - _currentUser.EmailConfirmed.ShouldBe(confirmationStatus); + _currentAccount.ShouldNotBeNull(); + _currentAccount.Email.ShouldBe(email); + _currentAccount.EmailConfirmed.ShouldBe(confirmationStatus); await Task.CompletedTask; - } public async Task Then_a_confirmation_email_should_be_sent() @@ -69,18 +68,16 @@ public partial class UserRegistrationSpecs : FeatureFixture emailSent.ShouldBeTrue(); await Task.CompletedTask; - } - public async Task Given_a_user_already_exists_with_email(string email) + public async Task Given_an_account_already_exists_with_email(string email) { - // Create a user first to ensure it exists - _currentUser = await _userService.CreateUserAsync(email, "Existing User"); - _currentUser.ShouldNotBeNull(); + // Create an account first to ensure it exists + _currentAccount = await _accountService.CreateAccountAsync(email, "ExistingPassword123!"); + _currentAccount.ShouldNotBeNull(); _currentEmail = email; await Task.CompletedTask; - } public async Task Then_registration_should_fail_with_error(string expectedErrorMessage) @@ -89,6 +86,5 @@ public partial class UserRegistrationSpecs : FeatureFixture _registrationError.Message.ShouldBe(expectedErrorMessage); await Task.CompletedTask; - } } diff --git a/PlanTempus.X.BDD/FeatureFixtures/AccountSecuritySpecs.cs b/PlanTempus.X.BDD/FeatureFixtures/AccountSecuritySpecs.cs index f0902a0..91cc54e 100644 --- a/PlanTempus.X.BDD/FeatureFixtures/AccountSecuritySpecs.cs +++ b/PlanTempus.X.BDD/FeatureFixtures/AccountSecuritySpecs.cs @@ -5,28 +5,29 @@ using PlanTempus.X.Services; using Shouldly; namespace PlanTempus.X.BDD.FeatureFixtures; - [TestClass] - [FeatureDescription(@"As a system administrator - I want to ensure account security is maintained - So users' data remains protected")] + +[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; + IAccountService _accountService; IEmailService _emailService; IOrganizationService _organizationService; IAuthService _authService; - protected User _currentUser; + protected Account _currentAccount; protected DateTime? _lockoutEnd; protected bool _isLocked; protected bool _loginSuccessful; - public async Task Given_user_exists(string email) + public async Task Given_account_exists(string email) { - _currentUser = await _userService.GetUserByEmailAsync(email); - if (_currentUser == null) + _currentAccount = await _accountService.GetAccountByEmailAsync(email); + if (_currentAccount == null) { - _currentUser = await _userService.CreateUserAsync(email, "Test User"); + _currentAccount = await _accountService.CreateAccountAsync(email, "TestPassword123!"); } } @@ -36,7 +37,7 @@ public partial class AccountSecuritySpecs : FeatureFixture { try { - await _authService.AttemptLoginAsync(_currentUser.Email, "WrongPassword"); + await _authService.AttemptLoginAsync(_currentAccount.Email, "WrongPassword"); } catch { @@ -47,29 +48,27 @@ public partial class AccountSecuritySpecs : FeatureFixture public async Task Then_the_account_should_be_locked() { - _currentUser = _userService.GetUserByEmail(_currentUser.Email); - _isLocked = _currentUser.IsLocked; + _currentAccount = _accountService.GetAccountByEmail(_currentAccount.Email); + _isLocked = _currentAccount.IsLocked; _isLocked.ShouldBeTrue(); await Task.CompletedTask; - } public async Task And_lockout_end_should_be_set() { - _lockoutEnd = _currentUser.LockoutEnd; + _lockoutEnd = _currentAccount.LockoutEnd; _lockoutEnd.ShouldNotBeNull(); _lockoutEnd.Value.ShouldBeGreaterThan(DateTime.UtcNow); await Task.CompletedTask; - } public async Task And_subsequent_login_attempts_should_fail_until_lockout_end() { try { - _loginSuccessful = await _authService.AttemptLoginAsync(_currentUser.Email, _currentUser.Password); + _loginSuccessful = await _authService.AttemptLoginAsync(_currentAccount.Email, _currentAccount.Password); } catch { @@ -78,4 +77,4 @@ public partial class AccountSecuritySpecs : FeatureFixture _loginSuccessful.ShouldBeFalse(); } -} \ No newline at end of file +} diff --git a/PlanTempus.X.BDD/FeatureFixtures/EmailConfirmationSpecs.cs b/PlanTempus.X.BDD/FeatureFixtures/EmailConfirmationSpecs.cs index 4e2bf49..d90a7cb 100644 --- a/PlanTempus.X.BDD/FeatureFixtures/EmailConfirmationSpecs.cs +++ b/PlanTempus.X.BDD/FeatureFixtures/EmailConfirmationSpecs.cs @@ -5,40 +5,41 @@ using PlanTempus.X.Services; using Shouldly; namespace PlanTempus.X.BDD.FeatureFixtures; - [TestClass] + +[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; + IAccountService _accountService; IEmailService _emailService; IOrganizationService _organizationService; - protected User _currentUser; + protected Account _currentAccount; protected string _currentEmail; protected string _confirmationLink; protected bool _redirectedToWelcome; protected string _errorMessage; - public async Task Given_a_user_exists_with_unconfirmed_email(string email) + public async Task Given_an_account_exists_with_unconfirmed_email(string email) { - _currentUser = await _userService.CreateUserAsync(email, "Test User"); - _currentUser.EmailConfirmed.ShouldBeFalse(); + _currentAccount = await _accountService.CreateAccountAsync(email, "TestPassword123!"); + _currentAccount.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); + await _accountService.ConfirmEmailAsync(_confirmationLink); _redirectedToWelcome = true; // Simulate redirect } - public async Task Then_the_users_email_confirmed_should_be_true() + public async Task Then_the_accounts_email_confirmed_should_be_true() { - _currentUser = _userService.GetUserByEmail(_currentEmail); - _currentUser.EmailConfirmed.ShouldBeTrue(); + _currentAccount = _accountService.GetAccountByEmail(_currentEmail); + _currentAccount.EmailConfirmed.ShouldBeTrue(); } public async Task And_I_should_be_redirected_to_the_welcome_page() @@ -50,7 +51,7 @@ public partial class EmailConfirmationSpecs : FeatureFixture { try { - await _userService.ConfirmEmailAsync("invalid-confirmation-token"); + await _accountService.ConfirmEmailAsync("invalid-confirmation-token"); } catch (Exception ex) { @@ -65,9 +66,9 @@ public partial class EmailConfirmationSpecs : FeatureFixture public async Task And_my_email_remains_unconfirmed() { - if (_currentUser != null) + if (_currentAccount != null) { - _currentUser.EmailConfirmed.ShouldBeFalse(); + _currentAccount.EmailConfirmed.ShouldBeFalse(); } } -} \ No newline at end of file +} diff --git a/PlanTempus.X.BDD/FeatureFixtures/OrganizationSetupSpecs.cs b/PlanTempus.X.BDD/FeatureFixtures/OrganizationSetupSpecs.cs index e59536f..6a28d36 100644 --- a/PlanTempus.X.BDD/FeatureFixtures/OrganizationSetupSpecs.cs +++ b/PlanTempus.X.BDD/FeatureFixtures/OrganizationSetupSpecs.cs @@ -5,38 +5,39 @@ using PlanTempus.X.Services; using Shouldly; namespace PlanTempus.X.BDD.FeatureFixtures; + [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; + IAccountService _accountService; IEmailService _emailService; IOrganizationService _organizationService; - IUserOrganizationService _userOrganizationService; + IAccountOrganizationService _accountOrganizationService; ITenantService _tenantService; IAuthService _authService; - protected User _currentUser; + protected Account _currentAccount; protected Organization _organization; protected Exception _setupError; - protected List _userOrganizations; + protected List _accountOrganizations; - public async Task Given_user_has_confirmed_their_email(string email) + public async Task Given_account_has_confirmed_their_email(string email) { - // Create a user with confirmed email - _currentUser = await _userService.CreateUserAsync(email, "Test User"); + // Create an account with confirmed email + _currentAccount = await _accountService.CreateAccountAsync(email, "TestPassword123!"); var confirmationLink = await _emailService.GetConfirmationLinkForEmail(email); - await _userService.ConfirmEmailAsync(confirmationLink); - _currentUser.EmailConfirmed.ShouldBeTrue(); + await _accountService.ConfirmEmailAsync(confirmationLink); + _currentAccount.EmailConfirmed.ShouldBeTrue(); } - public async Task When_user_submit_organization_name_and_valid_password(string orgName, string password) + public async Task When_account_submit_organization_name_and_valid_password(string orgName, string password) { try { - _organization = await _organizationService.SetupOrganizationAsync(_currentUser.Id, orgName, password); + _organization = await _organizationService.SetupOrganizationAsync(_currentAccount.Id, orgName, password); } catch (Exception ex) { @@ -48,18 +49,17 @@ public partial class OrganizationSetupSpecs : FeatureFixture { _organization.ShouldNotBeNull(); _organization.Name.ShouldBe("Acme Corp"); - _organization.CreatedBy.ShouldBe(_currentUser.Id); + _organization.CreatedBy.ShouldBe(_currentAccount.Id); await Task.CompletedTask; } - public async Task And_the_user_should_be_linked_to_the_organization_in_user_organizations() + public async Task And_the_account_should_be_linked_to_the_organization_in_account_organizations() { - var userOrg = _userOrganizationService.GetUserOrganization(_currentUser.Id, _organization.Id); - userOrg.ShouldNotBeNull(); + var accountOrg = _accountOrganizationService.GetAccountOrganization(_currentAccount.Id, _organization.Id); + accountOrg.ShouldNotBeNull(); await Task.CompletedTask; - } public async Task And_tenant_tables_should_be_created_for_the_organization() @@ -68,23 +68,21 @@ public partial class OrganizationSetupSpecs : FeatureFixture tenantTablesExist.ShouldBeTrue(); await Task.CompletedTask; - } - public async Task And_user_should_be_logged_into_the_system() + public async Task And_account_should_be_logged_into_the_system() { - var isAuthenticated = _authService.IsUserAuthenticated(_currentUser.Id); + var isAuthenticated = _authService.IsAccountAuthenticated(_currentAccount.Id); isAuthenticated.ShouldBeTrue(); await Task.CompletedTask; - } - public async Task When_user_submit_organization_name_without_password(string orgName) + public async Task When_account_submit_organization_name_without_password(string orgName) { try { - await _organizationService.SetupOrganizationAsync(_currentUser.Id, orgName, ""); + await _organizationService.SetupOrganizationAsync(_currentAccount.Id, orgName, ""); } catch (Exception ex) { @@ -98,37 +96,34 @@ public partial class OrganizationSetupSpecs : FeatureFixture _setupError.Message.ShouldBe(expectedErrorMessage); await Task.CompletedTask; - } - public async Task Given_user_has_completed_initial_setup(string email) + public async Task Given_account_has_completed_initial_setup(string email) { - await Given_user_has_confirmed_their_email(email); - await When_user_submit_organization_name_and_valid_password("First Org", "ValidP@ssw0rd"); - _userOrganizations = new List { _organization }; + await Given_account_has_confirmed_their_email(email); + await When_account_submit_organization_name_and_valid_password("First Org", "ValidP@ssw0rd"); + _accountOrganizations = new List { _organization }; } - public async Task When_user_create_a_new_organization(string orgName) + public async Task When_account_create_a_new_organization(string orgName) { - var newOrg = await _organizationService.CreateOrganizationAsync(_currentUser.Id, orgName); - _userOrganizations.Add(newOrg); + var newOrg = await _organizationService.CreateOrganizationAsync(_currentAccount.Id, orgName); + _accountOrganizations.Add(newOrg); } public async Task Then_a_new_organization_entry_should_be_created() { - _userOrganizations.Count.ShouldBe(2); - _userOrganizations[1].Name.ShouldBe("Second Org"); + _accountOrganizations.Count.ShouldBe(2); + _accountOrganizations[1].Name.ShouldBe("Second Org"); await Task.CompletedTask; - } - public async Task And_the_user_should_be_linked_to_both_organizations() + public async Task And_the_account_should_be_linked_to_both_organizations() { - var userOrgs = _userOrganizationService.GetUserOrganizations(_currentUser.Id); - userOrgs.Count.ShouldBe(2); + var accountOrgs = _accountOrganizationService.GetAccountOrganizations(_currentAccount.Id); + accountOrgs.Count.ShouldBe(2); await Task.CompletedTask; - } -} \ No newline at end of file +} diff --git a/PlanTempus.X.BDD/Scenarios/AccountRegistrationSpecs.cs b/PlanTempus.X.BDD/Scenarios/AccountRegistrationSpecs.cs new file mode 100644 index 0000000..23f2a7d --- /dev/null +++ b/PlanTempus.X.BDD/Scenarios/AccountRegistrationSpecs.cs @@ -0,0 +1,31 @@ +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 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") + ); + } +} diff --git a/PlanTempus.X.BDD/Scenarios/AccountSecuritySpecs.cs b/PlanTempus.X.BDD/Scenarios/AccountSecuritySpecs.cs index e446c18..6f0a16f 100644 --- a/PlanTempus.X.BDD/Scenarios/AccountSecuritySpecs.cs +++ b/PlanTempus.X.BDD/Scenarios/AccountSecuritySpecs.cs @@ -3,19 +3,20 @@ using LightBDD.Framework.Scenarios; using LightBDD.MsTest3; namespace PlanTempus.X.BDD.Scenarios; - [TestClass] + +[TestClass] public partial class AccountSecuritySpecs : FeatureFixtures.AccountSecuritySpecs { [Scenario] [TestMethod] - public async Task User_lockout_after_multiple_failed_attempts() + public async Task Account_lockout_after_multiple_failed_attempts() { await Runner.RunScenarioAsync( - _ => Given_user_exists("test@example.com"), + _ => Given_account_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() ); } -} \ No newline at end of file +} diff --git a/PlanTempus.X.BDD/Scenarios/EmailConfirmationSpecs.cs b/PlanTempus.X.BDD/Scenarios/EmailConfirmationSpecs.cs index 2565735..1594faf 100644 --- a/PlanTempus.X.BDD/Scenarios/EmailConfirmationSpecs.cs +++ b/PlanTempus.X.BDD/Scenarios/EmailConfirmationSpecs.cs @@ -3,7 +3,8 @@ using LightBDD.Framework.Scenarios; using LightBDD.MsTest3; namespace PlanTempus.X.BDD.Scenarios; - [TestClass] + +[TestClass] public partial class EmailConfirmationSpecs : FeatureFixtures.EmailConfirmationSpecs { [Scenario] @@ -11,9 +12,9 @@ public partial class EmailConfirmationSpecs : FeatureFixtures.EmailConfirmationS public async Task Confirm_valid_email_address() { await Runner.RunScenarioAsync( - _ => Given_a_user_exists_with_unconfirmed_email("test@example.com"), + _ => Given_an_account_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(), + _ => Then_the_accounts_email_confirmed_should_be_true(), _ => And_I_should_be_redirected_to_the_welcome_page() ); } @@ -28,4 +29,4 @@ public partial class EmailConfirmationSpecs : FeatureFixtures.EmailConfirmationS _ => And_my_email_remains_unconfirmed() ); } -} \ No newline at end of file +} diff --git a/PlanTempus.X.BDD/Scenarios/OrganizationSetupSpecs.cs b/PlanTempus.X.BDD/Scenarios/OrganizationSetupSpecs.cs index 4c82cee..8a2c3ed 100644 --- a/PlanTempus.X.BDD/Scenarios/OrganizationSetupSpecs.cs +++ b/PlanTempus.X.BDD/Scenarios/OrganizationSetupSpecs.cs @@ -3,7 +3,8 @@ using LightBDD.Framework.Scenarios; using LightBDD.MsTest3; namespace PlanTempus.X.BDD.Scenarios; - [TestClass] + +[TestClass] public partial class OrganizationSetupSpecs : FeatureFixtures.OrganizationSetupSpecs { [Scenario] @@ -11,12 +12,12 @@ public partial class OrganizationSetupSpecs : FeatureFixtures.OrganizationSetupS public async Task Complete_organization_setup_after_confirmation() { await Runner.RunScenarioAsync( - _ => Given_user_has_confirmed_their_email("test@example.com"), - _ => When_user_submit_organization_name_and_valid_password("Acme Corp", "ValidP@ssw0rd"), + _ => 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_user_should_be_linked_to_the_organization_in_user_organizations(), + _ => And_the_account_should_be_linked_to_the_organization_in_account_organizations(), _ => And_tenant_tables_should_be_created_for_the_organization(), - _ => And_user_should_be_logged_into_the_system() + _ => And_account_should_be_logged_into_the_system() ); } @@ -25,8 +26,8 @@ public partial class OrganizationSetupSpecs : FeatureFixtures.OrganizationSetupS public async Task Prevent_organization_setup_without_password() { await Runner.RunScenarioAsync( - _ => Given_user_has_confirmed_their_email("test@example.com"), - _ => When_user_submit_organization_name_without_password("Acme Corp"), + _ => 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") ); } @@ -36,10 +37,10 @@ public partial class OrganizationSetupSpecs : FeatureFixtures.OrganizationSetupS public async Task Handle_multiple_organization_creations() { await Runner.RunScenarioAsync( - _ => Given_user_has_completed_initial_setup("test@example.com"), - _ => When_user_create_a_new_organization("Second Org"), + _ => 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_user_should_be_linked_to_both_organizations() + _ => And_the_account_should_be_linked_to_both_organizations() ); } -} \ No newline at end of file +} diff --git a/PlanTempus.X.BDD/Scenarios/UserRegistrationSpecs.cs b/PlanTempus.X.BDD/Scenarios/UserRegistrationSpecs.cs deleted file mode 100644 index 3138b6f..0000000 --- a/PlanTempus.X.BDD/Scenarios/UserRegistrationSpecs.cs +++ /dev/null @@ -1,31 +0,0 @@ -using LightBDD.Framework; -using LightBDD.Framework.Scenarios; -using LightBDD.MsTest3; -namespace PlanTempus.X.BDD.Scenarios; - -[TestClass] -public partial class UserRegistrationSpecs : FeatureFixtures.UserRegistrationSpecs -{ - [Scenario] - [TestMethod] - public async Task Successful_user_registration_with_valid_email() - { - 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() - ); - } - - [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") - ); - } -} diff --git a/SetupInfrastructure/appconfiguration.json b/SetupInfrastructure/appconfiguration.json index 9bb0266..8fa50ec 100644 --- a/SetupInfrastructure/appconfiguration.json +++ b/SetupInfrastructure/appconfiguration.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "DefaultConnection": "Host=192.168.1.57;Port=5432;Database=ptmain;" + "DefaultConnection": "Host=192.168.1.63;Port=5432;Database=ptmain;" }, "ApplicationInsights": { "ConnectionString": "InstrumentationKey=07d2a2b9-5e8e-4924-836e-264f8438f6c5;IngestionEndpoint=https://northeurope-2.in.applicationinsights.azure.com/;LiveEndpoint=https://northeurope.livediagnostics.monitor.azure.com/;ApplicationId=56748c39-2fa3-4880-a1e2-24068e791548", diff --git a/Tests/CommandQueryHandlerTests/HandlerTest.cs b/Tests/CommandQueryHandlerTests/HandlerTest.cs index 8fde9c9..7fb9607 100644 --- a/Tests/CommandQueryHandlerTests/HandlerTest.cs +++ b/Tests/CommandQueryHandlerTests/HandlerTest.cs @@ -1,7 +1,7 @@ using Autofac; using Insight.Database; using PlanTempus.Components; -using PlanTempus.Components.Users.Create; +using PlanTempus.Components.Accounts.Create; using PlanTempus.Core.CommandQueries; using PlanTempus.Core.Database; using PlanTempus.Core.Database.ConnectionFactory; @@ -22,7 +22,7 @@ public class HandlerTest : TestFixture var commandHandler = Container.Resolve(); commandHandler.ShouldBeOfType(); - var command = new CreateUserCommand + var command = new CreateAccountCommand { Email = $"{GetRandomWord()}@dumbanddumber.com5", // Lloyd Christmas Password = "1234AceVentura#LOL", // Ace Ventura @@ -31,7 +31,7 @@ public class HandlerTest : TestFixture }; var result = await commandHandler.Handle(command); - + result.ShouldNotBeNull(); } } \ No newline at end of file diff --git a/Tests/PostgresTests.cs b/Tests/PostgresTests.cs index 933fd80..35e52a4 100644 --- a/Tests/PostgresTests.cs +++ b/Tests/PostgresTests.cs @@ -1,6 +1,6 @@ using Autofac; using Insight.Database; -using PlanTempus.Components.Users.Exceptions; +using PlanTempus.Components.Accounts.Exceptions; using PlanTempus.Core.Database; using PlanTempus.Core.Database.ConnectionFactory; using Shouldly; @@ -63,12 +63,12 @@ public class PostgresTests : TestFixture } [TestMethod] - public async Task TestForUniqueUserEmail() + public async Task TestForUniqueAccountEmail() { - using var db = _databaseOperations.CreateScope(nameof(TestForUniqueUserEmail)); + using var db = _databaseOperations.CreateScope(nameof(TestForUniqueAccountEmail)); try { - var sql = @"INSERT INTO system.users(email, password_hash, security_stamp, email_confirmed, access_failed_count, lockout_enabled, is_active) + var sql = @"INSERT INTO system.accounts(email, password_hash, security_stamp, email_confirmed, access_failed_count, lockout_enabled, is_active) VALUES(@Email, @PasswordHash, @SecurityStamp, @EmailConfirmed, @AccessFailedCount, @LockoutEnabled, @IsActive) RETURNING id, created_at, email, is_active"; @@ -83,18 +83,18 @@ public class PostgresTests : TestFixture IsActive = true }; - var user = await db.Connection.QuerySqlAsync(sql, parameters); + var account = await db.Connection.QuerySqlAsync(sql, parameters); //EmailAlreadyRegistreredException //try insert, to test exception var ex = await Should.ThrowAsync(async () => await db.Connection.QuerySqlAsync(sql, parameters)); - ex.ConstraintName.ShouldBe("users_email_key"); - } + ex.ConstraintName.ShouldBe("accounts_email_key"); + } catch (Exception ex) - { - db.Error(ex); - + { + db.Error(ex); + } }