@@ -40,6 +40,17 @@ describe('ProcessorChain', () => {
4040 return new ProcessorChain ( wrapRequestProcessors ( procs ) ) ;
4141 } ;
4242
43+ const makeDoneFn = ( endTest : Mocha . Done , tests : ( args : any [ ] ) => void ) : ( ( ) => void ) => {
44+ return ( ...args ) => {
45+ try {
46+ tests ( args ) ;
47+ endTest ( ) ;
48+ } catch ( err ) {
49+ endTest ( err ) ;
50+ }
51+ } ;
52+ } ;
53+
4354 it ( 'calls processors in correct order - no error handlers and no errors' , ( ) => {
4455 const procs : SinonSpy [ ] = [
4556 /* 0 */ makeRequestProcessor ( 'mw1' ) ,
@@ -54,20 +65,39 @@ describe('ProcessorChain', () => {
5465 assert . calledWithExactly ( done ) ; // `done` called with no args
5566 } ) ;
5667
57- it ( 'does not call error handlers when no errors' , ( ) => {
68+ it ( 'calls async processors returning promises in correct order - no error handlers and no errors' , ( endTest ) => {
69+ const procs : SinonSpy [ ] = [
70+ /* 0 */ makeRequestProcessor ( 'mw1' , { returnsResolvedPromise : true } ) ,
71+ /* 1 */ makeRequestProcessor ( 'mw2' ) ,
72+ /* 2 */ makeRequestProcessor ( 'mw3' , { returnsResolvedPromise : true } ) ,
73+ /* 3 */ makeRequestProcessor ( 'rh1' ) ,
74+ /* 4 */ makeRequestProcessor ( 'rh2' ) ,
75+ /* 5 */ makeRequestProcessor ( 'rh3' , { returnsResolvedPromise : true } ) ,
76+ ] ;
77+
78+ makeChain ( procs ) . run ( undefined , req , resp , makeDoneFn ( endTest , ( args ) => {
79+ assertAllCalledOnceInOrder ( ...procs ) ;
80+ expect ( args ) . to . eql ( [ ] ) ; // callback was called with no args
81+ } ) ) ;
82+ } ) ;
83+
84+ it ( 'does not call error handlers when no errors' , ( endTest ) => {
5885 const procs : SinonSpy [ ] = [
5986 /* 0 */ makeRequestProcessor ( 'mw1' ) ,
6087 /* 1 */ makeRequestProcessor ( 'rh1' ) ,
6188 /* 2 */ makeRequestProcessor ( 'eh1' , { handlesErrors : true } ) ,
6289 /* 3 */ makeRequestProcessor ( 'mw2' ) ,
6390 /* 4 */ makeRequestProcessor ( 'rh2' ) ,
64- /* 5 */ makeRequestProcessor ( 'eh2' , { handlesErrors : true } ) ,
91+ /* 5 */ makeRequestProcessor ( 'eh2' , { handlesErrors : true , returnsResolvedPromise : true } ) ,
92+ /* 6 */ makeRequestProcessor ( 'rh3' ) ,
93+ /* 7 */ makeRequestProcessor ( 'eh3' , { handlesErrors : true } ) ,
6594 ] ;
6695
67- makeChain ( procs ) . run ( undefined , req , resp , done ) ;
68- assertAllCalledOnceInOrder ( procs [ 0 ] , procs [ 1 ] , procs [ 3 ] , procs [ 4 ] , done ) ;
69- assertNotCalled ( procs [ 2 ] , procs [ 5 ] ) ;
70- assert . calledWithExactly ( done ) ; // `done` called with no args
96+ makeChain ( procs ) . run ( undefined , req , resp , makeDoneFn ( endTest , ( args ) => {
97+ assertAllCalledOnceInOrder ( procs [ 0 ] , procs [ 1 ] , procs [ 3 ] , procs [ 4 ] , procs [ 6 ] ) ;
98+ assertNotCalled ( procs [ 2 ] , procs [ 5 ] , procs [ 7 ] ) ;
99+ expect ( args ) . to . eql ( [ ] ) ; // callback was called with no args
100+ } ) ) ;
71101 } ) ;
72102
73103 it ( 'skips to error handlers on first thrown error' , ( ) => {
@@ -88,7 +118,26 @@ describe('ProcessorChain', () => {
88118 assert . calledWithExactly ( done ) ; // `done` called with no args
89119 } ) ;
90120
91- it ( 'calls subsequent error handlers when thrown error passed on (thrown error)' , ( ) => {
121+ it ( 'skips to error handlers on first rejected promise' , ( endTest ) => {
122+ const procs : SinonSpy [ ] = [
123+ /* 0 */ makeRequestProcessor ( 'mw1' ) ,
124+ /* 1 */ makeRequestProcessor ( 'mw2' , { returnsRejectedPromise : true } ) ,
125+ /* 2 */ makeRequestProcessor ( 'rh1' ) ,
126+ /* 3 */ makeRequestProcessor ( 'rh2' ) ,
127+ /* 4 */ makeRequestProcessor ( 'eh1' , { handlesErrors : true } ) ,
128+ // since the first error handler does not pass the error on when it calls `next`,
129+ // this second error handler will not be called
130+ /* 5 */ makeRequestProcessor ( 'eh2' , { handlesErrors : true } ) ,
131+ ] ;
132+
133+ makeChain ( procs ) . run ( undefined , req , resp , makeDoneFn ( endTest , ( args ) => {
134+ assertAllCalledOnceInOrder ( procs [ 0 ] , procs [ 1 ] , procs [ 4 ] ) ;
135+ assertNotCalled ( procs [ 2 ] , procs [ 3 ] , procs [ 5 ] ) ;
136+ expect ( args ) . to . eql ( [ ] ) ; // callback was called with no args
137+ } ) ) ;
138+ } ) ;
139+
140+ it ( 'calls subsequent error handlers when thrown error is passed on (thrown error)' , ( ) => {
92141 const procs : SinonSpy [ ] = [
93142 /* 0 */ makeRequestProcessor ( 'mw1' ) ,
94143 /* 1 */ makeRequestProcessor ( 'mw2' , { throwsError : true } ) ,
@@ -104,6 +153,103 @@ describe('ProcessorChain', () => {
104153 assertCalledWith ( done , 'Error from "mw2"' , true ) ; // `done` called with Error(string)
105154 } ) ;
106155
156+ it ( 'skips to error handlers on first rejected promise (empty rejection)' , ( endTest ) => {
157+ const procs : SinonSpy [ ] = [
158+ /* 0 */ makeRequestProcessor ( 'mw1' ) ,
159+ /* 1 */ makeRequestProcessor ( 'mw2' , { returnsEmptyRejectedPromise : true } ) ,
160+ /* 2 */ makeRequestProcessor ( 'rh1' ) ,
161+ /* 3 */ makeRequestProcessor ( 'rh2' ) ,
162+ /* 4 */ makeRequestProcessor ( 'eh1' , { handlesErrors : true } ) ,
163+ // since the first error handler does not pass the error on when it calls `next`,
164+ // this second error handler will not be called
165+ /* 5 */ makeRequestProcessor ( 'eh2' , { handlesErrors : true } ) ,
166+ ] ;
167+
168+ makeChain ( procs ) . run ( undefined , req , resp , makeDoneFn ( endTest , ( args ) => {
169+ assertAllCalledOnceInOrder ( procs [ 0 ] , procs [ 1 ] , procs [ 4 ] ) ;
170+ assertNotCalled ( procs [ 2 ] , procs [ 3 ] , procs [ 5 ] ) ;
171+ expect ( args ) . to . eql ( [ ] ) ; // callback was called with no args
172+ } ) ) ;
173+ } ) ;
174+
175+ it ( 'calls subsequent error handlers when a rejected promise\'s error is passed on' , ( endTest ) => {
176+ const procs : SinonSpy [ ] = [
177+ /* 0 */ makeRequestProcessor ( 'mw1' ) ,
178+ /* 1 */ makeRequestProcessor ( 'mw2' , { returnsRejectedPromise : true } ) ,
179+ /* 2 */ makeRequestProcessor ( 'rh1' ) ,
180+ /* 3 */ makeRequestProcessor ( 'rh2' ) ,
181+ /* 4 */ makeRequestProcessor ( 'eh1' , { handlesErrors : true , passesErrorToNext : true } ) ,
182+ /* 5 */ makeRequestProcessor ( 'eh2' , { handlesErrors : true , passesErrorToNext : true } ) ,
183+ ] ;
184+
185+ makeChain ( procs ) . run ( undefined , req , resp , makeDoneFn ( endTest , ( args ) => {
186+ assertAllCalledOnceInOrder ( procs [ 0 ] , procs [ 1 ] , procs [ 4 ] , procs [ 5 ] ) ;
187+ assertNotCalled ( procs [ 2 ] , procs [ 3 ] ) ;
188+ // callback called with error string (not Error instance)
189+ expect ( args ) . to . have . length ( 1 ) ;
190+ expect ( args [ 0 ] ) . to . eql ( 'Rejection from "mw2"' ) ;
191+ } ) ) ;
192+ } ) ;
193+
194+ it ( 'calls subsequent error handlers with default error from empty rejected promise' , ( endTest ) => {
195+ const procs : SinonSpy [ ] = [
196+ /* 0 */ makeRequestProcessor ( 'mw1' ) ,
197+ /* 1 */ makeRequestProcessor ( 'mw2' , { returnsEmptyRejectedPromise : true } ) ,
198+ /* 2 */ makeRequestProcessor ( 'rh1' ) ,
199+ /* 3 */ makeRequestProcessor ( 'rh2' ) ,
200+ /* 4 */ makeRequestProcessor ( 'eh1' , { handlesErrors : true , passesErrorToNext : true } ) ,
201+ /* 5 */ makeRequestProcessor ( 'eh2' , { handlesErrors : true , passesErrorToNext : true } ) ,
202+ ] ;
203+
204+ makeChain ( procs ) . run ( undefined , req , resp , makeDoneFn ( endTest , ( args ) => {
205+ assertAllCalledOnceInOrder ( procs [ 0 ] , procs [ 1 ] , procs [ 4 ] , procs [ 5 ] ) ;
206+ assertNotCalled ( procs [ 2 ] , procs [ 3 ] ) ;
207+ // callback called with Error(string)
208+ expect ( args ) . to . have . length ( 1 ) ;
209+ expect ( args [ 0 ] ) . to . be . an . instanceOf ( Error ) ;
210+ expect ( args [ 0 ] . message ) . to . eql ( 'Rejected promise' ) ;
211+ } ) ) ;
212+ } ) ;
213+
214+ it ( 'calls subsequent error handlers with error from rejected promise' , ( endTest ) => {
215+ const procs : SinonSpy [ ] = [
216+ /* 0 */ makeRequestProcessor ( 'mw1' ) ,
217+ /* 1 */ makeRequestProcessor ( 'mw2' , { throwsError : true } ) ,
218+ /* 2 */ makeRequestProcessor ( 'rh1' ) ,
219+ /* 3 */ makeRequestProcessor ( 'rh2' ) ,
220+ /* 4 */ makeRequestProcessor ( 'eh1' , { handlesErrors : true , returnsRejectedPromise : true } ) ,
221+ /* 5 */ makeRequestProcessor ( 'eh2' , { handlesErrors : true , passesErrorToNext : true } ) ,
222+ ] ;
223+
224+ makeChain ( procs ) . run ( undefined , req , resp , makeDoneFn ( endTest , ( args ) => {
225+ assertAllCalledOnceInOrder ( procs [ 0 ] , procs [ 1 ] , procs [ 4 ] , procs [ 5 ] ) ;
226+ assertNotCalled ( procs [ 2 ] , procs [ 3 ] ) ;
227+ // callback called with error string (not Error instance)
228+ expect ( args ) . to . have . length ( 1 ) ;
229+ expect ( args [ 0 ] ) . to . eql ( 'Rejection from "eh1"' ) ;
230+ } ) ) ;
231+ } ) ;
232+
233+ it ( 'calls subsequent error handlers with default error from empty rejected promise' , ( endTest ) => {
234+ const procs : SinonSpy [ ] = [
235+ /* 0 */ makeRequestProcessor ( 'mw1' ) ,
236+ /* 1 */ makeRequestProcessor ( 'mw2' , { throwsError : true } ) ,
237+ /* 2 */ makeRequestProcessor ( 'rh1' ) ,
238+ /* 3 */ makeRequestProcessor ( 'rh2' ) ,
239+ /* 4 */ makeRequestProcessor ( 'eh1' , { handlesErrors : true , returnsEmptyRejectedPromise : true } ) ,
240+ /* 5 */ makeRequestProcessor ( 'eh2' , { handlesErrors : true , passesErrorToNext : true } ) ,
241+ ] ;
242+
243+ makeChain ( procs ) . run ( undefined , req , resp , makeDoneFn ( endTest , ( args ) => {
244+ assertAllCalledOnceInOrder ( procs [ 0 ] , procs [ 1 ] , procs [ 4 ] , procs [ 5 ] ) ;
245+ assertNotCalled ( procs [ 2 ] , procs [ 3 ] ) ;
246+ // callback called with Error(string)
247+ expect ( args ) . to . have . length ( 1 ) ;
248+ expect ( args [ 0 ] ) . to . be . an . instanceOf ( Error ) ;
249+ expect ( args [ 0 ] . message ) . to . eql ( 'Rejected promise' ) ;
250+ } ) ) ;
251+ } ) ;
252+
107253 it ( 'skips to error handlers on first non-thrown error' , ( ) => {
108254 const procs : SinonSpy [ ] = [
109255 /* 0 */ makeRequestProcessor ( 'mw1' ) ,
@@ -122,7 +268,7 @@ describe('ProcessorChain', () => {
122268 assert . calledWithExactly ( done ) ; // `done` called with no args
123269 } ) ;
124270
125- it ( 'calls subsequent error handlers when thrown error passed on (non-thrown error)' , ( ) => {
271+ it ( 'calls subsequent error handlers when thrown error is passed on (non-thrown error)' , ( ) => {
126272 const procs : SinonSpy [ ] = [
127273 /* 0 */ makeRequestProcessor ( 'mw1' ) ,
128274 /* 1 */ makeRequestProcessor ( 'mw2' , { callsNextWithError : true } ) ,
@@ -159,6 +305,28 @@ describe('ProcessorChain', () => {
159305 assert . calledWithExactly ( done ) ; // `done` called with no args
160306 } ) ;
161307
308+ it ( 'resumes processors after error handler handles rejected promise' , ( endTest ) => {
309+ const procs : SinonSpy [ ] = [
310+ /* 0 */ makeRequestProcessor ( 'mw1' ) ,
311+ /* 1 */ makeRequestProcessor ( 'mw2' , { returnsRejectedPromise : true } ) ,
312+ /* 2 */ makeRequestProcessor ( 'rh1' ) ,
313+ /* 3 */ makeRequestProcessor ( 'rh2' ) ,
314+ /* 4 */ makeRequestProcessor ( 'eh1' , { handlesErrors : true } ) ,
315+ // since the previous error handler (eh1) did not pass the error on to the next,
316+ // then the next error handler (eh2) will not get called
317+ /* 5 */ makeRequestProcessor ( 'eh2' , { handlesErrors : true } ) ,
318+ // but these regular processor (route handler / middleware) will get called
319+ /* 6 */ makeRequestProcessor ( 'rh3' ) ,
320+ /* 7 */ makeRequestProcessor ( 'rh4' ) ,
321+ ] ;
322+
323+ makeChain ( procs ) . run ( undefined , req , resp , makeDoneFn ( endTest , ( args ) => {
324+ assertAllCalledOnceInOrder ( procs [ 0 ] , procs [ 1 ] , procs [ 4 ] , procs [ 6 ] , procs [ 7 ] ) ;
325+ assertNotCalled ( procs [ 2 ] , procs [ 3 ] , procs [ 5 ] ) ;
326+ expect ( args ) . to . eql ( [ ] ) ; // callback was called with no args
327+ } ) ) ;
328+ } ) ;
329+
162330 it ( 'short-circuits to done when next(\'route\') called' , ( ) => {
163331 const procs : SinonSpy [ ] = [
164332 /* 0 */ makeRequestProcessor ( 'mw1' ) ,
0 commit comments