1+ using System ;
2+ using System . Linq ;
3+ using System . Threading ;
4+ using System . Threading . Tasks ;
5+ using System . Reflection ;
6+ using Moq ;
7+ using Shouldly ;
8+ using Xunit ;
9+ using Paramore . Darker . Attributes ;
10+ using Paramore . Darker . Decorators ;
11+
12+ namespace Paramore . Darker . Tests
13+ {
14+ public class PipelineBuilderExceptionTests
15+ {
16+ private readonly Mock < IQueryHandlerFactory > _handlerFactory ;
17+ private readonly IQueryHandlerRegistry _handlerRegistry ;
18+ private readonly IQueryProcessor _queryProcessor ;
19+ private readonly Mock < IQueryHandlerDecoratorFactory > _decoratorFactory ;
20+
21+ // Query and handler for normal exception scenario
22+ private class ExceptionQuery : IQuery < ExceptionQuery . Result >
23+ {
24+ public class Result { }
25+ }
26+ private class ExceptionQueryHandler : QueryHandler < ExceptionQuery , ExceptionQuery . Result >
27+ {
28+ public override ExceptionQuery . Result Execute ( ExceptionQuery query )
29+ {
30+ throw new InvalidOperationException ( "Test exception from Execute" ) ;
31+ }
32+ public override Task < ExceptionQuery . Result > ExecuteAsync ( ExceptionQuery query , CancellationToken cancellationToken = default )
33+ {
34+ throw new ArgumentException ( "Test exception from ExecuteAsync" ) ;
35+ }
36+ }
37+
38+ // Query and handler for fallback exception scenario
39+ private class FallbackExceptionQuery : IQuery < FallbackExceptionQuery . Result >
40+ {
41+ public class Result { }
42+ }
43+ private class FallbackExceptionQueryHandler : QueryHandler < FallbackExceptionQuery , FallbackExceptionQuery . Result >
44+ {
45+ [ FallbackPolicy ( step : 1 , typeof ( InvalidOperationException ) ) ]
46+ public override FallbackExceptionQuery . Result Execute ( FallbackExceptionQuery query )
47+ {
48+ throw new InvalidOperationException ( "Test exception from Execute" ) ;
49+ }
50+ public override FallbackExceptionQuery . Result Fallback ( FallbackExceptionQuery query )
51+ {
52+ throw new NotSupportedException ( "Test exception from Fallback" ) ;
53+ }
54+ }
55+
56+ // Query and handler for null inner exception scenario
57+ private class NullInnerExceptionQuery : IQuery < string > { }
58+ private class NullInnerExceptionQueryHandler : QueryHandler < NullInnerExceptionQuery , string >
59+ {
60+ public override string Execute ( NullInnerExceptionQuery query )
61+ {
62+ throw new TargetInvocationException ( null ) ;
63+ }
64+ }
65+
66+ // Query and handler for decorator exception scenario
67+ private class DecoratorExceptionQuery : IQuery < DecoratorExceptionQuery . Result >
68+ {
69+ public class Result { }
70+ }
71+ private class DecoratorExceptionQueryHandler : QueryHandler < DecoratorExceptionQuery , DecoratorExceptionQuery . Result >
72+ {
73+ [ DecoratorException ( step : 1 ) ]
74+ public override DecoratorExceptionQuery . Result Execute ( DecoratorExceptionQuery query )
75+ {
76+ return new DecoratorExceptionQuery . Result ( ) ;
77+ }
78+ }
79+ private class TestExceptionDecorator < TQuery , TResult > : IQueryHandlerDecorator < TQuery , TResult >
80+ where TQuery : IQuery < TResult >
81+ {
82+ public IQueryContext Context { get ; set ; }
83+ public void InitializeFromAttributeParams ( object [ ] attributeParams ) { }
84+ public TResult Execute ( TQuery query , Func < TQuery , TResult > next , Func < TQuery , TResult > fallback )
85+ {
86+ throw new InvalidOperationException ( "Test exception from decorator" ) ;
87+ }
88+
89+ public Task < TResult > ExecuteAsync ( TQuery query , Func < TQuery , CancellationToken , Task < TResult > > next , Func < TQuery , CancellationToken , Task < TResult > > fallback ,
90+ CancellationToken cancellationToken = default ( CancellationToken ) )
91+ {
92+ throw new InvalidOperationException ( "Test exception from async decorator" ) ;
93+ }
94+
95+ }
96+
97+
98+ [ AttributeUsage ( AttributeTargets . Method ) ]
99+ public sealed class DecoratorExceptionAttribute : QueryHandlerAttribute
100+ {
101+
102+ public DecoratorExceptionAttribute ( int step ) : base ( step )
103+ {
104+ }
105+
106+ public override object [ ] GetAttributeParams ( )
107+ {
108+ return [ ] ;
109+ }
110+
111+ public override Type GetDecoratorType ( )
112+ {
113+ return typeof ( TestExceptionDecorator < , > ) ;
114+ }
115+ }
116+
117+ public PipelineBuilderExceptionTests ( )
118+ {
119+ _handlerRegistry = new QueryHandlerRegistry ( ) ;
120+ _handlerFactory = new Mock < IQueryHandlerFactory > ( ) ;
121+ _decoratorFactory = new Mock < IQueryHandlerDecoratorFactory > ( ) ;
122+ var decoratorRegistry = new Mock < IQueryHandlerDecoratorRegistry > ( ) ;
123+
124+ // Register the decorators
125+ decoratorRegistry . Setup ( x => x . Register ( typeof ( FallbackPolicyDecorator < , > ) ) ) ;
126+ decoratorRegistry . Setup ( x => x . Register ( typeof ( TestExceptionDecorator < , > ) ) ) ;
127+
128+ var handlerConfiguration = new HandlerConfiguration (
129+ _handlerRegistry ,
130+ _handlerFactory . Object ,
131+ decoratorRegistry . Object ,
132+ _decoratorFactory . Object ) ;
133+
134+ _queryProcessor = new QueryProcessor ( handlerConfiguration , new InMemoryQueryContextFactory ( ) ) ;
135+ }
136+
137+ [ Fact ]
138+ public void ShouldPreserveOriginalExceptionWhenHandlerThrowsException ( )
139+ {
140+ // Arrange
141+ _handlerRegistry . Register < ExceptionQuery , ExceptionQuery . Result , ExceptionQueryHandler > ( ) ;
142+ _handlerFactory . Setup ( x => x . Create ( typeof ( ExceptionQueryHandler ) ) ) . Returns ( new ExceptionQueryHandler ( ) ) ;
143+ var query = new ExceptionQuery ( ) ;
144+
145+ // Act & Assert
146+ var exception = Should . Throw < InvalidOperationException > ( ( ) => _queryProcessor . Execute ( query ) ) ;
147+ exception . Message . ShouldBe ( "Test exception from Execute" ) ;
148+ _handlerFactory . Verify ( x => x . Release ( It . IsAny < ExceptionQueryHandler > ( ) ) , Times . Once ) ;
149+ }
150+
151+ [ Fact ]
152+ public async Task ShouldPreserveOriginalExceptionWhenHandlerThrowsExceptionAsync ( )
153+ {
154+ // Arrange
155+ _handlerRegistry . Register < ExceptionQuery , ExceptionQuery . Result , ExceptionQueryHandler > ( ) ;
156+ _handlerFactory . Setup ( x => x . Create ( typeof ( ExceptionQueryHandler ) ) ) . Returns ( new ExceptionQueryHandler ( ) ) ;
157+ var query = new ExceptionQuery ( ) ;
158+
159+ // Act & Assert
160+ var exception = await Should . ThrowAsync < ArgumentException > ( async ( ) =>
161+ await _queryProcessor . ExecuteAsync ( query ) ) ;
162+ exception . Message . ShouldBe ( "Test exception from ExecuteAsync" ) ;
163+ _handlerFactory . Verify ( x => x . Release ( It . IsAny < ExceptionQueryHandler > ( ) ) , Times . Once ) ;
164+ }
165+
166+ [ Fact ]
167+ public void ShouldThrowNullReferenceExceptionWhenInnerExceptionIsNull ( )
168+ {
169+ // Arrange
170+ _handlerRegistry . Register < NullInnerExceptionQuery , string , NullInnerExceptionQueryHandler > ( ) ;
171+ _handlerFactory . Setup ( x => x . Create ( typeof ( NullInnerExceptionQueryHandler ) ) ) . Returns ( new NullInnerExceptionQueryHandler ( ) ) ;
172+ var query = new NullInnerExceptionQuery ( ) ;
173+
174+ // Act & Assert
175+ var exception = Should . Throw < NullReferenceException > ( ( ) => _queryProcessor . Execute ( query ) ) ;
176+ exception . InnerException . ShouldBeNull ( ) ;
177+ }
178+
179+ [ Fact ]
180+ public void ShouldThrowExceptionWhenFallbackThrowsException ( )
181+ {
182+
183+ // Arrange
184+ var decorator = new FallbackPolicyDecorator < IQuery < FallbackExceptionQuery . Result > , FallbackExceptionQuery . Result > ( ) ;
185+ _handlerRegistry . Register < FallbackExceptionQuery , FallbackExceptionQuery . Result , FallbackExceptionQueryHandler > ( ) ;
186+ _handlerFactory . Setup ( x => x . Create ( typeof ( FallbackExceptionQueryHandler ) ) ) . Returns ( new FallbackExceptionQueryHandler ( ) ) ;
187+ var decoratorType = typeof ( FallbackPolicyDecorator < IQuery < FallbackExceptionQuery . Result > , FallbackExceptionQuery . Result > ) ;
188+ _decoratorFactory . Setup ( x =>
189+ x . Create < IQueryHandlerDecorator < IQuery < FallbackExceptionQuery . Result > , FallbackExceptionQuery . Result > > (
190+ decoratorType ) ) . Returns ( decorator ) ;
191+ var query = new FallbackExceptionQuery ( ) ;
192+
193+ // Act & Assert
194+ var exception = Should . Throw < NotSupportedException > ( ( ) => _queryProcessor . Execute ( query ) ) ;
195+ exception . Message . ShouldBe ( "Test exception from Fallback" ) ;
196+ }
197+
198+ [ Fact ]
199+ public void ShouldThrowExceptionWhenDecoratorThrowsException ( )
200+ {
201+ var decorator = new TestExceptionDecorator < IQuery < DecoratorExceptionQuery . Result > , DecoratorExceptionQuery . Result > ( ) ;
202+ _handlerRegistry . Register < DecoratorExceptionQuery , DecoratorExceptionQuery . Result , DecoratorExceptionQueryHandler > ( ) ;
203+ _handlerFactory . Setup ( x => x . Create ( typeof ( DecoratorExceptionQueryHandler ) ) ) . Returns ( new DecoratorExceptionQueryHandler ( ) ) ;
204+ var decoratorType = typeof ( TestExceptionDecorator < IQuery < DecoratorExceptionQuery . Result > , DecoratorExceptionQuery . Result > ) ;
205+ _decoratorFactory . Setup ( x =>
206+ x . Create < IQueryHandlerDecorator < IQuery < DecoratorExceptionQuery . Result > , DecoratorExceptionQuery . Result > > (
207+ decoratorType ) ) . Returns ( decorator ) ;
208+ var query = new DecoratorExceptionQuery ( ) ;
209+
210+
211+ // Act & Assert
212+ var exception = Should . Throw < InvalidOperationException > ( ( ) => _queryProcessor . Execute ( query ) ) ;
213+ exception . Message . ShouldBe ( "Test exception from decorator" ) ;
214+ }
215+ }
216+ }
0 commit comments