test: replace booking sample suites with appointment coverage

Co-authored-by: Velik <hongochai10@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-02-23 13:04:13 +00:00
parent 0adcaadae3
commit 8f557bd106
5 changed files with 111 additions and 296 deletions

View File

@@ -0,0 +1,43 @@
using System.Net;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
namespace BookingService.FunctionalTests.Controllers;
/// <summary>
/// EN: Functional tests for booking resource endpoints.
/// VI: Functional tests cho endpoint tài nguyên đặt lịch.
/// </summary>
public class ResourcesControllerTests : IClassFixture<CustomWebApplicationFactory>
{
private readonly HttpClient _client;
public ResourcesControllerTests(CustomWebApplicationFactory factory)
{
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false,
});
}
[Fact]
public async Task GetResources_ShouldReturnOk()
{
// Act
var response = await _client.GetAsync($"/api/v1/resources?shopId={Guid.NewGuid()}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
[Fact]
public async Task HealthCheck_ShouldReturnHealthy()
{
// Act
var response = await _client.GetAsync("/health/live");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
}

View File

@@ -1,80 +0,0 @@
using System.Net;
using System.Net.Http.Json;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
namespace BookingService.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

@@ -1,65 +0,0 @@
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
using BookingService.API.Application.Commands;
using BookingService.Domain.AggregatesModel.SampleAggregate;
using BookingService.Domain.SeedWork;
using Xunit;
namespace BookingService.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

@@ -0,0 +1,68 @@
using FluentAssertions;
using BookingService.Domain.AggregatesModel.AppointmentAggregate;
using BookingService.Domain.Exceptions;
using Xunit;
namespace BookingService.UnitTests.Domain;
/// <summary>
/// EN: Unit tests for appointment aggregate behavior.
/// VI: Unit tests cho hành vi aggregate cuộc hẹn.
/// </summary>
public class AppointmentAggregateTests
{
[Fact]
public void CreateAppointment_ShouldInitializeAsScheduled()
{
// Act
var appointment = new Appointment(
Guid.NewGuid(),
Guid.NewGuid(),
DateTime.UtcNow.AddHours(1),
DateTime.UtcNow.AddHours(2),
Guid.NewGuid());
// Assert
appointment.Status.Should().Be("Scheduled");
appointment.Id.Should().NotBeEmpty();
}
[Fact]
public void Complete_AfterConfirmAndStart_ShouldSetCompleted()
{
// Arrange
var appointment = new Appointment(
Guid.NewGuid(),
Guid.NewGuid(),
DateTime.UtcNow.AddHours(2),
DateTime.UtcNow.AddHours(3));
appointment.Confirm();
appointment.MarkAsInProgress();
// Act
appointment.Complete();
// Assert
appointment.Status.Should().Be("Completed");
}
[Fact]
public void Cancel_WhenCompleted_ShouldThrowDomainException()
{
// Arrange
var appointment = new Appointment(
Guid.NewGuid(),
Guid.NewGuid(),
DateTime.UtcNow.AddHours(2),
DateTime.UtcNow.AddHours(3));
appointment.Confirm();
appointment.MarkAsInProgress();
appointment.Complete();
// Act
var action = () => appointment.Cancel("Customer requested");
// Assert
action.Should().Throw<DomainException>();
}
}

View File

@@ -1,151 +0,0 @@
using FluentAssertions;
using BookingService.Domain.AggregatesModel.SampleAggregate;
using BookingService.Domain.Exceptions;
using Xunit;
namespace BookingService.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");
}
}