feature: No user management endpoints (create, update, deactivate)

This commit is contained in:
voltsrage
2026-06-27 15:45:01 +08:00
parent 5a2e95c984
commit 299e6e1453
13 changed files with 684 additions and 4 deletions
@@ -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");
}
}