XML router now uses config object passed in through constructor.
[xmlrouter] / impl / src / test / java / org / wamblee / xmlrouter / impl / XMLRouterTest.java
1 /*
2  * Copyright 2005-2011 the original author or authors.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.wamblee.xmlrouter.impl;
17
18 import static org.mockito.Matchers.*;
19 import static org.mockito.Mockito.*;
20
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.concurrent.atomic.AtomicLong;
24 import java.util.logging.Level;
25
26 import javax.xml.transform.dom.DOMSource;
27
28 import org.junit.Before;
29 import org.junit.Test;
30 import org.wamblee.general.SystemClock;
31 import org.wamblee.xmlrouter.common.Id;
32 import org.wamblee.xmlrouter.config.DocumentType;
33 import org.wamblee.xmlrouter.config.Filter;
34 import org.wamblee.xmlrouter.config.Transformation;
35 import org.wamblee.xmlrouter.listener.EventInfo;
36 import org.wamblee.xmlrouter.listener.EventListener;
37 import org.wamblee.xmlrouter.listener.LoggingEventListener;
38 import org.wamblee.xmlrouter.subscribe.Destination;
39
40 public class XMLRouterTest {
41
42     public static class MyDestination implements Destination {
43
44         private boolean receiveResult;
45         private Collection<String> types;
46
47         public MyDestination(boolean aReceiveResult, Collection<String> aTypes) {
48             receiveResult = aReceiveResult;
49             types = aTypes;
50         }
51
52         @Override
53         public Collection<String> chooseFromTargetTypes(
54             Collection<String> aPossibleTargetTypes) {
55             return types;
56         }
57
58         @Override
59         public String getName() {
60             return "xxx";
61         }
62
63         @Override
64         public boolean receive(DOMSource aEvent) {
65             return receiveResult;
66         }
67     }
68
69     private ExtendedRouterConfig routerConfig;
70     private XMLRouter router;
71     private DOMSource source1;
72     private DOMSource source2;
73     private DOMSource source3;
74
75     private Destination destinationSpy;
76     private Id<Destination> destinationId;
77     private EventListener listener;
78
79     @Before
80     public void setUp() {
81         routerConfig = new SingleRouterConfig(new AtomicLong(1L));
82         EventListener logListener = new LoggingEventListener(Level.INFO);
83         listener = spy(logListener);
84         router = new XMLRouter(new SystemClock(), routerConfig, listener);
85         source1 = mock(DOMSource.class);
86         source2 = mock(DOMSource.class);
87         source3 = mock(DOMSource.class);
88     }
89
90     @Test
91     public void testNoInputDocumentsRegistered() {
92         Destination destination = new MyDestination(true, Arrays.asList("any"));
93         destinationSpy = spy(destination);
94
95         destinationId = router.registerDestination(destinationSpy);
96         router.publish("any", source1);
97         verify(listener).notDelivered(any(EventInfo.class));
98     }
99
100     @Test
101     public void testMisBehavingDocumentType() {
102         DocumentType type = mock(DocumentType.class);
103         doThrow(new RuntimeException("x")).when(type).isInstance(
104             any(DOMSource.class));
105         routerConfig.documentTypeConfig().add(type);
106         router.publish("xx", mock(DOMSource.class));
107         verify(listener).notDelivered(any(EventInfo.class));
108         // no exception should occur.
109     }
110
111     @Test
112     public void testMisBehavingFilter() {
113         registerDocumentType("any");
114         Filter filter = mock(Filter.class);
115         doThrow(new RuntimeException("x")).when(filter).isAllowed(anyString(),
116             any(DOMSource.class));
117         routerConfig.filterConfig().add(filter);
118         router.publish("xx", mock(DOMSource.class));
119         verify(listener).notDelivered(any(EventInfo.class));
120         // no exception should occur.
121     }
122
123     @Test
124     public void testOneDestinationNoTransformationSuccess() {
125         destinationSpy = registerDestination(true, "any");
126         registerDocumentType("any");
127
128         router.publish("any", source1);
129         verify(listener).delivered(any(EventInfo.class),
130             anyListOf(Transformation.class), anyLong(), anyString(), eq(true));
131         verify(destinationSpy).receive(same(source1));
132
133         // Unregister the destination.
134         router.unregisterDestination(destinationId);
135         resetMocks();
136         router.publish("any", source2);
137         verify(listener).notDelivered(any(EventInfo.class));
138         verifyNoMoreInteractions(destinationSpy);
139     }
140
141     private void resetMocks() {
142         reset(destinationSpy);
143         reset(listener);
144     }
145
146     private void registerDocumentType(String aType) {
147         DocumentType type = mock(DocumentType.class);
148         when(type.isInstance(any(DOMSource.class))).thenReturn(true);
149         when(type.getName()).thenReturn(aType);
150         Id<DocumentType> typeId = routerConfig.documentTypeConfig().add(type);
151     }
152
153     private void registerDocumentType(String aType, DOMSource aSource) {
154         DocumentType type = mock(DocumentType.class);
155         when(type.isInstance(same(aSource))).thenReturn(true);
156         when(type.getName()).thenReturn(aType);
157         Id<DocumentType> typeId = routerConfig.documentTypeConfig().add(type);
158     }
159
160     private Destination registerDestination(boolean aResult, String... types) {
161         Destination destination = new MyDestination(aResult,
162             Arrays.asList(types));
163         Destination myspy = spy(destination);
164         destinationId = router.registerDestination(myspy);
165         return myspy;
166     }
167
168     @Test
169     public void testOneDestinationNotMatches() {
170         destinationSpy = registerDestination(true);
171         registerDocumentType("any");
172
173         router.publish("any", source1);
174         verify(listener).notDelivered(any(EventInfo.class));
175         verify(destinationSpy, never()).receive(any(DOMSource.class));
176     }
177
178     @Test
179     public void testOneDestinationThrowsException() {
180         destinationSpy = registerDestination(true, "any");
181         registerDocumentType("any");
182
183         doThrow(new RuntimeException()).when(destinationSpy).receive(
184             any(DOMSource.class));
185
186         router.publish("any", source1);
187         verify(listener).notDelivered(any(EventInfo.class));
188         verify(destinationSpy).receive(same(source1));
189     }
190
191     @Test
192     public void testOneDestinationThrowsExceptionSecondDestinationStillHandled() {
193         testOneDestinationThrowsException();
194         Destination destination2 = new MyDestination(true, Arrays.asList("any"));
195         Destination destinationSpy2 = spy(destination2);
196         Id<Destination> destinationId2 = router
197             .registerDestination(destinationSpy2);
198
199         router.publish("any", source1);
200         verify(listener).delivered(any(EventInfo.class),
201             anyListOf(Transformation.class), anyLong(), anyString(), eq(true));
202
203         verify(destinationSpy2).receive(same(source1));
204
205     }
206
207     @Test
208     public void testDestinationChooseFromTargetTypesThrowsException() {
209         destinationSpy = registerDestination(true, "any");
210         registerDocumentType("any");
211
212         doThrow(new RuntimeException()).when(destinationSpy)
213             .chooseFromTargetTypes((Collection<String>) anyObject());
214
215         router.publish("any", source1);
216         verify(listener).notDelivered(any(EventInfo.class));
217         verify(destinationSpy, never()).receive(same(source1));
218     }
219
220     @Test
221     public void testDestinationChooseFromTargetTypesThrowsExceptionSecondDestinationStillOk() {
222         testDestinationChooseFromTargetTypesThrowsException();
223
224         Destination destination2 = new MyDestination(true, Arrays.asList("any"));
225         Destination destinationSpy2 = spy(destination2);
226         Id<Destination> destinationId2 = router
227             .registerDestination(destinationSpy2);
228         router.publish("any", source1);
229         verify(listener).delivered(any(EventInfo.class),
230             anyListOf(Transformation.class), anyLong(), anyString(), eq(true));
231
232         verify(destinationSpy, never()).receive(same(source1));
233         verify(destinationSpy2).receive(same(source1));
234     }
235
236     @Test
237     public void testDestinationChooseFromTargetTypesReturnsNull() {
238         destinationSpy = registerDestination(true, "any");
239         registerDocumentType("any");
240
241         when(
242             destinationSpy
243                 .chooseFromTargetTypes((Collection<String>) anyObject()))
244             .thenReturn(null);
245
246         router.publish("any", source1);
247         verify(listener).notDelivered(any(EventInfo.class));
248         verify(destinationSpy, never()).receive(same(source1));
249     }
250
251     @Test
252     public void testDestinationChooseFromTargetTypesReturnsNullSecondDestinationStillOk() {
253         testDestinationChooseFromTargetTypesReturnsNull();
254
255         Destination destination2 = new MyDestination(true, Arrays.asList("any"));
256         Destination destinationSpy2 = spy(destination2);
257         Id<Destination> destinationId2 = router
258             .registerDestination(destinationSpy2);
259         router.publish("any", source1);
260         verify(listener).delivered(any(EventInfo.class),
261             anyListOf(Transformation.class), anyLong(), anyString(), eq(true));
262
263         verify(destinationSpy, never()).receive(same(source1));
264         verify(destinationSpy2).receive(same(source1));
265     }
266
267     @Test
268     public void testOneTransformationOneDestination() {
269         registerDocumentType("any");
270         Transformation transformation = mock(Transformation.class);
271         when(transformation.getName()).thenReturn("trans");
272         when(transformation.getFromType()).thenReturn("any");
273         when(transformation.getToType()).thenReturn("bla");
274         when(transformation.transform(same(source1))).thenReturn(source2);
275         routerConfig.transformationConfig().add(transformation);
276
277         Destination destination = mock(Destination.class);
278         when(
279             destination.chooseFromTargetTypes((Collection<String>) anyObject()))
280             .thenReturn(Arrays.asList("bla"));
281
282         router.registerDestination(destination);
283
284         when(destination.receive(any(DOMSource.class))).thenReturn(true);
285         router.publish("bla", source1);
286         verify(listener).delivered(any(EventInfo.class),
287             anyListOf(Transformation.class), anyLong(), anyString(), eq(true));
288
289         verify(transformation).transform(source1);
290         verify(destination).receive(same(source2));
291
292         // now the same when the destination rejects the event.
293         when(destination.receive(any(DOMSource.class))).thenReturn(false);
294         router.publish("bla", source1);
295         verify(listener).notDelivered(any(EventInfo.class));
296     }
297
298     @Test
299     public void testMisbehavingTransformationOneDestination() {
300         registerDocumentType("any");
301         Transformation transformation = mock(Transformation.class);
302         when(transformation.getName()).thenReturn("trans");
303         when(transformation.getFromType()).thenReturn("any");
304         when(transformation.getToType()).thenReturn("bla");
305         doThrow(new RuntimeException("x")).when(transformation).transform(
306             same(source1));
307         routerConfig.transformationConfig().add(transformation);
308
309         Destination destination = mock(Destination.class);
310         when(
311             destination.chooseFromTargetTypes((Collection<String>) anyObject()))
312             .thenReturn(Arrays.asList("bla"));
313
314         router.registerDestination(destination);
315
316         when(destination.receive(any(DOMSource.class))).thenReturn(true);
317         router.publish("bla", source1);
318         verify(listener).notDelivered(any(EventInfo.class));
319     }
320
321     private Transformation createTransformation(String aFrom, String aTo,
322         DOMSource aSource, DOMSource aTarget) {
323         Transformation transformation = mock(Transformation.class);
324         when(transformation.getName()).thenReturn("trans");
325         when(transformation.getFromType()).thenReturn(aFrom);
326         when(transformation.getToType()).thenReturn(aTo);
327         when(transformation.transform(same(aSource))).thenReturn(aTarget);
328         return transformation;
329     }
330
331     @Test
332     public void testOneTransformationReturnsNull() {
333         registerDocumentType("any");
334         Transformation transformation = createTransformation("any", "bla",
335             source1, null);
336
337         routerConfig.transformationConfig().add(transformation);
338
339         Destination destination = mock(Destination.class);
340         when(
341             destination.chooseFromTargetTypes((Collection<String>) anyObject()))
342             .thenReturn(Arrays.asList("bla"));
343         router.registerDestination(destination);
344
345         router.publish("bla", source1);
346         verify(listener).notDelivered(any(EventInfo.class));
347
348         verify(transformation).transform(source1);
349         verify(destination, never()).receive(any(DOMSource.class));
350
351         // add second transformation that behaves normally
352         Transformation transformation2 = createTransformation("any", "bla2",
353             source1, source2);
354
355         routerConfig.transformationConfig().add(transformation2);
356         when(
357             destination.chooseFromTargetTypes((Collection<String>) anyObject()))
358             .thenReturn(Arrays.asList("bla", "bla2"));
359
360         reset(transformation);
361         when(transformation.getName()).thenReturn("trans");
362         when(transformation.getFromType()).thenReturn("any");
363         when(transformation.getToType()).thenReturn("bla");
364         when(transformation.transform(same(source1))).thenReturn(null);
365
366         when(destination.receive(any(DOMSource.class))).thenReturn(true);
367         router.publish("bla", source1);
368         verify(listener).delivered(any(EventInfo.class),
369             anyListOf(Transformation.class), anyLong(), anyString(), eq(true));
370
371         verify(transformation).transform(source1);
372         verify(transformation2).transform(source1);
373
374         verify(destination).receive(same(source2));
375
376     }
377
378     @Test
379     public void testChooseMultipleDestinationsOneType() {
380         Destination dest1 = registerDestination(true, "any");
381         Destination dest2 = registerDestination(true, "any");
382         registerDocumentType("any");
383
384         router.publish("source", source1);
385         verify(listener, times(2)).delivered(any(EventInfo.class),
386             anyListOf(Transformation.class), anyLong(), anyString(), eq(true));
387
388         verify(dest1).receive(same(source1));
389         verify(dest2).receive(same(source1));
390     }
391
392     @Test
393     public void testMultipleDeliveryToOneDestination() {
394         Destination dest = registerDestination(true, "any", "other");
395         registerDocumentType("any", source1);
396         registerDocumentType("other", source2);
397         Transformation transformation = createTransformation("any", "other",
398             source1, source2);
399         routerConfig.transformationConfig().add(transformation);
400
401         router.publish("source", source1);
402         verify(listener, times(2)).delivered(any(EventInfo.class),
403             anyListOf(Transformation.class), anyLong(), anyString(), eq(true));
404
405         verify(dest).receive(same(source1));
406         verify(dest).receive(same(source2));
407     }
408
409     @Test
410     public void testMultipleTransformations() {
411         Destination dest = registerDestination(true, "other");
412         registerDocumentType("any", source1);
413         registerDocumentType("other", source3);
414
415         Transformation t1 = createTransformation("any", "intermediate",
416             source1, source2);
417         routerConfig.transformationConfig().add(t1);
418         Transformation t2 = createTransformation("intermediate", "other",
419             source2, source3);
420         routerConfig.transformationConfig().add(t2);
421
422         router.publish("source", source1);
423         verify(listener).delivered(any(EventInfo.class),
424             anyListOf(Transformation.class), anyLong(), anyString(), eq(true));
425
426         verify(dest).receive(same(source3));
427     }
428
429     @Test
430     public void testDestinationGivesError() {
431         Destination destination = mock(Destination.class);
432         when(destination.getName()).thenReturn("name");
433         when(destination.chooseFromTargetTypes(anyCollectionOf(String.class)))
434             .thenReturn(Arrays.asList("any"));
435         doThrow(new RuntimeException("x")).when(destination).receive(
436             any(DOMSource.class));
437         router.registerDestination(destination);
438
439         registerDocumentType("any");
440
441         router.publish("source", source1);
442
443         verify(listener).delivered(any(EventInfo.class),
444             anyListOf(Transformation.class), anyLong(), anyString(), eq(false));
445
446     }
447 }