Turned the API packages into bundles.
[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.UUID;
24 import java.util.logging.Handler;
25 import java.util.logging.Level;
26 import java.util.logging.Logger;
27
28 import javax.xml.transform.dom.DOMSource;
29
30 import org.junit.Before;
31 import org.junit.Test;
32 import org.wamblee.general.SystemClock;
33 import org.wamblee.xmlrouter.common.Id;
34 import org.wamblee.xmlrouter.config.DocumentType;
35 import org.wamblee.xmlrouter.config.Filter;
36 import org.wamblee.xmlrouter.config.Transformation;
37 import org.wamblee.xmlrouter.listener.EventInfo;
38 import org.wamblee.xmlrouter.listener.EventListener;
39 import org.wamblee.xmlrouter.listener.LoggingEventListener;
40 import org.wamblee.xmlrouter.subscribe.Destination;
41
42 public class XMLRouterTest {
43
44     public static class MyDestination implements Destination {
45
46         private boolean receiveResult;
47         private Collection<String> types;
48
49         public MyDestination(boolean aReceiveResult, Collection<String> aTypes) {
50             receiveResult = aReceiveResult;
51             types = aTypes;
52         }
53
54         @Override
55         public Collection<String> chooseFromTargetTypes(
56             Collection<String> aPossibleTargetTypes) {
57             return types;
58         }
59
60         @Override
61         public String getName() {
62             return "xxx";
63         }
64
65         @Override
66         public boolean receive(DOMSource aEvent) {
67             return receiveResult;
68         }
69     }
70
71     private ExtendedRouterConfig routerConfig;
72     private XMLRouterConfiguration config;
73     private XMLRouter router;
74     private DOMSource source1;
75     private DOMSource source2;
76     private DOMSource source3;
77
78     private Destination destinationSpy;
79     private Id<Destination> destinationId;
80     private EventListener listener;
81
82     @Before
83     public void setUp() {
84
85         Logger logger = Logger.getLogger(XMLRouter.class.getName());
86         setLogLevel(logger, Level.FINEST);
87
88         routerConfig = new SingleRouterConfig("routerconfig");
89         config = new XMLRouterConfigurationImpl(routerConfig);
90         EventListener logListener = new LoggingEventListener(Level.INFO);
91         listener = spy(logListener);
92         router = new XMLRouter(new SystemClock(), config, listener);
93         source1 = mock(DOMSource.class);
94         source2 = mock(DOMSource.class);
95         source3 = mock(DOMSource.class);
96     }
97
98     private void setLogLevel(Logger aLogger, Level aLevel) {
99         aLogger.setLevel(aLevel);
100         for (Handler handler : aLogger.getHandlers()) {
101             handler.setLevel(Level.FINEST);
102         }
103         Logger parent = aLogger.getParent();
104         if (parent != null) {
105             setLogLevel(parent, aLevel);
106         }
107     }
108
109     @Test
110     public void testNoInputDocumentsRegistered() {
111         Destination destination = new MyDestination(true, Arrays.asList("any"));
112         destinationSpy = spy(destination);
113
114         destinationId = router.registerDestination(destinationSpy);
115         router.publish("any", source1);
116         verify(listener).notDelivered(any(EventInfo.class));
117     }
118
119     @Test
120     public void testMisBehavingDocumentType() {
121         DocumentType type = mockDocument("docid");
122         doThrow(new RuntimeException("x")).when(type).isInstance(
123             any(DOMSource.class));
124         routerConfig.documentTypeConfig().add(type);
125         router.publish("xx", mock(DOMSource.class));
126         verify(listener).notDelivered(any(EventInfo.class));
127         // no exception should occur.
128     }
129
130     private DocumentType mockDocument(String docid) {
131         DocumentType type = mock(DocumentType.class);
132         when(type.getId()).thenReturn(new Id<DocumentType>(docid));
133         return type;
134     }
135
136     @Test
137     public void testMisBehavingFilter() {
138         registerDocumentType("any");
139         Filter filter = mockFilter("filterid");
140         doThrow(new RuntimeException("x")).when(filter).isAllowed(anyString(),
141             any(DOMSource.class));
142         routerConfig.filterConfig().add(filter);
143         router.publish("xx", mock(DOMSource.class));
144         verify(listener).notDelivered(any(EventInfo.class));
145         // no exception should occur.
146     }
147
148     private Filter mockFilter(String filterId) {
149         Filter filter = mock(Filter.class);
150         when(filter.getId()).thenReturn(new Id<Filter>(filterId));
151         return filter;
152     }
153
154     @Test
155     public void testOneDestinationNoTransformationSuccess() {
156         destinationSpy = registerDestination(true, "any");
157         registerDocumentType("any");
158
159         router.publish("any", source1);
160         verify(listener).delivered(any(EventInfo.class),
161             anyListOf(Transformation.class), anyString(), eq(true));
162         verify(destinationSpy).receive(same(source1));
163
164         // Unregister the destination.
165         router.unregisterDestination(destinationId);
166         resetMocks();
167         router.publish("any", source2);
168         verify(listener).notDelivered(any(EventInfo.class));
169         verifyNoMoreInteractions(destinationSpy);
170     }
171
172     private void resetMocks() {
173         reset(destinationSpy);
174         reset(listener);
175     }
176
177     private void registerDocumentType(String aType) {
178         DocumentType type = mockDocument(UUID.randomUUID().toString());
179         when(type.isInstance(any(DOMSource.class))).thenReturn(true);
180         when(type.getName()).thenReturn(aType);
181         routerConfig.documentTypeConfig().add(type);
182     }
183
184     private void registerDocumentType(String aType, DOMSource aSource) {
185         DocumentType type = mock(DocumentType.class);
186         when(type.isInstance(same(aSource))).thenReturn(true);
187         when(type.getName()).thenReturn(aType);
188         when(type.getId()).thenReturn(new Id<DocumentType>(aType));
189         routerConfig.documentTypeConfig().add(type);
190     }
191
192     private Destination registerDestination(boolean aResult, String... types) {
193         Destination destination = new MyDestination(aResult,
194             Arrays.asList(types));
195         Destination myspy = spy(destination);
196         destinationId = router.registerDestination(myspy);
197         return myspy;
198     }
199
200     @Test
201     public void testOneDestinationNotMatches() {
202         destinationSpy = registerDestination(true);
203         registerDocumentType("any");
204
205         router.publish("any", source1);
206         verify(listener).notDelivered(any(EventInfo.class));
207         verify(destinationSpy, never()).receive(any(DOMSource.class));
208     }
209
210     @Test
211     public void testOneDestinationThrowsException() {
212         destinationSpy = registerDestination(true, "any");
213         registerDocumentType("any");
214
215         doThrow(new RuntimeException()).when(destinationSpy).receive(
216             any(DOMSource.class));
217
218         router.publish("any", source1);
219         verify(listener).notDelivered(any(EventInfo.class));
220         verify(destinationSpy).receive(same(source1));
221     }
222
223     @Test
224     public void testOneDestinationThrowsExceptionSecondDestinationStillHandled() {
225         testOneDestinationThrowsException();
226         Destination destination2 = new MyDestination(true, Arrays.asList("any"));
227         Destination destinationSpy2 = spy(destination2);
228         Id<Destination> destinationId2 = router
229             .registerDestination(destinationSpy2);
230
231         router.publish("any", source1);
232         verify(listener).delivered(any(EventInfo.class),
233             anyListOf(Transformation.class), anyString(), eq(true));
234
235         verify(destinationSpy2).receive(same(source1));
236
237     }
238
239     @Test
240     public void testDestinationChooseFromTargetTypesThrowsException() {
241         destinationSpy = registerDestination(true, "any");
242         registerDocumentType("any");
243
244         doThrow(new RuntimeException()).when(destinationSpy)
245             .chooseFromTargetTypes((Collection<String>) anyObject());
246
247         router.publish("any", source1);
248         verify(listener).notDelivered(any(EventInfo.class));
249         verify(destinationSpy, never()).receive(same(source1));
250     }
251
252     @Test
253     public void testDestinationChooseFromTargetTypesThrowsExceptionSecondDestinationStillOk() {
254         testDestinationChooseFromTargetTypesThrowsException();
255
256         Destination destination2 = new MyDestination(true, Arrays.asList("any"));
257         Destination destinationSpy2 = spy(destination2);
258         Id<Destination> destinationId2 = router
259             .registerDestination(destinationSpy2);
260         router.publish("any", source1);
261         verify(listener).delivered(any(EventInfo.class),
262             anyListOf(Transformation.class), anyString(), eq(true));
263
264         verify(destinationSpy, never()).receive(same(source1));
265         verify(destinationSpy2).receive(same(source1));
266     }
267
268     @Test
269     public void testDestinationChooseFromTargetTypesReturnsNull() {
270         destinationSpy = registerDestination(true, "any");
271         registerDocumentType("any");
272
273         when(
274             destinationSpy
275                 .chooseFromTargetTypes((Collection<String>) anyObject()))
276             .thenReturn(null);
277
278         router.publish("any", source1);
279         verify(listener).notDelivered(any(EventInfo.class));
280         verify(destinationSpy, never()).receive(same(source1));
281     }
282
283     @Test
284     public void testDestinationChooseFromTargetTypesReturnsNullSecondDestinationStillOk() {
285         testDestinationChooseFromTargetTypesReturnsNull();
286
287         Destination destination2 = new MyDestination(true, Arrays.asList("any"));
288         Destination destinationSpy2 = spy(destination2);
289         Id<Destination> destinationId2 = router
290             .registerDestination(destinationSpy2);
291         router.publish("any", source1);
292         verify(listener).delivered(any(EventInfo.class),
293             anyListOf(Transformation.class), anyString(), eq(true));
294
295         verify(destinationSpy, never()).receive(same(source1));
296         verify(destinationSpy2).receive(same(source1));
297     }
298
299     @Test
300     public void testOneTransformationOneDestination() {
301         registerDocumentType("any");
302         Transformation transformation = mock(Transformation.class);
303         when(transformation.getId())
304             .thenReturn(new Id<Transformation>("trans"));
305         when(transformation.getFromType()).thenReturn("any");
306         when(transformation.getToType()).thenReturn("bla");
307         when(transformation.transform(same(source1))).thenReturn(source2);
308         routerConfig.transformationConfig().add(transformation);
309         config.setRouterConfig(routerConfig);
310
311         Destination destination = mock(Destination.class);
312         when(
313             destination.chooseFromTargetTypes((Collection<String>) anyObject()))
314             .thenReturn(Arrays.asList("bla"));
315
316         router.registerDestination(destination);
317
318         when(destination.receive(any(DOMSource.class))).thenReturn(true);
319         router.publish("bla", source1);
320         verify(listener).delivered(any(EventInfo.class),
321             anyListOf(Transformation.class), anyString(), eq(true));
322
323         verify(transformation).transform(source1);
324         verify(destination).receive(same(source2));
325
326         // now the same when the destination rejects the event.
327         when(destination.receive(any(DOMSource.class))).thenReturn(false);
328         router.publish("bla", source1);
329         verify(listener).notDelivered(any(EventInfo.class));
330     }
331
332     @Test
333     public void testOneTransformationOneDestinationFilterRejectsDestinationDocType() {
334         registerDocumentType("any");
335
336         Transformation transformation = mock(Transformation.class);
337         when(transformation.getId())
338             .thenReturn(new Id<Transformation>("trans"));
339         when(transformation.getFromType()).thenReturn("any");
340         when(transformation.getToType()).thenReturn("bla");
341         when(transformation.transform(same(source1))).thenReturn(source2);
342         routerConfig.transformationConfig().add(transformation);
343
344         Filter filter = mock(Filter.class);
345         when(filter.getId()).thenReturn(new Id<Filter>("f"));
346         when(filter.isAllowed(anyString(), same(source2))).thenReturn(false);
347         when(filter.isAllowed(anyString(), same(source1))).thenReturn(true);
348         routerConfig.filterConfig().add(filter);
349
350         config.setRouterConfig(routerConfig);
351
352         Destination destination = mock(Destination.class);
353         when(
354             destination.chooseFromTargetTypes((Collection<String>) anyObject()))
355             .thenReturn(Arrays.asList("bla"));
356
357         router.registerDestination(destination);
358
359         when(destination.receive(any(DOMSource.class))).thenReturn(true);
360         router.publish("bla", source1);
361         verify(listener).notDelivered(any(EventInfo.class));
362
363         verify(transformation).transform(source1);
364         verify(destination, never()).receive(any(DOMSource.class));
365     }
366
367     @Test
368     public void testMisbehavingTransformationOneDestination() {
369         registerDocumentType("any");
370         Transformation transformation = mock(Transformation.class);
371         when(transformation.getId())
372             .thenReturn(new Id<Transformation>("trans"));
373         when(transformation.getFromType()).thenReturn("any");
374         when(transformation.getToType()).thenReturn("bla");
375         doThrow(new RuntimeException("x")).when(transformation).transform(
376             same(source1));
377         routerConfig.transformationConfig().add(transformation);
378
379         Destination destination = mock(Destination.class);
380         when(
381             destination.chooseFromTargetTypes((Collection<String>) anyObject()))
382             .thenReturn(Arrays.asList("bla"));
383
384         router.registerDestination(destination);
385
386         when(destination.receive(any(DOMSource.class))).thenReturn(true);
387         router.publish("bla", source1);
388         verify(listener).notDelivered(any(EventInfo.class));
389     }
390
391     private Transformation createTransformation(String aFrom, String aTo,
392         DOMSource aSource, DOMSource aTarget) {
393         Transformation transformation = mock(Transformation.class);
394         when(transformation.getId()).thenReturn(
395             new Id<Transformation>(UUID.randomUUID().toString()));
396         when(transformation.getFromType()).thenReturn(aFrom);
397         when(transformation.getToType()).thenReturn(aTo);
398         when(transformation.transform(same(aSource))).thenReturn(aTarget);
399         return transformation;
400     }
401
402     @Test
403     public void testOneTransformationReturnsNull() {
404         registerDocumentType("any");
405         Transformation transformation = createTransformation("any", "bla",
406             source1, null);
407
408         routerConfig.transformationConfig().add(transformation);
409         config.setRouterConfig(routerConfig);
410         Destination destination = mock(Destination.class);
411         when(
412             destination.chooseFromTargetTypes((Collection<String>) anyObject()))
413             .thenReturn(Arrays.asList("bla"));
414         router.registerDestination(destination);
415
416         router.publish("bla", source1);
417         verify(listener).notDelivered(any(EventInfo.class));
418
419         verify(transformation).transform(source1);
420         verify(destination, never()).receive(any(DOMSource.class));
421
422         // add second transformation that behaves normally
423         Transformation transformation2 = createTransformation("any", "bla2",
424             source1, source2);
425
426         routerConfig.transformationConfig().add(transformation2);
427         config.setRouterConfig(routerConfig);
428         when(
429             destination.chooseFromTargetTypes((Collection<String>) anyObject()))
430             .thenReturn(Arrays.asList("bla", "bla2"));
431
432         reset(transformation);
433         when(transformation.getId())
434             .thenReturn(new Id<Transformation>("trans"));
435         when(transformation.getFromType()).thenReturn("any");
436         when(transformation.getToType()).thenReturn("bla");
437         when(transformation.transform(same(source1))).thenReturn(null);
438
439         when(destination.receive(any(DOMSource.class))).thenReturn(true);
440         router.publish("bla", source1);
441         verify(listener).delivered(any(EventInfo.class),
442             anyListOf(Transformation.class), anyString(), eq(true));
443
444         verify(transformation).transform(source1);
445         verify(transformation2).transform(source1);
446
447         verify(destination).receive(same(source2));
448
449     }
450
451     @Test
452     public void testChooseMultipleDestinationsOneType() {
453         Destination dest1 = registerDestination(true, "any");
454         Destination dest2 = registerDestination(true, "any");
455         registerDocumentType("any");
456
457         router.publish("source", source1);
458         verify(listener, times(2)).delivered(any(EventInfo.class),
459             anyListOf(Transformation.class), anyString(), eq(true));
460
461         verify(dest1).receive(same(source1));
462         verify(dest2).receive(same(source1));
463     }
464
465     @Test
466     public void testMultipleDeliveryToOneDestination() {
467         Destination dest = registerDestination(true, "any", "other");
468         registerDocumentType("any", source1);
469         registerDocumentType("other", source2);
470         Transformation transformation = createTransformation("any", "other",
471             source1, source2);
472         routerConfig.transformationConfig().add(transformation);
473         config.setRouterConfig(routerConfig);
474
475         router.publish("source", source1);
476         verify(listener, times(2)).delivered(any(EventInfo.class),
477             anyListOf(Transformation.class), anyString(), eq(true));
478
479         verify(dest).receive(same(source1));
480         verify(dest).receive(same(source2));
481     }
482
483     @Test
484     public void testMultipleTransformations() {
485         Destination dest = registerDestination(true, "other");
486         registerDocumentType("any", source1);
487         registerDocumentType("other", source3);
488
489         Transformation t1 = createTransformation("any", "intermediate",
490             source1, source2);
491         routerConfig.transformationConfig().add(t1);
492         Transformation t2 = createTransformation("intermediate", "other",
493             source2, source3);
494         routerConfig.transformationConfig().add(t2);
495         config.setRouterConfig(routerConfig);
496
497         router.publish("source", source1);
498         verify(listener).delivered(any(EventInfo.class),
499             anyListOf(Transformation.class), anyString(), eq(true));
500
501         verify(dest).receive(same(source3));
502     }
503
504     @Test
505     public void testMultipleTransformationsFilterRejectsIntermediateDocument() {
506         Destination dest = registerDestination(true, "other");
507         registerDocumentType("any", source1);
508         registerDocumentType("other", source3);
509
510         Transformation t1 = createTransformation("any", "intermediate",
511             source1, source2);
512         routerConfig.transformationConfig().add(t1);
513         Transformation t2 = createTransformation("intermediate", "other",
514             source2, source3);
515         routerConfig.transformationConfig().add(t2);
516         config.setRouterConfig(routerConfig);
517
518         Filter filter = mock(Filter.class);
519         when(filter.getId()).thenReturn(new Id<Filter>("f"));
520         when(filter.isAllowed(anyString(), same(source1))).thenReturn(true);
521         when(filter.isAllowed(anyString(), same(source2))).thenReturn(false);
522         when(filter.isAllowed(anyString(), same(source3))).thenReturn(true);
523         routerConfig.filterConfig().add(filter);
524
525         router.publish("source", source1);
526         verify(listener).notDelivered(any(EventInfo.class));
527         verify(dest, never()).receive(any(DOMSource.class));
528     }
529
530     @Test
531     public void testDestinationGivesError() {
532         Destination destination = mock(Destination.class);
533         when(destination.getName()).thenReturn("name");
534         when(destination.chooseFromTargetTypes(anyCollectionOf(String.class)))
535             .thenReturn(Arrays.asList("any"));
536         doThrow(new RuntimeException("x")).when(destination).receive(
537             any(DOMSource.class));
538         router.registerDestination(destination);
539
540         registerDocumentType("any");
541
542         router.publish("source", source1);
543
544         verify(listener).delivered(any(EventInfo.class),
545             anyListOf(Transformation.class), anyString(), eq(false));
546
547     }
548 }