diff --git a/src/ReactiveDomain.Foundation/StreamStore/ReadModelBase.cs b/src/ReactiveDomain.Foundation/StreamStore/ReadModelBase.cs index 32461f48..a5e4d82e 100644 --- a/src/ReactiveDomain.Foundation/StreamStore/ReadModelBase.cs +++ b/src/ReactiveDomain.Foundation/StreamStore/ReadModelBase.cs @@ -182,9 +182,20 @@ protected virtual void Dispose(bool disposing) } _disposed = true; } + /// + /// Applies a message synchronously to the read model while ensuring that the + /// is respected and bypasses both the queue and listeners. This is primarily useful in tests. + /// + /// The message to apply. public void DirectApply(IMessage message) { DequeueMessage(message); } public void Handle(Message message) { ((IHandle)_queue).Handle(message); } public void Handle(IMessage message) { ((IHandle)_queue).Handle(message); } + /// + /// Publishes a message onto the read model's internal queue. + /// This bypasses the Listeners while ensuring that the + /// is respected. All messages will be processed in order from the queue thread. + /// + /// The message to publish. public void Publish(IMessage message) { ((IPublisher)_queue).Publish(message); } } } diff --git a/src/ReactiveDomain.Testing/Specifications/NullComfiguredConnection.cs b/src/ReactiveDomain.Testing/Specifications/NullComfiguredConnection.cs index 50c1ff5d..fb814030 100644 --- a/src/ReactiveDomain.Testing/Specifications/NullComfiguredConnection.cs +++ b/src/ReactiveDomain.Testing/Specifications/NullComfiguredConnection.cs @@ -4,14 +4,34 @@ namespace ReactiveDomain.Testing { + /// + /// An empty configured connection that produces null repositories, readers, etc. + /// Implements . + /// public class NullConfiguredConnection : IConfiguredConnection { + /// + /// Gets a . + /// public IStreamStoreConnection Connection => new NullConnection(); + /// + /// Gets a standard stream name builder + /// public IStreamNameBuilder StreamNamer => new PrefixedCamelCaseStreamNameBuilder(); + /// + /// Gets a default Json message serializer. + /// public IEventSerializer Serializer => new JsonMessageSerializer(); + /// + /// Gets a . + /// + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// A . public ICorrelatedRepository GetCorrelatedRepository( IRepository baseRepository = null, bool caching = false, @@ -20,21 +40,43 @@ public ICorrelatedRepository GetCorrelatedRepository( return new NullRepository(); } + /// + /// Gets a . + /// + /// The name of the listener. + /// A public IListener GetListener(string name) { return new NullListener(name); } + /// + /// Gets a . + /// + /// The name of the listener. + /// A public IListener GetQueuedListener(string name) { return new NullListener(name); } + /// + /// Gets a . + /// + /// The name of the reader. + /// This parameter is ignored. + /// A public IStreamReader GetReader(string name, Action handle) { return new NullReader(name); } + /// + /// Gets a . + /// + /// This parameter is ignored. + /// This parameter is ignored. + /// A . public IRepository GetRepository(bool caching = false, Func currentPolicyUserId = null) { return new NullRepository(); diff --git a/src/ReactiveDomain.Testing/Specifications/NullConnection.cs b/src/ReactiveDomain.Testing/Specifications/NullConnection.cs index d7e6f21c..badd6b46 100644 --- a/src/ReactiveDomain.Testing/Specifications/NullConnection.cs +++ b/src/ReactiveDomain.Testing/Specifications/NullConnection.cs @@ -3,61 +3,148 @@ namespace ReactiveDomain.Testing { + /// + /// An empty connection that implements . + /// public class NullConnection : IStreamStoreConnection { - + /// + /// The name of the connection. + /// public string ConnectionName => "NullConnection"; + /// + /// Drops the events and returns a write result at the default version. + /// + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// A at the default version. public WriteResult AppendToStream(string stream, long expectedVersion, UserCredentials credentials = null, params EventData[] events) { return new WriteResult(0); } + /// + /// Does nothing. Required for implementation of . + /// public void Close() { } + /// + /// Does nothing. Required for implementation of . + /// public void Connect() { } + /// + /// Does nothing. Required for implementation of . + /// + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. public void DeleteStream(string stream, long expectedVersion, UserCredentials credentials = null) { } + /// + /// Cleans up resources. + /// public void Dispose() { } + /// + /// Does nothing. Required for implementation of . + /// + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. public void HardDeleteStream(string stream, long expectedVersion, UserCredentials credentials = null) { } + /// + /// Gets an empty stream slice. + /// + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// An empty . public StreamEventsSlice ReadStreamBackward(string stream, long start, long count, UserCredentials credentials = null) { return new StreamEventsSlice(stream, 0, ReadDirection.Backward, Array.Empty(), 0, 0, true); } + /// + /// Gets an empty stream slice. + /// + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// An empty . public StreamEventsSlice ReadStreamForward(string stream, long start, long count, UserCredentials credentials = null) { return new StreamEventsSlice(stream, 0, ReadDirection.Forward, Array.Empty(), 0, 0, true); } + /// + /// Does nothing. Required for implementation of . + /// + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This connection. public IDisposable SubscribeToAll(Action eventAppeared, Action subscriptionDropped = null, UserCredentials credentials = null, bool resolveLinkTos = true) { return this; } + /// + /// Does nothing. Required for implementation of . + /// + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This connection. public IDisposable SubscribeToAllFrom(Position from, Action eventAppeared, CatchUpSubscriptionSettings settings = null, Action liveProcessingStarted = null, Action subscriptionDropped = null, UserCredentials credentials = null, bool resolveLinkTos = true) { return this; } + /// + /// Does nothing. Required for implementation of . + /// + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This connection. public IDisposable SubscribeToStream(string stream, Action eventAppeared, Action subscriptionDropped = null, UserCredentials credentials = null) { return this; } + /// + /// Does nothing. Required for implementation of . + /// + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// + /// This connection. public IDisposable SubscribeToStreamFrom(string stream, long? lastCheckpoint, CatchUpSubscriptionSettings settings, Action eventAppeared, Action liveProcessingStarted = null, Action subscriptionDropped = null, UserCredentials credentials = null) { return this; } - } } diff --git a/src/ReactiveDomain.Testing/Specifications/NullListener.cs b/src/ReactiveDomain.Testing/Specifications/NullListener.cs index e515c1e8..772b6649 100644 --- a/src/ReactiveDomain.Testing/Specifications/NullListener.cs +++ b/src/ReactiveDomain.Testing/Specifications/NullListener.cs @@ -6,55 +6,121 @@ namespace ReactiveDomain.Testing { + /// + /// An empty listener that implements . + /// public class NullListener : IListener, ISubscriber { - private string _stream = ""; - public long _position = 0; + private string _stream; + private long _position; + /// + /// Gets a + /// public ISubscriber EventStream => this; + /// + /// Gets the position of the stream. + /// public long Position => _position; + /// + /// Gets the name of the stream. + /// public string StreamName => _stream; + + /// + /// Creates an empty lister. + /// + /// This parameter is ignored. public NullListener(string name = "") - { - _stream = name; + { } + + /// + /// Cleans up resources. + /// public void Dispose() { } + /// + /// Starts the listener at the requested checkpoint. Since the listener is not connected to anything, + /// this simply sets the listener's initial checkpoint. + /// + /// The name of the stream. + /// The position at which the listener should start. + /// This parameter is ignored. + /// This parameter is ignored. public void Start(string stream, long? checkpoint = null, bool blockUntilLive = false, CancellationToken cancelWaitToken = default) { _stream = stream; _position = checkpoint ?? 0; } + /// + /// Does nothing. Required for implementation of . + /// + /// + /// public IDisposable SubscribeToAll(IHandle handler) { return this; } + /// + /// Does nothing. Required for implementation of . + /// + /// This type parameter is ignored. + /// This parameter is ignored. + /// false since the listener does not actually subscribe to anything. bool ISubscriber.HasSubscriberFor(bool includeDerived) { return false; } + /// + /// Does nothing other than set the stream name. Required for implementation of . + /// + /// The type of aggregate. Used for building the listener's stream name. + /// The ID of the aggregate whose stream to listen to. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. void IListener.Start(Guid id, long? checkpoint, bool blockUntilLive, CancellationToken cancelWaitToken) { _stream = new PrefixedCamelCaseStreamNameBuilder().GenerateForAggregate(typeof(TAggregate), id); } + /// + /// Does nothing other than set the stream name. Required for implementation of . + /// + /// The type of aggregate. Used for building the listener's stream name. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. void IListener.Start(long? checkpoint, bool blockUntilLive, CancellationToken cancelWaitToken) { _stream = nameof(TAggregate); } + /// + /// Does nothing. Required for implementation of . + /// + /// This type parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// IDisposable ISubscriber.Subscribe(IHandle handler, bool includeDerived) { return this; } + /// + /// Does nothing. Required for implementation of . + /// + /// This type parameter is ignored. + /// This parameter is ignored. void ISubscriber.Unsubscribe(IHandle handler) { } diff --git a/src/ReactiveDomain.Testing/Specifications/NullReader.cs b/src/ReactiveDomain.Testing/Specifications/NullReader.cs index 00ee93c8..2feac1ce 100644 --- a/src/ReactiveDomain.Testing/Specifications/NullReader.cs +++ b/src/ReactiveDomain.Testing/Specifications/NullReader.cs @@ -4,38 +4,100 @@ namespace ReactiveDomain.Testing { + /// + /// An empty reader. Implements . + /// public class NullReader : IStreamReader { - private string _name = ""; + private readonly string _name; + + /// + /// Creates an empty reader. + /// + /// The name of the reader. public NullReader(string name) { _name = name; } + + /// + /// Gets the reader's position, which is always 0. + /// public long? Position => 0; + /// + /// Gets the name of the stream. + /// public string StreamName => _name; private Action _handle = _ => { }; + + /// + /// Sets the reader's handler. The handler is not used since the Read methods are no-ops. + /// public Action Handle { set => _handle = value; } + /// + /// Does nothing. Required for implementation of . + /// public void Cancel() { } + /// + /// Cleans up resources. + /// public void Dispose() { } + /// + /// Does nothing. Required for implementation of . + /// + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// true public bool Read(string stream, Func completionCheck, long? checkpoint = null, long? count = null, bool readBackwards = false) { return true; } + /// + /// Does nothing. Required for implementation of . + /// + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// true public bool Read(Type tMessage, Func completionCheck, long? checkpoint = null, long? count = null, bool readBackwards = false) { return true; } + /// + /// Does nothing. Required for implementation of . + /// + /// This type parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// true bool IStreamReader.Read(Guid id, Func completionCheck, long? checkpoint, long? count, bool readBackwards) { return true; } + /// + /// Does nothing. Required for implementation of . + /// + /// This type parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// true bool IStreamReader.Read(Func completionCheck, long? checkpoint, long? count, bool readBackwards) { return true; diff --git a/src/ReactiveDomain.Testing/Specifications/NullRepository.cs b/src/ReactiveDomain.Testing/Specifications/NullRepository.cs index 7e6d4fa5..4abcc713 100644 --- a/src/ReactiveDomain.Testing/Specifications/NullRepository.cs +++ b/src/ReactiveDomain.Testing/Specifications/NullRepository.cs @@ -4,30 +4,77 @@ namespace ReactiveDomain.Testing { + /// + /// An empty repository. Implements and . + /// public class NullRepository : ICorrelatedRepository, IRepository { + /// + /// Does nothing. Required for implementation of and . + /// + /// This parameter is ignored. public void Delete(IEventSource aggregate) { } + /// + /// Does nothing. Required for implementation of . + /// + /// This type parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// null public TAggregate GetById(Guid id, ICorrelatedMessage source) where TAggregate : AggregateRoot, IEventSource { return null; } + /// + /// Does nothing. Required for implementation of . + /// + /// This type parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// null public TAggregate GetById(Guid id, int version, ICorrelatedMessage source) where TAggregate : AggregateRoot, IEventSource { return null; } + /// + /// Does nothing. Required for implementation of and . + /// + /// This parameter is ignored. public void HardDelete(IEventSource aggregate) { } + /// + /// Does nothing. Required for implementation of and . + /// + /// This parameter is ignored. public void Save(IEventSource aggregate) { } + /// + /// Does nothing. Required for implementation of . + /// + /// This type parameter is ignored. + /// This parameter is ignored. + /// Output parameter for the retrieved aggregate. + /// This parameter is ignored. + /// false public bool TryGetById(Guid id, out TAggregate aggregate, ICorrelatedMessage source) where TAggregate : AggregateRoot, IEventSource { aggregate = null; return false; } + /// + /// Does nothing. Required for implementation of . + /// + /// This type parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// Output parameter for the retrieved aggregate. + /// This parameter is ignored. + /// false public bool TryGetById(Guid id, int version, out TAggregate aggregate, ICorrelatedMessage source) where TAggregate : AggregateRoot, IEventSource { aggregate = null; @@ -35,17 +82,38 @@ public bool TryGetById(Guid id, int version, out TAggregate aggregat } + /// + /// Does nothing. Required for implementation of . + /// + /// This type parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. + /// null TAggregate IRepository.GetById(Guid id, int version) { return null; } + /// + /// Does nothing. Required for implementation of . + /// + /// This type parameter is ignored. + /// This parameter is ignored. + /// Output parameter for the retrieved aggregate. + /// This parameter is ignored. + /// false bool IRepository.TryGetById(Guid id, out TAggregate aggregate, int version) { aggregate = null; return false; } + /// + /// Does nothing. Required for implementation of . + /// + /// This type parameter is ignored. + /// This parameter is ignored. + /// This parameter is ignored. void IRepository.Update(ref TAggregate aggregate, int version) { } diff --git a/src/ReactiveDomain.Testing/Specifications/ReadModelTestSpecification.cs b/src/ReactiveDomain.Testing/Specifications/ReadModelTestSpecification.cs index 922b3d26..656583ad 100644 --- a/src/ReactiveDomain.Testing/Specifications/ReadModelTestSpecification.cs +++ b/src/ReactiveDomain.Testing/Specifications/ReadModelTestSpecification.cs @@ -3,13 +3,31 @@ using ReactiveDomain.Messaging.Bus; using System.Collections.Generic; -namespace ReactiveDomain.Testing { - public abstract class ReadModelTestSpecification : IPublisher { - void IPublisher.Publish(IMessage message) { - PublishedMessages.Add(message); - } - protected List PublishedMessages = new List(); - protected IConfiguredConnection Connection = new NullConfiguredConnection(); - - } +namespace ReactiveDomain.Testing +{ + /// + /// A base test class for testing read models. Includes a + /// that can be used creating read models under test without needing additional infrastructure, and can be used itself as an + /// to collect published messages from read models. These messages are recorded in for use in tests. + /// + public abstract class ReadModelTestSpecification : IPublisher + { + /// + /// Adds the message onto the test class's list of published messages. + /// Required for implementation of . + /// + /// The message to add. + void IPublisher.Publish(IMessage message) + { + PublishedMessages.Add(message); + } + /// + /// The list of messages recorded from the test class's Publish method. + /// + protected List PublishedMessages = new List(); + /// + /// A for use in creating read models under test without needing additional infrastructure. + /// + protected IConfiguredConnection Connection = new NullConfiguredConnection(); + } }