Skip to content

Commit 0c8cd67

Browse files
author
Daan Hoogland
committed
tests
1 parent a4b0440 commit 0c8cd67

2 files changed

Lines changed: 172 additions & 3 deletions

File tree

server/src/main/java/com/cloud/storage/snapshot/SnapshotSchedulerImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ protected void scheduleSnapshots() {
324324
* skipped and rescheduled to its next regular run instead. Kept as a single decision point so the caller only
325325
* needs one {@code continue}.
326326
*/
327-
private boolean shouldSkipSchedule(final SnapshotScheduleVO snapshotToBeExecuted, final VolumeVO volume) {
327+
protected boolean shouldSkipSchedule(final SnapshotScheduleVO snapshotToBeExecuted, final VolumeVO volume) {
328328
if (!canSnapshotBeScheduled(snapshotToBeExecuted, volume)) {
329329
return true;
330330
}
@@ -339,7 +339,7 @@ private boolean shouldSkipSchedule(final SnapshotScheduleVO snapshotToBeExecuted
339339
* Builds and dispatches the CreateSnapshotCmd async job for a scheduled snapshot, returning the "scheduled"
340340
* action event id so the caller can complete it if dispatch subsequently fails.
341341
*/
342-
private Long dispatchSnapshotCreateJob(final SnapshotScheduleVO snapshotToBeExecuted, final VolumeVO volume, final SnapshotScheduleVO tmpSnapshotScheduleVO) throws Exception {
342+
protected Long dispatchSnapshotCreateJob(final SnapshotScheduleVO snapshotToBeExecuted, final VolumeVO volume, final SnapshotScheduleVO tmpSnapshotScheduleVO) throws Exception {
343343
final long snapshotScheId = snapshotToBeExecuted.getId();
344344
final long policyId = snapshotToBeExecuted.getPolicyId();
345345
final long volumeId = snapshotToBeExecuted.getVolumeId();

server/src/test/java/com/cloud/storage/snapshot/SnapshotSchedulerImplTest.java

Lines changed: 170 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@
1616
// under the License.
1717
package com.cloud.storage.snapshot;
1818

19+
import com.cloud.api.ApiDispatcher;
1920
import com.cloud.event.ActionEventUtils;
2021
import com.cloud.event.EventTypes;
2122
import com.cloud.event.EventVO;
2223
import com.cloud.event.dao.EventDao;
24+
import com.cloud.server.TaggedResourceService;
2325
import com.cloud.storage.Snapshot;
2426
import com.cloud.storage.SnapshotPolicyVO;
2527
import com.cloud.storage.SnapshotScheduleVO;
2628
import com.cloud.storage.SnapshotVO;
2729
import com.cloud.storage.VolumeVO;
30+
import com.cloud.storage.dao.SnapshotDao;
2831
import com.cloud.storage.dao.SnapshotPolicyDao;
2932
import com.cloud.storage.dao.SnapshotScheduleDao;
3033
import com.cloud.storage.dao.VolumeDao;
@@ -34,6 +37,8 @@
3437
import com.cloud.vm.VMInstanceVO;
3538
import com.cloud.vm.VirtualMachine;
3639
import com.cloud.vm.dao.VMInstanceDao;
40+
import org.apache.cloudstack.framework.jobs.AsyncJobDispatcher;
41+
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
3742
import org.apache.cloudstack.framework.jobs.dao.AsyncJobDao;
3843
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
3944
import org.apache.cloudstack.jobs.JobInfo;
@@ -53,7 +58,9 @@
5358
import java.util.Date;
5459
import java.util.List;
5560

61+
import static org.mockito.Mockito.doNothing;
5662
import static org.mockito.Mockito.doReturn;
63+
import static org.mockito.Mockito.doThrow;
5764
import static org.mockito.Mockito.mock;
5865
import static org.mockito.Mockito.never;
5966
import static org.mockito.Mockito.verify;
@@ -107,6 +114,20 @@ public class SnapshotSchedulerImplTest {
107114
@Mock
108115
private SnapshotVO snapshotVoMock;
109116

117+
@Mock
118+
private ApiDispatcher apiDispatcherMock;
119+
120+
@Mock
121+
private AsyncJobManager asyncJobManagerMock;
122+
123+
@Mock
124+
private TaggedResourceService taggedResourceServiceMock;
125+
126+
@Mock
127+
private AsyncJobDispatcher asyncJobDispatcherMock;
128+
129+
@Mock
130+
private SnapshotDao snapshotDaoMock;
110131

111132
@Test
112133
public void scheduleNextSnapshotJobTestParameterIsNullReturnNull() {
@@ -343,7 +364,7 @@ public void countConsecutiveFailedAttemptsTestNoEventsReturnsZero() {
343364
// --- getScopedConfigValue (#13454) ---
344365

345366
@Test
346-
public void getScopedConfigValueTestFallsBackToGlobalDefaultWhenNoDepotConfigured() {
367+
public void getScopedConfigValueTestFallsBackToGlobalDefaultWhenNoScopeOverrideConfigured() {
347368
doReturn(1L).when(volumeVoMock).getAccountId();
348369
doReturn(1L).when(volumeVoMock).getDataCenterId();
349370
doReturn(1L).when(accountVoMock).getDomainId();
@@ -430,6 +451,119 @@ public void recordSnapshotAttemptOutcomeTestAtMaxNotifiesWithDistinctEventType()
430451
}
431452
}
432453

454+
@Test
455+
public void recordSnapshotAttemptOutcomeTestSucceededLogsInfoEventAndReturns() {
456+
doReturn(volumeVoMock).when(volumeDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
457+
458+
try (MockedStatic<ActionEventUtils> actionEventUtilsMocked = Mockito.mockStatic(ActionEventUtils.class)) {
459+
snapshotSchedulerImplSpy.recordSnapshotAttemptOutcome(snapshotScheduleVoMock, true, null);
460+
461+
actionEventUtilsMocked.verify(() -> ActionEventUtils.onCreatedActionEvent(
462+
Mockito.anyLong(), Mockito.anyLong(), Mockito.eq(EventVO.LEVEL_INFO), Mockito.eq(EventTypes.EVENT_SNAPSHOT_CREATE),
463+
Mockito.anyBoolean(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyString()));
464+
}
465+
466+
verify(accountDaoMock, never()).findById(Mockito.anyLong());
467+
verify(eventDaoMock, never()).listLatestEventsByResource(Mockito.anyLong(), Mockito.anyString(), Mockito.anyString(), Mockito.anyInt());
468+
}
469+
470+
// --- dispatchSnapshotCreateJob (#13454) ---
471+
472+
@Test
473+
public void dispatchSnapshotCreateJobTestBuildsAsyncJobAndReturnsScheduledEventId() throws Exception {
474+
doReturn(1L).when(snapshotScheduleVoMock).getId();
475+
doReturn(1L).when(snapshotScheduleVoMock).getPolicyId();
476+
doReturn(2L).when(snapshotScheduleVoMock).getVolumeId();
477+
doReturn(new Date()).when(snapshotScheduleVoMock).getScheduledTimestamp();
478+
479+
doReturn(1L).when(volumeVoMock).getAccountId();
480+
doReturn("volume-uuid").when(volumeVoMock).getUuid();
481+
482+
doReturn(Collections.emptyList()).when(taggedResourceServiceMock).listByResourceTypeAndId(Mockito.any(), Mockito.anyLong());
483+
doReturn(7L).when(asyncJobManagerMock).submitAsyncJob(Mockito.any(AsyncJobVO.class));
484+
doReturn("SnapshotDispatcher").when(asyncJobDispatcherMock).getName();
485+
snapshotSchedulerImplSpy.setAsyncJobDispatcher(asyncJobDispatcherMock);
486+
487+
try (MockedStatic<ActionEventUtils> actionEventUtilsMocked = Mockito.mockStatic(ActionEventUtils.class);
488+
MockedStatic<com.cloud.utils.component.ComponentContext> componentContextMocked = Mockito.mockStatic(com.cloud.utils.component.ComponentContext.class)) {
489+
actionEventUtilsMocked.when(() -> ActionEventUtils.onScheduledActionEvent(
490+
Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyLong())
491+
).thenReturn(42L);
492+
493+
Long eventId = snapshotSchedulerImplSpy.dispatchSnapshotCreateJob(snapshotScheduleVoMock, volumeVoMock, snapshotScheduleVoMock);
494+
495+
Assert.assertEquals(Long.valueOf(42L), eventId);
496+
}
497+
498+
verify(snapshotScheduleVoMock).setAsyncJobId(7L);
499+
verify(snapshotScheduleDaoMock).update(1L, snapshotScheduleVoMock);
500+
}
501+
502+
// --- shouldSkipSchedule / scheduleSnapshots (#13454, #6827) ---
503+
504+
@Test
505+
public void shouldSkipScheduleTestCannotBeScheduledReturnsTrueWithoutCheckingUnchangedVolume() {
506+
doReturn(false).when(snapshotSchedulerImplSpy).canSnapshotBeScheduled(Mockito.any(), Mockito.any());
507+
508+
boolean result = snapshotSchedulerImplSpy.shouldSkipSchedule(snapshotScheduleVoMock, volumeVoMock);
509+
510+
Assert.assertTrue(result);
511+
verify(snapshotSchedulerImplSpy, never()).shouldSkipUnchangedVolumeSnapshot(Mockito.any());
512+
}
513+
514+
@Test
515+
public void shouldSkipScheduleTestUnchangedVolumeReschedulesAndReturnsTrue() {
516+
doReturn(true).when(snapshotSchedulerImplSpy).canSnapshotBeScheduled(Mockito.any(), Mockito.any());
517+
doReturn(true).when(snapshotSchedulerImplSpy).shouldSkipUnchangedVolumeSnapshot(Mockito.any());
518+
doNothing().when(snapshotSchedulerImplSpy).skipAndRescheduleSnapshot(Mockito.any(), Mockito.any());
519+
520+
boolean result = snapshotSchedulerImplSpy.shouldSkipSchedule(snapshotScheduleVoMock, volumeVoMock);
521+
522+
Assert.assertTrue(result);
523+
verify(snapshotSchedulerImplSpy).skipAndRescheduleSnapshot(snapshotScheduleVoMock, volumeVoMock);
524+
}
525+
526+
@Test
527+
public void shouldSkipScheduleTestCanBeScheduledAndNotUnchangedReturnsFalse() {
528+
doReturn(true).when(snapshotSchedulerImplSpy).canSnapshotBeScheduled(Mockito.any(), Mockito.any());
529+
doReturn(false).when(snapshotSchedulerImplSpy).shouldSkipUnchangedVolumeSnapshot(Mockito.any());
530+
531+
boolean result = snapshotSchedulerImplSpy.shouldSkipSchedule(snapshotScheduleVoMock, volumeVoMock);
532+
533+
Assert.assertFalse(result);
534+
verify(snapshotSchedulerImplSpy, never()).skipAndRescheduleSnapshot(Mockito.any(), Mockito.any());
535+
}
536+
537+
@Test
538+
public void scheduleSnapshotsTestSkippedScheduleIsNeverLockedOrDispatched() throws Exception {
539+
ReflectionTestUtils.setField(snapshotSchedulerImplSpy, "_currentTimestamp", new Date());
540+
doReturn(List.of(snapshotScheduleVoMock)).when(snapshotScheduleDaoMock).getSchedulesToExecute(Mockito.any(Date.class));
541+
doReturn(volumeVoMock).when(volumeDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
542+
doReturn(true).when(snapshotSchedulerImplSpy).shouldSkipSchedule(Mockito.any(), Mockito.any());
543+
544+
snapshotSchedulerImplSpy.scheduleSnapshots();
545+
546+
verify(snapshotScheduleDaoMock, never()).acquireInLockTable(Mockito.anyLong());
547+
verify(snapshotSchedulerImplSpy, never()).dispatchSnapshotCreateJob(Mockito.any(), Mockito.any(), Mockito.any());
548+
}
549+
550+
@Test
551+
public void scheduleSnapshotsTestDispatchFailureIsHandledAndLockIsReleased() throws Exception {
552+
ReflectionTestUtils.setField(snapshotSchedulerImplSpy, "_currentTimestamp", new Date());
553+
doReturn(1L).when(snapshotScheduleVoMock).getId();
554+
doReturn(List.of(snapshotScheduleVoMock)).when(snapshotScheduleDaoMock).getSchedulesToExecute(Mockito.any(Date.class));
555+
doReturn(volumeVoMock).when(volumeDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
556+
doReturn(false).when(snapshotSchedulerImplSpy).shouldSkipSchedule(Mockito.any(), Mockito.any());
557+
doReturn(snapshotScheduleVoMock).when(snapshotScheduleDaoMock).acquireInLockTable(1L);
558+
doThrow(new RuntimeException("dispatch boom")).when(snapshotSchedulerImplSpy).dispatchSnapshotCreateJob(Mockito.any(), Mockito.any(), Mockito.any());
559+
doNothing().when(snapshotSchedulerImplSpy).handleFailedSnapshotDispatch(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
560+
561+
snapshotSchedulerImplSpy.scheduleSnapshots();
562+
563+
verify(snapshotSchedulerImplSpy).handleFailedSnapshotDispatch(Mockito.eq(snapshotScheduleVoMock), Mockito.eq(volumeVoMock), Mockito.eq(snapshotScheduleVoMock), Mockito.isNull(), Mockito.any(Exception.class));
564+
verify(snapshotScheduleDaoMock).releaseFromLockTable(1L);
565+
}
566+
433567
// --- shouldSkipUnchangedVolumeSnapshot (#6827) ---
434568

435569
@Test
@@ -538,4 +672,39 @@ public void skipAndRescheduleSnapshotTestUpdatesScheduleAndLogsEvent() {
538672
verify(snapshotScheduleDaoMock).update(1L, snapshotScheduleVoMock);
539673
verify(snapshotScheduleDaoMock).releaseFromLockTable(1L);
540674
}
675+
676+
@Test
677+
public void skipAndRescheduleSnapshotTestCouldNotAcquireLockDoesNothing() {
678+
doReturn(1L).when(snapshotScheduleVoMock).getId();
679+
doReturn(null).when(snapshotScheduleDaoMock).acquireInLockTable(1L);
680+
681+
try (MockedStatic<ActionEventUtils> actionEventUtilsMocked = Mockito.mockStatic(ActionEventUtils.class)) {
682+
snapshotSchedulerImplSpy.skipAndRescheduleSnapshot(snapshotScheduleVoMock, volumeVoMock);
683+
684+
actionEventUtilsMocked.verifyNoInteractions();
685+
}
686+
687+
verify(snapshotScheduleDaoMock, never()).update(Mockito.anyLong(), Mockito.any());
688+
verify(snapshotScheduleDaoMock, never()).releaseFromLockTable(Mockito.anyLong());
689+
}
690+
691+
// --- findLastSnapshot ---
692+
693+
@Test
694+
public void findLastSnapshotTestReturnsMostRecentSnapshot() {
695+
doReturn(List.of(snapshotVoMock)).when(snapshotDaoMock).listByVolumeId(Mockito.any(), Mockito.eq(1L));
696+
697+
SnapshotVO result = snapshotSchedulerImplSpy.findLastSnapshot(1L);
698+
699+
Assert.assertSame(snapshotVoMock, result);
700+
}
701+
702+
@Test
703+
public void findLastSnapshotTestNoSnapshotsReturnsNull() {
704+
doReturn(Collections.emptyList()).when(snapshotDaoMock).listByVolumeId(Mockito.any(), Mockito.eq(1L));
705+
706+
SnapshotVO result = snapshotSchedulerImplSpy.findLastSnapshot(1L);
707+
708+
Assert.assertNull(result);
709+
}
541710
}

0 commit comments

Comments
 (0)