feature: No user management endpoints (create, update, deactivate)
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
[Collection("Database")]
|
||||
public class UserManagementTests : IAsyncLifetime
|
||||
{
|
||||
private readonly ApiFixture _fixture;
|
||||
private HttpClient _client = null!;
|
||||
|
||||
public UserManagementTests(ApiFixture fixture) => _fixture = fixture;
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
using var scope = _fixture.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
await DbResetHelper.ResetAsync(db);
|
||||
await DataSeeder.SeedAsync(db);
|
||||
_client = await AuthHelper.LoginAsync(_fixture, "admin1");
|
||||
}
|
||||
|
||||
public Task DisposeAsync() => Task.CompletedTask;
|
||||
|
||||
[Fact]
|
||||
public async Task CreateUser_ValidRequest_Returns201WithUser()
|
||||
{
|
||||
var request = new
|
||||
{
|
||||
Username = "newclerk1",
|
||||
Password = "Secure1Pass",
|
||||
FullName = "New Clerk",
|
||||
Role = "DATA_ENTRY_CLERK"
|
||||
};
|
||||
|
||||
var response = await _client.PostAsJsonAsync("/api/v1/users", request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Created);
|
||||
|
||||
var body = await response.Content.ReadFromJsonAsync<JsonElement>();
|
||||
var data = body.GetProperty("data");
|
||||
data.GetProperty("username").GetString().Should().Be("newclerk1");
|
||||
data.GetProperty("fullName").GetString().Should().Be("New Clerk");
|
||||
data.GetProperty("role").GetString().Should().Be("DATA_ENTRY_CLERK");
|
||||
data.GetProperty("id").GetGuid().Should().NotBeEmpty();
|
||||
|
||||
using var scope = _fixture.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
var user = await db.Users.FirstOrDefaultAsync(u => u.Username == "newclerk1");
|
||||
user.Should().NotBeNull();
|
||||
user!.IsActive.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateUser_DuplicateUsername_Returns409()
|
||||
{
|
||||
var request = new
|
||||
{
|
||||
Username = "entry1",
|
||||
Password = "Secure1Pass",
|
||||
FullName = "Duplicate Entry",
|
||||
Role = "DATA_ENTRY_CLERK"
|
||||
};
|
||||
|
||||
var response = await _client.PostAsJsonAsync("/api/v1/users", request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Conflict);
|
||||
|
||||
var body = await response.Content.ReadFromJsonAsync<JsonElement>();
|
||||
body.GetProperty("error").GetProperty("code").GetString().Should().Be("USERNAME_TAKEN");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateUser_WeakPassword_Returns422()
|
||||
{
|
||||
var request = new
|
||||
{
|
||||
Username = "weakuser",
|
||||
Password = "short",
|
||||
FullName = "Weak Password User",
|
||||
Role = "VERIFIER"
|
||||
};
|
||||
|
||||
var response = await _client.PostAsJsonAsync("/api/v1/users", request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateUser_NonAdmin_Returns403()
|
||||
{
|
||||
var entryClient = await AuthHelper.LoginAsync(_fixture, "entry1");
|
||||
|
||||
var request = new
|
||||
{
|
||||
Username = "unauthorized",
|
||||
Password = "Secure1Pass",
|
||||
FullName = "Unauthorized Create",
|
||||
Role = "VERIFIER"
|
||||
};
|
||||
|
||||
var response = await entryClient.PostAsJsonAsync("/api/v1/users", request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Forbidden);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateUser_ChangeRoleAndName_Returns200()
|
||||
{
|
||||
// First create a user to update
|
||||
var createReq = new
|
||||
{
|
||||
Username = "toupdate1",
|
||||
Password = "Secure1Pass",
|
||||
FullName = "Original Name",
|
||||
Role = "DATA_ENTRY_CLERK"
|
||||
};
|
||||
var createResp = await _client.PostAsJsonAsync("/api/v1/users", createReq);
|
||||
createResp.StatusCode.Should().Be(HttpStatusCode.Created);
|
||||
var created = await createResp.Content.ReadFromJsonAsync<JsonElement>();
|
||||
var userId = created.GetProperty("data").GetProperty("id").GetGuid();
|
||||
|
||||
// Update
|
||||
var updateReq = new
|
||||
{
|
||||
FullName = "Updated Name",
|
||||
Role = "VERIFIER"
|
||||
};
|
||||
|
||||
var response = await _client.PatchAsJsonAsync($"/api/v1/users/{userId}", updateReq);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var body = await response.Content.ReadFromJsonAsync<JsonElement>();
|
||||
var data = body.GetProperty("data");
|
||||
data.GetProperty("fullName").GetString().Should().Be("Updated Name");
|
||||
data.GetProperty("role").GetString().Should().Be("VERIFIER");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateUser_Deactivate_Returns200()
|
||||
{
|
||||
var createReq = new
|
||||
{
|
||||
Username = "todeactivate1",
|
||||
Password = "Secure1Pass",
|
||||
FullName = "Will Be Deactivated",
|
||||
Role = "INTAKE_CLERK"
|
||||
};
|
||||
var createResp = await _client.PostAsJsonAsync("/api/v1/users", createReq);
|
||||
createResp.StatusCode.Should().Be(HttpStatusCode.Created);
|
||||
var created = await createResp.Content.ReadFromJsonAsync<JsonElement>();
|
||||
var userId = created.GetProperty("data").GetProperty("id").GetGuid();
|
||||
|
||||
var response = await _client.PatchAsJsonAsync(
|
||||
$"/api/v1/users/{userId}", new { IsActive = false });
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
using var scope = _fixture.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
var user = await db.Users.FindAsync(userId);
|
||||
user!.IsActive.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateUser_NotFound_Returns404()
|
||||
{
|
||||
var response = await _client.PatchAsJsonAsync(
|
||||
$"/api/v1/users/{Guid.NewGuid()}", new { FullName = "Ghost" });
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResetPassword_AdminResets_Returns204()
|
||||
{
|
||||
var createReq = new
|
||||
{
|
||||
Username = "resetme1",
|
||||
Password = "OldPass1Word",
|
||||
FullName = "Reset Target",
|
||||
Role = "DATA_ENTRY_CLERK"
|
||||
};
|
||||
var createResp = await _client.PostAsJsonAsync("/api/v1/users", createReq);
|
||||
createResp.StatusCode.Should().Be(HttpStatusCode.Created);
|
||||
var created = await createResp.Content.ReadFromJsonAsync<JsonElement>();
|
||||
var userId = created.GetProperty("data").GetProperty("id").GetGuid();
|
||||
|
||||
var response = await _client.PostAsJsonAsync(
|
||||
$"/api/v1/users/{userId}/reset-password",
|
||||
new { NewPassword = "NewPass1Word" });
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.NoContent);
|
||||
|
||||
// Verify new password works by logging in
|
||||
var loginClient = _fixture.CreateClient();
|
||||
var loginResp = await loginClient.PostAsJsonAsync("/api/v1/auth/login",
|
||||
new { username = "resetme1", password = "NewPass1Word" });
|
||||
loginResp.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ChangePassword_CorrectCurrent_Returns204()
|
||||
{
|
||||
// Create a user, then log in as them and change their own password
|
||||
var createReq = new
|
||||
{
|
||||
Username = "selfchange1",
|
||||
Password = "Original1Pass",
|
||||
FullName = "Self Changer",
|
||||
Role = "VERIFIER"
|
||||
};
|
||||
await _client.PostAsJsonAsync("/api/v1/users", createReq);
|
||||
|
||||
var userClient = await AuthHelper.LoginAsync(_fixture, "selfchange1", "Original1Pass");
|
||||
|
||||
var response = await userClient.PostAsJsonAsync("/api/v1/users/me/change-password",
|
||||
new { CurrentPassword = "Original1Pass", NewPassword = "Changed1Pass" });
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.NoContent);
|
||||
|
||||
// Verify new password works
|
||||
var loginClient = _fixture.CreateClient();
|
||||
var loginResp = await loginClient.PostAsJsonAsync("/api/v1/auth/login",
|
||||
new { username = "selfchange1", password = "Changed1Pass" });
|
||||
loginResp.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ChangePassword_WrongCurrent_Returns422()
|
||||
{
|
||||
var createReq = new
|
||||
{
|
||||
Username = "wrongpw1",
|
||||
Password = "Correct1Pass",
|
||||
FullName = "Wrong Password",
|
||||
Role = "VERIFIER"
|
||||
};
|
||||
await _client.PostAsJsonAsync("/api/v1/users", createReq);
|
||||
|
||||
var userClient = await AuthHelper.LoginAsync(_fixture, "wrongpw1", "Correct1Pass");
|
||||
|
||||
var response = await userClient.PostAsJsonAsync("/api/v1/users/me/change-password",
|
||||
new { CurrentPassword = "Wrong1Pass", NewPassword = "New1Pass" });
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
|
||||
|
||||
var body = await response.Content.ReadFromJsonAsync<JsonElement>();
|
||||
body.GetProperty("error").GetProperty("code").GetString().Should().Be("INVALID_PASSWORD");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user