feat(storage-service): Update storage configuration and enhance service initialization

- Changed the default bucket name in `appsettings.Development.json` from "storage" to "goodgo" and updated MinIO endpoint and credentials for improved access.
- Modified the service initialization in `Program.cs` to include the environment name, enhancing configuration flexibility.
- Added a missing namespace in `CustomWebApplicationFactory.cs` for better test setup.
- Removed obsolete unit test files for `CreateSampleCommandHandler` and `SampleAggregate`, streamlining the test suite.
This commit is contained in:
Ho Ngoc Hai
2026-01-13 02:27:18 +07:00
parent ee3cd7c770
commit 0b59b7d219
6 changed files with 6 additions and 301 deletions

View File

@@ -24,7 +24,7 @@ try
.WriteTo.Console());
// EN: Add Infrastructure services / VI: Thêm Infrastructure services
builder.Services.AddInfrastructure(builder.Configuration);
builder.Services.AddInfrastructure(builder.Configuration, builder.Environment.EnvironmentName);
// EN: Add MediatR with behaviors / VI: Thêm MediatR với behaviors
builder.Services.AddMediatR(cfg =>

View File

@@ -12,15 +12,15 @@
},
"Storage": {
"Provider": "minio",
"DefaultBucket": "storage",
"DefaultBucket": "goodgo",
"PreSignedUrlExpirationSeconds": 3600,
"MaxFileSizeBytes": 104857600,
"MinIO": {
"Endpoint": "localhost:9000",
"Endpoint": "167.114.174.113:9000",
"AccessKey": "minioadmin",
"SecretKey": "minioadmin",
"SecretKey": "Velik@2026",
"UseSSL": false,
"Region": ""
"Region": "us-east-1"
},
"AliyunOSS": {
"Endpoint": "",

View File

@@ -1,80 +0,0 @@
using System.Net;
using System.Net.Http.Json;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
namespace StorageService.FunctionalTests.Controllers;
/// <summary>
/// EN: Functional tests for Samples API endpoints.
/// VI: Functional tests cho các endpoints API Samples.
/// </summary>
public class SamplesControllerTests : IClassFixture<CustomWebApplicationFactory>
{
private readonly HttpClient _client;
public SamplesControllerTests(CustomWebApplicationFactory factory)
{
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
}
[Fact]
public async Task GetSamples_ShouldReturnOkWithEmptyList()
{
// Act
var response = await _client.GetAsync("/api/v1/samples");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var content = await response.Content.ReadFromJsonAsync<ApiResponse<List<object>>>();
content?.Success.Should().BeTrue();
}
[Fact]
public async Task CreateSample_WithValidData_ShouldReturnCreated()
{
// Arrange
var request = new { Name = "Test Sample", Description = "Test Description" };
// Act
var response = await _client.PostAsJsonAsync("/api/v1/samples", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.Created);
var content = await response.Content.ReadFromJsonAsync<ApiResponse<CreateSampleResult>>();
content?.Success.Should().BeTrue();
content?.Data?.Id.Should().NotBeEmpty();
}
[Fact]
public async Task GetSample_WithInvalidId_ShouldReturnNotFound()
{
// Arrange
var invalidId = Guid.NewGuid();
// Act
var response = await _client.GetAsync($"/api/v1/samples/{invalidId}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
}
[Fact]
public async Task HealthCheck_ShouldReturnHealthy()
{
// Act
var response = await _client.GetAsync("/health/live");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
// EN: Helper DTOs for deserialization
// VI: Helper DTOs để deserialize
private record ApiResponse<T>(bool Success, T? Data);
private record CreateSampleResult(Guid Id);
}

View File

@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using StorageService.Infrastructure;
using StorageService.Infrastructure.Persistence;
namespace StorageService.FunctionalTests;

View File

@@ -1,65 +0,0 @@
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
using StorageService.API.Application.Commands;
using StorageService.Domain.AggregatesModel.SampleAggregate;
using StorageService.Domain.SeedWork;
using Xunit;
namespace StorageService.UnitTests.Application;
/// <summary>
/// EN: Unit tests for CreateSampleCommandHandler.
/// VI: Unit tests cho CreateSampleCommandHandler.
/// </summary>
public class CreateSampleCommandHandlerTests
{
private readonly Mock<ISampleRepository> _mockRepository;
private readonly Mock<ILogger<CreateSampleCommandHandler>> _mockLogger;
private readonly CreateSampleCommandHandler _handler;
public CreateSampleCommandHandlerTests()
{
_mockRepository = new Mock<ISampleRepository>();
_mockLogger = new Mock<ILogger<CreateSampleCommandHandler>>();
var mockUnitOfWork = new Mock<IUnitOfWork>();
mockUnitOfWork.Setup(u => u.SaveEntitiesAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
_mockRepository.SetupGet(r => r.UnitOfWork).Returns(mockUnitOfWork.Object);
_handler = new CreateSampleCommandHandler(_mockRepository.Object, _mockLogger.Object);
}
[Fact]
public async Task Handle_WithValidCommand_ShouldCreateSampleAndReturnId()
{
// Arrange
var command = new CreateSampleCommand("Test Sample", "Test Description");
_mockRepository.Setup(r => r.Add(It.IsAny<Sample>()))
.Returns((Sample s) => s);
// Act
var result = await _handler.Handle(command, CancellationToken.None);
// Assert
result.Should().NotBeNull();
result.Id.Should().NotBeEmpty();
_mockRepository.Verify(r => r.Add(It.IsAny<Sample>()), Times.Once);
}
[Fact]
public async Task Handle_WithValidCommand_ShouldCallSaveEntities()
{
// Arrange
var command = new CreateSampleCommand("Test Sample", null);
// Act
await _handler.Handle(command, CancellationToken.None);
// Assert
_mockRepository.Verify(r => r.UnitOfWork.SaveEntitiesAsync(It.IsAny<CancellationToken>()), Times.Once);
}
}

View File

@@ -1,151 +0,0 @@
using FluentAssertions;
using StorageService.Domain.AggregatesModel.SampleAggregate;
using StorageService.Domain.Exceptions;
using Xunit;
namespace StorageService.UnitTests.Domain;
/// <summary>
/// EN: Unit tests for Sample aggregate.
/// VI: Unit tests cho Sample aggregate.
/// </summary>
public class SampleAggregateTests
{
[Fact]
public void CreateSample_WithValidName_ShouldCreateWithDraftStatus()
{
// Arrange
var name = "Test Sample";
var description = "Test Description";
// Act
var sample = new Sample(name, description);
// Assert
sample.Name.Should().Be(name);
sample.Description.Should().Be(description);
sample.Status.Should().Be(SampleStatus.Draft);
sample.Id.Should().NotBeEmpty();
sample.DomainEvents.Should().ContainSingle(); // SampleCreatedDomainEvent
}
[Fact]
public void CreateSample_WithEmptyName_ShouldThrowException()
{
// Arrange
var name = "";
// Act
var act = () => new Sample(name);
// Assert
act.Should().Throw<SampleDomainException>()
.WithMessage("Sample name cannot be empty");
}
[Fact]
public void Activate_WhenDraft_ShouldChangeToActive()
{
// Arrange
var sample = new Sample("Test Sample");
sample.ClearDomainEvents();
// Act
sample.Activate();
// Assert
sample.Status.Should().Be(SampleStatus.Active);
sample.DomainEvents.Should().ContainSingle(); // SampleStatusChangedDomainEvent
}
[Fact]
public void Activate_WhenNotDraft_ShouldThrowException()
{
// Arrange
var sample = new Sample("Test Sample");
sample.Activate();
// Act
var act = () => sample.Activate();
// Assert
act.Should().Throw<SampleDomainException>()
.WithMessage("Only draft samples can be activated");
}
[Fact]
public void Complete_WhenActive_ShouldChangeToCompleted()
{
// Arrange
var sample = new Sample("Test Sample");
sample.Activate();
sample.ClearDomainEvents();
// Act
sample.Complete();
// Assert
sample.Status.Should().Be(SampleStatus.Completed);
}
[Fact]
public void Cancel_WhenDraftOrActive_ShouldChangeToCancelled()
{
// Arrange
var sample = new Sample("Test Sample");
// Act
sample.Cancel();
// Assert
sample.Status.Should().Be(SampleStatus.Cancelled);
}
[Fact]
public void Cancel_WhenCompleted_ShouldThrowException()
{
// Arrange
var sample = new Sample("Test Sample");
sample.Activate();
sample.Complete();
// Act
var act = () => sample.Cancel();
// Assert
act.Should().Throw<SampleDomainException>()
.WithMessage("Cannot cancel a completed sample");
}
[Fact]
public void Update_WhenNotCancelled_ShouldUpdateNameAndDescription()
{
// Arrange
var sample = new Sample("Original Name", "Original Description");
var newName = "Updated Name";
var newDescription = "Updated Description";
// Act
sample.Update(newName, newDescription);
// Assert
sample.Name.Should().Be(newName);
sample.Description.Should().Be(newDescription);
sample.UpdatedAt.Should().NotBeNull();
}
[Fact]
public void Update_WhenCancelled_ShouldThrowException()
{
// Arrange
var sample = new Sample("Test Sample");
sample.Cancel();
// Act
var act = () => sample.Update("New Name", null);
// Assert
act.Should().Throw<SampleDomainException>()
.WithMessage("Cannot update a cancelled sample");
}
}