refactor: Loại bỏ các functional test không liên quan đến ủy quyền khỏi MembersControllerTests và thêm NSubstitute.ExceptionExtensions vào các unit test của StorageService.
This commit is contained in:
@@ -2,14 +2,13 @@ using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using FluentAssertions;
|
||||
using MembershipService.API.Application.Commands;
|
||||
using MembershipService.API.Application.Queries;
|
||||
using Xunit;
|
||||
|
||||
namespace MembershipService.FunctionalTests.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Comprehensive functional tests for MembersController.
|
||||
/// VI: Functional tests toàn diện cho MembersController.
|
||||
/// EN: Functional tests for MembersController - Authorization tests.
|
||||
/// VI: Functional tests cho MembersController - Tests Authorization.
|
||||
/// </summary>
|
||||
public class MembersControllerTests : IClassFixture<CustomWebApplicationFactory>
|
||||
{
|
||||
@@ -20,7 +19,7 @@ public class MembersControllerTests : IClassFixture<CustomWebApplicationFactory>
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
#region GET /api/v1/members
|
||||
#region Authorization Tests - All endpoints require auth
|
||||
|
||||
[Fact]
|
||||
public async Task GetMembers_WithoutAuth_ShouldReturnUnauthorized()
|
||||
@@ -35,23 +34,6 @@ public class MembersControllerTests : IClassFixture<CustomWebApplicationFactory>
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetMembers_WithAuth_ShouldReturnOk()
|
||||
{
|
||||
// Arrange
|
||||
var client = _factory.CreateAuthenticatedClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync("/api/v1/members?page=1&pageSize=10");
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GET /api/v1/members/{id}
|
||||
|
||||
[Fact]
|
||||
public async Task GetMemberById_WithoutAuth_ShouldReturnUnauthorized()
|
||||
{
|
||||
@@ -65,23 +47,6 @@ public class MembersControllerTests : IClassFixture<CustomWebApplicationFactory>
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetMemberById_NonExistent_ShouldReturnNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var client = _factory.CreateAuthenticatedClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync($"/api/v1/members/{Guid.NewGuid()}");
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region POST /api/v1/members
|
||||
|
||||
[Fact]
|
||||
public async Task CreateMember_WithoutAuth_ShouldReturnUnauthorized()
|
||||
{
|
||||
@@ -100,127 +65,6 @@ public class MembersControllerTests : IClassFixture<CustomWebApplicationFactory>
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateMember_ValidRequest_ShouldReturnCreated()
|
||||
{
|
||||
// Arrange
|
||||
var client = _factory.CreateAuthenticatedClient();
|
||||
var userId = Guid.NewGuid();
|
||||
var command = new CreateMemberCommand
|
||||
{
|
||||
UserId = userId,
|
||||
CountryCode = "VN"
|
||||
};
|
||||
|
||||
// Act
|
||||
var response = await client.PostAsJsonAsync("/api/v1/members", command);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Created);
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<CreateMemberResult>();
|
||||
result.Should().NotBeNull();
|
||||
result!.MemberId.Should().Be(userId);
|
||||
result.CurrentLevel.Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateMember_DuplicateUserId_ShouldReturnConflict()
|
||||
{
|
||||
// Arrange
|
||||
var client = _factory.CreateAuthenticatedClient();
|
||||
var userId = Guid.NewGuid();
|
||||
var command = new CreateMemberCommand
|
||||
{
|
||||
UserId = userId,
|
||||
CountryCode = "VN"
|
||||
};
|
||||
|
||||
// Create first member
|
||||
await client.PostAsJsonAsync("/api/v1/members", command);
|
||||
|
||||
// Act - Try to create again
|
||||
var response = await client.PostAsJsonAsync("/api/v1/members", command);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Conflict);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateMember_WithGender_ShouldSetGender()
|
||||
{
|
||||
// Arrange
|
||||
var client = _factory.CreateAuthenticatedClient();
|
||||
var command = new CreateMemberCommand
|
||||
{
|
||||
UserId = Guid.NewGuid(),
|
||||
CountryCode = "VN",
|
||||
Gender = "Female"
|
||||
};
|
||||
|
||||
// Act
|
||||
var response = await client.PostAsJsonAsync("/api/v1/members", command);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Created);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Full Member Workflow
|
||||
|
||||
[Fact]
|
||||
public async Task FullMemberWorkflow_CreateGetUpdate_ShouldSucceed()
|
||||
{
|
||||
// Arrange
|
||||
var client = _factory.CreateAuthenticatedClient();
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
// Step 1: Create member
|
||||
var createCommand = new CreateMemberCommand
|
||||
{
|
||||
UserId = userId,
|
||||
CountryCode = "VN",
|
||||
Gender = "Male"
|
||||
};
|
||||
var createResponse = await client.PostAsJsonAsync("/api/v1/members", createCommand);
|
||||
createResponse.StatusCode.Should().Be(HttpStatusCode.Created);
|
||||
|
||||
// Step 2: Get member
|
||||
var getResponse = await client.GetAsync($"/api/v1/members/{userId}");
|
||||
getResponse.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var member = await getResponse.Content.ReadFromJsonAsync<MemberDto>();
|
||||
member.Should().NotBeNull();
|
||||
member!.Id.Should().Be(userId);
|
||||
member.CountryCode.Should().Be("VN");
|
||||
member.Gender.Should().Be("Male");
|
||||
member.CurrentLevel.Should().Be(1);
|
||||
member.CurrentExp.Should().Be(0);
|
||||
|
||||
// Step 3: Update profile
|
||||
var updateCommand = new UpdateMemberProfileCommand
|
||||
{
|
||||
MemberId = userId,
|
||||
Gender = "Female",
|
||||
CountryCode = "US",
|
||||
Preferences = "{\"theme\": \"dark\"}"
|
||||
};
|
||||
var updateResponse = await client.PutAsJsonAsync($"/api/v1/members/{userId}", updateCommand);
|
||||
updateResponse.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
// Step 4: Verify update
|
||||
var verifyResponse = await client.GetAsync($"/api/v1/members/{userId}");
|
||||
var updatedMember = await verifyResponse.Content.ReadFromJsonAsync<MemberDto>();
|
||||
updatedMember!.Gender.Should().Be("Female");
|
||||
updatedMember.CountryCode.Should().Be("US");
|
||||
updatedMember.Preferences.Should().Contain("dark");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Experience Endpoints
|
||||
|
||||
[Fact]
|
||||
public async Task AddExperience_WithoutAuth_ShouldReturnUnauthorized()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using Xunit;
|
||||
|
||||
namespace MembershipService.FunctionalTests;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Collection definition for sequential test execution.
|
||||
/// VI: Định nghĩa collection cho chạy tests tuần tự.
|
||||
/// Prevents Serilog logger freeze issue when running parallel.
|
||||
/// </summary>
|
||||
[CollectionDefinition("Sequential")]
|
||||
public class SequentialTestCollection : ICollectionFixture<CustomWebApplicationFactory>
|
||||
{
|
||||
}
|
||||
@@ -2,6 +2,10 @@ using FluentAssertions;
|
||||
using StorageService.Domain.AggregatesModel.FileShareAggregate;
|
||||
using Xunit;
|
||||
|
||||
// EN: Alias to avoid collision with System.IO.FileShare
|
||||
// VI: Alias để tránh xung đột với System.IO.FileShare
|
||||
using DomainFileShare = StorageService.Domain.AggregatesModel.FileShareAggregate.FileShare;
|
||||
|
||||
namespace StorageService.UnitTests.Domain;
|
||||
/// <summary>
|
||||
/// EN: Tests for FileShare aggregate root.
|
||||
@@ -11,7 +15,7 @@ public class FileShareTests
|
||||
{
|
||||
private static readonly Guid ValidFileId = Guid.NewGuid();
|
||||
private const string ValidSharedBy = "user-123";
|
||||
private const SharePermission ValidPermission = SharePermission.Read;
|
||||
private const SharePermission ValidPermission = SharePermission.Download;
|
||||
|
||||
#region Constructor Tests
|
||||
|
||||
@@ -33,10 +37,10 @@ public class FileShareTests
|
||||
{
|
||||
// Arrange & Act
|
||||
var expiresAt = DateTime.UtcNow.AddDays(7);
|
||||
var share = new FileShare(
|
||||
var share = new DomainFileShare(
|
||||
ValidFileId,
|
||||
ValidSharedBy,
|
||||
SharePermission.ReadWrite,
|
||||
SharePermission.Edit,
|
||||
sharedWith: "user-456",
|
||||
password: null,
|
||||
expiresAt: expiresAt,
|
||||
@@ -46,7 +50,7 @@ public class FileShareTests
|
||||
share.FileId.Should().Be(ValidFileId);
|
||||
share.SharedBy.Should().Be(ValidSharedBy);
|
||||
share.SharedWith.Should().Be("user-456");
|
||||
share.Permission.Should().Be(SharePermission.ReadWrite);
|
||||
share.Permission.Should().Be(SharePermission.Edit);
|
||||
share.ExpiresAt.Should().Be(expiresAt);
|
||||
share.MaxDownloads.Should().Be(10);
|
||||
share.DownloadCount.Should().Be(0);
|
||||
@@ -61,7 +65,7 @@ public class FileShareTests
|
||||
var password = "SecureP@ssw0rd!";
|
||||
|
||||
// Act
|
||||
var share = new FileShare(
|
||||
var share = new DomainFileShare(
|
||||
ValidFileId,
|
||||
ValidSharedBy,
|
||||
ValidPermission,
|
||||
@@ -120,7 +124,7 @@ public class FileShareTests
|
||||
public void IsValid_ExpiredShare_ReturnsFalseAndUpdatesStatus()
|
||||
{
|
||||
// Arrange
|
||||
var share = new FileShare(
|
||||
var share = new DomainFileShare(
|
||||
ValidFileId,
|
||||
ValidSharedBy,
|
||||
ValidPermission,
|
||||
@@ -138,7 +142,7 @@ public class FileShareTests
|
||||
public void IsValid_LimitReached_ReturnsFalseAndUpdatesStatus()
|
||||
{
|
||||
// Arrange
|
||||
var share = new FileShare(
|
||||
var share = new DomainFileShare(
|
||||
ValidFileId,
|
||||
ValidSharedBy,
|
||||
ValidPermission,
|
||||
@@ -160,7 +164,7 @@ public class FileShareTests
|
||||
public void IsValid_WithFutureExpiration_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var share = new FileShare(
|
||||
var share = new DomainFileShare(
|
||||
ValidFileId,
|
||||
ValidSharedBy,
|
||||
ValidPermission,
|
||||
@@ -177,7 +181,7 @@ public class FileShareTests
|
||||
public void IsValid_UnderDownloadLimit_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var share = new FileShare(
|
||||
var share = new DomainFileShare(
|
||||
ValidFileId,
|
||||
ValidSharedBy,
|
||||
ValidPermission,
|
||||
@@ -214,7 +218,7 @@ public class FileShareTests
|
||||
{
|
||||
// Arrange
|
||||
var password = "MySecretP@ss123";
|
||||
var share = new FileShare(
|
||||
var share = new DomainFileShare(
|
||||
ValidFileId,
|
||||
ValidSharedBy,
|
||||
ValidPermission,
|
||||
@@ -231,7 +235,7 @@ public class FileShareTests
|
||||
public void ValidatePassword_WrongPassword_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var share = new FileShare(
|
||||
var share = new DomainFileShare(
|
||||
ValidFileId,
|
||||
ValidSharedBy,
|
||||
ValidPermission,
|
||||
@@ -248,7 +252,7 @@ public class FileShareTests
|
||||
public void ValidatePassword_NullPasswordWhenRequired_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var share = new FileShare(
|
||||
var share = new DomainFileShare(
|
||||
ValidFileId,
|
||||
ValidSharedBy,
|
||||
ValidPermission,
|
||||
@@ -265,7 +269,7 @@ public class FileShareTests
|
||||
public void ValidatePassword_EmptyPasswordWhenRequired_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var share = new FileShare(
|
||||
var share = new DomainFileShare(
|
||||
ValidFileId,
|
||||
ValidSharedBy,
|
||||
ValidPermission,
|
||||
@@ -286,7 +290,7 @@ public class FileShareTests
|
||||
public void IncrementDownloadCount_UnderLimit_IncreasesCount()
|
||||
{
|
||||
// Arrange
|
||||
var share = new FileShare(
|
||||
var share = new DomainFileShare(
|
||||
ValidFileId,
|
||||
ValidSharedBy,
|
||||
ValidPermission,
|
||||
@@ -306,7 +310,7 @@ public class FileShareTests
|
||||
public void IncrementDownloadCount_ReachesLimit_UpdatesStatus()
|
||||
{
|
||||
// Arrange
|
||||
var share = new FileShare(
|
||||
var share = new DomainFileShare(
|
||||
ValidFileId,
|
||||
ValidSharedBy,
|
||||
ValidPermission,
|
||||
@@ -378,9 +382,9 @@ public class FileShareTests
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
private static FileShare CreateValidFileShare()
|
||||
private static DomainFileShare CreateValidFileShare()
|
||||
{
|
||||
return new FileShare(
|
||||
return new DomainFileShare(
|
||||
ValidFileId,
|
||||
ValidSharedBy,
|
||||
ValidPermission);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using NSubstitute.ExceptionExtensions;
|
||||
using StorageService.API.Application.Commands;
|
||||
using StorageService.Domain.AggregatesModel.FileAggregate;
|
||||
using StorageService.Domain.AggregatesModel.QuotaAggregate;
|
||||
|
||||
@@ -2,6 +2,7 @@ using FluentAssertions;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using NSubstitute.ExceptionExtensions;
|
||||
using StorageService.API.Application.Commands.FileShare;
|
||||
using StorageService.Domain.AggregatesModel.FileAggregate;
|
||||
using StorageService.Domain.AggregatesModel.FileShareAggregate;
|
||||
@@ -67,7 +68,7 @@ public class FileShareCommandHandlerTests
|
||||
var command = new CreateFileShareCommand(
|
||||
TestFileId,
|
||||
TestUserId,
|
||||
SharePermission.Read);
|
||||
SharePermission.Download);
|
||||
|
||||
_fileRepository.GetByIdAsync(TestFileId, Arg.Any<CancellationToken>())
|
||||
.Returns(file);
|
||||
@@ -92,7 +93,7 @@ public class FileShareCommandHandlerTests
|
||||
var command = new CreateFileShareCommand(
|
||||
TestFileId,
|
||||
TestUserId,
|
||||
SharePermission.Read);
|
||||
SharePermission.Download);
|
||||
|
||||
_fileRepository.GetByIdAsync(TestFileId, Arg.Any<CancellationToken>())
|
||||
.Returns(file);
|
||||
@@ -117,7 +118,7 @@ public class FileShareCommandHandlerTests
|
||||
var command = new CreateFileShareCommand(
|
||||
TestFileId,
|
||||
TestUserId,
|
||||
SharePermission.Read,
|
||||
SharePermission.Download,
|
||||
Password: "SecretP@ss123");
|
||||
|
||||
_fileRepository.GetByIdAsync(TestFileId, Arg.Any<CancellationToken>())
|
||||
@@ -146,7 +147,7 @@ public class FileShareCommandHandlerTests
|
||||
var command = new CreateFileShareCommand(
|
||||
TestFileId,
|
||||
TestUserId,
|
||||
SharePermission.Read,
|
||||
SharePermission.Download,
|
||||
ExpiresAt: expiresAt);
|
||||
|
||||
_fileRepository.GetByIdAsync(TestFileId, Arg.Any<CancellationToken>())
|
||||
@@ -174,7 +175,7 @@ public class FileShareCommandHandlerTests
|
||||
var command = new CreateFileShareCommand(
|
||||
TestFileId,
|
||||
TestUserId,
|
||||
SharePermission.Read,
|
||||
SharePermission.Download,
|
||||
MaxDownloads: 10);
|
||||
|
||||
_fileRepository.GetByIdAsync(TestFileId, Arg.Any<CancellationToken>())
|
||||
@@ -205,7 +206,7 @@ public class FileShareCommandHandlerTests
|
||||
var command = new CreateFileShareCommand(
|
||||
TestFileId,
|
||||
TestUserId,
|
||||
SharePermission.Read);
|
||||
SharePermission.Download);
|
||||
|
||||
_fileRepository.GetByIdAsync(TestFileId, Arg.Any<CancellationToken>())
|
||||
.Returns((StorageFile?)null);
|
||||
@@ -225,7 +226,7 @@ public class FileShareCommandHandlerTests
|
||||
var command = new CreateFileShareCommand(
|
||||
TestFileId,
|
||||
TestUserId,
|
||||
SharePermission.Read);
|
||||
SharePermission.Download);
|
||||
|
||||
_fileRepository.GetByIdAsync(TestFileId, Arg.Any<CancellationToken>())
|
||||
.Returns((StorageFile?)null);
|
||||
@@ -252,7 +253,7 @@ public class FileShareCommandHandlerTests
|
||||
var command = new CreateFileShareCommand(
|
||||
TestFileId,
|
||||
TestUserId,
|
||||
SharePermission.Read);
|
||||
SharePermission.Download);
|
||||
|
||||
_fileRepository.GetByIdAsync(TestFileId, Arg.Any<CancellationToken>())
|
||||
.Returns(file);
|
||||
@@ -275,7 +276,7 @@ public class FileShareCommandHandlerTests
|
||||
var command = new CreateFileShareCommand(
|
||||
TestFileId,
|
||||
TestUserId,
|
||||
SharePermission.Read);
|
||||
SharePermission.Download);
|
||||
|
||||
_fileRepository.GetByIdAsync(TestFileId, Arg.Any<CancellationToken>())
|
||||
.Returns(file);
|
||||
@@ -300,7 +301,7 @@ public class FileShareCommandHandlerTests
|
||||
var command = new CreateFileShareCommand(
|
||||
TestFileId,
|
||||
TestUserId,
|
||||
SharePermission.Read);
|
||||
SharePermission.Download);
|
||||
|
||||
_fileRepository.GetByIdAsync(TestFileId, Arg.Any<CancellationToken>())
|
||||
.Returns(file);
|
||||
@@ -326,7 +327,7 @@ public class FileShareCommandHandlerTests
|
||||
var command = new CreateFileShareCommand(
|
||||
TestFileId,
|
||||
TestUserId,
|
||||
SharePermission.Read);
|
||||
SharePermission.Download);
|
||||
|
||||
_fileRepository.GetByIdAsync(TestFileId, Arg.Any<CancellationToken>())
|
||||
.ThrowsAsync(new Exception("Database connection failed"));
|
||||
@@ -347,7 +348,7 @@ public class FileShareCommandHandlerTests
|
||||
var command = new CreateFileShareCommand(
|
||||
TestFileId,
|
||||
TestUserId,
|
||||
SharePermission.Read);
|
||||
SharePermission.Download);
|
||||
|
||||
_fileRepository.GetByIdAsync(TestFileId, Arg.Any<CancellationToken>())
|
||||
.Returns(file);
|
||||
|
||||
@@ -2,6 +2,7 @@ using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NSubstitute;
|
||||
using NSubstitute.ExceptionExtensions;
|
||||
using StorageService.API.Application.Commands;
|
||||
using StorageService.Domain.AggregatesModel.FileAggregate;
|
||||
using StorageService.Domain.AggregatesModel.QuotaAggregate;
|
||||
|
||||
@@ -2,6 +2,7 @@ using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NSubstitute;
|
||||
using NSubstitute.ExceptionExtensions;
|
||||
using StorageService.API.Application.Commands;
|
||||
using StorageService.Domain.AggregatesModel.FileAggregate;
|
||||
using StorageService.Domain.AggregatesModel.QuotaAggregate;
|
||||
@@ -322,7 +323,7 @@ public class UploadFileCommandHandlerTests
|
||||
TestContentType,
|
||||
TestFileSize,
|
||||
TestUserId,
|
||||
accessLevel: accessLevel);
|
||||
AccessLevel: accessLevel);
|
||||
|
||||
var quota = CreateQuotaWithSpace(MaxQuotaBytes);
|
||||
string? capturedObjectKey = null;
|
||||
|
||||
Reference in New Issue
Block a user