Files
Ho Ngoc Hai 76d75c753b Migrate
2026-05-23 18:37:02 +07:00

407 lines
14 KiB
C#

using System.Net;
using System.Net.Http.Json;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using Xunit;
using FluentAssertions;
using IamService.API.Application.Common;
using IamService.API.Controllers;
namespace IamService.FunctionalTests.Controllers;
/// <summary>
/// EN: Functional tests for GroupsController endpoints.
/// VI: Functional tests cho các endpoints của GroupsController.
/// </summary>
public class GroupsControllerTests : IClassFixture<CustomWebApplicationFactory>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory _factory;
private readonly Guid _testOrganizationId;
public GroupsControllerTests(CustomWebApplicationFactory factory)
{
_factory = factory;
_client = factory.CreateClient();
// EN: Add authorization header with test token
// VI: Thêm authorization header với test token
_client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", GenerateTestToken());
// EN: Create a test organization for all group tests
// VI: Tạo organization test cho tất cả group tests
_testOrganizationId = CreateTestOrganizationAsync().GetAwaiter().GetResult();
}
#region Create Group Tests
[Fact]
public async Task CreateGroup_ValidRequest_Returns201WithLocation()
{
// Arrange
var request = new CreateGroupRequest
{
Name = $"Test Group {Guid.NewGuid():N}",
OrganizationId = _testOrganizationId,
Description = "A test group"
};
// Act
var response = await _client.PostAsJsonAsync("/api/v1/groups", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.Created);
response.Headers.Location.Should().NotBeNull();
var result = await response.Content.ReadFromJsonAsync<ApiResponse<GroupResponse>>();
result.Should().NotBeNull();
result!.Success.Should().BeTrue();
result.Data.Should().NotBeNull();
result.Data!.Name.Should().Be(request.Name);
result.Data.OrganizationId.Should().Be(_testOrganizationId);
result.Data.MemberCount.Should().Be(0);
}
[Fact]
public async Task CreateGroup_WithoutDescription_Returns201()
{
// Arrange
var request = new CreateGroupRequest
{
Name = $"No Description Group {Guid.NewGuid():N}",
OrganizationId = _testOrganizationId
};
// Act
var response = await _client.PostAsJsonAsync("/api/v1/groups", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.Created);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<GroupResponse>>();
result!.Data!.Description.Should().BeNull();
}
[Fact]
public async Task CreateGroup_NonExistentOrganization_Returns400()
{
// Arrange
var request = new CreateGroupRequest
{
Name = "Group for Non-existent Org",
OrganizationId = Guid.NewGuid() // Non-existent org
};
// Act
var response = await _client.PostAsJsonAsync("/api/v1/groups", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<GroupResponse>>();
result!.Success.Should().BeFalse();
result.Error!.Code.Should().Be("ORG_NOT_FOUND");
}
#endregion
#region Get Group Tests
[Fact]
public async Task GetGroupById_ExistingId_Returns200WithData()
{
// Arrange - Create group first
var createRequest = new CreateGroupRequest
{
Name = $"Get Test Group {Guid.NewGuid():N}",
OrganizationId = _testOrganizationId
};
var createResponse = await _client.PostAsJsonAsync("/api/v1/groups", createRequest);
var createResult = await createResponse.Content.ReadFromJsonAsync<ApiResponse<GroupResponse>>();
var groupId = createResult!.Data!.Id;
// Act
var response = await _client.GetAsync($"/api/v1/groups/{groupId}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<GroupResponse>>();
result!.Success.Should().BeTrue();
result.Data!.Id.Should().Be(groupId);
}
[Fact]
public async Task GetGroupById_NonExistingId_Returns404()
{
// Arrange
var nonExistentId = Guid.NewGuid();
// Act
var response = await _client.GetAsync($"/api/v1/groups/{nonExistentId}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<GroupResponse>>();
result!.Error!.Code.Should().Be("GROUP_NOT_FOUND");
}
[Fact]
public async Task GetGroupsByOrganization_ExistingOrg_Returns200()
{
// Arrange - Create some groups
var groupName = $"Org Group {Guid.NewGuid():N}";
var createRequest = new CreateGroupRequest
{
Name = groupName,
OrganizationId = _testOrganizationId
};
await _client.PostAsJsonAsync("/api/v1/groups", createRequest);
// Act
var response = await _client.GetAsync($"/api/v1/groups?organizationId={_testOrganizationId}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<IEnumerable<GroupResponse>>>();
result!.Success.Should().BeTrue();
result.Data.Should().NotBeNull();
}
#endregion
#region Add Member Tests
[Fact]
public async Task AddMember_ValidRequest_Returns200()
{
// Arrange - Create group first
var createRequest = new CreateGroupRequest
{
Name = $"Member Test Group {Guid.NewGuid():N}",
OrganizationId = _testOrganizationId
};
var createResponse = await _client.PostAsJsonAsync("/api/v1/groups", createRequest);
var createResult = await createResponse.Content.ReadFromJsonAsync<ApiResponse<GroupResponse>>();
var groupId = createResult!.Data!.Id;
var addMemberRequest = new AddGroupMemberRequest
{
UserId = Guid.NewGuid()
};
// Act
var response = await _client.PostAsJsonAsync($"/api/v1/groups/{groupId}/members", addMemberRequest);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<GroupMemberResponse>>();
result!.Success.Should().BeTrue();
result.Data!.GroupId.Should().Be(groupId);
result.Data.UserId.Should().Be(addMemberRequest.UserId);
result.Data.Role.Should().Be("Member"); // Default role
}
[Fact]
public async Task AddMember_WithAdminRole_Returns200WithAdminRole()
{
// Arrange - Create group first
var createRequest = new CreateGroupRequest
{
Name = $"Admin Role Test Group {Guid.NewGuid():N}",
OrganizationId = _testOrganizationId
};
var createResponse = await _client.PostAsJsonAsync("/api/v1/groups", createRequest);
var createResult = await createResponse.Content.ReadFromJsonAsync<ApiResponse<GroupResponse>>();
var groupId = createResult!.Data!.Id;
var addMemberRequest = new AddGroupMemberRequest
{
UserId = Guid.NewGuid(),
RoleId = 2 // Admin role
};
// Act
var response = await _client.PostAsJsonAsync($"/api/v1/groups/{groupId}/members", addMemberRequest);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<GroupMemberResponse>>();
result!.Data!.Role.Should().Be("Admin");
}
[Fact]
public async Task AddMember_GroupNotFound_Returns404()
{
// Arrange
var addMemberRequest = new AddGroupMemberRequest
{
UserId = Guid.NewGuid()
};
// Act
var response = await _client.PostAsJsonAsync($"/api/v1/groups/{Guid.NewGuid()}/members", addMemberRequest);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
}
[Fact]
public async Task AddMember_DuplicateMember_Returns400()
{
// Arrange - Create group and add member first
var createRequest = new CreateGroupRequest
{
Name = $"Duplicate Member Test Group {Guid.NewGuid():N}",
OrganizationId = _testOrganizationId
};
var createResponse = await _client.PostAsJsonAsync("/api/v1/groups", createRequest);
var createResult = await createResponse.Content.ReadFromJsonAsync<ApiResponse<GroupResponse>>();
var groupId = createResult!.Data!.Id;
var userId = Guid.NewGuid();
var addMemberRequest = new AddGroupMemberRequest { UserId = userId };
await _client.PostAsJsonAsync($"/api/v1/groups/{groupId}/members", addMemberRequest);
// Act - Try to add same member again
var response = await _client.PostAsJsonAsync($"/api/v1/groups/{groupId}/members", addMemberRequest);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<GroupMemberResponse>>();
result!.Error!.Code.Should().Be("ALREADY_MEMBER");
}
#endregion
#region Remove Member Tests
[Fact]
public async Task RemoveMember_ExistingMember_Returns200()
{
// Arrange - Create group and add member
var createRequest = new CreateGroupRequest
{
Name = $"Remove Member Test Group {Guid.NewGuid():N}",
OrganizationId = _testOrganizationId
};
var createResponse = await _client.PostAsJsonAsync("/api/v1/groups", createRequest);
var createResult = await createResponse.Content.ReadFromJsonAsync<ApiResponse<GroupResponse>>();
var groupId = createResult!.Data!.Id;
var userId = Guid.NewGuid();
await _client.PostAsJsonAsync($"/api/v1/groups/{groupId}/members", new AddGroupMemberRequest { UserId = userId });
// Act
var response = await _client.DeleteAsync($"/api/v1/groups/{groupId}/members/{userId}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
[Fact]
public async Task RemoveMember_NonExistingMember_Returns404()
{
// Arrange - Create group
var createRequest = new CreateGroupRequest
{
Name = $"Remove Non-Existing Member Test {Guid.NewGuid():N}",
OrganizationId = _testOrganizationId
};
var createResponse = await _client.PostAsJsonAsync("/api/v1/groups", createRequest);
var createResult = await createResponse.Content.ReadFromJsonAsync<ApiResponse<GroupResponse>>();
var groupId = createResult!.Data!.Id;
// Act - Try to remove non-existing member
var response = await _client.DeleteAsync($"/api/v1/groups/{groupId}/members/{Guid.NewGuid()}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
}
#endregion
#region Delete Group Tests
[Fact]
public async Task DeleteGroup_ExistingGroup_Returns200()
{
// Arrange - Create group first
var createRequest = new CreateGroupRequest
{
Name = $"Delete Test Group {Guid.NewGuid():N}",
OrganizationId = _testOrganizationId
};
var createResponse = await _client.PostAsJsonAsync("/api/v1/groups", createRequest);
var createResult = await createResponse.Content.ReadFromJsonAsync<ApiResponse<GroupResponse>>();
var groupId = createResult!.Data!.Id;
// Act
var response = await _client.DeleteAsync($"/api/v1/groups/{groupId}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
[Fact]
public async Task DeleteGroup_NonExistingGroup_Returns404()
{
// Act
var response = await _client.DeleteAsync($"/api/v1/groups/{Guid.NewGuid()}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
}
#endregion
#region Helper Methods
private async Task<Guid> CreateTestOrganizationAsync()
{
var slug = $"test-org-{Guid.NewGuid():N}".Substring(0, 30);
var request = new CreateOrganizationRequest
{
Name = "Test Organization for Groups",
Slug = slug
};
var response = await _client.PostAsJsonAsync("/api/v1/organizations", request);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<OrganizationResponse>>();
return result?.Data?.Id ?? Guid.NewGuid();
}
private static string GenerateTestToken()
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Email, "test@example.com"),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim("name", "Test User")
};
var key = new SymmetricSecurityKey("test-secret-key-for-testing-purposes-only-32-chars!"u8.ToArray());
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: "http://localhost",
audience: "api",
claims: claims,
expires: DateTime.UtcNow.AddHours(1),
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
#endregion
}