OrderLine + computed TotalInCents; both inbound mappers reconstruct a one-line order from the flat DTO/row (derived data is re-derived, not written); builder realises WithTotal as a single line. Outbound mappers, DTO, OrderData, DbContext and WebApp untouched — the small-steps payoff. yak: exercise-2-solution-on-branch-exercise-2-solution-hw7e
147 lines
5.2 KiB
C#
147 lines
5.2 KiB
C#
using After;
|
|
using static BeforeAfter.Tests.TestData.ClientBuilder;
|
|
using static BeforeAfter.Tests.TestData.OrderBuilder;
|
|
|
|
namespace BeforeAfter.Tests;
|
|
|
|
/// <summary>
|
|
/// The after <see cref="DbContext"/> stores data-only persistence records
|
|
/// (<see cref="ClientData"/> / <see cref="OrderData"/>), never domain objects:
|
|
/// <see cref="DbContext"/>'s <c>Save</c> maps domain state into a row —
|
|
/// inserting on the first save, refreshing in place on later ones — and the
|
|
/// stored graph is flattened to ids.
|
|
/// </summary>
|
|
public class AfterPersistenceTests
|
|
{
|
|
[Fact]
|
|
public void Save_maps_domain_state_into_data_records()
|
|
{
|
|
var db = new DbContext();
|
|
var order = ValidOrder().WithDescription("one").WithTotal(150_00).Build();
|
|
var client = ValidClient().WithName("Acme").WithOrder(order).Build();
|
|
|
|
db.Save(client);
|
|
db.Save(order);
|
|
|
|
var clientRow = Assert.Single(db.Clients);
|
|
Assert.Equal(client.Id, clientRow.Id);
|
|
Assert.Equal("Acme", clientRow.Name);
|
|
|
|
var orderRow = Assert.Single(db.Orders);
|
|
Assert.Equal(order.Id, orderRow.Id);
|
|
Assert.Equal("one", orderRow.Description);
|
|
Assert.Equal(150_00, orderRow.TotalInCents);
|
|
Assert.Equal(client.Id, orderRow.ClientId); // the graph is flattened to a key
|
|
}
|
|
|
|
[Fact]
|
|
public void An_order_without_client_stores_an_empty_client_id()
|
|
{
|
|
var db = new DbContext();
|
|
var order = ValidOrder().WithDescription("orphan").Build();
|
|
|
|
db.Save(order);
|
|
|
|
Assert.Equal(Guid.Empty, Assert.Single(db.Orders).ClientId);
|
|
}
|
|
|
|
[Fact]
|
|
public void Saving_again_refreshes_the_row_instead_of_duplicating_it()
|
|
{
|
|
var db = new DbContext();
|
|
var order = ValidOrder().WithDescription("one").WithTotal(150_00).Build();
|
|
var client = ValidClient().WithName("Acme").WithOrder(order).Build();
|
|
db.Save(client);
|
|
db.Save(order);
|
|
|
|
order.Description = "one (edited)";
|
|
order.Lines[0].AmountInCents = 99_00;
|
|
client.Name = "Acme GmbH";
|
|
db.Save(order);
|
|
db.Save(client);
|
|
|
|
// One row per saved object, refreshed with the *current* state:
|
|
var orderRow = Assert.Single(db.Orders);
|
|
Assert.Equal(order.Id, orderRow.Id);
|
|
Assert.Equal("one (edited)", orderRow.Description);
|
|
Assert.Equal(99_00, orderRow.TotalInCents);
|
|
Assert.Equal(client.Id, orderRow.ClientId);
|
|
|
|
var clientRow = Assert.Single(db.Clients);
|
|
Assert.Equal("Acme GmbH", clientRow.Name);
|
|
}
|
|
|
|
[Fact]
|
|
public void Rows_are_reachable_through_Set_and_Find()
|
|
{
|
|
var db = new DbContext();
|
|
var order = ValidOrder().WithDescription("one").WithTotal(150_00).Build();
|
|
var client = ValidClient().WithName("Acme").WithOrder(order).Build();
|
|
db.Save(client);
|
|
db.Save(order);
|
|
|
|
Assert.Single(db.Set<ClientData>());
|
|
Assert.Single(db.Set<OrderData>());
|
|
|
|
var found = db.Find<OrderData>(row => row.Description == "one");
|
|
Assert.Equal(order.Id, found!.Id);
|
|
Assert.Null(db.Find<OrderData>(row => row.Description == "no such order"));
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Read-back: LoadOrder / LoadClient reassemble domain objects from the
|
|
// stored rows (identity round-trip for now — OrderLines come later).
|
|
// ---------------------------------------------------------------------
|
|
|
|
[Fact]
|
|
public void Loading_an_order_restores_its_state_and_its_client()
|
|
{
|
|
var db = new DbContext();
|
|
var order = ValidOrder().WithDescription("one").WithTotal(150_00).Build();
|
|
var client = ValidClient().WithName("Acme").WithOrder(order).Build();
|
|
db.Save(client);
|
|
db.Save(order);
|
|
|
|
var loaded = db.LoadOrder(order.Id);
|
|
|
|
Assert.NotNull(loaded);
|
|
Assert.Equal(order.Id, loaded!.Id);
|
|
Assert.Equal("one", loaded.Description);
|
|
Assert.Equal(150_00, loaded.TotalInCents);
|
|
|
|
// The graph is reassembled: the loaded order knows its client again,
|
|
// and the client's collection holds the loaded order.
|
|
Assert.NotNull(loaded.Client);
|
|
Assert.Equal("Acme", loaded.Client!.Name);
|
|
Assert.Same(loaded, Assert.Single(loaded.Client.Orders));
|
|
}
|
|
|
|
[Fact]
|
|
public void Loading_a_client_restores_it_with_all_of_its_orders()
|
|
{
|
|
var db = new DbContext();
|
|
var first = ValidOrder().WithDescription("one").WithTotal(150_00).Build();
|
|
var second = ValidOrder().WithDescription("two").WithTotal(250_00).Build();
|
|
var client = ValidClient().WithName("Acme").WithOrder(first).WithOrder(second).Build();
|
|
db.Save(client);
|
|
db.Save(first);
|
|
db.Save(second);
|
|
|
|
var loaded = db.LoadClient(client.Id);
|
|
|
|
Assert.NotNull(loaded);
|
|
Assert.Equal("Acme", loaded!.Name);
|
|
Assert.Equal(2, loaded.Orders.Count);
|
|
Assert.All(loaded.Orders, o => Assert.Same(loaded, o.Client));
|
|
}
|
|
|
|
[Fact]
|
|
public void Loading_an_unknown_id_returns_null()
|
|
{
|
|
var db = new DbContext();
|
|
|
|
Assert.Null(db.LoadOrder(Guid.NewGuid()));
|
|
Assert.Null(db.LoadClient(Guid.NewGuid()));
|
|
}
|
|
}
|