cqrs_proptest/lib.rs
1//! # cqrs-proptest
2//!
3//! `cqrs-proptest` contains various utilities for building property tests with the
4//! `proptest` crate and aggregates from `cqrs` or `cqrs-core`.
5//!
6//! ## Examples
7//!
8//! ```
9//! use cqrs_core::{Aggregate, AggregateEvent, Event};
10//! use cqrs_proptest::AggregateFromEventSequence;
11//! use proptest::{prelude::*, strategy::{LazyTupleUnion, ValueTree, WA}, test_runner::TestRunner, prop_oneof};
12//!
13//! #[derive(Debug, Default)]
14//! struct MyAggregate {
15//! active: bool
16//! }
17//!
18//! impl Aggregate for MyAggregate {
19//! fn aggregate_type() -> &'static str {
20//! "my_aggregate"
21//! }
22//! }
23//!
24//! #[derive(Clone, Copy, Debug)]
25//! struct CreatedEvent{};
26//!
27//! impl Event for CreatedEvent {
28//! fn event_type(&self) -> &'static str {
29//! "created"
30//! }
31//! }
32//!
33//! impl AggregateEvent<MyAggregate> for CreatedEvent {
34//! fn apply_to(self, aggregate: &mut MyAggregate) {
35//! aggregate.active = true;
36//! }
37//! }
38//!
39//! #[derive(Clone, Copy, Debug)]
40//! struct DeletedEvent{};
41//!
42//! impl Event for DeletedEvent {
43//! fn event_type(&self) -> &'static str {
44//! "deleted"
45//! }
46//! }
47//!
48//! impl AggregateEvent<MyAggregate> for DeletedEvent {
49//! fn apply_to(self, aggregate: &mut MyAggregate) {
50//! aggregate.active = false;
51//! }
52//! }
53//!
54//! #[derive(Clone, Copy, Debug)]
55//! enum MyEvents {
56//! Created(CreatedEvent),
57//! Deleted(DeletedEvent),
58//! }
59//!
60//! impl Event for MyEvents {
61//! fn event_type(&self) -> &'static str {
62//! match *self {
63//! MyEvents::Created(ref e) => e.event_type(),
64//! MyEvents::Deleted(ref e) => e.event_type(),
65//! }
66//! }
67//! }
68//!
69//! impl AggregateEvent<MyAggregate> for MyEvents {
70//! fn apply_to(self, aggregate: &mut MyAggregate) {
71//! match self {
72//! MyEvents::Created(e) => e.apply_to(aggregate),
73//! MyEvents::Deleted(e) => e.apply_to(aggregate),
74//! }
75//! }
76//! }
77//!
78//! impl Arbitrary for MyEvents {
79//! type Parameters = ();
80//!
81//! fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
82//! prop_oneof![
83//! Just(MyEvents::Created(CreatedEvent{})),
84//! Just(MyEvents::Deleted(DeletedEvent{})),
85//! ]
86//! }
87//!
88//! type Strategy = LazyTupleUnion<(WA<Just<Self>>, WA<Just<Self>>)>;
89//! }
90//!
91//! any::<AggregateFromEventSequence<MyAggregate, MyEvents>>()
92//! .new_tree(&mut TestRunner::default())
93//! .unwrap()
94//! .current()
95//! .into_aggregate();
96//!
97//! let parameters = (prop::collection::SizeRange::from(1..10), ());
98//! any_with::<AggregateFromEventSequence<MyAggregate, MyEvents>>(parameters)
99//! .new_tree(&mut TestRunner::default())
100//! .unwrap()
101//! .current();
102//! ```
103
104#![warn(unused_import_braces, unused_imports, unused_qualifications)]
105#![deny(
106 missing_debug_implementations,
107 missing_copy_implementations,
108 trivial_casts,
109 trivial_numeric_casts,
110 unsafe_code,
111 unused_must_use,
112 missing_docs
113)]
114
115use cqrs_core::{Aggregate, AggregateEvent, DeserializableEvent, Event, SerializableEvent};
116use proptest::prelude::*;
117use std::{fmt, marker::PhantomData};
118
119/// Produces a strategy to generate an arbitrary vector of events, given a strategy
120/// to generate an arbitrary event and a size range.
121///
122/// # Examples
123///
124/// ```
125/// use cqrs_core::Event;
126/// use cqrs_proptest::arb_events;
127/// use proptest::{prelude::*, strategy::ValueTree, test_runner::TestRunner, prop_oneof};
128///
129/// #[derive(Clone, Copy, Debug)]
130/// struct CreatedEvent{};
131///
132/// impl Event for CreatedEvent {
133/// fn event_type(&self) -> &'static str {
134/// "created"
135/// }
136/// }
137///
138/// #[derive(Clone, Copy, Debug)]
139/// struct DeletedEvent{};
140///
141/// impl Event for DeletedEvent {
142/// fn event_type(&self) -> &'static str {
143/// "deleted"
144/// }
145/// }
146///
147/// #[derive(Clone, Copy, Debug)]
148/// enum MyEvents {
149/// Created(CreatedEvent),
150/// Deleted(DeletedEvent),
151/// }
152///
153/// impl Event for MyEvents {
154/// fn event_type(&self) -> &'static str {
155/// match *self {
156/// MyEvents::Created(ref e) => e.event_type(),
157/// MyEvents::Deleted(ref e) => e.event_type(),
158/// }
159/// }
160/// }
161///
162/// fn arb_my_events() -> impl Strategy<Value = MyEvents> {
163/// prop_oneof![
164/// Just(MyEvents::Created(CreatedEvent{})),
165/// Just(MyEvents::Deleted(DeletedEvent{})),
166/// ]
167/// }
168///
169/// arb_events(arb_my_events(), 0..10)
170/// .new_tree(&mut TestRunner::default())
171/// .unwrap()
172/// .current();
173/// ```
174pub fn arb_events<E: Event + fmt::Debug>(
175 event_strategy: impl Strategy<Value = E>,
176 size: impl Into<prop::collection::SizeRange>,
177) -> impl Strategy<Value = Vec<E>> {
178 prop::collection::vec(event_strategy, size)
179}
180
181/// Produces a strategy to generate an arbitrary aggregate, given a strategy to generate
182/// an arbitrary vector of events
183///
184/// # Examples
185///
186/// ```
187/// use cqrs_core::{Aggregate, AggregateEvent, Event};
188/// use cqrs_proptest::{arb_aggregate, arb_events};
189/// use proptest::{prelude::*, strategy::{LazyTupleUnion, ValueTree, WA}, test_runner::TestRunner, prop_oneof};
190///
191/// #[derive(Debug, Default)]
192/// struct MyAggregate {
193/// active: bool
194/// }
195///
196/// impl Aggregate for MyAggregate {
197/// fn aggregate_type() -> &'static str {
198/// "my_aggregate"
199/// }
200/// }
201///
202/// #[derive(Clone, Copy, Debug)]
203/// struct CreatedEvent{};
204///
205/// impl Event for CreatedEvent {
206/// fn event_type(&self) -> &'static str {
207/// "created"
208/// }
209/// }
210///
211/// impl AggregateEvent<MyAggregate> for CreatedEvent {
212/// fn apply_to(self, aggregate: &mut MyAggregate) {
213/// aggregate.active = true;
214/// }
215/// }
216///
217/// #[derive(Clone, Copy, Debug)]
218/// struct DeletedEvent{};
219///
220/// impl Event for DeletedEvent {
221/// fn event_type(&self) -> &'static str {
222/// "deleted"
223/// }
224/// }
225///
226/// impl AggregateEvent<MyAggregate> for DeletedEvent {
227/// fn apply_to(self, aggregate: &mut MyAggregate) {
228/// aggregate.active = false;
229/// }
230/// }
231///
232/// #[derive(Clone, Copy, Debug)]
233/// enum MyEvents {
234/// Created(CreatedEvent),
235/// Deleted(DeletedEvent),
236/// }
237///
238/// impl Event for MyEvents {
239/// fn event_type(&self) -> &'static str {
240/// match *self {
241/// MyEvents::Created(ref e) => e.event_type(),
242/// MyEvents::Deleted(ref e) => e.event_type(),
243/// }
244/// }
245/// }
246///
247/// impl AggregateEvent<MyAggregate> for MyEvents {
248/// fn apply_to(self, aggregate: &mut MyAggregate) {
249/// match self {
250/// MyEvents::Created(e) => e.apply_to(aggregate),
251/// MyEvents::Deleted(e) => e.apply_to(aggregate),
252/// }
253/// }
254/// }
255///
256/// impl Arbitrary for MyEvents {
257/// type Parameters = ();
258///
259/// fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
260/// prop_oneof![
261/// Just(MyEvents::Created(CreatedEvent{})),
262/// Just(MyEvents::Deleted(DeletedEvent{})),
263/// ]
264/// }
265///
266/// type Strategy = LazyTupleUnion<(WA<Just<Self>>, WA<Just<Self>>)>;
267/// }
268///
269/// arb_aggregate::<MyAggregate, MyEvents, _>(arb_events(any::<MyEvents>(), 0..10))
270/// .new_tree(&mut TestRunner::default())
271/// .unwrap()
272/// .current();
273/// ```
274pub fn arb_aggregate<A, E, S>(events_strategy: S) -> impl Strategy<Value = A>
275where
276 A: Aggregate + fmt::Debug,
277 E: AggregateEvent<A> + fmt::Debug,
278 S: Strategy<Value = Vec<E>>,
279{
280 events_strategy.prop_map(|e| {
281 let mut aggregate = A::default();
282 for event in e {
283 aggregate.apply(event);
284 }
285 aggregate
286 })
287}
288
289/// Given a serializable event, constructs a buffer, serializes the event to the buffer, and then
290/// deserializes the event, returning the deserialized value.
291///
292/// # Examples
293///
294/// ```
295/// use cqrs_core::{Event, SerializableEvent, DeserializableEvent};
296/// use cqrs_proptest::roundtrip_through_serialization;
297/// use serde::{Serialize, Deserialize};
298///
299/// #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
300/// struct CreatedEvent{};
301///
302/// impl Event for CreatedEvent {
303/// fn event_type(&self) -> &'static str {
304/// "created"
305/// }
306/// }
307///
308/// #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
309/// struct DeletedEvent{};
310///
311/// impl Event for DeletedEvent {
312/// fn event_type(&self) -> &'static str {
313/// "deleted"
314/// }
315/// }
316///
317/// #[derive(Clone, Copy, Debug, PartialEq, Eq)]
318/// enum MyEvents {
319/// Created(CreatedEvent),
320/// Deleted(DeletedEvent),
321/// }
322///
323/// impl Event for MyEvents {
324/// fn event_type(&self) -> &'static str {
325/// match *self {
326/// MyEvents::Created(ref e) => e.event_type(),
327/// MyEvents::Deleted(ref e) => e.event_type(),
328/// }
329/// }
330/// }
331///
332/// impl SerializableEvent for MyEvents {
333/// type Error = serde_json::Error;
334///
335/// fn serialize_event_to_buffer(&self, buffer: &mut Vec<u8>) -> Result<(), Self::Error> {
336/// match *self {
337/// MyEvents::Created(ref e) => serde_json::to_writer(buffer, e),
338/// MyEvents::Deleted(ref e) => serde_json::to_writer(buffer, e),
339/// }
340/// }
341/// }
342///
343/// impl DeserializableEvent for MyEvents {
344/// type Error = serde_json::Error;
345///
346/// fn deserialize_event_from_buffer(buffer: &[u8], event_type: &str) -> Result<Option<Self>, Self::Error> {
347/// match event_type {
348/// "created" => serde_json::from_reader(buffer).map(MyEvents::Created).map(Some),
349/// "deleted" => serde_json::from_reader(buffer).map(MyEvents::Deleted).map(Some),
350/// _ => Ok(None),
351/// }
352/// }
353/// }
354///
355/// let original = MyEvents::Created(CreatedEvent{});
356/// let roundtrip = roundtrip_through_serialization(&original);
357/// assert_eq!(original, roundtrip);
358/// ```
359pub fn roundtrip_through_serialization<E: SerializableEvent + DeserializableEvent>(
360 original: &E,
361) -> E {
362 let mut buffer = Vec::default();
363 original
364 .serialize_event_to_buffer(&mut buffer)
365 .expect("serialization");
366
367 let roundtrip =
368 E::deserialize_event_from_buffer(&buffer, original.event_type()).expect("deserialization");
369 roundtrip.expect("known event type")
370}
371
372/// A wrapper for an aggregate that was generated from an arbitrary sequence of events.
373///
374/// # Examples
375///
376/// ```
377/// use cqrs_core::{Aggregate, AggregateEvent, Event};
378/// use cqrs_proptest::AggregateFromEventSequence;
379/// use proptest::{prelude::*, strategy::{LazyTupleUnion, ValueTree, WA}, test_runner::TestRunner, prop_oneof};
380///
381/// #[derive(Debug, Default)]
382/// struct MyAggregate {
383/// active: bool
384/// }
385///
386/// impl Aggregate for MyAggregate {
387/// fn aggregate_type() -> &'static str {
388/// "my_aggregate"
389/// }
390/// }
391///
392/// #[derive(Clone, Copy, Debug)]
393/// struct CreatedEvent{};
394///
395/// impl Event for CreatedEvent {
396/// fn event_type(&self) -> &'static str {
397/// "created"
398/// }
399/// }
400///
401/// impl AggregateEvent<MyAggregate> for CreatedEvent {
402/// fn apply_to(self, aggregate: &mut MyAggregate) {
403/// aggregate.active = true;
404/// }
405/// }
406///
407/// #[derive(Clone, Copy, Debug)]
408/// struct DeletedEvent{};
409///
410/// impl Event for DeletedEvent {
411/// fn event_type(&self) -> &'static str {
412/// "deleted"
413/// }
414/// }
415///
416/// impl AggregateEvent<MyAggregate> for DeletedEvent {
417/// fn apply_to(self, aggregate: &mut MyAggregate) {
418/// aggregate.active = false;
419/// }
420/// }
421///
422/// #[derive(Clone, Copy, Debug)]
423/// enum MyEvents {
424/// Created(CreatedEvent),
425/// Deleted(DeletedEvent),
426/// }
427///
428/// impl Event for MyEvents {
429/// fn event_type(&self) -> &'static str {
430/// match *self {
431/// MyEvents::Created(ref e) => e.event_type(),
432/// MyEvents::Deleted(ref e) => e.event_type(),
433/// }
434/// }
435/// }
436///
437/// impl AggregateEvent<MyAggregate> for MyEvents {
438/// fn apply_to(self, aggregate: &mut MyAggregate) {
439/// match self {
440/// MyEvents::Created(e) => e.apply_to(aggregate),
441/// MyEvents::Deleted(e) => e.apply_to(aggregate),
442/// }
443/// }
444/// }
445///
446/// impl Arbitrary for MyEvents {
447/// type Parameters = ();
448///
449/// fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
450/// prop_oneof![
451/// Just(MyEvents::Created(CreatedEvent{})),
452/// Just(MyEvents::Deleted(DeletedEvent{})),
453/// ]
454/// }
455///
456/// type Strategy = LazyTupleUnion<(WA<Just<Self>>, WA<Just<Self>>)>;
457/// }
458///
459/// any::<AggregateFromEventSequence<MyAggregate, MyEvents>>()
460/// .new_tree(&mut TestRunner::default())
461/// .unwrap()
462/// .current()
463/// .into_aggregate();
464///
465/// let parameters = (prop::collection::SizeRange::from(1..10), ());
466/// any_with::<AggregateFromEventSequence<MyAggregate, MyEvents>>(parameters)
467/// .new_tree(&mut TestRunner::default())
468/// .unwrap()
469/// .current();
470/// ```
471#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
472pub struct AggregateFromEventSequence<A, E>
473where
474 A: Aggregate,
475 E: AggregateEvent<A>,
476{
477 aggregate: A,
478 _phantom: PhantomData<*const E>,
479}
480
481impl<A, E> fmt::Debug for AggregateFromEventSequence<A, E>
482where
483 A: Aggregate + fmt::Debug,
484 E: AggregateEvent<A>,
485{
486 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
487 f.debug_tuple("AggregateFromEventSequence")
488 .field(&self.aggregate)
489 .finish()
490 }
491}
492
493impl<A, E> From<A> for AggregateFromEventSequence<A, E>
494where
495 A: Aggregate,
496 E: AggregateEvent<A>,
497{
498 #[inline]
499 fn from(aggregate: A) -> Self {
500 AggregateFromEventSequence {
501 aggregate,
502 _phantom: PhantomData,
503 }
504 }
505}
506
507impl<A, E> AggregateFromEventSequence<A, E>
508where
509 A: Aggregate,
510 E: AggregateEvent<A>,
511{
512 /// Unwraps the generated aggregate.
513 #[inline]
514 pub fn into_aggregate(self) -> A {
515 self.aggregate
516 }
517}
518
519impl<A, E> Arbitrary for AggregateFromEventSequence<A, E>
520where
521 A: Aggregate + fmt::Debug,
522 E: AggregateEvent<A> + Arbitrary + 'static,
523{
524 type Parameters = (prop::collection::SizeRange, <E as Arbitrary>::Parameters);
525 type Strategy = BoxedStrategy<Self>;
526
527 fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
528 any_with::<Vec<E>>(args)
529 .prop_map(|events| {
530 let mut aggregate = A::default();
531 for event in events {
532 aggregate.apply(event);
533 }
534 AggregateFromEventSequence {
535 aggregate,
536 _phantom: PhantomData,
537 }
538 })
539 .boxed()
540 }
541}