test: replace sample tests with service-specific order and ads suites
Co-authored-by: Velik <hongochai10@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace AdsBillingService.FunctionalTests.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Functional tests for billing account, credit line, and invoice admin flows.
|
||||
/// VI: Functional tests cho luồng tài khoản billing, hạn mức tín dụng và quản trị hóa đơn.
|
||||
/// </summary>
|
||||
public class AdsBillingControllerTests : IClassFixture<CustomWebApplicationFactory>
|
||||
{
|
||||
private readonly HttpClient _client;
|
||||
|
||||
public AdsBillingControllerTests(CustomWebApplicationFactory factory)
|
||||
{
|
||||
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
|
||||
{
|
||||
AllowAutoRedirect = false,
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BillingAccountFlow_ShouldCreateAccount_AddFunds_AndReturnBalance()
|
||||
{
|
||||
// Arrange
|
||||
var advertiserId = Guid.NewGuid();
|
||||
var createRequest = new
|
||||
{
|
||||
advertiserId,
|
||||
walletId = Guid.NewGuid(),
|
||||
paymentMethod = "prepaid",
|
||||
};
|
||||
|
||||
// Act
|
||||
var createResponse = await _client.PostAsJsonAsync("/api/v1/ads-billing/accounts", createRequest);
|
||||
var accountId = await createResponse.Content.ReadFromJsonAsync<Guid>();
|
||||
|
||||
var addFundsResponse = await _client.PostAsJsonAsync(
|
||||
$"/api/v1/ads-billing/accounts/{accountId}/add-funds",
|
||||
new { amount = 500m });
|
||||
|
||||
var balanceResponse = await _client.GetAsync($"/api/v1/ads-billing/accounts/{accountId}/balance");
|
||||
|
||||
// Assert
|
||||
createResponse.StatusCode.Should().Be(HttpStatusCode.Created);
|
||||
accountId.Should().NotBe(Guid.Empty);
|
||||
addFundsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
balanceResponse.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var balancePayload = await balanceResponse.Content.ReadFromJsonAsync<JsonElement>();
|
||||
balancePayload.GetProperty("balance").GetDecimal().Should().Be(500m);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreditLineRequest_ShouldIncreaseCreditLimit()
|
||||
{
|
||||
// Arrange
|
||||
var advertiserId = Guid.NewGuid();
|
||||
var createRequest = new
|
||||
{
|
||||
advertiserId,
|
||||
walletId = Guid.NewGuid(),
|
||||
paymentMethod = "postpaid",
|
||||
};
|
||||
|
||||
var createResponse = await _client.PostAsJsonAsync("/api/v1/ads-billing/accounts", createRequest);
|
||||
createResponse.StatusCode.Should().Be(HttpStatusCode.Created);
|
||||
|
||||
// Act
|
||||
var increaseResponse = await _client.PostAsJsonAsync(
|
||||
"/api/v1/ads-billing/credit-lines/request",
|
||||
new
|
||||
{
|
||||
advertiserId,
|
||||
requestedAmount = 1500m,
|
||||
reason = "Scale campaign budget",
|
||||
});
|
||||
|
||||
var creditLineResponse = await _client.GetAsync($"/api/v1/ads-billing/credit-lines/{advertiserId}");
|
||||
|
||||
// Assert
|
||||
increaseResponse.StatusCode.Should().Be(HttpStatusCode.Accepted);
|
||||
creditLineResponse.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var creditPayload = await creditLineResponse.Content.ReadFromJsonAsync<JsonElement>();
|
||||
creditPayload.GetProperty("creditLimit").GetDecimal().Should().Be(1500m);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RegenerateInvoice_WithoutCharges_ShouldReturnBadRequest()
|
||||
{
|
||||
// Arrange
|
||||
var advertiserId = Guid.NewGuid();
|
||||
var createResponse = await _client.PostAsJsonAsync(
|
||||
"/api/v1/ads-billing/accounts",
|
||||
new
|
||||
{
|
||||
advertiserId,
|
||||
walletId = Guid.NewGuid(),
|
||||
paymentMethod = "postpaid",
|
||||
});
|
||||
|
||||
createResponse.StatusCode.Should().Be(HttpStatusCode.Created);
|
||||
var accountId = await createResponse.Content.ReadFromJsonAsync<Guid>();
|
||||
|
||||
// Act
|
||||
var regenerateResponse = await _client.PostAsJsonAsync(
|
||||
"/api/v1/admin/ads-billing/invoices/regenerate",
|
||||
new
|
||||
{
|
||||
billingAccountId = accountId,
|
||||
startDate = DateTime.UtcNow.AddDays(-7),
|
||||
endDate = DateTime.UtcNow,
|
||||
});
|
||||
|
||||
// Assert
|
||||
regenerateResponse.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HealthCheck_ShouldReturnHealthy()
|
||||
{
|
||||
// Act
|
||||
var response = await _client.GetAsync("/health/live");
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace AdsBillingService.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);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace AdsServingService.FunctionalTests.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Functional tests for ads serving and admin auction endpoints.
|
||||
/// VI: Functional tests cho endpoint phục vụ quảng cáo và quản trị auction.
|
||||
/// </summary>
|
||||
public class AdsServingControllerTests : IClassFixture<CustomWebApplicationFactory>
|
||||
{
|
||||
private readonly HttpClient _client;
|
||||
|
||||
public AdsServingControllerTests(CustomWebApplicationFactory factory)
|
||||
{
|
||||
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
|
||||
{
|
||||
AllowAutoRedirect = false,
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ServeAd_ShouldReturnServedAdPayload()
|
||||
{
|
||||
// Arrange
|
||||
var request = new
|
||||
{
|
||||
userId = Guid.NewGuid(),
|
||||
placementType = "feed",
|
||||
userContext = new Dictionary<string, string>
|
||||
{
|
||||
["segment"] = "high-intent",
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/v1/ads/serve", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var content = await response.Content.ReadFromJsonAsync<JsonElement>();
|
||||
var adId = content.GetProperty("adId").GetGuid();
|
||||
var finalPrice = content.GetProperty("finalPrice").GetDecimal();
|
||||
|
||||
adId.Should().NotBe(Guid.Empty);
|
||||
finalPrice.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetAuctionStatistics_AfterServe_ShouldReflectPersistedAuctions()
|
||||
{
|
||||
// Arrange
|
||||
var request = new
|
||||
{
|
||||
userId = Guid.NewGuid(),
|
||||
placementType = "story",
|
||||
userContext = new Dictionary<string, string>(),
|
||||
};
|
||||
|
||||
// Act
|
||||
var serveResponse = await _client.PostAsJsonAsync("/api/v1/ads/serve", request);
|
||||
var statsResponse = await _client.GetAsync("/api/v1/admin/auctions/statistics");
|
||||
|
||||
// Assert
|
||||
serveResponse.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
statsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var stats = await statsResponse.Content.ReadFromJsonAsync<JsonElement>();
|
||||
stats.GetProperty("totalAuctions").GetInt32().Should().BeGreaterThanOrEqualTo(1);
|
||||
stats.GetProperty("totalBidsPlaced").GetInt64().Should().BeGreaterThanOrEqualTo(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TrackImpression_ShouldReturnAccepted()
|
||||
{
|
||||
// Arrange
|
||||
var request = new
|
||||
{
|
||||
adId = Guid.NewGuid(),
|
||||
userId = Guid.NewGuid(),
|
||||
timestamp = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/v1/ads/events/impression", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Accepted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HealthCheck_ShouldReturnHealthy()
|
||||
{
|
||||
// Act
|
||||
var response = await _client.GetAsync("/health/live");
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace AdsServingService.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);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Net;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace OrderService.FunctionalTests.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Functional tests for health check endpoints.
|
||||
/// VI: Functional tests cho các endpoint kiểm tra sức khỏe.
|
||||
/// </summary>
|
||||
public class HealthChecksControllerTests : IClassFixture<CustomWebApplicationFactory>
|
||||
{
|
||||
private readonly HttpClient _client;
|
||||
|
||||
public HealthChecksControllerTests(CustomWebApplicationFactory factory)
|
||||
{
|
||||
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
|
||||
{
|
||||
AllowAutoRedirect = false,
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LiveHealthCheck_ShouldReturnOk()
|
||||
{
|
||||
// Act
|
||||
var response = await _client.GetAsync("/health/live");
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace OrderService.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);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using FluentAssertions;
|
||||
using OrderService.Domain.AggregatesModel.OrderAggregate;
|
||||
using OrderService.Domain.Exceptions;
|
||||
using Xunit;
|
||||
|
||||
namespace OrderService.UnitTests.Domain;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Unit tests for Order aggregate domain behavior.
|
||||
/// VI: Unit tests cho hành vi domain của aggregate Order.
|
||||
/// </summary>
|
||||
public class OrderAggregateTests
|
||||
{
|
||||
[Fact]
|
||||
public void CreateOrder_WithValidShopId_ShouldStartInDraftStatus()
|
||||
{
|
||||
// Arrange
|
||||
var shopId = Guid.NewGuid();
|
||||
|
||||
// Act
|
||||
var order = new Order(shopId);
|
||||
|
||||
// Assert
|
||||
order.Id.Should().NotBe(Guid.Empty);
|
||||
order.ShopId.Should().Be(shopId);
|
||||
order.Status.Should().Be(OrderStatus.Draft);
|
||||
order.TotalAmount.Should().Be(0m);
|
||||
order.DomainEvents.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddItem_InDraftStatus_ShouldRecalculateTotalAmount()
|
||||
{
|
||||
// Arrange
|
||||
var order = new Order(Guid.NewGuid());
|
||||
var firstItem = new OrderItem(Guid.NewGuid(), "Coffee", "PreparedFood", 2, 30_000m);
|
||||
var secondItem = new OrderItem(Guid.NewGuid(), "Cake", "PreparedFood", 1, 45_000m);
|
||||
|
||||
// Act
|
||||
order.AddItem(firstItem);
|
||||
order.AddItem(secondItem);
|
||||
|
||||
// Assert
|
||||
order.Items.Should().HaveCount(2);
|
||||
order.TotalAmount.Should().Be(105_000m);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MarkAsValidated_WithoutItems_ShouldThrowDomainException()
|
||||
{
|
||||
// Arrange
|
||||
var order = new Order(Guid.NewGuid());
|
||||
|
||||
// Act
|
||||
var act = () => order.MarkAsValidated();
|
||||
|
||||
// Assert
|
||||
act.Should().Throw<DomainException>()
|
||||
.WithMessage("Cannot validate order with no items");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FullLifecycle_DraftToCompleted_ShouldUpdateStatusSequentially()
|
||||
{
|
||||
// Arrange
|
||||
var order = new Order(Guid.NewGuid());
|
||||
order.AddItem(new OrderItem(Guid.NewGuid(), "Shoes", "Physical", 1, 1_200_000m));
|
||||
|
||||
// Act
|
||||
order.MarkAsValidated();
|
||||
order.MarkAsPaid();
|
||||
order.MarkAsProcessing();
|
||||
order.MarkAsCompleted();
|
||||
|
||||
// Assert
|
||||
order.Status.Should().Be(OrderStatus.Completed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Cancel_CompletedOrder_ShouldThrowDomainException()
|
||||
{
|
||||
// Arrange
|
||||
var order = new Order(Guid.NewGuid());
|
||||
order.AddItem(new OrderItem(Guid.NewGuid(), "Laptop", "Physical", 1, 25_000_000m));
|
||||
order.MarkAsValidated();
|
||||
order.MarkAsPaid();
|
||||
order.MarkAsProcessing();
|
||||
order.MarkAsCompleted();
|
||||
|
||||
// Act
|
||||
var act = () => order.Cancel("Customer changed mind");
|
||||
|
||||
// Assert
|
||||
act.Should().Throw<DomainException>()
|
||||
.WithMessage("Cannot cancel completed order");
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
using FluentAssertions;
|
||||
using OrderService.Domain.AggregatesModel.SampleAggregate;
|
||||
using OrderService.Domain.Exceptions;
|
||||
using Xunit;
|
||||
|
||||
namespace OrderService.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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user