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