using System.Net; using System.Net.Http.Json; using System.Text.Json; using Azaion.E2E.Helpers; using FluentAssertions; using Xunit; namespace Azaion.E2E.Tests; [Collection("E2E")] public sealed class UserManagementTests { private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true }; private sealed record ErrorResponse(int ErrorCode, string Message); private sealed record UserDto(Guid Id, string Email, int Role, bool IsEnabled); private readonly TestFixture _fixture; public UserManagementTests(TestFixture fixture) => _fixture = fixture; private static string UserBasePath(string email) => $"/users/{Uri.EscapeDataString(email)}"; [Fact] public async Task Registration_with_valid_data_succeeds() { var email = $"testuser-{Guid.NewGuid():N}@azaion.com"; using var client = _fixture.CreateAuthenticatedClient(_fixture.AdminToken); try { // Arrange var body = new { email, password = "SecurePass1!", role = 10 }; // Act using var response = await client.PostAsync("/users", body); // Assert response.StatusCode.Should().BeOneOf(HttpStatusCode.OK, HttpStatusCode.NoContent); } finally { using var deleteResponse = await client.DeleteAsync(UserBasePath(email)); } } [Fact] public async Task List_users_returns_non_empty_array() { // Arrange using var client = _fixture.CreateAuthenticatedClient(_fixture.AdminToken); // Act using var response = await client.GetAsync("/users"); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); var users = await response.Content.ReadFromJsonAsync(JsonOptions); users.Should().NotBeNull(); users!.Length.Should().BeGreaterThanOrEqualTo(1); } [Fact] public async Task List_users_filtered_by_email_contains_only_matches() { // Arrange using var client = _fixture.CreateAuthenticatedClient(_fixture.AdminToken); // Act using var response = await client.GetAsync("/users?searchEmail=" + Uri.EscapeDataString("admin")); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); var users = await response.Content.ReadFromJsonAsync(JsonOptions); users.Should().NotBeNull(); users!.Should().NotBeEmpty(); users.Should().OnlyContain(u => u.Email.Contains("admin", StringComparison.OrdinalIgnoreCase)); } [Fact] public async Task Set_user_role_succeeds() { var email = $"testuser-{Guid.NewGuid():N}@azaion.com"; using var client = _fixture.CreateAuthenticatedClient(_fixture.AdminToken); try { // Arrange using (var createResp = await client.PostAsync("/users", new { email, password = "SecurePass1!", role = 10 })) { createResp.StatusCode.Should().BeOneOf(HttpStatusCode.OK, HttpStatusCode.NoContent); } // Act using var response = await client.PutAsync($"{UserBasePath(email)}/set-role/50"); // Assert response.StatusCode.Should().BeOneOf(HttpStatusCode.OK, HttpStatusCode.NoContent); } finally { using var deleteResponse = await client.DeleteAsync(UserBasePath(email)); } } [Fact] public async Task Disable_user_succeeds() { var email = $"testuser-{Guid.NewGuid():N}@azaion.com"; using var client = _fixture.CreateAuthenticatedClient(_fixture.AdminToken); try { // Arrange using (var createResp = await client.PostAsync("/users", new { email, password = "SecurePass1!", role = 10 })) { createResp.StatusCode.Should().BeOneOf(HttpStatusCode.OK, HttpStatusCode.NoContent); } // Act using var response = await client.PutAsync($"{UserBasePath(email)}/disable"); // Assert response.StatusCode.Should().BeOneOf(HttpStatusCode.OK, HttpStatusCode.NoContent); } finally { using var deleteResponse = await client.DeleteAsync(UserBasePath(email)); } } [Fact] public async Task Delete_user_succeeds_and_user_not_in_search_results() { var email = $"testuser-{Guid.NewGuid():N}@azaion.com"; using var client = _fixture.CreateAuthenticatedClient(_fixture.AdminToken); try { // Arrange using (var createResp = await client.PostAsync("/users", new { email, password = "SecurePass1!", role = 10 })) { createResp.StatusCode.Should().BeOneOf(HttpStatusCode.OK, HttpStatusCode.NoContent); } // Act using var deleteResponse = await client.DeleteAsync(UserBasePath(email)); // Assert deleteResponse.StatusCode.Should().BeOneOf(HttpStatusCode.OK, HttpStatusCode.NoContent); using var verifyResponse = await client.GetAsync("/users?searchEmail=" + Uri.EscapeDataString(email)); verifyResponse.StatusCode.Should().Be(HttpStatusCode.OK); var users = await verifyResponse.Content.ReadFromJsonAsync(JsonOptions); users.Should().NotBeNull(); users!.Should().NotContain(u => u.Email.Equals(email, StringComparison.OrdinalIgnoreCase)); } finally { using var deleteResponse = await client.DeleteAsync(UserBasePath(email)); } } [Fact] public async Task Registration_rejects_empty_email_with_400() { // Arrange using var client = _fixture.CreateAuthenticatedClient(_fixture.AdminToken); // Act using var response = await client.PostAsync("/users", new { email = "", password = "ValidPass123", role = 10 }); // Assert response.StatusCode.Should().Be(HttpStatusCode.BadRequest); } [Fact] public async Task Registration_rejects_invalid_email_format_with_400() { // Arrange using var client = _fixture.CreateAuthenticatedClient(_fixture.AdminToken); // Act using var response = await client.PostAsync("/users", new { email = "notavalidemail", password = "ValidPass123", role = 10 }); // Assert response.StatusCode.Should().Be(HttpStatusCode.BadRequest); } [Fact] public async Task Registration_rejects_short_password_with_400() { // Arrange using var client = _fixture.CreateAuthenticatedClient(_fixture.AdminToken); // Act using var response = await client.PostAsync("/users", new { email = "validmail@test.com", password = "short", role = 10 }); // Assert response.StatusCode.Should().Be(HttpStatusCode.BadRequest); } [Fact] public async Task Registration_rejects_duplicate_admin_email_with_409() { // Arrange using var client = _fixture.CreateAuthenticatedClient(_fixture.AdminToken); // Act using var response = await client.PostAsync("/users", new { email = _fixture.AdminEmail, password = "DuplicateP1!", role = 10 }); // Assert response.StatusCode.Should().Be(HttpStatusCode.Conflict); var err = await response.Content.ReadFromJsonAsync(JsonOptions); err.Should().NotBeNull(); err!.ErrorCode.Should().Be(20); } }