server: fix snapshot physical size (#10216)

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
Co-authored-by: Wei Zhou <weizhou@apache.org>
This commit is contained in:
Abhishek Kumar 2025-02-04 15:54:41 +05:30 committed by GitHub
parent 238d0c5e30
commit 37c29f82ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 171 additions and 54 deletions

View File

@ -16,6 +16,8 @@
// under the License. // under the License.
package com.cloud.api.query; package com.cloud.api.query;
import static com.cloud.vm.VmDetailConstants.SSH_PUBLIC_KEY;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
@ -34,39 +36,6 @@ import java.util.stream.Stream;
import javax.inject.Inject; import javax.inject.Inject;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolHostVO;
import com.cloud.event.EventVO;
import com.cloud.event.dao.EventDao;
import com.cloud.host.HostVO;
import com.cloud.offering.ServiceOffering;
import com.cloud.service.ServiceOfferingDetailsVO;
import com.cloud.storage.VMTemplateStoragePoolVO;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.storage.dao.VMTemplatePoolDao;
import com.cloud.host.Host;
import com.cloud.host.dao.HostDao;
import com.cloud.network.as.AutoScaleVmGroupVmMapVO;
import com.cloud.network.as.dao.AutoScaleVmGroupDao;
import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.dao.PublicIpQuarantineDao;
import com.cloud.network.PublicIpQuarantine;
import com.cloud.network.vo.PublicIpQuarantineVO;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.user.AccountVO;
import com.cloud.user.SSHKeyPairVO;
import com.cloud.user.dao.SSHKeyPairDao;
import com.cloud.vm.InstanceGroupVMMapVO;
import com.cloud.vm.NicVO;
import com.cloud.vm.UserVmDetailVO;
import com.cloud.vm.dao.InstanceGroupVMMapDao;
import com.cloud.vm.dao.NicDao;
import com.cloud.vm.dao.UserVmDetailsDao;
import com.cloud.storage.VolumeVO;
import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.acl.SecurityChecker;
@ -108,6 +77,7 @@ import org.apache.cloudstack.api.command.user.account.ListAccountsCmd;
import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd; import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd;
import org.apache.cloudstack.api.command.user.address.ListQuarantinedIpsCmd; import org.apache.cloudstack.api.command.user.address.ListQuarantinedIpsCmd;
import org.apache.cloudstack.api.command.user.affinitygroup.ListAffinityGroupsCmd; import org.apache.cloudstack.api.command.user.affinitygroup.ListAffinityGroupsCmd;
import org.apache.cloudstack.api.command.user.bucket.ListBucketsCmd;
import org.apache.cloudstack.api.command.user.event.ListEventsCmd; import org.apache.cloudstack.api.command.user.event.ListEventsCmd;
import org.apache.cloudstack.api.command.user.iso.ListIsosCmd; import org.apache.cloudstack.api.command.user.iso.ListIsosCmd;
import org.apache.cloudstack.api.command.user.job.ListAsyncJobsCmd; import org.apache.cloudstack.api.command.user.job.ListAsyncJobsCmd;
@ -129,6 +99,7 @@ import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd;
import org.apache.cloudstack.api.command.user.zone.ListZonesCmd; import org.apache.cloudstack.api.command.user.zone.ListZonesCmd;
import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.AccountResponse;
import org.apache.cloudstack.api.response.AsyncJobResponse; import org.apache.cloudstack.api.response.AsyncJobResponse;
import org.apache.cloudstack.api.response.BucketResponse;
import org.apache.cloudstack.api.response.DetailOptionsResponse; import org.apache.cloudstack.api.response.DetailOptionsResponse;
import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.cloudstack.api.response.DiskOfferingResponse;
import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.DomainResponse;
@ -255,22 +226,38 @@ import com.cloud.dc.dao.DedicatedResourceDao;
import com.cloud.domain.Domain; import com.cloud.domain.Domain;
import com.cloud.domain.DomainVO; import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao; import com.cloud.domain.dao.DomainDao;
import com.cloud.event.EventVO;
import com.cloud.event.dao.EventDao;
import com.cloud.event.dao.EventJoinDao; import com.cloud.event.dao.EventJoinDao;
import com.cloud.exception.CloudAuthenticationException; import com.cloud.exception.CloudAuthenticationException;
import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.PermissionDeniedException;
import com.cloud.ha.HighAvailabilityManager; import com.cloud.ha.HighAvailabilityManager;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor;
import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.network.PublicIpQuarantine;
import com.cloud.network.RouterHealthCheckResult; import com.cloud.network.RouterHealthCheckResult;
import com.cloud.network.VNF; import com.cloud.network.VNF;
import com.cloud.network.VpcVirtualNetworkApplianceService; import com.cloud.network.VpcVirtualNetworkApplianceService;
import com.cloud.network.as.AutoScaleVmGroupVmMapVO;
import com.cloud.network.as.dao.AutoScaleVmGroupDao;
import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.dao.PublicIpQuarantineDao;
import com.cloud.network.dao.RouterHealthCheckResultDao; import com.cloud.network.dao.RouterHealthCheckResultDao;
import com.cloud.network.dao.RouterHealthCheckResultVO; import com.cloud.network.dao.RouterHealthCheckResultVO;
import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.router.VirtualNetworkApplianceManager;
import com.cloud.network.security.SecurityGroupVMMapVO; import com.cloud.network.security.SecurityGroupVMMapVO;
import com.cloud.network.security.dao.SecurityGroupVMMapDao; import com.cloud.network.security.dao.SecurityGroupVMMapDao;
import com.cloud.network.vo.PublicIpQuarantineVO;
import com.cloud.offering.DiskOffering; import com.cloud.offering.DiskOffering;
import com.cloud.offering.ServiceOffering;
import com.cloud.org.Grouping; import com.cloud.org.Grouping;
import com.cloud.projects.Project; import com.cloud.projects.Project;
import com.cloud.projects.Project.ListProjectResourcesCriteria; import com.cloud.projects.Project.ListProjectResourcesCriteria;
@ -286,6 +273,7 @@ import com.cloud.server.ResourceManagerUtil;
import com.cloud.server.ResourceMetaDataService; import com.cloud.server.ResourceMetaDataService;
import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag;
import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.server.ResourceTag.ResourceObjectType;
import com.cloud.service.ServiceOfferingDetailsVO;
import com.cloud.service.ServiceOfferingVO; import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.service.dao.ServiceOfferingDetailsDao;
@ -298,24 +286,34 @@ import com.cloud.storage.SnapshotVO;
import com.cloud.storage.Storage; import com.cloud.storage.Storage;
import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.Storage.TemplateType; import com.cloud.storage.Storage.TemplateType;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolHostVO;
import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.StoragePoolStatus;
import com.cloud.storage.StoragePoolTagVO; import com.cloud.storage.StoragePoolTagVO;
import com.cloud.storage.VMTemplateStoragePoolVO;
import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.Volume; import com.cloud.storage.Volume;
import com.cloud.storage.VolumeApiServiceImpl; import com.cloud.storage.VolumeApiServiceImpl;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.BucketDao; import com.cloud.storage.dao.BucketDao;
import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.storage.dao.StoragePoolTagsDao; import com.cloud.storage.dao.StoragePoolTagsDao;
import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VMTemplatePoolDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.tags.ResourceTagVO; import com.cloud.tags.ResourceTagVO;
import com.cloud.tags.dao.ResourceTagDao; import com.cloud.tags.dao.ResourceTagDao;
import com.cloud.template.VirtualMachineTemplate.State; import com.cloud.template.VirtualMachineTemplate.State;
import com.cloud.template.VirtualMachineTemplate.TemplateFilter; import com.cloud.template.VirtualMachineTemplate.TemplateFilter;
import com.cloud.user.Account; import com.cloud.user.Account;
import com.cloud.user.AccountManager; import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.DomainManager; import com.cloud.user.DomainManager;
import com.cloud.user.SSHKeyPairVO;
import com.cloud.user.User; import com.cloud.user.User;
import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.SSHKeyPairDao;
import com.cloud.user.dao.UserDao; import com.cloud.user.dao.UserDao;
import com.cloud.utils.DateUtil; import com.cloud.utils.DateUtil;
import com.cloud.utils.NumbersUtil; import com.cloud.utils.NumbersUtil;
@ -331,18 +329,20 @@ import com.cloud.utils.db.SearchCriteria.Func;
import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.SearchCriteria.Op;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.DomainRouterVO; import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.InstanceGroupVMMapVO;
import com.cloud.vm.NicVO;
import com.cloud.vm.UserVmDetailVO;
import com.cloud.vm.UserVmVO; import com.cloud.vm.UserVmVO;
import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.VmDetailConstants; import com.cloud.vm.VmDetailConstants;
import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.InstanceGroupVMMapDao;
import com.cloud.vm.dao.NicDao;
import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.UserVmDetailsDao;
import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.dao.VMInstanceDao;
import org.apache.cloudstack.api.command.user.bucket.ListBucketsCmd;
import org.apache.cloudstack.api.response.BucketResponse;
import static com.cloud.vm.VmDetailConstants.SSH_PUBLIC_KEY;
@Component @Component
public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements QueryService, Configurable { public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements QueryService, Configurable {
@ -5627,7 +5627,6 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
Integer count = snapshotDataPair.second(); Integer count = snapshotDataPair.second();
if (count == 0) { if (count == 0) {
// empty result
return snapshotDataPair; return snapshotDataPair;
} }
List<SnapshotJoinVO> snapshotData = snapshotDataPair.first(); List<SnapshotJoinVO> snapshotData = snapshotDataPair.first();
@ -5637,7 +5636,6 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
} else { } else {
snapshots = snapshotJoinDao.searchBySnapshotStorePair(snapshotData.stream().map(SnapshotJoinVO::getSnapshotStorePair).toArray(String[]::new)); snapshots = snapshotJoinDao.searchBySnapshotStorePair(snapshotData.stream().map(SnapshotJoinVO::getSnapshotStorePair).toArray(String[]::new));
} }
return new Pair<>(snapshots, count); return new Pair<>(snapshots, count);
} }

View File

@ -23,10 +23,7 @@ import org.apache.cloudstack.api.ResponseObject;
import org.apache.cloudstack.api.response.SnapshotResponse; import org.apache.cloudstack.api.response.SnapshotResponse;
import com.cloud.api.query.vo.SnapshotJoinVO; import com.cloud.api.query.vo.SnapshotJoinVO;
import com.cloud.utils.Pair;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.GenericDao;
import com.cloud.utils.db.SearchCriteria;
public interface SnapshotJoinDao extends GenericDao<SnapshotJoinVO, Long> { public interface SnapshotJoinDao extends GenericDao<SnapshotJoinVO, Long> {
@ -34,8 +31,7 @@ public interface SnapshotJoinDao extends GenericDao<SnapshotJoinVO, Long> {
SnapshotResponse setSnapshotResponse(SnapshotResponse snapshotResponse, SnapshotJoinVO snapshot); SnapshotResponse setSnapshotResponse(SnapshotResponse snapshotResponse, SnapshotJoinVO snapshot);
Pair<List<SnapshotJoinVO>, Integer> searchIncludingRemovedAndCount(final SearchCriteria<SnapshotJoinVO> sc, final Filter filter);
List<SnapshotJoinVO> searchBySnapshotStorePair(String... pairs); List<SnapshotJoinVO> searchBySnapshotStorePair(String... pairs);
List<SnapshotJoinVO> findByDistinctIds(Long zoneId, Long... ids); List<SnapshotJoinVO> findByDistinctIds(Long zoneId, Long... ids);
} }

View File

@ -18,6 +18,8 @@
package com.cloud.api.query.dao; package com.cloud.api.query.dao;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -33,6 +35,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.query.QueryService; import org.apache.cloudstack.query.QueryService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import com.cloud.api.ApiDBUtils; import com.cloud.api.ApiDBUtils;
@ -45,7 +48,6 @@ import com.cloud.storage.Volume.Type;
import com.cloud.storage.VolumeVO; import com.cloud.storage.VolumeVO;
import com.cloud.user.Account; import com.cloud.user.Account;
import com.cloud.user.AccountService; import com.cloud.user.AccountService;
import com.cloud.utils.Pair;
import com.cloud.utils.db.Filter; import com.cloud.utils.db.Filter;
import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria;
@ -195,13 +197,6 @@ public class SnapshotJoinDaoImpl extends GenericDaoBaseWithTagInformation<Snapsh
return snapshotResponse; return snapshotResponse;
} }
@Override
public Pair<List<SnapshotJoinVO>, Integer> searchIncludingRemovedAndCount(final SearchCriteria<SnapshotJoinVO> sc, final Filter filter) {
List<SnapshotJoinVO> objects = searchIncludingRemoved(sc, filter, null, false);
Integer count = getDistinctCount(sc);
return new Pair<>(objects, count);
}
@Override @Override
public List<SnapshotJoinVO> searchBySnapshotStorePair(String... pairs) { public List<SnapshotJoinVO> searchBySnapshotStorePair(String... pairs) {
// set detail batch query size // set detail batch query size
@ -246,14 +241,33 @@ public class SnapshotJoinDaoImpl extends GenericDaoBaseWithTagInformation<Snapsh
return uvList; return uvList;
} }
protected List<SnapshotJoinVO> findById(Long zoneId, long id) {
SearchBuilder<SnapshotJoinVO> sb = createSearchBuilder();
sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ);
sb.done();
SearchCriteria<SnapshotJoinVO> sc = sb.create();
sc.setParameters("id", id);
if (zoneId != null) {
sc.setParameters("zoneId", zoneId);
}
List<SnapshotJoinVO> snapshotJoinVOS = search(sc, null);
if (CollectionUtils.isEmpty(snapshotJoinVOS)) {
return null;
}
snapshotJoinVOS.sort(Comparator.comparing(SnapshotJoinVO::getSnapshotStorePair));
return Collections.singletonList(snapshotJoinVOS.get(0));
}
@Override @Override
public List<SnapshotJoinVO> findByDistinctIds(Long zoneId, Long... ids) { public List<SnapshotJoinVO> findByDistinctIds(Long zoneId, Long... ids) {
if (ids == null || ids.length == 0) { if (ids == null || ids.length == 0) {
return new ArrayList<>(); return new ArrayList<>();
} }
if (ids.length == 1) {
return findById(zoneId, ids[0]);
}
Filter searchFilter = new Filter(SnapshotJoinVO.class, "snapshotStorePair", QueryService.SortKeyAscending.value(), null, null); Filter searchFilter = new Filter(SnapshotJoinVO.class, "snapshotStorePair", QueryService.SortKeyAscending.value(), null, null);
SearchCriteria<SnapshotJoinVO> sc = snapshotIdsSearch.create(); SearchCriteria<SnapshotJoinVO> sc = snapshotIdsSearch.create();
if (zoneId != null) { if (zoneId != null) {
sc.setParameters("zoneId", zoneId); sc.setParameters("zoneId", zoneId);

View File

@ -0,0 +1,109 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.api.query.dao;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.api.query.vo.SnapshotJoinVO;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
@RunWith(MockitoJUnitRunner.class)
public class SnapshotJoinDaoImplTest {
@Spy
@InjectMocks
SnapshotJoinDaoImpl snapshotJoinDao = new SnapshotJoinDaoImpl();
@Mock
SearchCriteria<SnapshotJoinVO> mockSearchCriteria;
@Before
public void setUp() {
SnapshotJoinVO mockSnap = mock(SnapshotJoinVO.class);
SearchBuilder<SnapshotJoinVO> mockSearchBuilder = mock(SearchBuilder.class);
when(mockSearchBuilder.entity()).thenReturn(mockSnap);
doReturn(mockSearchBuilder).when(snapshotJoinDao).createSearchBuilder();
when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria);
}
@Test
public void testFindById_WithNullZoneId_EmptyResult() {
Long zoneId = null;
long id = 1L;
doReturn(Collections.emptyList()).when(snapshotJoinDao).search(mockSearchCriteria, null);
List<SnapshotJoinVO> result = snapshotJoinDao.findById(zoneId, id);
assertNull(result);
verify(mockSearchCriteria).setParameters("id", id);
verify(mockSearchCriteria, never()).setParameters("zoneId", zoneId);
}
@Test
public void testFindById_WithValidZoneId_EmptyResult() {
Long zoneId = 1L;
long id = 1L;
doReturn(Collections.emptyList()).when(snapshotJoinDao).search(mockSearchCriteria, null);
List<SnapshotJoinVO> result = snapshotJoinDao.findById(zoneId, id);
assertNull(result);
verify(mockSearchCriteria).setParameters("id", id);
verify(mockSearchCriteria).setParameters("zoneId", zoneId);
}
@Test
public void testFindById_WithValidResults() {
Long zoneId = 1L;
long id = 1L;
SnapshotJoinVO snapshot1 = mock(SnapshotJoinVO.class);
when(snapshot1.getSnapshotStorePair()).thenReturn("Primary_1");
SnapshotJoinVO snapshot2 = mock(SnapshotJoinVO.class);
when(snapshot2.getSnapshotStorePair()).thenReturn("Image_1");
doReturn(Arrays.asList(snapshot1, snapshot2)).when(snapshotJoinDao).search(mockSearchCriteria, null);
List<SnapshotJoinVO> result = snapshotJoinDao.findById(zoneId, id);
assertNotNull(result);
assertEquals(1, result.size());
assertEquals("Image_1", result.get(0).getSnapshotStorePair());
verify(mockSearchCriteria).setParameters("id", id);
verify(mockSearchCriteria).setParameters("zoneId", zoneId);
}
@Test
public void testFindById_WithNullResults() {
long id = 1L;
doReturn(null).when(snapshotJoinDao).search(mockSearchCriteria, null);
List<SnapshotJoinVO> result = snapshotJoinDao.findById(null, id);
assertNull(result);
}
}