mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 02:39:03 +00:00
Add user registration bdd tests
This commit is contained in:
@@ -1,69 +1,75 @@
|
|||||||
Feature: User Registration
|
Feature: User Registration
|
||||||
As a new user
|
As a new user
|
||||||
I want to register an account
|
I want to register an account
|
||||||
So that I can log in and access authenticated routes
|
So that I can log in and access authenticated routes
|
||||||
@Ignore
|
|
||||||
Scenario: Successful registration with valid details
|
Scenario: Successful registration with valid details
|
||||||
Given the API is running
|
Given the API is running
|
||||||
When I submit a registration request with values:
|
When I submit a registration request with values:
|
||||||
| Username | FirstName | LastName | Email | DateOfBirth | Password |
|
| Username | FirstName | LastName | Email | DateOfBirth | Password |
|
||||||
| newuser | New | User | newuser@example.com | 1990-01-01 | Password1! |
|
| newuser | New | User | newuser@example.com | 1990-01-01 | Password1! |
|
||||||
Then the response has HTTP status 201
|
Then the response has HTTP status 201
|
||||||
And the response JSON should have "message" equal "User registered successfully."
|
And the response JSON should have "message" equal "User registered successfully."
|
||||||
And the response JSON should have an access token
|
And the response JSON should have an access token
|
||||||
@Ignore
|
|
||||||
Scenario: Registration fails with existing username
|
@Ignore
|
||||||
Given the API is running
|
Scenario: Registration fails with existing username
|
||||||
And I have an existing account with username "existinguser"
|
Given the API is running
|
||||||
When I submit a registration request with values:
|
And I have an existing account with username "existinguser"
|
||||||
| Username | FirstName | LastName | Email | DateOfBirth | Password |
|
When I submit a registration request with values:
|
||||||
| existinguser | Existing | User | existing@example.com | 1990-01-01 | Password1! |
|
| Username | FirstName | LastName | Email | DateOfBirth | Password |
|
||||||
Then the response has HTTP status 409
|
| existinguser | Existing | User | existing@example.com | 1990-01-01 | Password1! |
|
||||||
And the response JSON should have "message" equal "Username already exists."
|
Then the response has HTTP status 409
|
||||||
@Ignore
|
And the response JSON should have "message" equal "Username already exists."
|
||||||
Scenario: Registration fails with existing email
|
|
||||||
Given the API is running
|
@Ignore
|
||||||
And I have an existing account with email "existing@example.com"
|
Scenario: Registration fails with existing email
|
||||||
When I submit a registration request with values:
|
Given the API is running
|
||||||
| Username | FirstName | LastName | Email | DateOfBirth | Password |
|
And I have an existing account with email "existing@example.com"
|
||||||
| newuser | New | User | existing@example.com | 1990-01-01 | Password1! |
|
When I submit a registration request with values:
|
||||||
Then the response has HTTP status 409
|
| Username | FirstName | LastName | Email | DateOfBirth | Password |
|
||||||
And the response JSON should have "message" equal "Email already in use."
|
| newuser | New | User | existing@example.com | 1990-01-01 | Password1! |
|
||||||
@Ignore
|
Then the response has HTTP status 409
|
||||||
Scenario: Registration fails with missing required fields
|
And the response JSON should have "message" equal "Email already in use."
|
||||||
Given the API is running
|
|
||||||
When I submit a registration request with values:
|
@Ignore
|
||||||
| Username | FirstName | LastName | Email | DateOfBirth | Password |
|
Scenario: Registration fails with missing required fields
|
||||||
| | New | User | | | Password1! |
|
Given the API is running
|
||||||
Then the response has HTTP status 400
|
When I submit a registration request with values:
|
||||||
And the response JSON should have "message" equal "Username is required."
|
| Username | FirstName | LastName | Email | DateOfBirth | Password |
|
||||||
@Ignore
|
| | New | User | | | Password1! |
|
||||||
Scenario: Registration fails with invalid email format
|
Then the response has HTTP status 400
|
||||||
Given the API is running
|
And the response JSON should have "message" equal "Username is required."
|
||||||
When I submit a registration request with values:
|
|
||||||
| Username | FirstName | LastName | Email | DateOfBirth | Password |
|
@Ignore
|
||||||
| newuser | New | User | invalidemail | 1990-01-01 | Password1! |
|
Scenario: Registration fails with invalid email format
|
||||||
Then the response has HTTP status 400
|
Given the API is running
|
||||||
And the response JSON should have "message" equal "Invalid email format."
|
When I submit a registration request with values:
|
||||||
@Ignore
|
| Username | FirstName | LastName | Email | DateOfBirth | Password |
|
||||||
Scenario: Registration fails with weak password
|
| newuser | New | User | invalidemail | 1990-01-01 | Password1! |
|
||||||
Given the API is running
|
Then the response has HTTP status 400
|
||||||
When I submit a registration request with values:
|
And the response JSON should have "message" equal "Invalid email format."
|
||||||
| Username | FirstName | LastName | Email | DateOfBirth | Password |
|
|
||||||
| newuser | New | User | newuser@example.com | 1990-01-01 | weakpass |
|
@Ignore
|
||||||
Then the response has HTTP status 400
|
Scenario: Registration fails with weak password
|
||||||
And the response JSON should have "message" equal "Password does not meet complexity requirements."
|
Given the API is running
|
||||||
@Ignore
|
When I submit a registration request with values:
|
||||||
Scenario: Cannot register a user younger than 19 years of age (regulatory requirement)
|
| Username | FirstName | LastName | Email | DateOfBirth | Password |
|
||||||
Given the API is running
|
| newuser | New | User | newuser@example.com | 1990-01-01 | weakpass |
|
||||||
When I submit a registration request with values:
|
Then the response has HTTP status 400
|
||||||
| Username | FirstName | LastName | Email | DateOfBirth | Password |
|
And the response JSON should have "message" equal "Password does not meet complexity requirements."
|
||||||
| younguser | Young | User | younguser@example.com | | Password1! |
|
|
||||||
Then the response has HTTP status 400
|
@Ignore
|
||||||
And the response JSON should have "message" equal "You must be at least 19 years old to register."
|
Scenario: Cannot register a user younger than 19 years of age (regulatory requirement)
|
||||||
@Ignore
|
Given the API is running
|
||||||
Scenario: Registration endpoint only accepts POST requests
|
When I submit a registration request with values:
|
||||||
Given the API is running
|
| Username | FirstName | LastName | Email | DateOfBirth | Password |
|
||||||
When I submit a registration request using a GET request
|
| younguser | Young | User | younguser@example.com | | Password1! |
|
||||||
Then the response has HTTP status 404
|
Then the response has HTTP status 400
|
||||||
And the response JSON should have "message" equal "Not Found."
|
And the response JSON should have "message" equal "You must be at least 19 years old to register."
|
||||||
|
|
||||||
|
Scenario: Registration endpoint only accepts POST requests
|
||||||
|
Given the API is running
|
||||||
|
When I submit a registration request using a GET request
|
||||||
|
Then the response has HTTP status 404
|
||||||
|
And the response JSON should have "message" equal "Not Found."
|
||||||
@@ -157,4 +157,64 @@ public class AuthSteps(ScenarioContext scenario)
|
|||||||
scenario[ResponseKey] = response;
|
scenario[ResponseKey] = response;
|
||||||
scenario[ResponseBodyKey] = responseBody;
|
scenario[ResponseBodyKey] = responseBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[When("I submit a registration request with values:")]
|
||||||
|
public async Task WhenISubmitARegistrationRequestWithValues(Table table)
|
||||||
|
{
|
||||||
|
var client = GetClient();
|
||||||
|
var row = table.Rows[0];
|
||||||
|
|
||||||
|
var registrationData = new
|
||||||
|
{
|
||||||
|
username = row.TryGetValue("Username", out var value) ? value : null,
|
||||||
|
firstName = row.TryGetValue("FirstName", out var value1) ? value1 : null,
|
||||||
|
lastName = row.TryGetValue("LastName", out var value2) ? value2 : null,
|
||||||
|
email = row.TryGetValue("Email", out var value3) ? value3 : null,
|
||||||
|
dateOfBirth = row.ContainsKey("DateOfBirth") && !string.IsNullOrEmpty(row["DateOfBirth"])
|
||||||
|
? row["DateOfBirth"]
|
||||||
|
: null,
|
||||||
|
password = row.ContainsKey("Password") ? row["Password"] : null
|
||||||
|
};
|
||||||
|
|
||||||
|
var body = JsonSerializer.Serialize(registrationData);
|
||||||
|
|
||||||
|
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/api/auth/register")
|
||||||
|
{
|
||||||
|
Content = new StringContent(body, System.Text.Encoding.UTF8, "application/json")
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await client.SendAsync(requestMessage);
|
||||||
|
var responseBody = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
scenario[ResponseKey] = response;
|
||||||
|
scenario[ResponseBodyKey] = responseBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Given("I have an existing account with username {string}")]
|
||||||
|
public void GivenIHaveAnExistingAccountWithUsername(string username)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Given("I have an existing account with email {string}")]
|
||||||
|
public void GivenIHaveAnExistingAccountWithEmail(string email)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[When("I submit a registration request using a GET request")]
|
||||||
|
public async Task WhenISubmitARegistrationRequestUsingAGetRequest()
|
||||||
|
{
|
||||||
|
var client = GetClient();
|
||||||
|
var requestMessage = new HttpRequestMessage(HttpMethod.Get, "/api/auth/register")
|
||||||
|
{
|
||||||
|
Content = new StringContent("{}", System.Text.Encoding.UTF8, "application/json")
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await client.SendAsync(requestMessage);
|
||||||
|
var responseBody = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
scenario[ResponseKey] = response;
|
||||||
|
scenario[ResponseBodyKey] = responseBody;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -22,10 +22,10 @@ namespace Repository.Core.Repositories.Auth
|
|||||||
{
|
{
|
||||||
await using var connection = await CreateConnection();
|
await using var connection = await CreateConnection();
|
||||||
await using var command = connection.CreateCommand();
|
await using var command = connection.CreateCommand();
|
||||||
|
|
||||||
command.CommandText = "USP_RegisterUser";
|
command.CommandText = "USP_RegisterUser";
|
||||||
command.CommandType = CommandType.StoredProcedure;
|
command.CommandType = CommandType.StoredProcedure;
|
||||||
|
|
||||||
// Input parameters
|
|
||||||
AddParameter(command, "@Username", username);
|
AddParameter(command, "@Username", username);
|
||||||
AddParameter(command, "@FirstName", firstName);
|
AddParameter(command, "@FirstName", firstName);
|
||||||
AddParameter(command, "@LastName", lastName);
|
AddParameter(command, "@LastName", lastName);
|
||||||
@@ -33,11 +33,9 @@ namespace Repository.Core.Repositories.Auth
|
|||||||
AddParameter(command, "@DateOfBirth", dateOfBirth);
|
AddParameter(command, "@DateOfBirth", dateOfBirth);
|
||||||
AddParameter(command, "@Hash", passwordHash);
|
AddParameter(command, "@Hash", passwordHash);
|
||||||
|
|
||||||
// Execute and retrieve the generated UserAccountId from result set
|
|
||||||
var result = await command.ExecuteScalarAsync();
|
var result = await command.ExecuteScalarAsync();
|
||||||
var userAccountId = result != null ? (Guid)result : Guid.Empty;
|
var userAccountId = result != null ? (Guid)result : Guid.Empty;
|
||||||
|
|
||||||
// Return the newly created user account
|
|
||||||
return new Entities.UserAccount
|
return new Entities.UserAccount
|
||||||
{
|
{
|
||||||
UserAccountId = userAccountId,
|
UserAccountId = userAccountId,
|
||||||
@@ -104,12 +102,6 @@ namespace Repository.Core.Repositories.Auth
|
|||||||
await command.ExecuteNonQueryAsync();
|
await command.ExecuteNonQueryAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task InvalidateCredentialsByUserAccountIdAsync(Guid userAccountId)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException("InvalidateCredentialsByUserAccountIdAsync");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maps a data reader row to a UserAccount entity.
|
/// Maps a data reader row to a UserAccount entity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -172,4 +164,4 @@ namespace Repository.Core.Repositories.Auth
|
|||||||
command.Parameters.Add(p);
|
command.Parameters.Add(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,12 +55,5 @@ namespace Repository.Core.Repositories.Auth
|
|||||||
/// <param name="userAccountId">ID of the user account</param>
|
/// <param name="userAccountId">ID of the user account</param>
|
||||||
/// <param name="newPasswordHash">New hashed password</param>
|
/// <param name="newPasswordHash">New hashed password</param>
|
||||||
Task RotateCredentialAsync(Guid userAccountId, string newPasswordHash);
|
Task RotateCredentialAsync(Guid userAccountId, string newPasswordHash);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalidates all credentials for a user account (e.g., for logout or security purposes).
|
|
||||||
/// Uses stored procedure: USP_InvalidateUserCredential
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userAccountId">ID of the user account</param>
|
|
||||||
Task InvalidateCredentialsByUserAccountIdAsync(Guid userAccountId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,225 +8,212 @@ namespace Repository.Tests.Auth;
|
|||||||
|
|
||||||
public class AuthRepositoryTest
|
public class AuthRepositoryTest
|
||||||
{
|
{
|
||||||
private static AuthRepository CreateRepo(MockDbConnection conn)
|
private static AuthRepository CreateRepo(MockDbConnection conn)
|
||||||
=> new(new TestConnectionFactory(conn));
|
=> new(new TestConnectionFactory(conn));
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task RegisterUserAsync_CreatesUserWithCredential_ReturnsUserAccount()
|
public async Task RegisterUserAsync_CreatesUserWithCredential_ReturnsUserAccount()
|
||||||
{
|
{
|
||||||
var expectedUserId = Guid.NewGuid();
|
var expectedUserId = Guid.NewGuid();
|
||||||
var conn = new MockDbConnection();
|
var conn = new MockDbConnection();
|
||||||
|
|
||||||
conn.Mocks
|
conn.Mocks
|
||||||
.When(cmd => cmd.CommandText == "USP_RegisterUser")
|
.When(cmd => cmd.CommandText == "USP_RegisterUser")
|
||||||
.ReturnsTable(MockTable.WithColumns(("UserAccountId", typeof(Guid)))
|
.ReturnsTable(MockTable.WithColumns(("UserAccountId", typeof(Guid)))
|
||||||
.AddRow(expectedUserId));
|
.AddRow(expectedUserId));
|
||||||
|
|
||||||
var repo = CreateRepo(conn);
|
var repo = CreateRepo(conn);
|
||||||
var result = await repo.RegisterUserAsync(
|
var result = await repo.RegisterUserAsync(
|
||||||
username: "testuser",
|
username: "testuser",
|
||||||
firstName: "Test",
|
firstName: "Test",
|
||||||
lastName: "User",
|
lastName: "User",
|
||||||
email: "test@example.com",
|
email: "test@example.com",
|
||||||
dateOfBirth: new DateTime(1990, 1, 1),
|
dateOfBirth: new DateTime(1990, 1, 1),
|
||||||
passwordHash: "hashedpassword123"
|
passwordHash: "hashedpassword123"
|
||||||
);
|
);
|
||||||
|
|
||||||
result.Should().NotBeNull();
|
result.Should().NotBeNull();
|
||||||
result.UserAccountId.Should().Be(expectedUserId);
|
result.UserAccountId.Should().Be(expectedUserId);
|
||||||
result.Username.Should().Be("testuser");
|
result.Username.Should().Be("testuser");
|
||||||
result.FirstName.Should().Be("Test");
|
result.FirstName.Should().Be("Test");
|
||||||
result.LastName.Should().Be("User");
|
result.LastName.Should().Be("User");
|
||||||
result.Email.Should().Be("test@example.com");
|
result.Email.Should().Be("test@example.com");
|
||||||
result.DateOfBirth.Should().Be(new DateTime(1990, 1, 1));
|
result.DateOfBirth.Should().Be(new DateTime(1990, 1, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetUserByEmailAsync_ReturnsUser_WhenExists()
|
public async Task GetUserByEmailAsync_ReturnsUser_WhenExists()
|
||||||
{
|
{
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
var conn = new MockDbConnection();
|
var conn = new MockDbConnection();
|
||||||
|
|
||||||
conn.Mocks
|
conn.Mocks
|
||||||
.When(cmd => cmd.CommandText == "usp_GetUserAccountByEmail")
|
.When(cmd => cmd.CommandText == "usp_GetUserAccountByEmail")
|
||||||
.ReturnsTable(MockTable.WithColumns(
|
.ReturnsTable(MockTable.WithColumns(
|
||||||
("UserAccountId", typeof(Guid)),
|
("UserAccountId", typeof(Guid)),
|
||||||
("Username", typeof(string)),
|
("Username", typeof(string)),
|
||||||
("FirstName", typeof(string)),
|
("FirstName", typeof(string)),
|
||||||
("LastName", typeof(string)),
|
("LastName", typeof(string)),
|
||||||
("Email", typeof(string)),
|
("Email", typeof(string)),
|
||||||
("CreatedAt", typeof(DateTime)),
|
("CreatedAt", typeof(DateTime)),
|
||||||
("UpdatedAt", typeof(DateTime?)),
|
("UpdatedAt", typeof(DateTime?)),
|
||||||
("DateOfBirth", typeof(DateTime)),
|
("DateOfBirth", typeof(DateTime)),
|
||||||
("Timer", typeof(byte[]))
|
("Timer", typeof(byte[]))
|
||||||
).AddRow(
|
).AddRow(
|
||||||
userId,
|
userId,
|
||||||
"emailuser",
|
"emailuser",
|
||||||
"Email",
|
"Email",
|
||||||
"User",
|
"User",
|
||||||
"emailuser@example.com",
|
"emailuser@example.com",
|
||||||
DateTime.UtcNow,
|
DateTime.UtcNow,
|
||||||
null,
|
null,
|
||||||
new DateTime(1990, 5, 15),
|
new DateTime(1990, 5, 15),
|
||||||
null
|
null
|
||||||
));
|
));
|
||||||
|
|
||||||
var repo = CreateRepo(conn);
|
var repo = CreateRepo(conn);
|
||||||
var result = await repo.GetUserByEmailAsync("emailuser@example.com");
|
var result = await repo.GetUserByEmailAsync("emailuser@example.com");
|
||||||
|
|
||||||
result.Should().NotBeNull();
|
result.Should().NotBeNull();
|
||||||
result!.UserAccountId.Should().Be(userId);
|
result!.UserAccountId.Should().Be(userId);
|
||||||
result.Username.Should().Be("emailuser");
|
result.Username.Should().Be("emailuser");
|
||||||
result.Email.Should().Be("emailuser@example.com");
|
result.Email.Should().Be("emailuser@example.com");
|
||||||
result.FirstName.Should().Be("Email");
|
result.FirstName.Should().Be("Email");
|
||||||
result.LastName.Should().Be("User");
|
result.LastName.Should().Be("User");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetUserByEmailAsync_ReturnsNull_WhenNotExists()
|
public async Task GetUserByEmailAsync_ReturnsNull_WhenNotExists()
|
||||||
{
|
{
|
||||||
var conn = new MockDbConnection();
|
var conn = new MockDbConnection();
|
||||||
|
|
||||||
conn.Mocks
|
conn.Mocks
|
||||||
.When(cmd => cmd.CommandText == "usp_GetUserAccountByEmail")
|
.When(cmd => cmd.CommandText == "usp_GetUserAccountByEmail")
|
||||||
.ReturnsTable(MockTable.Empty());
|
.ReturnsTable(MockTable.Empty());
|
||||||
|
|
||||||
var repo = CreateRepo(conn);
|
var repo = CreateRepo(conn);
|
||||||
var result = await repo.GetUserByEmailAsync("nonexistent@example.com");
|
var result = await repo.GetUserByEmailAsync("nonexistent@example.com");
|
||||||
|
|
||||||
result.Should().BeNull();
|
result.Should().BeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetUserByUsernameAsync_ReturnsUser_WhenExists()
|
public async Task GetUserByUsernameAsync_ReturnsUser_WhenExists()
|
||||||
{
|
{
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
var conn = new MockDbConnection();
|
var conn = new MockDbConnection();
|
||||||
|
|
||||||
conn.Mocks
|
conn.Mocks
|
||||||
.When(cmd => cmd.CommandText == "usp_GetUserAccountByUsername")
|
.When(cmd => cmd.CommandText == "usp_GetUserAccountByUsername")
|
||||||
.ReturnsTable(MockTable.WithColumns(
|
.ReturnsTable(MockTable.WithColumns(
|
||||||
("UserAccountId", typeof(Guid)),
|
("UserAccountId", typeof(Guid)),
|
||||||
("Username", typeof(string)),
|
("Username", typeof(string)),
|
||||||
("FirstName", typeof(string)),
|
("FirstName", typeof(string)),
|
||||||
("LastName", typeof(string)),
|
("LastName", typeof(string)),
|
||||||
("Email", typeof(string)),
|
("Email", typeof(string)),
|
||||||
("CreatedAt", typeof(DateTime)),
|
("CreatedAt", typeof(DateTime)),
|
||||||
("UpdatedAt", typeof(DateTime?)),
|
("UpdatedAt", typeof(DateTime?)),
|
||||||
("DateOfBirth", typeof(DateTime)),
|
("DateOfBirth", typeof(DateTime)),
|
||||||
("Timer", typeof(byte[]))
|
("Timer", typeof(byte[]))
|
||||||
).AddRow(
|
).AddRow(
|
||||||
userId,
|
userId,
|
||||||
"usernameuser",
|
"usernameuser",
|
||||||
"Username",
|
"Username",
|
||||||
"User",
|
"User",
|
||||||
"username@example.com",
|
"username@example.com",
|
||||||
DateTime.UtcNow,
|
DateTime.UtcNow,
|
||||||
null,
|
null,
|
||||||
new DateTime(1985, 8, 20),
|
new DateTime(1985, 8, 20),
|
||||||
null
|
null
|
||||||
));
|
));
|
||||||
|
|
||||||
var repo = CreateRepo(conn);
|
var repo = CreateRepo(conn);
|
||||||
var result = await repo.GetUserByUsernameAsync("usernameuser");
|
var result = await repo.GetUserByUsernameAsync("usernameuser");
|
||||||
|
|
||||||
result.Should().NotBeNull();
|
result.Should().NotBeNull();
|
||||||
result!.UserAccountId.Should().Be(userId);
|
result!.UserAccountId.Should().Be(userId);
|
||||||
result.Username.Should().Be("usernameuser");
|
result.Username.Should().Be("usernameuser");
|
||||||
result.Email.Should().Be("username@example.com");
|
result.Email.Should().Be("username@example.com");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetUserByUsernameAsync_ReturnsNull_WhenNotExists()
|
public async Task GetUserByUsernameAsync_ReturnsNull_WhenNotExists()
|
||||||
{
|
{
|
||||||
var conn = new MockDbConnection();
|
var conn = new MockDbConnection();
|
||||||
|
|
||||||
conn.Mocks
|
conn.Mocks
|
||||||
.When(cmd => cmd.CommandText == "usp_GetUserAccountByUsername")
|
.When(cmd => cmd.CommandText == "usp_GetUserAccountByUsername")
|
||||||
.ReturnsTable(MockTable.Empty());
|
.ReturnsTable(MockTable.Empty());
|
||||||
|
|
||||||
var repo = CreateRepo(conn);
|
var repo = CreateRepo(conn);
|
||||||
var result = await repo.GetUserByUsernameAsync("nonexistent");
|
var result = await repo.GetUserByUsernameAsync("nonexistent");
|
||||||
|
|
||||||
result.Should().BeNull();
|
result.Should().BeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetActiveCredentialByUserAccountIdAsync_ReturnsCredential_WhenExists()
|
public async Task GetActiveCredentialByUserAccountIdAsync_ReturnsCredential_WhenExists()
|
||||||
{
|
{
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
var credentialId = Guid.NewGuid();
|
var credentialId = Guid.NewGuid();
|
||||||
var conn = new MockDbConnection();
|
var conn = new MockDbConnection();
|
||||||
|
|
||||||
conn.Mocks
|
conn.Mocks
|
||||||
.When(cmd => cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId")
|
.When(cmd => cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId")
|
||||||
.ReturnsTable(MockTable.WithColumns(
|
.ReturnsTable(MockTable.WithColumns(
|
||||||
("UserCredentialId", typeof(Guid)),
|
("UserCredentialId", typeof(Guid)),
|
||||||
("UserAccountId", typeof(Guid)),
|
("UserAccountId", typeof(Guid)),
|
||||||
("Hash", typeof(string)),
|
("Hash", typeof(string)),
|
||||||
("CreatedAt", typeof(DateTime)),
|
("CreatedAt", typeof(DateTime)),
|
||||||
("Timer", typeof(byte[]))
|
("Timer", typeof(byte[]))
|
||||||
).AddRow(
|
).AddRow(
|
||||||
credentialId,
|
credentialId,
|
||||||
userId,
|
userId,
|
||||||
"hashed_password_value",
|
"hashed_password_value",
|
||||||
DateTime.UtcNow,
|
DateTime.UtcNow,
|
||||||
null
|
null
|
||||||
));
|
));
|
||||||
|
|
||||||
var repo = CreateRepo(conn);
|
var repo = CreateRepo(conn);
|
||||||
var result = await repo.GetActiveCredentialByUserAccountIdAsync(userId);
|
var result = await repo.GetActiveCredentialByUserAccountIdAsync(userId);
|
||||||
|
|
||||||
result.Should().NotBeNull();
|
result.Should().NotBeNull();
|
||||||
result!.UserCredentialId.Should().Be(credentialId);
|
result!.UserCredentialId.Should().Be(credentialId);
|
||||||
result.UserAccountId.Should().Be(userId);
|
result.UserAccountId.Should().Be(userId);
|
||||||
result.Hash.Should().Be("hashed_password_value");
|
result.Hash.Should().Be("hashed_password_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetActiveCredentialByUserAccountIdAsync_ReturnsNull_WhenNotExists()
|
public async Task GetActiveCredentialByUserAccountIdAsync_ReturnsNull_WhenNotExists()
|
||||||
{
|
{
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
var conn = new MockDbConnection();
|
var conn = new MockDbConnection();
|
||||||
|
|
||||||
conn.Mocks
|
conn.Mocks
|
||||||
.When(cmd => cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId")
|
.When(cmd => cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId")
|
||||||
.ReturnsTable(MockTable.Empty());
|
.ReturnsTable(MockTable.Empty());
|
||||||
|
|
||||||
var repo = CreateRepo(conn);
|
var repo = CreateRepo(conn);
|
||||||
var result = await repo.GetActiveCredentialByUserAccountIdAsync(userId);
|
var result = await repo.GetActiveCredentialByUserAccountIdAsync(userId);
|
||||||
|
|
||||||
result.Should().BeNull();
|
result.Should().BeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task RotateCredentialAsync_ExecutesSuccessfully()
|
public async Task RotateCredentialAsync_ExecutesSuccessfully()
|
||||||
{
|
{
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
var newPasswordHash = "new_hashed_password";
|
var newPasswordHash = "new_hashed_password";
|
||||||
var conn = new MockDbConnection();
|
var conn = new MockDbConnection();
|
||||||
|
|
||||||
conn.Mocks
|
conn.Mocks
|
||||||
.When(cmd => cmd.CommandText == "USP_RotateUserCredential")
|
.When(cmd => cmd.CommandText == "USP_RotateUserCredential")
|
||||||
.ReturnsScalar(1);
|
.ReturnsScalar(1);
|
||||||
|
|
||||||
var repo = CreateRepo(conn);
|
var repo = CreateRepo(conn);
|
||||||
|
|
||||||
// Should not throw
|
// Should not throw
|
||||||
var act = async () => await repo.RotateCredentialAsync(userId, newPasswordHash);
|
var act = async () => await repo.RotateCredentialAsync(userId, newPasswordHash);
|
||||||
await act.Should().NotThrowAsync();
|
await act.Should().NotThrowAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task InvalidateCredentialsByUserAccountIdAsync_ExecutesSuccessfully()
|
|
||||||
{
|
|
||||||
var userId = Guid.NewGuid();
|
|
||||||
var conn = new MockDbConnection();
|
|
||||||
|
|
||||||
var repo = CreateRepo(conn);
|
|
||||||
|
|
||||||
// Should complete without error
|
|
||||||
var act = async () => await repo.InvalidateCredentialsByUserAccountIdAsync(userId);
|
|
||||||
await act.Should().NotThrowAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ public class AuthService(
|
|||||||
IPasswordService passwordService
|
IPasswordService passwordService
|
||||||
) : IAuthService
|
) : IAuthService
|
||||||
{
|
{
|
||||||
public async Task<UserAccount?> RegisterAsync(UserAccount userAccount, string password)
|
public async Task<UserAccount> RegisterAsync(UserAccount userAccount, string password)
|
||||||
{
|
{
|
||||||
// Check if user already exists
|
// Check if user already exists
|
||||||
var user = await authRepo.GetUserByUsernameAsync(userAccount.Username);
|
var user = await authRepo.GetUserByUsernameAsync(userAccount.Username);
|
||||||
if (user is not null)
|
if (user is not null)
|
||||||
{
|
{
|
||||||
return null;
|
return null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
// password hashing
|
// password hashing
|
||||||
@@ -45,9 +45,4 @@ public class AuthService(
|
|||||||
if (activeCred is null) return null;
|
if (activeCred is null) return null;
|
||||||
return !passwordService.Verify(password, activeCred.Hash) ? null : user;
|
return !passwordService.Verify(password, activeCred.Hash) ? null : user;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public async Task InvalidateAsync(Guid userAccountId)
|
|
||||||
{
|
|
||||||
await authRepo.InvalidateCredentialsByUserAccountIdAsync(userAccountId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user