public async Task PublishEvent_MultipleHandlersWithSingleInterface_NotifiesAllHandlers() { var eventTracker = new TestEventTracker <SourceStartedEvent>(); var serviceProvider = new TestServiceProvider( configureServices: services => services .AddSingleton(eventTracker), configureDaisy: daisy => daisy .AddEventHandlerSingleton <TestEventTrackerHandler <SourceStartedEvent> >() .AddEventHandlerSingleton <SourceStartedEventTrackerHandler>() ); var chainBuilder = new TestChain <Signal> { ConfigureSourcesAction = sources => sources .Add <SignalTestSource>("test") }; var chain = await chainBuilder.BuildAsync(serviceProvider); chain.StartAllSources(); await chain.Sources.WaitForCompletionAsync(); Assert.Equal(2, eventTracker.TrackedHandlerIds.Count); Assert.Equal(2, eventTracker.TrackedEvents.Count); }
public async Task AddEventHandler_SingleHandlerWithMultipleInterfaces_NotifiesSingleInstance() { var sourceStartedTracker = new TestEventTracker <SourceStartedEvent>(); var sourceStoppedTracker = new TestEventTracker <SourceStoppedEvent>(); var serviceProvider = new TestServiceProvider( configureServices: services => services .AddSingleton(sourceStartedTracker) .AddSingleton(sourceStoppedTracker), configureDaisy: daisy => daisy .AddEventHandlerSingleton <SourceEventHandler>() ); var chainBuilder = new TestChain <Signal> { ConfigureSourcesAction = sources => sources .Add <SignalTestSource>("test") }; var chain = await chainBuilder.BuildAsync(serviceProvider); chain.StartAllSources(); await chain.Sources.WaitForCompletionAsync(); Assert.Single(sourceStartedTracker.TrackedHandlerIds); Assert.Single(sourceStoppedTracker.TrackedHandlerIds); Assert.Equal(sourceStoppedTracker.TrackedHandlerIds, sourceStoppedTracker.TrackedHandlerIds); }
public async Task SourceException_NotifiesSourceStoppedHandler() { var eventTracker = new TestEventTracker <SourceStoppedEvent>(); var serviceProvider = new TestServiceProvider( configureServices: services => services .AddSingleton(eventTracker), configureDaisy: daisy => daisy .AddEventHandlerSingleton <TestEventTrackerHandler <SourceStoppedEvent> >() ); var chainBuilder = new TestChain <Signal> { ConfigureSourcesAction = sources => sources .Add <ThrowingTestSource <Signal> >("test") }; var chain = await chainBuilder.BuildAsync(serviceProvider); chain.StartAllSources(); await chain.Sources.WaitForCompletionAsync(); var source = chain.Sources[0]; Assert.Single(eventTracker.TrackedEvents, e => e.ChainName == chain.Name && e.SourceIndex == source.Index && e.SourceName == source.Name && e.Result == SourceResult.Faulted && e.SourceExecutionId != default && e.Exception is TestException); }
public async Task SourceCompletion_DisposesSource() { var sourceName = "test"; var tracker = new TestExecutionTracker(); var serviceProvider = new TestServiceProvider( configureServices: services => { services.AddSingleton(tracker); }); var chainBuilder = new TestChain <Signal> { ConfigureSourcesAction = sources => { sources.Add <SignalTestSource>(sourceName); } }; var chain = await chainBuilder.BuildAsync(serviceProvider); // Source is disposed once in constructor of SourceConnector<> Assert.True(tracker.DisposeCountBySourceName.ContainsKey(sourceName)); Assert.Equal(1, tracker.DisposeCountBySourceName[sourceName]); chain.StartAllSources(); await chain.Sources.WaitForCompletionAsync(); Assert.Equal(2, tracker.DisposeCountBySourceName[sourceName]); }
public async Task Process_CallsSubChainOnMatch() { const string payload1 = "Payload1"; const string payload2 = "Payload2"; const string payload3 = "Payload3"; var result1 = new List <string>(); var result2 = new List <string>(); var chainBuilder = new TestChain <string> { ConfigureRootAction = root => root .If(payload1.Equals, then => then .TestInspect(onProcess: (input, _) => result1.Add(input)) ) .If(payload2.Equals, then => then .TestInspect(onProcess: (input, _) => result2.Add(input)) ) }; using var chain = await chainBuilder.BuildAsync(); await chain.ExecuteAsync(payload1, CancellationToken.None); await chain.ExecuteAsync(payload2, CancellationToken.None); await chain.ExecuteAsync(payload3, CancellationToken.None); Assert.Single(result1); Assert.Single(result1, payload1); Assert.Single(result2); Assert.Single(result2, payload2); }
public async Task Configure_Sources_UsesSources() { string[] payloads = { "test1", "test2" }; var result = new ConcurrentBag <string>(); var chainBuilder = new TestChain <string> { ConfigureSourcesAction = sources => { sources.Add <SignalTestSource, Signal>("1", _ => payloads[0]); sources.Add <SignalTestSource, Signal>("2", _ => payloads[1]); }, ConfigureRootAction = root => { root.TestInspect(onProcess: (input, _) => result.Add(input)); } }; var chain = await chainBuilder.BuildAsync(); chain.StartAllSources(); await chain.Sources.WaitForCompletionAsync(); foreach (var payload in payloads) { Assert.Contains(payload, result); } }
public async Task ExecuteAsync_WithThrowingConnector_ReturnsWrappedException() { var chainBuilder = new TestChain <Signal> { ConfigureRootAction = root => root.SubChain(subChain => subChain .Link <ThrowingLink <Signal>, Signal>() ) }; var chain = await chainBuilder.BuildAsync(); var throwingConnector = chain.Connectors[1]; var result = await chain.ExecuteAsync(Signal.Static, CancellationToken.None); Assert.True( result is { Status: ExecutionResultStatus.Faulted, Exception: ChainException { InnerException: TestException, Connector: {} connector, } } && connector.Equals(throwingConnector));
public async Task SourceCanceled_SourceStoppedHandler() { var eventTracker = new TestEventTracker <SourceStoppedEvent>(); var serviceProvider = new TestServiceProvider( configureServices: services => services .AddSingleton(eventTracker), configureDaisy: daisy => daisy .AddEventHandlerSingleton <TestEventTrackerHandler <SourceStoppedEvent> >() ); var chainBuilder = new TestChain <Signal> { ConfigureSourcesAction = sources => sources .Add <InfiniteDelaySource>("test") }; var chain = await chainBuilder.BuildAsync(serviceProvider); chain.StartAllSources(); await chain.StopAllSourcesAsync(); var source = chain.Sources[0]; Assert.Single(eventTracker.TrackedEvents, e => e.ChainName == chain.Name && e.SourceIndex == source.Index && e.SourceName == source.Name && e.SourceExecutionId != default && e.Result == SourceResult.Canceled); }
public async Task Stop_WhenRunning_StopsExecution() { var result = new ConcurrentBag <Signal>(); var chainBuilder = new TestChain <Signal>() { ConfigureSourcesAction = sources => { sources.Add <SignalTestSource>("signal"); }, ConfigureRootAction = root => { root .TestInspect(onProcess: (input, _) => result.Add(input)) .TestDelay(500) .TestInspect(onProcess: (input, _) => result.Add(input)); } }; var chain = await chainBuilder.BuildAsync(); chain.StartAllSources(); await Task.Delay(250); await chain.StopAllSourcesAsync(true); Assert.Single(result); }
public async Task Cancel_CancellationToken_FailsChain() { var result = new ConcurrentBag <Signal>(); var chainBuilder = new TestChain <Signal>() { ConfigureRootAction = root => { root .TestInspect(onProcess: (input, _) => result.Add(input)) .TestDelay(500) .TestInspect(onProcess: (input, _) => result.Add(input)); } }; var chain = await chainBuilder.BuildAsync(); var cancellationSource = new CancellationTokenSource(); cancellationSource.CancelAfter(250); var executionResult = await chain.ExecuteAsync(Signal.Static, cancellationSource.Token); Assert.Equal(ExecutionResultStatus.Faulted, executionResult.Status); Assert.Single(result); }
public async void ExecuteAsync_Completes() { var chainBuilder = new TestChain <Signal> { ConfigureRootAction = root => root.Link <NoopLink <Signal>, Signal>() }; var chain = await chainBuilder.BuildAsync(); await chain.ExecuteAsync(Signal.Static, CancellationToken.None); }
public async Task ConfigureRoot_MultipleTimes_Throws() { var chainBuilder = new TestChain <Signal> { ConfigureRootAction = root => { root.Link <NoopLink <Signal>, Signal>(); root.Link <NoopLink <Signal>, Signal>(); } }; await Assert.ThrowsAsync <NotSupportedException>(async() => await chainBuilder.BuildAsync()); }
public async Task Get_Name_ReturnsName() { var chainBuilder = new TestChain <Signal>("TestChainName") { ConfigureRootAction = root => { root.Link <NoopLink <Signal>, Signal>(); } }; var chain = await chainBuilder.BuildAsync(); Assert.Equal(chainBuilder.Name, chain.Name); }
public async Task Execute_WithCanceledCancellationToken_Throws() { var chainBuilder = new TestChain <Signal> { ConfigureRootAction = root => root.Link <NoopLink <Signal>, Signal>() }; var chain = await chainBuilder.BuildAsync(); await Assert.ThrowsAsync <OperationCanceledException>(async() => { await chain.ExecuteAsync(Signal.Static, new CancellationToken(true)); }); }
public async Task GetConnectors_ReturnsConnectorsWithIndexes() { var chainBuilder = new TestChain <Signal> { ConfigureRootAction = root => root .Link <NoopLink <Signal>, Signal>() .Link <NoopLink <Signal>, Signal>() .Link <NoopLink <Signal>, Signal>() }; var chain = await chainBuilder.BuildAsync(); var indexes = chain.Connectors.Select(c => c.Index); Assert.Equal(new[] { 0, 1, 2 }, indexes); }
public async Task Dispose_DisposesSubChain() { var disposed = false; var chainBuilder = new TestChain <Signal[]> { ConfigureRootAction = root => root .SubChain(subChain => subChain .TestInspect(onDispose: () => disposed = true) ) }; var chain = await chainBuilder.BuildAsync(); chain.Dispose(); Assert.True(disposed); }
public async Task GetSources_ReturnsSourceConnectorsWithIndexes() { var chainBuilder = new TestChain <Signal> { ConfigureSourcesAction = sources => { sources.Add <SignalTestSource>("1"); sources.Add <SignalTestSource>("2"); sources.Add <SignalTestSource>("3"); } }; var chain = await chainBuilder.BuildAsync(); var indexes = chain.Sources.Select(s => s.Index); Assert.Equal(new[] { 0, 1, 2 }, indexes); }
public async Task Set_LockStrategy_UsesLockStrategy() { var result = new ConcurrentBag <Signal>(); var lockStrategy = new SharedLockStrategy(1); var chainBuilder = new TestChain <Signal> { LockStrategy = lockStrategy, ConfigureRootAction = root => { root.TestInspect(onProcess: (input, _) => result.Add(input)); } }; var chain = await chainBuilder.BuildAsync(); await lockStrategy.RequestLockAsync(CancellationToken.None); const int releaseTimeout = 500; var releaseTask = Task.Run(async() => { await Task.Delay(releaseTimeout); lockStrategy.ReleaseLock(); }); var assertEmptyTask = Task.Run(async() => { await Task.Delay(releaseTimeout / 2); Assert.Empty(result); }); var assertResultTask = Task.Run(async() => { await Task.Delay(releaseTimeout * 2); Assert.Single(result); }); await Task.WhenAll( chain.ExecuteAsync(Signal.Static, CancellationToken.None), releaseTask, assertEmptyTask, assertResultTask ); }