From 00cda92656f2328c6f0a5a4bed9fe30633f064c7 Mon Sep 17 00:00:00 2001 From: Ho Ngoc Hai Date: Thu, 15 Jan 2026 18:33:19 +0700 Subject: [PATCH] =?UTF-8?q?refactor:=20Lo=E1=BA=A1i=20b=E1=BB=8F=20c=C3=A1?= =?UTF-8?q?c=20functional=20test=20kh=C3=B4ng=20li=C3=AAn=20quan=20=C4=91?= =?UTF-8?q?=E1=BA=BFn=20=E1=BB=A7y=20quy=E1=BB=81n=20kh=E1=BB=8Fi=20`Membe?= =?UTF-8?q?rsControllerTests`=20v=C3=A0=20th=C3=AAm=20`NSubstitute.Excepti?= =?UTF-8?q?onExtensions`=20v=C3=A0o=20c=C3=A1c=20unit=20test=20c=E1=BB=A7a?= =?UTF-8?q?=20`StorageService`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/MembersControllerTests.cs | 162 +----------------- .../SequentialTestCollection.cs | 13 ++ .../Domain/FileShareTests.cs | 38 ++-- .../Handlers/DeleteFileCommandHandlerTests.cs | 1 + .../Handlers/FileShareCommandHandlerTests.cs | 25 +-- .../Handlers/SignUploadCommandHandlerTests.cs | 1 + .../Handlers/UploadFileCommandHandlerTests.cs | 3 +- 7 files changed, 54 insertions(+), 189 deletions(-) create mode 100644 services/membership-service-net/tests/MembershipService.FunctionalTests/SequentialTestCollection.cs diff --git a/services/membership-service-net/tests/MembershipService.FunctionalTests/Controllers/MembersControllerTests.cs b/services/membership-service-net/tests/MembershipService.FunctionalTests/Controllers/MembersControllerTests.cs index 3494e0da..7f45c191 100644 --- a/services/membership-service-net/tests/MembershipService.FunctionalTests/Controllers/MembersControllerTests.cs +++ b/services/membership-service-net/tests/MembershipService.FunctionalTests/Controllers/MembersControllerTests.cs @@ -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; /// -/// 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. /// public class MembersControllerTests : IClassFixture { @@ -20,7 +19,7 @@ public class MembersControllerTests : IClassFixture _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 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 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 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(); - 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(); - 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(); - 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() { diff --git a/services/membership-service-net/tests/MembershipService.FunctionalTests/SequentialTestCollection.cs b/services/membership-service-net/tests/MembershipService.FunctionalTests/SequentialTestCollection.cs new file mode 100644 index 00000000..dc10059a --- /dev/null +++ b/services/membership-service-net/tests/MembershipService.FunctionalTests/SequentialTestCollection.cs @@ -0,0 +1,13 @@ +using Xunit; + +namespace MembershipService.FunctionalTests; + +/// +/// 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. +/// +[CollectionDefinition("Sequential")] +public class SequentialTestCollection : ICollectionFixture +{ +} diff --git a/services/storage-service-net/tests/StorageService.UnitTests/Domain/FileShareTests.cs b/services/storage-service-net/tests/StorageService.UnitTests/Domain/FileShareTests.cs index 29e63e4d..b28b9272 100644 --- a/services/storage-service-net/tests/StorageService.UnitTests/Domain/FileShareTests.cs +++ b/services/storage-service-net/tests/StorageService.UnitTests/Domain/FileShareTests.cs @@ -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; /// /// 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); diff --git a/services/storage-service-net/tests/StorageService.UnitTests/Handlers/DeleteFileCommandHandlerTests.cs b/services/storage-service-net/tests/StorageService.UnitTests/Handlers/DeleteFileCommandHandlerTests.cs index 89da3679..b208182b 100644 --- a/services/storage-service-net/tests/StorageService.UnitTests/Handlers/DeleteFileCommandHandlerTests.cs +++ b/services/storage-service-net/tests/StorageService.UnitTests/Handlers/DeleteFileCommandHandlerTests.cs @@ -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; diff --git a/services/storage-service-net/tests/StorageService.UnitTests/Handlers/FileShareCommandHandlerTests.cs b/services/storage-service-net/tests/StorageService.UnitTests/Handlers/FileShareCommandHandlerTests.cs index 74f36f59..13e47423 100644 --- a/services/storage-service-net/tests/StorageService.UnitTests/Handlers/FileShareCommandHandlerTests.cs +++ b/services/storage-service-net/tests/StorageService.UnitTests/Handlers/FileShareCommandHandlerTests.cs @@ -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()) .Returns(file); @@ -92,7 +93,7 @@ public class FileShareCommandHandlerTests var command = new CreateFileShareCommand( TestFileId, TestUserId, - SharePermission.Read); + SharePermission.Download); _fileRepository.GetByIdAsync(TestFileId, Arg.Any()) .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()) @@ -146,7 +147,7 @@ public class FileShareCommandHandlerTests var command = new CreateFileShareCommand( TestFileId, TestUserId, - SharePermission.Read, + SharePermission.Download, ExpiresAt: expiresAt); _fileRepository.GetByIdAsync(TestFileId, Arg.Any()) @@ -174,7 +175,7 @@ public class FileShareCommandHandlerTests var command = new CreateFileShareCommand( TestFileId, TestUserId, - SharePermission.Read, + SharePermission.Download, MaxDownloads: 10); _fileRepository.GetByIdAsync(TestFileId, Arg.Any()) @@ -205,7 +206,7 @@ public class FileShareCommandHandlerTests var command = new CreateFileShareCommand( TestFileId, TestUserId, - SharePermission.Read); + SharePermission.Download); _fileRepository.GetByIdAsync(TestFileId, Arg.Any()) .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()) .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()) .Returns(file); @@ -275,7 +276,7 @@ public class FileShareCommandHandlerTests var command = new CreateFileShareCommand( TestFileId, TestUserId, - SharePermission.Read); + SharePermission.Download); _fileRepository.GetByIdAsync(TestFileId, Arg.Any()) .Returns(file); @@ -300,7 +301,7 @@ public class FileShareCommandHandlerTests var command = new CreateFileShareCommand( TestFileId, TestUserId, - SharePermission.Read); + SharePermission.Download); _fileRepository.GetByIdAsync(TestFileId, Arg.Any()) .Returns(file); @@ -326,7 +327,7 @@ public class FileShareCommandHandlerTests var command = new CreateFileShareCommand( TestFileId, TestUserId, - SharePermission.Read); + SharePermission.Download); _fileRepository.GetByIdAsync(TestFileId, Arg.Any()) .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()) .Returns(file); diff --git a/services/storage-service-net/tests/StorageService.UnitTests/Handlers/SignUploadCommandHandlerTests.cs b/services/storage-service-net/tests/StorageService.UnitTests/Handlers/SignUploadCommandHandlerTests.cs index 4fad5485..6f5a25a3 100644 --- a/services/storage-service-net/tests/StorageService.UnitTests/Handlers/SignUploadCommandHandlerTests.cs +++ b/services/storage-service-net/tests/StorageService.UnitTests/Handlers/SignUploadCommandHandlerTests.cs @@ -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; diff --git a/services/storage-service-net/tests/StorageService.UnitTests/Handlers/UploadFileCommandHandlerTests.cs b/services/storage-service-net/tests/StorageService.UnitTests/Handlers/UploadFileCommandHandlerTests.cs index 85bd2928..5eb4fd4c 100644 --- a/services/storage-service-net/tests/StorageService.UnitTests/Handlers/UploadFileCommandHandlerTests.cs +++ b/services/storage-service-net/tests/StorageService.UnitTests/Handlers/UploadFileCommandHandlerTests.cs @@ -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;