Fix issue when AE leader differs from prior install snapshot leader
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / test / java / org / opendaylight / controller / cluster / raft / SnapshotManagerTest.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.controller.cluster.raft;
10
11 import static org.junit.Assert.assertArrayEquals;
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertFalse;
14 import static org.junit.Assert.assertTrue;
15 import static org.mockito.Matchers.any;
16 import static org.mockito.Matchers.anyLong;
17 import static org.mockito.Mockito.doReturn;
18 import static org.mockito.Mockito.doThrow;
19 import static org.mockito.Mockito.mock;
20 import static org.mockito.Mockito.never;
21 import static org.mockito.Mockito.reset;
22 import static org.mockito.Mockito.times;
23 import static org.mockito.Mockito.verify;
24 import akka.actor.ActorRef;
25 import akka.persistence.SnapshotSelectionCriteria;
26 import akka.testkit.TestActorRef;
27 import java.util.Arrays;
28 import org.junit.After;
29 import org.junit.Before;
30 import org.junit.Test;
31 import org.mockito.ArgumentCaptor;
32 import org.mockito.Mock;
33 import org.mockito.MockitoAnnotations;
34 import org.opendaylight.controller.cluster.DataPersistenceProvider;
35 import org.opendaylight.controller.cluster.raft.SnapshotManager.LastAppliedTermInformationReader;
36 import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot;
37 import org.opendaylight.controller.cluster.raft.base.messages.SendInstallSnapshot;
38 import org.opendaylight.controller.cluster.raft.base.messages.SnapshotComplete;
39 import org.opendaylight.controller.cluster.raft.behaviors.RaftActorBehavior;
40 import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
41 import org.slf4j.LoggerFactory;
42
43 public class SnapshotManagerTest extends AbstractActorTest {
44
45     @Mock
46     private RaftActorContext mockRaftActorContext;
47
48     @Mock
49     private ConfigParams mockConfigParams;
50
51     @Mock
52     private ReplicatedLog mockReplicatedLog;
53
54     @Mock
55     private DataPersistenceProvider mockDataPersistenceProvider;
56
57     @Mock
58     private RaftActorBehavior mockRaftActorBehavior;
59
60     @Mock
61     private Runnable mockProcedure;
62
63     @Mock
64     private ElectionTerm mockElectionTerm;
65
66     private SnapshotManager snapshotManager;
67
68     private TestActorFactory factory;
69
70     private TestActorRef<MessageCollectorActor> actorRef;
71
72     @Before
73     public void setUp(){
74         MockitoAnnotations.initMocks(this);
75
76         doReturn(false).when(mockRaftActorContext).hasFollowers();
77         doReturn(mockConfigParams).when(mockRaftActorContext).getConfigParams();
78         doReturn(10L).when(mockConfigParams).getSnapshotBatchCount();
79         doReturn(70).when(mockConfigParams).getSnapshotDataThresholdPercentage();
80         doReturn(mockReplicatedLog).when(mockRaftActorContext).getReplicatedLog();
81         doReturn("123").when(mockRaftActorContext).getId();
82         doReturn(mockDataPersistenceProvider).when(mockRaftActorContext).getPersistenceProvider();
83         doReturn(mockRaftActorBehavior).when(mockRaftActorContext).getCurrentBehavior();
84         doReturn("123").when(mockRaftActorBehavior).getLeaderId();
85
86         doReturn(mockElectionTerm).when(mockRaftActorContext).getTermInformation();
87         doReturn(5L).when(mockElectionTerm).getCurrentTerm();
88         doReturn("member5").when(mockElectionTerm).getVotedFor();
89
90         snapshotManager = new SnapshotManager(mockRaftActorContext, LoggerFactory.getLogger(this.getClass()));
91         factory = new TestActorFactory(getSystem());
92
93         actorRef = factory.createTestActor(MessageCollectorActor.props(), factory.generateActorId("test-"));
94         doReturn(actorRef).when(mockRaftActorContext).getActor();
95
96         snapshotManager.setCreateSnapshotRunnable(mockProcedure);
97     }
98
99     @After
100     public void tearDown(){
101         factory.close();
102     }
103
104     @Test
105     public void testConstruction(){
106         assertEquals(false, snapshotManager.isCapturing());
107     }
108
109     @Test
110     public void testCaptureToInstall() throws Exception {
111
112         // Force capturing toInstall = true
113         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(1, 0,
114                 new MockRaftActorContext.MockPayload()), 0, "follower-1");
115
116         assertEquals(true, snapshotManager.isCapturing());
117
118         verify(mockProcedure).run();
119
120         CaptureSnapshot captureSnapshot = snapshotManager.getCaptureSnapshot();
121
122         // LastIndex and LastTerm are picked up from the lastLogEntry
123         assertEquals(0L, captureSnapshot.getLastIndex());
124         assertEquals(1L, captureSnapshot.getLastTerm());
125
126         // Since the actor does not have any followers (no peer addresses) lastApplied will be from lastLogEntry
127         assertEquals(0L, captureSnapshot.getLastAppliedIndex());
128         assertEquals(1L, captureSnapshot.getLastAppliedTerm());
129
130         //
131         assertEquals(-1L, captureSnapshot.getReplicatedToAllIndex());
132         assertEquals(-1L, captureSnapshot.getReplicatedToAllTerm());
133         actorRef.underlyingActor().clear();
134     }
135
136     @Test
137     public void testCapture() throws Exception {
138         boolean capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
139                 new MockRaftActorContext.MockPayload()), 9);
140
141         assertTrue(capture);
142
143         assertEquals(true, snapshotManager.isCapturing());
144
145         verify(mockProcedure).run();
146
147         CaptureSnapshot captureSnapshot = snapshotManager.getCaptureSnapshot();
148
149         // LastIndex and LastTerm are picked up from the lastLogEntry
150         assertEquals(9L, captureSnapshot.getLastIndex());
151         assertEquals(1L, captureSnapshot.getLastTerm());
152
153         // Since the actor does not have any followers (no peer addresses) lastApplied will be from lastLogEntry
154         assertEquals(9L, captureSnapshot.getLastAppliedIndex());
155         assertEquals(1L, captureSnapshot.getLastAppliedTerm());
156
157         //
158         assertEquals(-1L, captureSnapshot.getReplicatedToAllIndex());
159         assertEquals(-1L, captureSnapshot.getReplicatedToAllTerm());
160
161         actorRef.underlyingActor().clear();
162
163     }
164
165     @Test
166     public void testCaptureWithNullLastLogEntry() throws Exception {
167         boolean capture = snapshotManager.capture(null, 1);
168
169         assertTrue(capture);
170
171         assertEquals(true, snapshotManager.isCapturing());
172
173         verify(mockProcedure).run();
174
175         CaptureSnapshot captureSnapshot = snapshotManager.getCaptureSnapshot();
176
177         System.out.println(captureSnapshot);
178
179         // LastIndex and LastTerm are picked up from the lastLogEntry
180         assertEquals(-1L, captureSnapshot.getLastIndex());
181         assertEquals(-1L, captureSnapshot.getLastTerm());
182
183         // Since the actor does not have any followers (no peer addresses) lastApplied will be from lastLogEntry
184         assertEquals(-1L, captureSnapshot.getLastAppliedIndex());
185         assertEquals(-1L, captureSnapshot.getLastAppliedTerm());
186
187         //
188         assertEquals(-1L, captureSnapshot.getReplicatedToAllIndex());
189         assertEquals(-1L, captureSnapshot.getReplicatedToAllTerm());
190         actorRef.underlyingActor().clear();
191
192     }
193
194     @Test
195     public void testCaptureWithCreateProcedureError () throws Exception {
196         doThrow(new RuntimeException("mock")).when(mockProcedure).run();
197
198         boolean capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
199                 new MockRaftActorContext.MockPayload()), 9);
200
201         assertFalse(capture);
202
203         assertEquals(false, snapshotManager.isCapturing());
204
205         verify(mockProcedure).run();
206     }
207
208     @Test
209     public void testIllegalCapture() throws Exception {
210         boolean capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
211                 new MockRaftActorContext.MockPayload()), 9);
212
213         assertTrue(capture);
214
215         verify(mockProcedure).run();
216
217         reset(mockProcedure);
218
219         // This will not cause snapshot capture to start again
220         capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
221                 new MockRaftActorContext.MockPayload()), 9);
222
223         assertFalse(capture);
224
225         verify(mockProcedure, never()).run();
226     }
227
228     @Test
229     public void testPersistWhenReplicatedToAllIndexMinusOne(){
230         doReturn(7L).when(mockReplicatedLog).getSnapshotIndex();
231         doReturn(1L).when(mockReplicatedLog).getSnapshotTerm();
232
233         doReturn(true).when(mockRaftActorContext).hasFollowers();
234
235         doReturn(8L).when(mockRaftActorContext).getLastApplied();
236
237         MockRaftActorContext.MockReplicatedLogEntry lastLogEntry = new MockRaftActorContext.MockReplicatedLogEntry(
238                 3L, 9L, new MockRaftActorContext.MockPayload());
239
240         MockRaftActorContext.MockReplicatedLogEntry lastAppliedEntry = new MockRaftActorContext.MockReplicatedLogEntry(
241                 2L, 8L, new MockRaftActorContext.MockPayload());
242
243         doReturn(lastAppliedEntry).when(mockReplicatedLog).get(8L);
244         doReturn(Arrays.asList(lastLogEntry)).when(mockReplicatedLog).getFrom(9L);
245
246         // when replicatedToAllIndex = -1
247         snapshotManager.capture(lastLogEntry, -1);
248
249         byte[] bytes = new byte[] {1,2,3,4,5,6,7,8,9,10};
250         snapshotManager.persist(bytes, Runtime.getRuntime().totalMemory());
251
252         ArgumentCaptor<Snapshot> snapshotArgumentCaptor = ArgumentCaptor.forClass(Snapshot.class);
253         verify(mockDataPersistenceProvider).saveSnapshot(snapshotArgumentCaptor.capture());
254
255         Snapshot snapshot = snapshotArgumentCaptor.getValue();
256
257         assertEquals("getLastTerm", 3L, snapshot.getLastTerm());
258         assertEquals("getLastIndex", 9L, snapshot.getLastIndex());
259         assertEquals("getLastAppliedTerm", 2L, snapshot.getLastAppliedTerm());
260         assertEquals("getLastAppliedIndex", 8L, snapshot.getLastAppliedIndex());
261         assertArrayEquals("getState", bytes, snapshot.getState());
262         assertEquals("getUnAppliedEntries", Arrays.asList(lastLogEntry), snapshot.getUnAppliedEntries());
263         assertEquals("electionTerm", mockElectionTerm.getCurrentTerm(), snapshot.getElectionTerm());
264         assertEquals("electionVotedFor", mockElectionTerm.getVotedFor(), snapshot.getElectionVotedFor());
265
266         verify(mockReplicatedLog).snapshotPreCommit(7L, 1L);
267     }
268
269     @Test
270     public void testPersistWhenReplicatedToAllIndexNotMinus(){
271         doReturn(45L).when(mockReplicatedLog).getSnapshotIndex();
272         doReturn(6L).when(mockReplicatedLog).getSnapshotTerm();
273         ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class);
274         doReturn(replicatedLogEntry).when(mockReplicatedLog).get(9);
275         doReturn(6L).when(replicatedLogEntry).getTerm();
276         doReturn(9L).when(replicatedLogEntry).getIndex();
277
278         // when replicatedToAllIndex != -1
279         snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9,
280                 new MockRaftActorContext.MockPayload()), 9);
281
282         byte[] bytes = new byte[] {1,2,3,4,5,6,7,8,9,10};
283         snapshotManager.persist(bytes, Runtime.getRuntime().totalMemory());
284
285         ArgumentCaptor<Snapshot> snapshotArgumentCaptor = ArgumentCaptor.forClass(Snapshot.class);
286         verify(mockDataPersistenceProvider).saveSnapshot(snapshotArgumentCaptor.capture());
287
288         Snapshot snapshot = snapshotArgumentCaptor.getValue();
289
290         assertEquals("getLastTerm", 6L, snapshot.getLastTerm());
291         assertEquals("getLastIndex", 9L, snapshot.getLastIndex());
292         assertEquals("getLastAppliedTerm", 6L, snapshot.getLastAppliedTerm());
293         assertEquals("getLastAppliedIndex", 9L, snapshot.getLastAppliedIndex());
294         assertArrayEquals("getState", bytes, snapshot.getState());
295         assertEquals("getUnAppliedEntries size", 0, snapshot.getUnAppliedEntries().size());
296
297         verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
298
299         verify(mockRaftActorBehavior).setReplicatedToAllIndex(9);
300     }
301
302     @Test
303     public void testPersistWhenReplicatedLogDataSizeGreaterThanThreshold(){
304         doReturn(Integer.MAX_VALUE).when(mockReplicatedLog).dataSize();
305
306         // when replicatedToAllIndex = -1
307         snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6,9,
308                 new MockRaftActorContext.MockPayload()), -1);
309
310         snapshotManager.persist(new byte[]{}, Runtime.getRuntime().totalMemory());
311
312         verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
313
314         verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
315
316         verify(mockRaftActorBehavior, never()).setReplicatedToAllIndex(anyLong());
317     }
318
319     @Test
320     public void testPersistWhenReplicatedLogSizeExceedsSnapshotBatchCount() {
321         doReturn(10L).when(mockReplicatedLog).size(); // matches snapshotBatchCount
322         doReturn(100).when(mockReplicatedLog).dataSize();
323
324         doReturn(5L).when(mockReplicatedLog).getSnapshotIndex();
325         doReturn(5L).when(mockReplicatedLog).getSnapshotTerm();
326
327         long replicatedToAllIndex = 1;
328         ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class);
329         doReturn(replicatedLogEntry).when(mockReplicatedLog).get(replicatedToAllIndex);
330         doReturn(6L).when(replicatedLogEntry).getTerm();
331         doReturn(replicatedToAllIndex).when(replicatedLogEntry).getIndex();
332
333         snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
334                 new MockRaftActorContext.MockPayload()), replicatedToAllIndex);
335
336         snapshotManager.persist(new byte[]{}, 2000000L);
337
338         verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
339
340         verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
341
342         verify(mockRaftActorBehavior).setReplicatedToAllIndex(replicatedToAllIndex);
343     }
344
345     @Test
346     public void testPersistSendInstallSnapshot(){
347         doReturn(Integer.MAX_VALUE).when(mockReplicatedLog).dataSize();
348
349         // when replicatedToAllIndex = -1
350         boolean capture = snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
351                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
352
353         assertTrue(capture);
354
355         byte[] bytes = new byte[] {1,2,3,4,5,6,7,8,9,10};
356
357         snapshotManager.persist(bytes, Runtime.getRuntime().totalMemory());
358
359         assertEquals(true, snapshotManager.isCapturing());
360
361         verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
362
363         verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
364
365         ArgumentCaptor<SendInstallSnapshot> sendInstallSnapshotArgumentCaptor
366                 = ArgumentCaptor.forClass(SendInstallSnapshot.class);
367
368         verify(mockRaftActorBehavior).handleMessage(any(ActorRef.class), sendInstallSnapshotArgumentCaptor.capture());
369
370         SendInstallSnapshot sendInstallSnapshot = sendInstallSnapshotArgumentCaptor.getValue();
371
372         assertTrue(Arrays.equals(bytes, sendInstallSnapshot.getSnapshot().getState()));
373     }
374
375     @Test
376     public void testCallingPersistWithoutCaptureWillDoNothing(){
377         snapshotManager.persist(new byte[]{}, Runtime.getRuntime().totalMemory());
378
379         verify(mockDataPersistenceProvider, never()).saveSnapshot(any(Snapshot.class));
380
381         verify(mockReplicatedLog, never()).snapshotPreCommit(9L, 6L);
382
383         verify(mockRaftActorBehavior, never()).handleMessage(any(ActorRef.class), any(SendInstallSnapshot.class));
384     }
385     @Test
386     public void testCallingPersistTwiceWillDoNoHarm(){
387         doReturn(Integer.MAX_VALUE).when(mockReplicatedLog).dataSize();
388
389         // when replicatedToAllIndex = -1
390         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
391                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
392
393         snapshotManager.persist(new byte[]{}, Runtime.getRuntime().totalMemory());
394
395         snapshotManager.persist(new byte[]{}, Runtime.getRuntime().totalMemory());
396
397         verify(mockDataPersistenceProvider).saveSnapshot(any(Snapshot.class));
398
399         verify(mockReplicatedLog).snapshotPreCommit(9L, 6L);
400
401         verify(mockRaftActorBehavior).handleMessage(any(ActorRef.class), any(SendInstallSnapshot.class));
402     }
403
404     @Test
405     public void testCommit(){
406         doReturn(50L).when(mockDataPersistenceProvider).getLastSequenceNumber();
407
408         // when replicatedToAllIndex = -1
409         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
410                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
411
412         snapshotManager.persist(new byte[]{}, Runtime.getRuntime().totalMemory());
413
414         assertEquals(true, snapshotManager.isCapturing());
415
416         snapshotManager.commit(100L, 1234L);
417
418         assertEquals(false, snapshotManager.isCapturing());
419
420         verify(mockReplicatedLog).snapshotCommit();
421
422         verify(mockDataPersistenceProvider).deleteMessages(50L);
423
424         ArgumentCaptor<SnapshotSelectionCriteria> criteriaCaptor = ArgumentCaptor.forClass(SnapshotSelectionCriteria.class);
425
426         verify(mockDataPersistenceProvider).deleteSnapshots(criteriaCaptor.capture());
427
428         assertEquals(100L, criteriaCaptor.getValue().maxSequenceNr());
429         assertEquals(1233L, criteriaCaptor.getValue().maxTimestamp());
430
431         MessageCollectorActor.expectFirstMatching(actorRef, SnapshotComplete.class);
432     }
433
434     @Test
435     public void testCommitBeforePersist(){
436         // when replicatedToAllIndex = -1
437         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
438                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
439
440         snapshotManager.commit(100L, 0);
441
442         verify(mockReplicatedLog, never()).snapshotCommit();
443
444         verify(mockDataPersistenceProvider, never()).deleteMessages(100L);
445
446         verify(mockDataPersistenceProvider, never()).deleteSnapshots(any(SnapshotSelectionCriteria.class));
447
448     }
449
450     @Test
451     public void testCommitBeforeCapture(){
452         snapshotManager.commit(100L, 0);
453
454         verify(mockReplicatedLog, never()).snapshotCommit();
455
456         verify(mockDataPersistenceProvider, never()).deleteMessages(anyLong());
457
458         verify(mockDataPersistenceProvider, never()).deleteSnapshots(any(SnapshotSelectionCriteria.class));
459
460     }
461
462     @Test
463     public void testCallingCommitMultipleTimesCausesNoHarm(){
464         doReturn(50L).when(mockDataPersistenceProvider).getLastSequenceNumber();
465
466         // when replicatedToAllIndex = -1
467         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
468                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
469
470         snapshotManager.persist(new byte[]{}, Runtime.getRuntime().totalMemory());
471
472         snapshotManager.commit(100L, 0);
473
474         snapshotManager.commit(100L, 0);
475
476         verify(mockReplicatedLog, times(1)).snapshotCommit();
477
478         verify(mockDataPersistenceProvider, times(1)).deleteMessages(50L);
479
480         verify(mockDataPersistenceProvider, times(1)).deleteSnapshots(any(SnapshotSelectionCriteria.class));
481     }
482
483     @Test
484     public void testRollback(){
485         // when replicatedToAllIndex = -1
486         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
487                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
488
489         snapshotManager.persist(new byte[]{}, Runtime.getRuntime().totalMemory());
490
491         snapshotManager.rollback();
492
493         verify(mockReplicatedLog).snapshotRollback();
494
495         MessageCollectorActor.expectFirstMatching(actorRef, SnapshotComplete.class);
496     }
497
498
499     @Test
500     public void testRollbackBeforePersist(){
501         // when replicatedToAllIndex = -1
502         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
503                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
504
505         snapshotManager.rollback();
506
507         verify(mockReplicatedLog, never()).snapshotRollback();
508     }
509
510     @Test
511     public void testRollbackBeforeCapture(){
512         snapshotManager.rollback();
513
514         verify(mockReplicatedLog, never()).snapshotRollback();
515     }
516
517     @Test
518     public void testCallingRollbackMultipleTimesCausesNoHarm(){
519         // when replicatedToAllIndex = -1
520         snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(6, 9,
521                 new MockRaftActorContext.MockPayload()), -1, "follower-1");
522
523         snapshotManager.persist(new byte[]{}, Runtime.getRuntime().totalMemory());
524
525         snapshotManager.rollback();
526
527         snapshotManager.rollback();
528
529         verify(mockReplicatedLog, times(1)).snapshotRollback();
530     }
531
532     @Test
533     public void testTrimLogWhenTrimIndexLessThanLastApplied() {
534         doReturn(20L).when(mockRaftActorContext).getLastApplied();
535
536         ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class);
537         doReturn(true).when(mockReplicatedLog).isPresent(10);
538         doReturn(replicatedLogEntry).when((mockReplicatedLog)).get(10);
539         doReturn(5L).when(replicatedLogEntry).getTerm();
540
541         long retIndex = snapshotManager.trimLog(10);
542         assertEquals("return index", 10L, retIndex);
543
544         verify(mockReplicatedLog).snapshotPreCommit(10, 5);
545         verify(mockReplicatedLog).snapshotCommit();
546
547         verify(mockRaftActorBehavior, never()).setReplicatedToAllIndex(anyLong());
548     }
549
550     @Test
551     public void testTrimLogWhenLastAppliedNotSet() {
552         doReturn(-1L).when(mockRaftActorContext).getLastApplied();
553
554         ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class);
555         doReturn(true).when(mockReplicatedLog).isPresent(10);
556         doReturn(replicatedLogEntry).when((mockReplicatedLog)).get(10);
557         doReturn(5L).when(replicatedLogEntry).getTerm();
558
559         long retIndex = snapshotManager.trimLog(10);
560         assertEquals("return index", -1L, retIndex);
561
562         verify(mockReplicatedLog, never()).snapshotPreCommit(anyLong(), anyLong());
563         verify(mockReplicatedLog, never()).snapshotCommit();
564
565         verify(mockRaftActorBehavior, never()).setReplicatedToAllIndex(anyLong());
566     }
567
568     @Test
569     public void testTrimLogWhenLastAppliedZero() {
570         doReturn(0L).when(mockRaftActorContext).getLastApplied();
571
572         ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class);
573         doReturn(true).when(mockReplicatedLog).isPresent(10);
574         doReturn(replicatedLogEntry).when((mockReplicatedLog)).get(10);
575         doReturn(5L).when(replicatedLogEntry).getTerm();
576
577         long retIndex = snapshotManager.trimLog(10);
578         assertEquals("return index", -1L, retIndex);
579
580         verify(mockReplicatedLog, never()).snapshotPreCommit(anyLong(), anyLong());
581         verify(mockReplicatedLog, never()).snapshotCommit();
582
583         verify(mockRaftActorBehavior, never()).setReplicatedToAllIndex(anyLong());
584     }
585
586     @Test
587     public void testTrimLogWhenTrimIndexNotPresent() {
588         doReturn(20L).when(mockRaftActorContext).getLastApplied();
589
590         doReturn(false).when(mockReplicatedLog).isPresent(10);
591
592         long retIndex = snapshotManager.trimLog(10);
593         assertEquals("return index", -1L, retIndex);
594
595         verify(mockReplicatedLog, never()).snapshotPreCommit(anyLong(), anyLong());
596         verify(mockReplicatedLog, never()).snapshotCommit();
597
598         // Trim index is greater than replicatedToAllIndex so should update it.
599         verify(mockRaftActorBehavior).setReplicatedToAllIndex(10L);
600     }
601
602     @Test
603     public void testTrimLogAfterCapture(){
604         boolean capture = snapshotManager.capture(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
605                 new MockRaftActorContext.MockPayload()), 9);
606
607         assertTrue(capture);
608
609         assertEquals(true, snapshotManager.isCapturing());
610
611         ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class);
612         doReturn(20L).when(mockRaftActorContext).getLastApplied();
613         doReturn(true).when(mockReplicatedLog).isPresent(10);
614         doReturn(replicatedLogEntry).when((mockReplicatedLog)).get(10);
615         doReturn(5L).when(replicatedLogEntry).getTerm();
616
617         snapshotManager.trimLog(10);
618
619         verify(mockReplicatedLog, never()).snapshotPreCommit(anyLong(), anyLong());
620         verify(mockReplicatedLog, never()).snapshotCommit();
621
622     }
623
624     @Test
625     public void testTrimLogAfterCaptureToInstall(){
626         boolean capture = snapshotManager.captureToInstall(new MockRaftActorContext.MockReplicatedLogEntry(1,9,
627                 new MockRaftActorContext.MockPayload()), 9, "follower-1");
628
629         assertTrue(capture);
630
631         assertEquals(true, snapshotManager.isCapturing());
632
633         ReplicatedLogEntry replicatedLogEntry = mock(ReplicatedLogEntry.class);
634         doReturn(20L).when(mockRaftActorContext).getLastApplied();
635         doReturn(true).when(mockReplicatedLog).isPresent(10);
636         doReturn(replicatedLogEntry).when((mockReplicatedLog)).get(10);
637         doReturn(5L).when(replicatedLogEntry).getTerm();
638
639         snapshotManager.trimLog(10);
640
641         verify(mockReplicatedLog, never()).snapshotPreCommit(10, 5);
642         verify(mockReplicatedLog, never()).snapshotCommit();
643
644     }
645
646     @Test
647     public void testLastAppliedTermInformationReader() {
648
649         LastAppliedTermInformationReader reader = new LastAppliedTermInformationReader();
650
651         doReturn(4L).when(mockReplicatedLog).getSnapshotTerm();
652         doReturn(7L).when(mockReplicatedLog).getSnapshotIndex();
653
654         ReplicatedLogEntry lastLogEntry = new MockRaftActorContext.MockReplicatedLogEntry(6L, 9L,
655                 new MockRaftActorContext.MockPayload());
656
657         // No followers and valid lastLogEntry
658         reader.init(mockReplicatedLog, 1L, lastLogEntry, false);
659
660         assertEquals("getTerm", 6L, reader.getTerm());
661         assertEquals("getIndex", 9L, reader.getIndex());
662
663         // No followers and null lastLogEntry
664         reader.init(mockReplicatedLog, 1L, null, false);
665
666         assertEquals("getTerm", -1L, reader.getTerm());
667         assertEquals("getIndex", -1L, reader.getIndex());
668
669         // Followers and valid originalIndex entry
670         doReturn(new MockRaftActorContext.MockReplicatedLogEntry(5L, 8L,
671                 new MockRaftActorContext.MockPayload())).when(mockReplicatedLog).get(8L);
672         reader.init(mockReplicatedLog, 8L, lastLogEntry, true);
673
674         assertEquals("getTerm", 5L, reader.getTerm());
675         assertEquals("getIndex", 8L, reader.getIndex());
676
677         // Followers and null originalIndex entry and valid snapshot index
678         reader.init(mockReplicatedLog, 7L, lastLogEntry, true);
679
680         assertEquals("getTerm", 4L, reader.getTerm());
681         assertEquals("getIndex", 7L, reader.getIndex());
682
683         // Followers and null originalIndex entry and invalid snapshot index
684         doReturn(-1L).when(mockReplicatedLog).getSnapshotIndex();
685         reader.init(mockReplicatedLog, 7L, lastLogEntry, true);
686
687         assertEquals("getTerm", -1L, reader.getTerm());
688         assertEquals("getIndex", -1L, reader.getIndex());
689     }
690 }