Files
pos-system/microservices/services/iam-service-net/tests/IamService.FunctionalTests/Controllers/OrganizationsControllerTests.cs
Ho Ngoc Hai 76d75c753b Migrate
2026-05-23 18:37:02 +07:00

373 lines
13 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 OrganizationsController endpoints.
/// VI: Functional tests cho các endpoints của OrganizationsController.
/// </summary>
public class OrganizationsControllerTests : IClassFixture<CustomWebApplicationFactory>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory _factory;
public OrganizationsControllerTests(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());
}
#region Create Organization Tests
[Fact]
public async Task CreateOrganization_ValidRequest_Returns201WithLocation()
{
// Arrange
var request = new CreateOrganizationRequest
{
Name = $"Test Organization {Guid.NewGuid():N}",
Slug = $"test-org-{Guid.NewGuid():N}".Substring(0, 30),
Description = "A test organization"
};
// Act
var response = await _client.PostAsJsonAsync("/api/v1/organizations", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.Created);
response.Headers.Location.Should().NotBeNull();
var result = await response.Content.ReadFromJsonAsync<ApiResponse<OrganizationResponse>>();
result.Should().NotBeNull();
result!.Success.Should().BeTrue();
result.Data.Should().NotBeNull();
result.Data!.Name.Should().Be(request.Name);
result.Data.Slug.Should().Be(request.Slug);
result.Data.Status.Should().Be("Active");
}
[Fact]
public async Task CreateOrganization_DuplicateSlug_Returns409Conflict()
{
// Arrange
var uniqueSlug = $"duplicate-{Guid.NewGuid():N}".Substring(0, 30);
var request = new CreateOrganizationRequest
{
Name = "Organization 1",
Slug = uniqueSlug
};
// First creation - should succeed
var firstResponse = await _client.PostAsJsonAsync("/api/v1/organizations", request);
firstResponse.StatusCode.Should().Be(HttpStatusCode.Created);
// Second creation with same slug - should fail
request.Name = "Organization 2"; // Different name, same slug
// Act
var response = await _client.PostAsJsonAsync("/api/v1/organizations", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.Conflict);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<OrganizationResponse>>();
result.Should().NotBeNull();
result!.Success.Should().BeFalse();
result.Error.Should().NotBeNull();
result.Error!.Code.Should().Be("SLUG_EXISTS");
}
[Fact]
public async Task CreateOrganization_WithParentOrganization_Returns201WithParentId()
{
// Arrange - Create parent first
var parentSlug = $"parent-{Guid.NewGuid():N}".Substring(0, 30);
var parentRequest = new CreateOrganizationRequest
{
Name = "Parent Organization",
Slug = parentSlug
};
var parentResponse = await _client.PostAsJsonAsync("/api/v1/organizations", parentRequest);
var parentResult = await parentResponse.Content.ReadFromJsonAsync<ApiResponse<OrganizationResponse>>();
var parentId = parentResult!.Data!.Id;
// Create child
var childSlug = $"child-{Guid.NewGuid():N}".Substring(0, 30);
var childRequest = new CreateOrganizationRequest
{
Name = "Child Organization",
Slug = childSlug,
ParentOrganizationId = parentId
};
// Act
var response = await _client.PostAsJsonAsync("/api/v1/organizations", childRequest);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.Created);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<OrganizationResponse>>();
result!.Data!.ParentOrganizationId.Should().Be(parentId);
}
#endregion
#region Get Organization Tests
[Fact]
public async Task GetOrganizationById_ExistingId_Returns200WithData()
{
// Arrange - Create organization first
var slug = $"get-test-{Guid.NewGuid():N}".Substring(0, 30);
var createRequest = new CreateOrganizationRequest
{
Name = "Get Test Organization",
Slug = slug
};
var createResponse = await _client.PostAsJsonAsync("/api/v1/organizations", createRequest);
var createResult = await createResponse.Content.ReadFromJsonAsync<ApiResponse<OrganizationResponse>>();
var orgId = createResult!.Data!.Id;
// Act
var response = await _client.GetAsync($"/api/v1/organizations/{orgId}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<OrganizationResponse>>();
result.Should().NotBeNull();
result!.Success.Should().BeTrue();
result.Data!.Id.Should().Be(orgId);
result.Data.Name.Should().Be("Get Test Organization");
}
[Fact]
public async Task GetOrganizationById_NonExistingId_Returns404()
{
// Arrange
var nonExistentId = Guid.NewGuid();
// Act
var response = await _client.GetAsync($"/api/v1/organizations/{nonExistentId}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<OrganizationResponse>>();
result!.Success.Should().BeFalse();
result.Error!.Code.Should().Be("ORG_NOT_FOUND");
}
[Fact]
public async Task GetOrganizationBySlug_ExistingSlug_Returns200()
{
// Arrange - Create organization first
var slug = $"slug-test-{Guid.NewGuid():N}".Substring(0, 30);
var createRequest = new CreateOrganizationRequest
{
Name = "Slug Test Organization",
Slug = slug
};
await _client.PostAsJsonAsync("/api/v1/organizations", createRequest);
// Act
var response = await _client.GetAsync($"/api/v1/organizations/slug/{slug}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<OrganizationResponse>>();
result!.Data!.Slug.Should().Be(slug);
}
[Fact]
public async Task GetOrganizationBySlug_NonExistingSlug_Returns404()
{
// Act
var response = await _client.GetAsync("/api/v1/organizations/slug/non-existent-slug-12345");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
}
#endregion
#region Update Organization Tests
[Fact]
public async Task UpdateOrganization_ValidRequest_Returns200()
{
// Arrange - Create organization first
var slug = $"update-test-{Guid.NewGuid():N}".Substring(0, 30);
var createRequest = new CreateOrganizationRequest
{
Name = "Original Name",
Slug = slug,
Description = "Original Description"
};
var createResponse = await _client.PostAsJsonAsync("/api/v1/organizations", createRequest);
var createResult = await createResponse.Content.ReadFromJsonAsync<ApiResponse<OrganizationResponse>>();
var orgId = createResult!.Data!.Id;
var updateRequest = new UpdateOrganizationRequest
{
Name = "Updated Name",
Description = "Updated Description"
};
// Act
var response = await _client.PutAsJsonAsync($"/api/v1/organizations/{orgId}", updateRequest);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<OrganizationResponse>>();
result!.Data!.Name.Should().Be("Updated Name");
result.Data.Description.Should().Be("Updated Description");
}
[Fact]
public async Task UpdateOrganization_NonExistingId_Returns404()
{
// Arrange
var updateRequest = new UpdateOrganizationRequest
{
Name = "Updated Name"
};
// Act
var response = await _client.PutAsJsonAsync($"/api/v1/organizations/{Guid.NewGuid()}", updateRequest);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
}
#endregion
#region Archive Organization Tests
[Fact]
public async Task ArchiveOrganization_ExistingOrg_Returns200()
{
// Arrange - Create organization first
var slug = $"archive-test-{Guid.NewGuid():N}".Substring(0, 30);
var createRequest = new CreateOrganizationRequest
{
Name = "Archive Test Organization",
Slug = slug
};
var createResponse = await _client.PostAsJsonAsync("/api/v1/organizations", createRequest);
var createResult = await createResponse.Content.ReadFromJsonAsync<ApiResponse<OrganizationResponse>>();
var orgId = createResult!.Data!.Id;
// Act
var response = await _client.DeleteAsync($"/api/v1/organizations/{orgId}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
[Fact]
public async Task ArchiveOrganization_NonExistingId_Returns404()
{
// Act
var response = await _client.DeleteAsync($"/api/v1/organizations/{Guid.NewGuid()}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
}
#endregion
#region Hierarchy Tests
[Fact]
public async Task GetOrganizationHierarchy_ExistingOrg_Returns200()
{
// Arrange - Create organization first
var slug = $"hierarchy-{Guid.NewGuid():N}".Substring(0, 30);
var createRequest = new CreateOrganizationRequest
{
Name = "Hierarchy Organization",
Slug = slug
};
var createResponse = await _client.PostAsJsonAsync("/api/v1/organizations", createRequest);
var createResult = await createResponse.Content.ReadFromJsonAsync<ApiResponse<OrganizationResponse>>();
var orgId = createResult!.Data!.Id;
// Act
var response = await _client.GetAsync($"/api/v1/organizations/{orgId}/hierarchy");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
[Fact]
public async Task GetChildOrganizations_ExistingOrg_Returns200()
{
// Arrange - Create parent organization first
var slug = $"parent-child-{Guid.NewGuid():N}".Substring(0, 30);
var createRequest = new CreateOrganizationRequest
{
Name = "Parent Organization",
Slug = slug
};
var createResponse = await _client.PostAsJsonAsync("/api/v1/organizations", createRequest);
var createResult = await createResponse.Content.ReadFromJsonAsync<ApiResponse<OrganizationResponse>>();
var orgId = createResult!.Data!.Id;
// Act
var response = await _client.GetAsync($"/api/v1/organizations/{orgId}/children");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
#endregion
#region Helper Methods
/// <summary>
/// EN: Generate a test JWT token for authentication.
/// VI: Tạo JWT token test để xác thực.
/// </summary>
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
}