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