1
+ using System ;
2
+ using System . Linq ;
3
+ using ReactiveDomain . Messaging ;
4
+ using ReactiveDomain . Testing . EventStore ;
5
+ using ReactiveDomain . Util ;
6
+ using Xunit ;
7
+
8
+ namespace ReactiveDomain . Foundation . Tests
9
+ {
10
+ // ReSharper disable once InconsistentNaming
11
+ public class when_using_correlated_repository
12
+ {
13
+ private readonly CorrelatedStreamStoreRepository _correlatedRepo ;
14
+ private readonly Guid _accountId = Guid . NewGuid ( ) ;
15
+
16
+ public when_using_correlated_repository ( )
17
+ {
18
+ var mockStore = new MockStreamStoreConnection ( "testRepo" ) ;
19
+ mockStore . Connect ( ) ;
20
+ var repo = new StreamStoreRepository ( new PrefixedCamelCaseStreamNameBuilder ( ) , mockStore , new JsonMessageSerializer ( ) ) ;
21
+ _correlatedRepo = new CorrelatedStreamStoreRepository ( repo ) ;
22
+ ICorrelatedMessage source = MessageBuilder . New ( ( ) => new CreateAccount ( _accountId ) ) ;
23
+ var account = new Account ( _accountId , source ) ;
24
+ account . Credit ( 7 ) ;
25
+ account . Credit ( 13 ) ;
26
+ account . Credit ( 31 ) ;
27
+ repo . Save ( account ) ;
28
+ }
29
+
30
+ [ Fact ]
31
+ public void can_get_by_id ( )
32
+ {
33
+ ICorrelatedMessage source = MessageBuilder . New ( ( ) => new CreditAccount ( _accountId , 50 ) ) ;
34
+ var retrievedAccount = _correlatedRepo . GetById < Account > ( _accountId , source ) ;
35
+ Assert . NotNull ( retrievedAccount ) ;
36
+ Assert . Equal ( 51 , retrievedAccount . Balance ) ;
37
+ Assert . Equal ( _accountId , retrievedAccount . Id ) ;
38
+
39
+ }
40
+
41
+ [ Fact ]
42
+ public void can_get_by_id_at_version ( )
43
+ {
44
+ ICorrelatedMessage source = MessageBuilder . New ( ( ) => new CreditAccount ( _accountId , 50 ) ) ;
45
+ var retrievedAccount = _correlatedRepo . GetById < Account > ( _accountId , 1 , source ) ;
46
+ Assert . NotNull ( retrievedAccount ) ;
47
+ Assert . Equal ( 0 , retrievedAccount . Balance ) ;
48
+ Assert . Equal ( _accountId , retrievedAccount . Id ) ;
49
+
50
+ retrievedAccount = _correlatedRepo . GetById < Account > ( _accountId , 2 , source ) ;
51
+ Assert . NotNull ( retrievedAccount ) ;
52
+ Assert . Equal ( 7 , retrievedAccount . Balance ) ;
53
+ Assert . Equal ( _accountId , retrievedAccount . Id ) ;
54
+
55
+ }
56
+ [ Fact ]
57
+ public void can_try_get_by_id ( )
58
+ {
59
+ ICorrelatedMessage source = MessageBuilder . New ( ( ) => new CreditAccount ( _accountId , 50 ) ) ;
60
+ Assert . True ( _correlatedRepo . TryGetById < Account > ( _accountId , out var retrievedAccount , source ) ) ;
61
+ Assert . NotNull ( retrievedAccount ) ;
62
+ Assert . Equal ( 51 , retrievedAccount . Balance ) ;
63
+ Assert . Equal ( _accountId , retrievedAccount . Id ) ;
64
+
65
+ }
66
+
67
+ [ Fact ]
68
+ public void can_try_get_by_id_at_version ( )
69
+ {
70
+ ICorrelatedMessage source = MessageBuilder . New ( ( ) => new CreditAccount ( _accountId , 50 ) ) ;
71
+ Assert . True ( _correlatedRepo . TryGetById < Account > ( _accountId , 1 , out var retrievedAccount , source ) ) ;
72
+ Assert . NotNull ( retrievedAccount ) ;
73
+ Assert . Equal ( 0 , retrievedAccount . Balance ) ;
74
+ Assert . Equal ( _accountId , retrievedAccount . Id ) ;
75
+
76
+ Assert . True ( _correlatedRepo . TryGetById < Account > ( _accountId , 3 , out retrievedAccount , source ) ) ;
77
+ Assert . NotNull ( retrievedAccount ) ;
78
+ Assert . Equal ( 20 , retrievedAccount . Balance ) ;
79
+ Assert . Equal ( _accountId , retrievedAccount . Id ) ;
80
+
81
+ }
82
+ [ Fact ]
83
+ public void try_get_does_not_throw ( )
84
+ {
85
+ var badId = Guid . NewGuid ( ) ;
86
+ ICorrelatedMessage source = MessageBuilder . New ( ( ) => new CreditAccount ( _accountId , 50 ) ) ;
87
+ Assert . False ( _correlatedRepo . TryGetById < Account > ( badId , out var retrievedAccount , source ) ) ;
88
+ Assert . Null ( retrievedAccount ) ;
89
+
90
+ }
91
+ [ Fact ]
92
+ public void invalid_get_rethrows ( )
93
+ {
94
+ var badId = Guid . NewGuid ( ) ;
95
+ ICorrelatedMessage source = MessageBuilder . New ( ( ) => new CreditAccount ( _accountId , 50 ) ) ;
96
+ Assert . Throws < AggregateNotFoundException > ( ( ) => _correlatedRepo . GetById < Account > ( badId , source ) ) ;
97
+ }
98
+
99
+ [ Fact ]
100
+ public void new_correlated_aggregates_inject_source_information ( )
101
+ {
102
+ var newAccountId = Guid . NewGuid ( ) ;
103
+ ICorrelatedMessage source = MessageBuilder . New ( ( ) => new CreateAccount ( newAccountId ) ) ;
104
+ var newAccount = new Account ( newAccountId , source ) ;
105
+ newAccount . Credit ( 7 ) ;
106
+ newAccount . Credit ( 13 ) ;
107
+ newAccount . Credit ( 31 ) ;
108
+
109
+ var eventSource = ( IEventSource ) newAccount ;
110
+ var correlatedEvents = eventSource . TakeEvents ( ) . Select ( evt => evt as ICorrelatedMessage ) . ToArray ( ) ;
111
+ foreach ( var evt in correlatedEvents ) {
112
+ Assert . Equal ( source . MsgId , evt . CausationId ) ;
113
+ Assert . Equal ( source . CorrelationId , evt . CorrelationId ) ;
114
+ }
115
+ }
116
+ [ Fact ]
117
+ public void updated_correlated_aggregates_inject_source_information ( )
118
+ {
119
+
120
+ ICorrelatedMessage source = MessageBuilder . New ( ( ) => new CreditAccount ( _accountId , 50 ) ) ;
121
+ var retrievedAccount = _correlatedRepo . GetById < Account > ( _accountId , source ) ;
122
+ retrievedAccount . Credit ( 7 ) ;
123
+ retrievedAccount . Credit ( 13 ) ;
124
+ retrievedAccount . Credit ( 31 ) ;
125
+
126
+ var eventSource = ( IEventSource ) retrievedAccount ;
127
+ var correlatedEvents = eventSource . TakeEvents ( ) . Select ( evt => evt as ICorrelatedMessage ) . ToArray ( ) ;
128
+ foreach ( var evt in correlatedEvents ) {
129
+ Assert . Equal ( source . MsgId , evt . CausationId ) ;
130
+ Assert . Equal ( source . CorrelationId , evt . CorrelationId ) ;
131
+ }
132
+ }
133
+
134
+
135
+ [ Fact ]
136
+ public void can_save_new_correlated_aggregates ( )
137
+ {
138
+ var newAccountId = Guid . NewGuid ( ) ;
139
+ ICorrelatedMessage source = MessageBuilder . New ( ( ) => new CreateAccount ( newAccountId ) ) ;
140
+ var newAccount = new Account ( newAccountId , source ) ;
141
+ newAccount . Credit ( 7 ) ;
142
+ newAccount . Credit ( 13 ) ;
143
+ newAccount . Credit ( 31 ) ;
144
+ _correlatedRepo . Save ( newAccount ) ;
145
+
146
+ var retrievedAccount = _correlatedRepo . GetById < Account > ( newAccountId , source ) ;
147
+ Assert . NotNull ( retrievedAccount ) ;
148
+ Assert . Equal ( 51 , retrievedAccount . Balance ) ;
149
+ Assert . Equal ( newAccountId , retrievedAccount . Id ) ;
150
+ }
151
+
152
+ [ Fact ]
153
+ public void can_save_updated_correlated_aggregates ( )
154
+ {
155
+ ICorrelatedMessage source = MessageBuilder . New ( ( ) => new CreditAccount ( _accountId , 50 ) ) ;
156
+
157
+ var retrievedAccount = _correlatedRepo . GetById < Account > ( _accountId , source ) ;
158
+ Assert . NotNull ( retrievedAccount ) ;
159
+ Assert . Equal ( _accountId , retrievedAccount . Id ) ;
160
+
161
+ retrievedAccount . Credit ( 50 ) ;
162
+ _correlatedRepo . Save ( retrievedAccount ) ;
163
+
164
+ var retrievedAccount2 = _correlatedRepo . GetById < Account > ( _accountId , source ) ;
165
+ Assert . NotNull ( retrievedAccount2 ) ;
166
+ Assert . Equal ( _accountId , retrievedAccount2 . Id ) ;
167
+ Assert . Equal ( 101 , retrievedAccount . Balance ) ;
168
+
169
+ }
170
+
171
+
172
+ public class Account : AggregateRoot
173
+ {
174
+ //n.b. for infrastructure testing only not for prod or business unit tests
175
+ public long Balance { get ; private set ; }
176
+ //reflection constructor
177
+ // ReSharper disable once UnusedMember.Local
178
+ private Account ( )
179
+ {
180
+ Register < AccountCreated > ( evt => Id = evt . AccountId ) ;
181
+ Register < AccountCredited > ( evt => { Balance += evt . Amount ; } ) ;
182
+ }
183
+ public Account ( Guid id , ICorrelatedMessage source ) : this ( )
184
+ {
185
+ ( ( ICorrelatedEventSource ) this ) . Source = source ;
186
+ Ensure . NotEmptyGuid ( id , "id" ) ;
187
+ Raise ( new AccountCreated ( id ) ) ;
188
+ }
189
+
190
+ public void Credit ( uint amount )
191
+ {
192
+ Raise ( new AccountCredited ( Id , amount ) ) ;
193
+ }
194
+ }
195
+ public class CreateAccount : Command
196
+ {
197
+ public readonly Guid AccountId ;
198
+ public CreateAccount ( Guid accountId )
199
+ {
200
+ AccountId = accountId ;
201
+ }
202
+ }
203
+ public class AccountCreated : Event
204
+ {
205
+ public readonly Guid AccountId ;
206
+ public AccountCreated ( Guid accountId )
207
+ {
208
+ AccountId = accountId ;
209
+ }
210
+ }
211
+ public class CreditAccount : Command
212
+ {
213
+ public readonly Guid AccountId ;
214
+ public readonly uint Amount ;
215
+ public CreditAccount ( Guid accountId , uint amount )
216
+ {
217
+ AccountId = accountId ;
218
+ Amount = amount ;
219
+ }
220
+ }
221
+ //use of base class Event is optional
222
+ public class AccountCredited : ICorrelatedMessage
223
+ {
224
+ public Guid MsgId { get ; }
225
+ public Guid CorrelationId { get ; set ; }
226
+ public Guid CausationId { get ; set ; }
227
+ public readonly Guid AccountId ;
228
+ public readonly uint Amount ;
229
+
230
+ public AccountCredited ( Guid accountId , uint amount )
231
+ {
232
+ MsgId = Guid . NewGuid ( ) ;
233
+ AccountId = accountId ;
234
+ Amount = amount ;
235
+ }
236
+ }
237
+ }
238
+ }
0 commit comments