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