public async Task Recipients_are_canceled_after_timeout() { var collection = new RecipientsCollection(); collection.Add <SomeType>(); collection.Add <SomeCancellableType>(); collection.Add(new SomeAlmostNeverEndingType()); collection.Add((int n) => n.ToString()); collection.Add(async(int n, CancellationToken token) => { using var cancellation = new CancellationTokenTaskSource <bool>(token); await CatchCancellation(cancellation.Task); return(n.ToString()); }); var aggregator = new Aggregator(collection) { CancellationWindow = TimeSpan.FromSeconds(2) }; { var response = await aggregator.Send(42, TimeSpan.FromSeconds(2)); Assert.Equal(collection.RecipientsCount, response.Completed.Count); Assert.Empty(response.Incomplete); } { var response = await aggregator.Send <string>(42, TimeSpan.FromSeconds(2)); Assert.Equal(collection.RecipientsCount, response.Completed.Count); Assert.Empty(response.Incomplete); } }
public async Task Cancellation_window_is_applied_correctly() { var collection = new RecipientsCollection(); collection.Add <SomeAlmostNeverEndingType>(); collection.Add <SomeLongProcessingType>(); var aggregator = new Aggregator(collection) { CancellationWindow = TimeSpan.FromSeconds(3) }; { var response = await aggregator.Send(42, timeout : TimeSpan.FromSeconds(1)); Assert.Equal(1, response.Completed.Count); Assert.Equal(1, response.Incomplete.Count); } { // Gives SomeLongProcessingType the time to complete // even though it doesn't accept a cancellation token. aggregator.AllowCancellationWindowOnAllRecipients = true; var response = await aggregator.Send(42, timeout : TimeSpan.FromSeconds(1)); Assert.Equal(2, response.Completed.Count); Assert.Equal(0, response.Incomplete.Count); } }
public async Task Responses_expose_the_recipient_name_and_type() { var collection = new RecipientsCollection(); collection.Add((int n) => n.ToString(), name: "Some delegate"); collection.Add(new SomeFaultingType(), name: "Some faulting type"); collection.Add <SomeNeverEndingType>(name: "Some never ending type"); var localAggregator = new Aggregator(collection); var result = await localAggregator.Send <string>(42, timeout : TimeSpan.FromSeconds(1)); Assert.NotNull(result); Assert.Equal(1, result.Completed.Count); Assert.Equal("Some delegate", result.Completed[0].Recipient.Name); Assert.Null(result.Completed[0].Recipient.Type); Assert.Equal(1, result.Faulted.Count); Assert.Equal("Some faulting type", result.Faulted[0].Recipient.Name); Assert.Equal(typeof(SomeFaultingType), result.Faulted[0].Recipient.Type); Assert.Equal(1, result.Incomplete.Count); Assert.Equal("Some never ending type", result.Incomplete[0].Recipient.Name); Assert.Equal(typeof(SomeNeverEndingType), result.Incomplete[0].Recipient.Type); }
public async Task Colliding_recipients_use_all_methods_by_design() { var collection = new RecipientsCollection(); collection.Add <SomeCollidingType>(CollisionStrategy.UseAllMethodsMatching); var collisionDetected = false; collection.OnCollision += _ => collisionDetected = true; var aggregator = new Aggregator(collection); var(completed, faulted, incomplete) = await aggregator.Send(42); Assert.Equal(2, completed.Count); Assert.Empty(faulted); Assert.Empty(incomplete); Assert.False(collisionDetected); var stringsOnly = await aggregator.Send <string>(42); Assert.Equal(2, stringsOnly.Completed.Count); Assert.Empty(stringsOnly.Faulted); Assert.Empty(stringsOnly.Incomplete); Assert.False(collisionDetected); }
public async Task Recipients_comply_with_lifetime() { var transients = 0; var scoped = 0; var singletons = 0; var collection = new RecipientsCollection(); collection.Add(() => { transients++; return(new SomeType()); }, name: null, lifetime: Lifetime.Transient); collection.Add(() => { scoped++; return(new SomeOtherType()); }, name: null, lifetime: Lifetime.Scoped); collection.Add(() => { singletons++; return(new SomeAsyncType()); }, name: null, lifetime: Lifetime.Singleton); var aggregator = new Aggregator(collection); var anotherAggregator = new Aggregator(collection); await aggregator.Send(42); Assert.Equal(1, transients); Assert.Equal(1, scoped); Assert.Equal(1, singletons); await anotherAggregator.Send(42); Assert.Equal(2, transients); Assert.Equal(2, scoped); Assert.Equal(1, singletons); await Task.WhenAll(aggregator.Send(42), anotherAggregator.Send(42)); Assert.Equal(4, transients); Assert.Equal(2, scoped); Assert.Equal(1, singletons); }
public AggregatorTests() { var collection = new RecipientsCollection(); collection.Add <SomeType>(); collection.Add <SomeAsyncType>(); collection.Add <SomePossiblyAsyncType>(); collection.Add <SomeCollidingType>(); collection.Add <SomeFaultingType>(); collection.Add <SomeNeverEndingType>(); _aggregator = new Aggregator(collection); }
public async Task Recipients_can_return_nullable() { var collection = new RecipientsCollection(); collection.Add <SomeTypeReturningNullable>(); var aggregator = new Aggregator(collection); var response = await aggregator.Send(42); Assert.NotNull(response); Assert.Single(response.Completed); Assert.Empty(response.Faulted); var completed = response.Completed[0]; Assert.Equal(typeof(SomeTypeReturningNullable), completed.Recipient.Type); Assert.Null(completed.Result); }
public async Task Colliding_recipients_are_ignored_by_design() { var collection = new RecipientsCollection(); collection.Add <SomeCollidingType>(CollisionStrategy.IgnoreRecipient); var collisionDetected = false; collection.OnCollision += _ => collisionDetected = true; var aggregator = new Aggregator(collection); var(completed, faulted, incomplete) = await aggregator.Send(42); Assert.Empty(completed); Assert.Empty(faulted); Assert.Empty(incomplete); Assert.True(collisionDetected); }
public Aggregator(RecipientsCollection collection) { _scope = collection.CreateScope(); CancellationWindow = TimeSpan.FromMilliseconds(100); }