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