Exercise baseline

This commit is contained in:
2026-09-17 20:42:49 +01:00
commit 863c7a6349
36 changed files with 3898 additions and 0 deletions
+163
View File
@@ -0,0 +1,163 @@
using Domain;
using static TestData.ClientBuilder;
using static TestData.OrderBuilder;
namespace Web.Tests;
/// <summary>The hand-written <see cref="DtoMappers"/> map both ways, never mutate their input, and round-trip losslessly; domain objects come from the test-data builders, DTO initializers stay literal.</summary>
public class DtoMapperTests
{
// (1) Mappers map all scalar fields, in both directions.
[Fact]
public void ToClientDto_maps_all_scalar_fields()
{
var id = Guid.NewGuid();
var client = ValidClient().WithId(id).WithName("Acme").Build();
var dto = DtoMappers.ToClientDto(client);
Assert.Equal(id, dto.Id);
Assert.Equal("Acme", dto.Name);
}
[Fact]
public void ToOrderDto_maps_all_scalar_fields()
{
var id = Guid.NewGuid();
var order = ValidOrder().WithId(id).WithDescription("one").WithTotal(150_00).Build();
var dto = DtoMappers.ToOrderDto(order);
Assert.Equal(id, dto.Id);
Assert.Equal("one", dto.Description);
Assert.Equal(150_00, dto.TotalInCents);
}
[Fact]
public void FromClientDto_maps_all_scalar_fields()
{
var id = Guid.NewGuid();
var dto = new ClientDto { Id = id, Name = "Acme" };
var client = DtoMappers.FromClientDto(dto);
Assert.Equal(id, client.Id);
Assert.Equal("Acme", client.Name);
}
[Fact]
public void FromOrderDto_maps_all_scalar_fields()
{
var id = Guid.NewGuid();
var dto = new OrderDto { Id = id, Description = "one", TotalInCents = 150_00 };
var order = DtoMappers.FromOrderDto(dto);
Assert.Equal(id, order.Id);
Assert.Equal("one", order.Description);
Assert.Equal(150_00, order.TotalInCents);
}
// (2) Mappers map the relations, and the back-references stay consistent.
[Fact]
public void ToClientDto_maps_orders_and_keeps_the_back_reference_consistent()
{
var order = ValidOrder().WithDescription("one").Build();
var client = ValidClient().WithName("Acme").WithOrder(order).Build();
var dto = DtoMappers.ToClientDto(client);
var orderDto = Assert.Single(dto.Orders);
// The order is mapped with the client's data:
Assert.Equal(order.Id, orderDto.Id);
Assert.Equal(order.Description, orderDto.Description);
// The back-reference cycle is resolved to the SAME dto reference:
Assert.Same(dto, orderDto.ClientDto);
}
[Fact]
public void FromClientDto_wires_the_order_client_back_reference()
{
var orderDto = new OrderDto { Description = "one" };
var dto = new ClientDto { Name = "Acme", Orders = new[] { orderDto } };
var client = DtoMappers.FromClientDto(dto);
var order = Assert.Single(client.Orders);
Assert.Equal(orderDto.Description, order.Description);
// Navigation fix-up: the reconstructed order points at the client:
Assert.Same(client, order.Client);
}
// (3) Round-trips preserve all state (compared by values, not reference).
[Fact]
public void Domain_round_trip_preserves_all_state()
{
var order = ValidOrder().WithId(Guid.NewGuid()).WithDescription("one").WithTotal(150_00).Build();
var client = ValidClient().WithId(Guid.NewGuid()).WithName("Acme").WithOrder(order).Build();
var roundTrip = DtoMappers.FromClientDto(DtoMappers.ToClientDto(client));
Assert.Equal(client.Id, roundTrip.Id);
Assert.Equal(client.Name, roundTrip.Name);
var roundTripOrder = Assert.Single(roundTrip.Orders);
Assert.Equal(order.Id, roundTripOrder.Id);
Assert.Equal(order.Description, roundTripOrder.Description);
Assert.Equal(order.TotalInCents, roundTripOrder.TotalInCents);
// The back-reference is still consistent on the reconstructed graph:
Assert.Same(roundTrip, roundTripOrder.Client);
}
[Fact]
public void Dto_round_trip_preserves_all_state()
{
var order = ValidOrder().WithId(Guid.NewGuid()).WithDescription("one").WithTotal(150_00).Build();
var client = ValidClient().WithId(Guid.NewGuid()).WithName("Acme").WithOrder(order).Build();
var dto = DtoMappers.ToClientDto(client);
var dtoOrder = dto.Orders[0];
var roundTrip = DtoMappers.ToClientDto(DtoMappers.FromClientDto(dto));
Assert.Equal(dto.Id, roundTrip.Id);
Assert.Equal(dto.Name, roundTrip.Name);
var roundTripOrder = Assert.Single(roundTrip.Orders);
Assert.Equal(dtoOrder.Id, roundTripOrder.Id);
Assert.Equal(dtoOrder.Description, roundTripOrder.Description);
Assert.Equal(dtoOrder.TotalInCents, roundTripOrder.TotalInCents);
Assert.Same(roundTrip, roundTripOrder.ClientDto);
}
// (4) Mappers do not mutate their inputs (snapshot fields, map, compare).
[Fact]
public void ToClientDto_does_not_mutate_the_domain()
{
var order = ValidOrder().WithId(Guid.NewGuid()).WithDescription("one").Build();
var client = ValidClient().WithId(Guid.NewGuid()).WithName("Acme").WithOrder(order).Build();
var ordersCount = client.Orders.Count;
DtoMappers.ToClientDto(client);
DtoMappers.ToOrderDto(order);
Assert.Equal("Acme", client.Name);
Assert.Equal(ordersCount, client.Orders.Count);
Assert.Same(client, order.Client); // back-reference untouched
Assert.Equal("one", order.Description);
}
[Fact]
public void FromClientDto_does_not_mutate_the_dto()
{
var orderDto = new OrderDto { Description = "one" };
var dto = new ClientDto { Id = Guid.NewGuid(), Name = "Acme", Orders = new[] { orderDto } };
DtoMappers.FromClientDto(dto);
DtoMappers.FromOrderDto(orderDto);
Assert.Equal("Acme", dto.Name);
Assert.Same(orderDto, Assert.Single(dto.Orders)); // collection untouched
Assert.Null(orderDto.ClientDto); // back-reference untouched
Assert.Equal("one", orderDto.Description);
}
}