HONEYCOMB-170 Add more information to RevertFailedEx
[honeycomb.git] / infra / translate-impl / src / test / java / io / fd / honeycomb / translate / impl / write / registry / FlatWriterRegistryTest.java
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
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
17 package io.fd.honeycomb.translate.impl.write.registry;
18
19 import static org.hamcrest.CoreMatchers.hasItem;
20 import static org.hamcrest.CoreMatchers.is;
21 import static org.hamcrest.Matchers.hasSize;
22 import static org.junit.Assert.assertThat;
23 import static org.junit.Assert.fail;
24 import static org.mockito.Matchers.any;
25 import static org.mockito.Matchers.eq;
26 import static org.mockito.Mockito.doThrow;
27 import static org.mockito.Mockito.inOrder;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.verify;
30 import static org.mockito.Mockito.verifyNoMoreInteractions;
31 import static org.mockito.Mockito.verifyZeroInteractions;
32 import static org.mockito.Mockito.when;
33
34 import com.google.common.collect.HashMultimap;
35 import com.google.common.collect.ImmutableMap;
36 import com.google.common.collect.ImmutableMultimap;
37 import com.google.common.collect.Multimap;
38 import io.fd.honeycomb.translate.util.DataObjects;
39 import io.fd.honeycomb.translate.util.DataObjects.DataObject1;
40 import io.fd.honeycomb.translate.util.DataObjects.DataObject2;
41 import io.fd.honeycomb.translate.write.DataObjectUpdate;
42 import io.fd.honeycomb.translate.write.WriteContext;
43 import io.fd.honeycomb.translate.write.Writer;
44 import io.fd.honeycomb.translate.write.registry.WriterRegistry;
45 import org.junit.Before;
46 import org.junit.Test;
47 import org.mockito.InOrder;
48 import org.mockito.Mock;
49 import org.mockito.MockitoAnnotations;
50 import org.opendaylight.yangtools.yang.binding.DataObject;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52
53 public class FlatWriterRegistryTest {
54
55     @Mock
56     private Writer<DataObject1> writer1;
57     @Mock
58     private Writer<DataObject2> writer2;
59     @Mock
60     private Writer<DataObjects.DataObject3> writer3;
61     @Mock
62     private Writer<DataObjects.DataObject1ChildK> writer4;
63     @Mock
64     private WriteContext ctx;
65     @Mock
66     private WriteContext revertWriteContext;
67
68     @Before
69     public void setUp() throws Exception {
70         MockitoAnnotations.initMocks(this);
71         when(writer1.getManagedDataObjectType()).thenReturn(DataObject1.IID);
72         when(writer2.getManagedDataObjectType()).thenReturn(DataObject2.IID);
73         when(writer3.getManagedDataObjectType()).thenReturn(DataObjects.DataObject3.IID);
74     }
75
76     @Test
77     public void testMultipleUpdatesForSingleWriter() throws Exception {
78         final FlatWriterRegistry flatWriterRegistry =
79                 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
80
81         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
82         final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
83         final InstanceIdentifier<DataObject1> iid2 = InstanceIdentifier.create(DataObject1.class);
84         final DataObject1 dataObject = mock(DataObject1.class);
85         updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
86         updates.put(DataObject1.IID, DataObjectUpdate.create(iid2, dataObject, dataObject));
87         flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
88
89         verify(writer1).update(iid, dataObject, dataObject, ctx);
90         verify(writer1).update(iid2, dataObject, dataObject, ctx);
91         // Invoked when registry is being created
92         verifyNoMoreInteractions(writer1);
93         verifyZeroInteractions(writer2);
94     }
95
96     @Test
97     public void testMultipleUpdatesForMultipleWriters() throws Exception {
98         final FlatWriterRegistry flatWriterRegistry =
99                 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
100
101         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
102         final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
103         final DataObject1 dataObject = mock(DataObject1.class);
104         updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
105         final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
106         final DataObject2 dataObject2 = mock(DataObject2.class);
107         updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2));
108         flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
109
110         final InOrder inOrder = inOrder(writer1, writer2);
111         inOrder.verify(writer1).update(iid, dataObject, dataObject, ctx);
112         inOrder.verify(writer2).update(iid2, dataObject2, dataObject2, ctx);
113
114         verifyNoMoreInteractions(writer1);
115         verifyNoMoreInteractions(writer2);
116     }
117
118     @Test
119     public void testMultipleDeletesForMultipleWriters() throws Exception {
120         final FlatWriterRegistry flatWriterRegistry =
121                 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
122
123         final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create();
124         final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
125         final DataObject1 dataObject = mock(DataObject1.class);
126         deletes.put(DataObject1.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null)));
127         final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
128         final DataObject2 dataObject2 = mock(DataObject2.class);
129         deletes.put(
130                 DataObject2.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null)));
131         flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(ImmutableMultimap.of(), deletes), ctx);
132
133         final InOrder inOrder = inOrder(writer1, writer2);
134         // Reversed order of invocation, first writer2 and then writer1
135         inOrder.verify(writer2).update(iid2, dataObject2, null, ctx);
136         inOrder.verify(writer1).update(iid, dataObject, null, ctx);
137
138         verifyNoMoreInteractions(writer1);
139         verifyNoMoreInteractions(writer2);
140     }
141
142     @Test
143     public void testMultipleUpdatesAndDeletesForMultipleWriters() throws Exception {
144         final FlatWriterRegistry flatWriterRegistry =
145                 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
146
147         final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create();
148         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
149         final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
150         final DataObject1 dataObject = mock(DataObject1.class);
151         // Writer 1 delete
152         deletes.put(DataObject1.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null)));
153         // Writer 1 update
154         updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
155         final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
156         final DataObject2 dataObject2 = mock(DataObject2.class);
157         // Writer 2 delete
158         deletes.put(
159                 DataObject2.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null)));
160         // Writer 2 update
161         updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2));
162         flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, deletes), ctx);
163
164         final InOrder inOrder = inOrder(writer1, writer2);
165         // Reversed order of invocation, first writer2 and then writer1 for deletes
166         inOrder.verify(writer2).update(iid2, dataObject2, null, ctx);
167         inOrder.verify(writer1).update(iid, dataObject, null, ctx);
168         // Then also updates are processed
169         inOrder.verify(writer1).update(iid, dataObject, dataObject, ctx);
170         inOrder.verify(writer2).update(iid2, dataObject2, dataObject2, ctx);
171
172         verifyNoMoreInteractions(writer1);
173         verifyNoMoreInteractions(writer2);
174     }
175
176     @Test(expected = IllegalArgumentException.class)
177     public void testMultipleUpdatesOneMissing() throws Exception {
178         final FlatWriterRegistry flatWriterRegistry =
179                 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1));
180
181         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
182         addUpdate(updates, DataObject1.class);
183         addUpdate(updates, DataObject2.class);
184         flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
185     }
186
187     @Test
188     public void testMultipleUpdatesOneFailing() throws Exception {
189         final FlatWriterRegistry flatWriterRegistry =
190                 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
191
192         // Writer1 always fails
193         doThrow(new RuntimeException()).when(writer1)
194                 .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
195
196         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
197         addUpdate(updates, DataObject1.class);
198         addUpdate(updates, DataObject2.class);
199
200         try {
201             flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
202             fail("Bulk update should have failed on writer1");
203         } catch (WriterRegistry.BulkUpdateException e) {
204             assertThat(e.getUnrevertedSubtrees(), hasSize(2));
205             assertThat(e.getUnrevertedSubtrees(), hasItem(InstanceIdentifier.create(DataObject2.class)));
206             assertThat(e.getUnrevertedSubtrees(), hasItem(InstanceIdentifier.create(DataObject1.class)));
207         }
208     }
209
210     @Test
211     public void testMultipleUpdatesOneFailingThenRevertWithSuccess() throws Exception {
212         final FlatWriterRegistry flatWriterRegistry =
213                 new FlatWriterRegistry(
214                         ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2, DataObjects.DataObject3.IID, writer3));
215
216         // Writer1 always fails
217         doThrow(new RuntimeException()).when(writer3)
218                 .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
219
220         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
221         addUpdate(updates, DataObject1.class);
222         addUpdate(updates, DataObjects.DataObject3.class);
223         final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
224         final DataObject2 before2 = mock(DataObject2.class);
225         final DataObject2 after2 = mock(DataObject2.class);
226         updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, before2, after2));
227
228         try {
229             flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
230             fail("Bulk update should have failed on writer1");
231         } catch (WriterRegistry.BulkUpdateException e) {
232             assertThat(e.getUnrevertedSubtrees().size(), is(1));
233
234             final InOrder inOrder = inOrder(writer1, writer2, writer3);
235             inOrder.verify(writer1)
236                 .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
237             inOrder.verify(writer2)
238                 .update(iid2, before2, after2, ctx);
239             inOrder.verify(writer3)
240                 .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
241
242             e.revertChanges(revertWriteContext);
243             // Revert changes. Successful updates are iterated in reverse
244             // also binding other write context,to verify if update context is not reused
245             inOrder.verify(writer2)
246                     .update(iid2, after2, before2, revertWriteContext);
247             inOrder.verify(writer1)
248                     .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), eq(revertWriteContext));
249             verifyNoMoreInteractions(writer3);
250         }
251     }
252
253     @Test
254     public void testMultipleUpdatesOneFailingThenRevertWithFail() throws Exception {
255         final FlatWriterRegistry flatWriterRegistry =
256                 new FlatWriterRegistry(
257                         ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2, DataObjects.DataObject3.IID, writer3));
258
259         // Writer1 always fails
260         doThrow(new RuntimeException()).when(writer3)
261                 .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
262
263         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
264         addUpdate(updates, DataObject1.class);
265         addUpdate(updates, DataObject2.class);
266         addUpdate(updates, DataObjects.DataObject3.class);
267
268         try {
269             flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
270             fail("Bulk update should have failed on writer1");
271         } catch (WriterRegistry.BulkUpdateException e) {
272             // Writer1 always fails from now
273             doThrow(new RuntimeException()).when(writer1)
274                     .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
275             try {
276                 e.revertChanges(revertWriteContext);
277             } catch (WriterRegistry.Reverter.RevertFailedException e1) {
278                 assertThat(e1.getNotRevertedChanges().size(), is(1));
279                 assertThat(e1.getNotRevertedChanges(),
280                         hasItem(InstanceIdentifier.create(DataObject1.class)));
281             }
282         }
283     }
284
285     @Test
286     public void testMutlipleUpdatesWithOneKeyedContainer() throws Exception {
287         final InstanceIdentifier internallyKeyedIdentifier = InstanceIdentifier.create(DataObject1.class)
288                 .child(DataObjects.DataObject1ChildK.class, new DataObjects.DataObject1ChildKey());
289
290         final FlatWriterRegistry flatWriterRegistry =
291                 new FlatWriterRegistry(
292                         ImmutableMap.of(DataObject1.IID, writer1, DataObjects.DataObject1ChildK.IID,writer4));
293
294         // Writer1 always fails
295         doThrow(new RuntimeException()).when(writer1)
296                 .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class),
297                         any(WriteContext.class));
298
299         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
300         addKeyedUpdate(updates,DataObjects.DataObject1ChildK.class);
301         addUpdate(updates, DataObject1.class);
302         try {
303             flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
304             fail("Bulk update should have failed on writer1");
305         } catch (WriterRegistry.BulkUpdateException e) {
306             // Writer1 always fails from now
307             doThrow(new RuntimeException()).when(writer1)
308                     .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class),
309                             any(WriteContext.class));
310             try {
311                 e.revertChanges(revertWriteContext);
312             } catch (WriterRegistry.Reverter.RevertFailedException e1) {
313                 assertThat(e1.getNotRevertedChanges().size(), is(1));
314                 assertThat(e1.getNotRevertedChanges(),
315                         hasItem(InstanceIdentifier.create(DataObject1.class)));
316             }
317         }
318     }
319
320     private <D extends DataObject> void addKeyedUpdate(final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates,
321                                                        final Class<D> type) throws Exception {
322         final InstanceIdentifier<D> iid = (InstanceIdentifier<D>) type.getDeclaredField("IID").get(null);
323         final InstanceIdentifier<D> keyedIid = (InstanceIdentifier<D>) type.getDeclaredField("INTERNALLY_KEYED_IID").get(null);
324         updates.put(iid, DataObjectUpdate.create(keyedIid, mock(type), mock(type)));
325     }
326
327     private <D extends DataObject> void addUpdate(final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates,
328                            final Class<D> type) throws Exception {
329         final InstanceIdentifier<D> iid = (InstanceIdentifier<D>) type.getDeclaredField("IID").get(null);
330         updates.put(iid, DataObjectUpdate.create(iid, mock(type), mock(type)));
331     }
332 }