/
GettingStateFromEventsTests.cs
175 lines (151 loc) · 5.59 KB
/
GettingStateFromEventsTests.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
using FluentAssertions;
using IntroductionToEventSourcing.GettingStateFromEvents.Tools;
using Marten;
using Xunit;
namespace IntroductionToEventSourcing.GettingStateFromEvents.Immutable;
using static ShoppingCartEvent;
// EVENTS
public abstract record ShoppingCartEvent
{
public record ShoppingCartOpened(
Guid ShoppingCartId,
Guid ClientId
): ShoppingCartEvent;
public record ProductItemAddedToShoppingCart(
Guid ShoppingCartId,
PricedProductItem ProductItem
): ShoppingCartEvent;
public record ProductItemRemovedFromShoppingCart(
Guid ShoppingCartId,
PricedProductItem ProductItem
): ShoppingCartEvent;
public record ShoppingCartConfirmed(
Guid ShoppingCartId,
DateTime ConfirmedAt
): ShoppingCartEvent;
public record ShoppingCartCanceled(
Guid ShoppingCartId,
DateTime CanceledAt
): ShoppingCartEvent;
// This won't allow
private ShoppingCartEvent(){}
}
// VALUE OBJECTS
public record PricedProductItem(
Guid ProductId,
int Quantity,
decimal UnitPrice
);
// ENTITY
public record ShoppingCart(
Guid Id,
Guid ClientId,
ShoppingCartStatus Status,
PricedProductItem[] ProductItems,
DateTime? ConfirmedAt = null,
DateTime? CanceledAt = null
){
public static ShoppingCart Create(ShoppingCartOpened opened) =>
new ShoppingCart(
opened.ShoppingCartId,
opened.ClientId,
ShoppingCartStatus.Pending,
Array.Empty<PricedProductItem>()
);
public ShoppingCart Apply(ProductItemAddedToShoppingCart productItemAdded) =>
this with
{
ProductItems = ProductItems
.Concat(new[] { productItemAdded.ProductItem })
.GroupBy(pi => pi.ProductId)
.Select(group => group.Count() == 1
? group.First()
: new PricedProductItem(
group.Key,
group.Sum(pi => pi.Quantity),
group.First().UnitPrice
)
)
.ToArray()
};
public ShoppingCart Apply(ProductItemRemovedFromShoppingCart productItemRemoved) =>
this with
{
ProductItems = ProductItems
.Select(pi => pi.ProductId == productItemRemoved.ProductItem.ProductId
? new PricedProductItem(
pi.ProductId,
pi.Quantity - productItemRemoved.ProductItem.Quantity,
pi.UnitPrice
)
: pi
)
.Where(pi => pi.Quantity > 0)
.ToArray()
};
public ShoppingCart Apply(ShoppingCartConfirmed confirmed) =>
this with
{
Status = ShoppingCartStatus.Confirmed,
ConfirmedAt = confirmed.ConfirmedAt
};
public ShoppingCart Apply(ShoppingCartCanceled canceled) =>
this with
{
Status = ShoppingCartStatus.Canceled,
CanceledAt = canceled.CanceledAt
};
}
public enum ShoppingCartStatus
{
Pending = 1,
Confirmed = 2,
Canceled = 4
}
public class GettingStateFromEventsTests: MartenTest
{
/// <summary>
/// Solution - Immutable entity
/// </summary>
/// <param name="documentSession"></param>
/// <param name="shoppingCartId"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private static async Task<ShoppingCart> GetShoppingCart(IDocumentSession documentSession, Guid shoppingCartId,
CancellationToken cancellationToken)
{
var shoppingCart = await documentSession.Events.AggregateStreamAsync<ShoppingCart>(shoppingCartId, token: cancellationToken);
return shoppingCart ?? throw new InvalidOperationException("Shopping Cart was not found!");
}
[Fact]
public async Task GettingState_ForSequenceOfEvents_ShouldSucceed()
{
var shoppingCartId = Guid.NewGuid();
var clientId = Guid.NewGuid();
var shoesId = Guid.NewGuid();
var tShirtId = Guid.NewGuid();
var twoPairsOfShoes = new PricedProductItem(shoesId, 2, 100);
var pairOfShoes = new PricedProductItem(shoesId, 1, 100);
var tShirt = new PricedProductItem(tShirtId, 1, 50);
var events = new object[]
{
new ShoppingCartOpened(shoppingCartId, clientId),
new ProductItemAddedToShoppingCart(shoppingCartId, twoPairsOfShoes),
new ProductItemAddedToShoppingCart(shoppingCartId, tShirt),
new ProductItemRemovedFromShoppingCart(shoppingCartId, pairOfShoes),
new ShoppingCartConfirmed(shoppingCartId, DateTime.UtcNow),
new ShoppingCartCanceled(shoppingCartId, DateTime.UtcNow)
};
await AppendEvents(shoppingCartId, events, CancellationToken.None);
var shoppingCart = await GetShoppingCart(DocumentSession, shoppingCartId, CancellationToken.None);
shoppingCart.Id.Should().Be(shoppingCartId);
shoppingCart.ClientId.Should().Be(clientId);
shoppingCart.ProductItems.Should().HaveCount(2);
shoppingCart.ProductItems[0].ProductId.Should().Be(shoesId);
shoppingCart.ProductItems[0].Quantity.Should().Be(pairOfShoes.Quantity);
shoppingCart.ProductItems[0].UnitPrice.Should().Be(pairOfShoes.UnitPrice);
shoppingCart.ProductItems[1].ProductId.Should().Be(tShirtId);
shoppingCart.ProductItems[1].Quantity.Should().Be(tShirt.Quantity);
shoppingCart.ProductItems[1].UnitPrice.Should().Be(tShirt.UnitPrice);
}
}