| // 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 org.apache.cloudstack.dns; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.ArgumentMatchers.any; |
| |
| import static org.mockito.ArgumentMatchers.anyLong; |
| import static org.mockito.ArgumentMatchers.anyString; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.ArgumentMatchers.nullable; |
| import static org.mockito.Mockito.doNothing; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| import java.io.IOException; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.cloudstack.acl.SecurityChecker; |
| import org.apache.cloudstack.api.ApiConstants; |
| import org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd; |
| import org.apache.cloudstack.api.command.user.dns.AssociateDnsZoneToNetworkCmd; |
| import org.apache.cloudstack.api.command.user.dns.CreateDnsRecordCmd; |
| import org.apache.cloudstack.api.command.user.dns.CreateDnsZoneCmd; |
| import org.apache.cloudstack.api.command.user.dns.DeleteDnsRecordCmd; |
| import org.apache.cloudstack.api.command.user.dns.DeleteDnsServerCmd; |
| |
| import org.apache.cloudstack.api.command.user.dns.DisassociateDnsZoneFromNetworkCmd; |
| import org.apache.cloudstack.api.command.user.dns.ListDnsRecordsCmd; |
| import org.apache.cloudstack.api.command.user.dns.ListDnsServersCmd; |
| import org.apache.cloudstack.api.command.user.dns.ListDnsZonesCmd; |
| import org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd; |
| import org.apache.cloudstack.api.command.user.dns.UpdateDnsZoneCmd; |
| import org.apache.cloudstack.api.response.DnsRecordResponse; |
| import org.apache.cloudstack.api.response.DnsServerResponse; |
| import org.apache.cloudstack.api.response.DnsZoneNetworkMapResponse; |
| import org.apache.cloudstack.api.response.DnsZoneResponse; |
| import org.apache.cloudstack.api.response.ListResponse; |
| import org.apache.cloudstack.context.CallContext; |
| import org.apache.cloudstack.dns.dao.NicDnsJoinDao; |
| import org.apache.cloudstack.dns.dao.DnsServerDao; |
| import org.apache.cloudstack.dns.dao.DnsServerJoinDao; |
| import org.apache.cloudstack.dns.dao.DnsZoneDao; |
| import org.apache.cloudstack.dns.dao.DnsZoneJoinDao; |
| import org.apache.cloudstack.dns.dao.DnsZoneNetworkMapDao; |
| import org.apache.cloudstack.dns.exception.DnsConflictException; |
| import org.apache.cloudstack.dns.exception.DnsNotFoundException; |
| import org.apache.cloudstack.dns.exception.DnsProviderException; |
| import org.apache.cloudstack.dns.exception.DnsTransportException; |
| import org.apache.cloudstack.dns.vo.DnsServerJoinVO; |
| import org.apache.cloudstack.dns.vo.DnsServerVO; |
| import org.apache.cloudstack.dns.vo.DnsZoneJoinVO; |
| import org.apache.cloudstack.dns.vo.DnsZoneNetworkMapVO; |
| import org.apache.cloudstack.dns.vo.DnsZoneVO; |
| import org.apache.cloudstack.dns.vo.NicDnsJoinVO; |
| import org.apache.cloudstack.framework.messagebus.MessageBus; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.InjectMocks; |
| import org.mockito.Mock; |
| import org.mockito.MockedStatic; |
| import org.mockito.Mockito; |
| import org.mockito.junit.MockitoJUnitRunner; |
| import org.springframework.test.util.ReflectionTestUtils; |
| |
| import com.cloud.domain.dao.DomainDao; |
| import com.cloud.event.ActionEventUtils; |
| import com.cloud.event.EventTypes; |
| import com.cloud.exception.InvalidParameterValueException; |
| import com.cloud.exception.PermissionDeniedException; |
| import com.cloud.network.dao.NetworkDao; |
| import com.cloud.network.dao.NetworkVO; |
| import com.cloud.user.Account; |
| import com.cloud.user.AccountManager; |
| import com.cloud.user.AccountVO; |
| import com.cloud.user.dao.AccountDao; |
| import com.cloud.utils.Pair; |
| import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn; |
| import com.cloud.utils.db.Transaction; |
| import com.cloud.utils.db.TransactionCallback; |
| import com.cloud.utils.exception.CloudRuntimeException; |
| import com.cloud.vm.VMInstanceVO; |
| import com.cloud.vm.VirtualMachine; |
| import com.cloud.vm.dao.NicDao; |
| import com.cloud.vm.dao.NicDetailsDao; |
| import com.cloud.vm.dao.VMInstanceDao; |
| |
| @RunWith(MockitoJUnitRunner.class) |
| public class DnsProviderManagerImplTest { |
| |
| private static final long ACCOUNT_ID = 1L; |
| private static final long DOMAIN_ID = 10L; |
| private static final long SERVER_ID = 100L; |
| private static final long ZONE_ID = 200L; |
| private static final long NETWORK_ID = 300L; |
| |
| @InjectMocks |
| DnsProviderManagerImpl manager; |
| |
| @Mock |
| AccountManager accountMgr; |
| @Mock |
| DnsServerDao dnsServerDao; |
| @Mock |
| DnsZoneDao dnsZoneDao; |
| @Mock |
| DnsZoneJoinDao dnsZoneJoinDao; |
| @Mock |
| DnsServerJoinDao dnsServerJoinDao; |
| @Mock |
| DnsZoneNetworkMapDao dnsZoneNetworkMapDao; |
| @Mock |
| NetworkDao networkDao; |
| @Mock |
| DomainDao domainDao; |
| @Mock |
| NicDao nicDao; |
| @Mock |
| NicDetailsDao nicDetailsDao; |
| @Mock |
| NicDnsJoinDao nicDnsJoinDao; |
| @Mock |
| MessageBus messageBus; |
| @Mock |
| VMInstanceDao vmInstanceDao; |
| @Mock |
| DnsProvider dnsProviderMock; |
| @Mock |
| Account callerMock; |
| |
| private MockedStatic<CallContext> callContextMocked; |
| private CallContext callContextMock; |
| |
| // Support VOs |
| private DnsServerVO serverVO; |
| private DnsZoneVO zoneVO; |
| |
| @Before |
| public void setUp() throws Exception { |
| callContextMocked = Mockito.mockStatic(CallContext.class); |
| callContextMock = mock(CallContext.class); |
| callContextMocked.when(CallContext::current).thenReturn(callContextMock); |
| when(callContextMock.getCallingAccount()).thenReturn(callerMock); |
| when(callerMock.getId()).thenReturn(ACCOUNT_ID); |
| when(callerMock.getDomainId()).thenReturn(DOMAIN_ID); |
| |
| serverVO = Mockito.spy( |
| new DnsServerVO("test-server", "http://pdns:8081", 8081, DnsProviderType.PowerDNS, null, |
| "apikey", false, null, Collections.singletonList("ns1.example.com"), ACCOUNT_ID, DOMAIN_ID)); |
| |
| Map<String, String> serverDetails = new HashMap<>(); |
| serverDetails.put("pdsnServerId", "localhost"); |
| serverVO.setDetails(serverDetails); |
| Mockito.lenient().doReturn(SERVER_ID).when(serverVO).getId(); |
| |
| zoneVO = Mockito.spy( |
| new DnsZoneVO("example.com", DnsZone.ZoneType.Public, SERVER_ID, ACCOUNT_ID, DOMAIN_ID, "Test zone")); |
| Mockito.lenient().doReturn(ZONE_ID).when(zoneVO).getId(); |
| |
| when(dnsProviderMock.getProviderType()).thenReturn(DnsProviderType.PowerDNS); |
| manager.setDnsProviders(Collections.singletonList(dnsProviderMock)); |
| |
| doNothing().when(accountMgr).checkAccess(any(Account.class), |
| nullable(SecurityChecker.AccessType.class), eq(true), any()); |
| |
| when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); |
| } |
| |
| @After |
| public void tearDown() { |
| callContextMocked.close(); |
| } |
| |
| @Test(expected = CloudRuntimeException.class) |
| public void testGetProviderByTypeNull() { |
| // Setting providers to empty to force lookup failure |
| manager.setDnsProviders(Collections.emptyList()); |
| // Trigger via provisionDnsZone which calls getProviderByType |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| manager.provisionDnsZone(ZONE_ID, false); |
| } |
| |
| @Test |
| public void testListProviderNamesReturnsList() { |
| List<String> names = manager.listProviderNames(); |
| assertEquals(1, names.size()); |
| assertEquals("PowerDNS", names.get(0)); |
| } |
| |
| @Test |
| public void testListProviderNamesWithNullProviders() { |
| manager.setDnsProviders(null); |
| List<String> names = manager.listProviderNames(); |
| assertTrue(names.isEmpty()); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testAllocateDnsZoneBlankName() { |
| CreateDnsZoneCmd cmd = mock(CreateDnsZoneCmd.class); |
| when(cmd.getName()).thenReturn(" "); |
| manager.allocateDnsZone(cmd); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testAllocateDnsZoneServerNotFound() { |
| CreateDnsZoneCmd cmd = mock(CreateDnsZoneCmd.class); |
| when(cmd.getName()).thenReturn("example.com"); |
| when(cmd.getDnsServerId()).thenReturn(SERVER_ID); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(null); |
| manager.allocateDnsZone(cmd); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testAllocateDnsZoneAlreadyExists() { |
| CreateDnsZoneCmd cmd = mock(CreateDnsZoneCmd.class); |
| when(cmd.getName()).thenReturn("example.com"); |
| when(cmd.getDnsServerId()).thenReturn(SERVER_ID); |
| when(cmd.getType()).thenReturn(DnsZone.ZoneType.Public); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| Mockito.doReturn(SERVER_ID).when(serverVO).getId(); |
| Mockito.doReturn(ACCOUNT_ID).when(serverVO).getAccountId(); |
| when(dnsZoneDao.findByNameServerAndType(anyString(), anyLong(), any())).thenReturn(zoneVO); |
| manager.allocateDnsZone(cmd); |
| } |
| |
| @Test |
| public void testAllocateDnsZoneOwnerSuccess() { |
| CreateDnsZoneCmd cmd = mock(CreateDnsZoneCmd.class); |
| when(cmd.getName()).thenReturn("example.com"); |
| when(cmd.getDnsServerId()).thenReturn(SERVER_ID); |
| when(cmd.getType()).thenReturn(DnsZone.ZoneType.Public); |
| when(cmd.getDescription()).thenReturn("desc"); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| Mockito.doReturn(SERVER_ID).when(serverVO).getId(); |
| Mockito.doReturn(ACCOUNT_ID).when(serverVO).getAccountId(); |
| when(dnsZoneDao.findByNameServerAndType(anyString(), anyLong(), any())).thenReturn(null); |
| when(dnsZoneDao.persist(any(DnsZoneVO.class))).thenReturn(zoneVO); |
| DnsZone result = manager.allocateDnsZone(cmd); |
| assertNotNull(result); |
| } |
| |
| @Test(expected = PermissionDeniedException.class) |
| public void testAllocateDnsZoneNonOwnerPrivateServer() { |
| CreateDnsZoneCmd cmd = mock(CreateDnsZoneCmd.class); |
| when(cmd.getName()).thenReturn("tenant.com"); |
| when(cmd.getDnsServerId()).thenReturn(SERVER_ID); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| Mockito.doReturn(ACCOUNT_ID + 99).when(serverVO).getAccountId(); // different owner |
| |
| manager.allocateDnsZone(cmd); |
| } |
| |
| @Test(expected = PermissionDeniedException.class) |
| public void testAllocateDnsZoneNonOwnerShadowingOtherAccountZoneRejected() { |
| CreateDnsZoneCmd cmd = mock(CreateDnsZoneCmd.class); |
| when(cmd.getName()).thenReturn("www.tenant1.cloud.example"); |
| when(cmd.getDnsServerId()).thenReturn(SERVER_ID); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| Mockito.doReturn(SERVER_ID).when(serverVO).getId(); |
| Mockito.doReturn(ACCOUNT_ID + 99).when(serverVO).getAccountId(); // different owner |
| Mockito.doReturn(true).when(serverVO).getPublicServer(); |
| Mockito.doReturn("cloud.example").when(serverVO).getPublicDomainSuffix(); |
| DnsZoneVO victimZone = new DnsZoneVO("tenant1.cloud.example", DnsZone.ZoneType.Public, SERVER_ID, |
| ACCOUNT_ID + 50, DOMAIN_ID, "victim zone"); |
| when(dnsZoneDao.listByDnsServerId(SERVER_ID)).thenReturn(Collections.singletonList(victimZone)); |
| |
| manager.allocateDnsZone(cmd); |
| } |
| |
| @Test(expected = PermissionDeniedException.class) |
| public void testAllocateDnsZoneNonOwnerParentOfOtherAccountZoneRejected() { |
| CreateDnsZoneCmd cmd = mock(CreateDnsZoneCmd.class); |
| when(cmd.getName()).thenReturn("tenant1.cloud.example"); |
| when(cmd.getDnsServerId()).thenReturn(SERVER_ID); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| Mockito.doReturn(SERVER_ID).when(serverVO).getId(); |
| Mockito.doReturn(ACCOUNT_ID + 99).when(serverVO).getAccountId(); // different owner |
| Mockito.doReturn(true).when(serverVO).getPublicServer(); |
| Mockito.doReturn("cloud.example").when(serverVO).getPublicDomainSuffix(); |
| DnsZoneVO victimZone = new DnsZoneVO("www.tenant1.cloud.example", DnsZone.ZoneType.Public, SERVER_ID, |
| ACCOUNT_ID + 50, DOMAIN_ID, "victim zone"); |
| when(dnsZoneDao.listByDnsServerId(SERVER_ID)).thenReturn(Collections.singletonList(victimZone)); |
| |
| manager.allocateDnsZone(cmd); |
| } |
| |
| @Test |
| public void testAllocateDnsZoneNonOwnerPublicServerSuccess() { |
| CreateDnsZoneCmd cmd = mock(CreateDnsZoneCmd.class); |
| when(cmd.getName()).thenReturn("tenant2.cloud.example"); |
| when(cmd.getDnsServerId()).thenReturn(SERVER_ID); |
| when(cmd.getType()).thenReturn(DnsZone.ZoneType.Public); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| Mockito.doReturn(SERVER_ID).when(serverVO).getId(); |
| Mockito.doReturn(ACCOUNT_ID + 99).when(serverVO).getAccountId(); // different owner |
| Mockito.doReturn(true).when(serverVO).getPublicServer(); |
| Mockito.doReturn("cloud.example").when(serverVO).getPublicDomainSuffix(); |
| when(dnsZoneDao.listByDnsServerId(SERVER_ID)).thenReturn(Collections.emptyList()); |
| when(dnsZoneDao.findByNameServerAndType(anyString(), anyLong(), any())).thenReturn(null); |
| when(dnsZoneDao.persist(any(DnsZoneVO.class))).thenReturn(zoneVO); |
| |
| DnsZone result = manager.allocateDnsZone(cmd); |
| assertNotNull(result); |
| verify(dnsZoneDao).persist(Mockito.argThat(z -> "tenant2.cloud.example".equals(((DnsZoneVO) z).getName()))); |
| } |
| |
| @Test(expected = CloudRuntimeException.class) |
| public void testProvisionDnsZoneNotFound() { |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(null); |
| manager.provisionDnsZone(ZONE_ID, false); |
| } |
| |
| @Test |
| public void testProvisionDnsZoneSuccess() throws Exception { |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| when(dnsProviderMock.provisionZone(any(), any())).thenReturn("example.com."); |
| when(dnsZoneDao.update(anyLong(), any())).thenReturn(true); |
| DnsZone result = manager.provisionDnsZone(ZONE_ID, false); |
| assertNotNull(result); |
| verify(dnsProviderMock).provisionZone(serverVO, zoneVO); |
| verify(dnsZoneDao).update(anyLong(), any()); |
| } |
| |
| @Test(expected = CloudRuntimeException.class) |
| public void testProvisionDnsZoneConflictException() throws Exception { |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| when(dnsProviderMock.provisionZone(any(), any())).thenThrow(new DnsConflictException("conflict")); |
| manager.provisionDnsZone(ZONE_ID, false); |
| verify(dnsZoneDao).remove(ZONE_ID); |
| } |
| |
| @Test(expected = CloudRuntimeException.class) |
| public void testProvisionDnsZoneTransportException() throws Exception { |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| when(dnsProviderMock.provisionZone(any(), any())) |
| .thenThrow(new DnsTransportException("unreachable", new IOException("i/o"))); |
| manager.provisionDnsZone(ZONE_ID, false); |
| verify(dnsZoneDao).remove(ZONE_ID); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testDeleteDnsZoneNotFound() { |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(null); |
| manager.deleteDnsZone(ZONE_ID, false); |
| } |
| |
| @Test(expected = CloudRuntimeException.class) |
| public void testDeleteDnsZoneServerMissing() { |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(null); |
| manager.deleteDnsZone(ZONE_ID, false); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testUpdateDnsZoneNotFound() { |
| UpdateDnsZoneCmd cmd = mock(UpdateDnsZoneCmd.class); |
| when(cmd.getId()).thenReturn(ZONE_ID); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(null); |
| manager.updateDnsZone(cmd); |
| } |
| |
| @Test |
| public void testUpdateDnsZoneNoChange() { |
| UpdateDnsZoneCmd cmd = mock(UpdateDnsZoneCmd.class); |
| when(cmd.getId()).thenReturn(ZONE_ID); |
| when(cmd.getDescription()).thenReturn(null); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| DnsZone result = manager.updateDnsZone(cmd); |
| assertNotNull(result); |
| verify(dnsZoneDao, never()).update(anyLong(), any()); |
| } |
| |
| @Test |
| public void testUpdateDnsZoneWithDescription() throws Exception { |
| UpdateDnsZoneCmd cmd = mock(UpdateDnsZoneCmd.class); |
| when(cmd.getId()).thenReturn(ZONE_ID); |
| when(cmd.getDescription()).thenReturn("Updated description"); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| doNothing().when(dnsProviderMock).updateZone(any(), any()); |
| when(dnsZoneDao.update(anyLong(), any())).thenReturn(true); |
| DnsZone result = manager.updateDnsZone(cmd); |
| assertNotNull(result); |
| verify(dnsProviderMock).updateZone(serverVO, zoneVO); |
| } |
| |
| @Test(expected = CloudRuntimeException.class) |
| public void testUpdateDnsZoneServerMissing() { |
| UpdateDnsZoneCmd cmd = mock(UpdateDnsZoneCmd.class); |
| when(cmd.getId()).thenReturn(ZONE_ID); |
| when(cmd.getDescription()).thenReturn("New description"); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(null); |
| manager.updateDnsZone(cmd); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testDeleteDnsServerNotFound() { |
| DeleteDnsServerCmd cmd = mock(DeleteDnsServerCmd.class); |
| when(cmd.getId()).thenReturn(SERVER_ID); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(null); |
| manager.deleteDnsServer(cmd); |
| } |
| |
| @Test |
| public void testDeleteDnsServerWithCleanup() throws Exception { |
| DeleteDnsServerCmd cmd = mock(DeleteDnsServerCmd.class); |
| when(cmd.getId()).thenReturn(SERVER_ID); |
| when(cmd.getCleanup()).thenReturn(true); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| doNothing().when(accountMgr).checkAccess(any(Account.class), |
| nullable(SecurityChecker.AccessType.class), eq(true), any()); |
| |
| List<Long> zones = Collections.singletonList(ZONE_ID); |
| when(dnsZoneDao.findDnsZoneIdsByServerId(SERVER_ID)).thenReturn(zones); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsZoneNetworkMapDao.findByZoneId(ZONE_ID)).thenReturn(null); |
| when(dnsServerDao.remove(SERVER_ID)).thenReturn(true); |
| when(dnsZoneDao.remove(ZONE_ID)).thenReturn(true); |
| |
| try (MockedStatic<Transaction> transactionMock = Mockito.mockStatic(Transaction.class)) { |
| transactionMock.when(() -> Transaction.execute(any(TransactionCallback.class))).thenAnswer(invocation -> { |
| TransactionCallback<Boolean> callback = invocation.getArgument(0); |
| return callback.doInTransaction(null); |
| }); |
| |
| boolean res = manager.deleteDnsServer(cmd); |
| assertTrue(res); |
| verify(dnsServerDao).remove(SERVER_ID); |
| verify(dnsProviderMock).deleteZone(any(), any()); |
| } |
| } |
| |
| @Test |
| public void testDeleteDnsZoneSuccess() throws Exception { |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(anyLong())).thenReturn(serverVO); |
| doNothing().when(accountMgr).checkAccess(any(Account.class), |
| nullable(SecurityChecker.AccessType.class), eq(true), any()); |
| when(dnsZoneNetworkMapDao.findByZoneId(ZONE_ID)).thenReturn(null); |
| when(dnsZoneDao.remove(ZONE_ID)).thenReturn(true); |
| |
| try (MockedStatic<Transaction> transactionMock = Mockito.mockStatic(Transaction.class)) { |
| transactionMock.when(() -> Transaction.execute(any(TransactionCallback.class))).thenAnswer(invocation -> { |
| TransactionCallback<Boolean> callback = invocation.getArgument(0); |
| return callback.doInTransaction(null); |
| }); |
| |
| boolean res = manager.deleteDnsZone(ZONE_ID, false); |
| assertTrue(res); |
| verify(dnsZoneDao).remove(ZONE_ID); |
| verify(dnsProviderMock).deleteZone(any(), any()); |
| } |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testListDnsRecordsZoneNotFound() { |
| ListDnsRecordsCmd cmd = mock(ListDnsRecordsCmd.class); |
| when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(null); |
| manager.listDnsRecords(cmd); |
| } |
| |
| @Test(expected = CloudRuntimeException.class) |
| public void testListDnsRecordsServerMissing() { |
| ListDnsRecordsCmd cmd = mock(ListDnsRecordsCmd.class); |
| when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(null); |
| manager.listDnsRecords(cmd); |
| } |
| |
| @Test |
| public void testListDnsRecordsSuccess() throws Exception { |
| ListDnsRecordsCmd cmd = mock(ListDnsRecordsCmd.class); |
| when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| DnsRecord record = new DnsRecord("www.example.com", DnsRecord.RecordType.A, |
| Collections.singletonList("1.2.3.4"), 300); |
| when(dnsProviderMock.listRecords(any(), any())).thenReturn(Collections.singletonList(record)); |
| ListResponse<DnsRecordResponse> result = manager.listDnsRecords(cmd); |
| assertNotNull(result); |
| assertEquals(1, result.getCount().intValue()); |
| } |
| |
| @Test(expected = CloudRuntimeException.class) |
| public void testListDnsRecordsZoneNotFoundInProvider() throws Exception { |
| ListDnsRecordsCmd cmd = mock(ListDnsRecordsCmd.class); |
| when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| when(dnsProviderMock.listRecords(any(), any())).thenThrow(new DnsNotFoundException("not found")); |
| manager.listDnsRecords(cmd); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testDisassociateZoneNoMappingFound() { |
| DisassociateDnsZoneFromNetworkCmd cmd = mock(DisassociateDnsZoneFromNetworkCmd.class); |
| when(cmd.getNetworkId()).thenReturn(NETWORK_ID); |
| when(dnsZoneNetworkMapDao.findByNetworkId(NETWORK_ID)).thenReturn(null); |
| manager.disassociateZoneFromNetwork(cmd); |
| } |
| |
| @Test |
| public void testDisassociateZoneOrphanedMapping() { |
| DisassociateDnsZoneFromNetworkCmd cmd = mock(DisassociateDnsZoneFromNetworkCmd.class); |
| when(cmd.getNetworkId()).thenReturn(NETWORK_ID); |
| DnsZoneNetworkMapVO mapping = mock(DnsZoneNetworkMapVO.class); |
| when(mapping.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(mapping.getId()).thenReturn(500L); |
| when(dnsZoneNetworkMapDao.findByNetworkId(NETWORK_ID)).thenReturn(mapping); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(null); // zone missing (orphan) |
| when(dnsZoneNetworkMapDao.remove(500L)).thenReturn(true); |
| boolean result = manager.disassociateZoneFromNetwork(cmd); |
| assertTrue(result); |
| } |
| |
| @Test |
| public void testDisassociateZoneSuccess() { |
| DisassociateDnsZoneFromNetworkCmd cmd = mock(DisassociateDnsZoneFromNetworkCmd.class); |
| when(cmd.getNetworkId()).thenReturn(NETWORK_ID); |
| DnsZoneNetworkMapVO mapping = mock(DnsZoneNetworkMapVO.class); |
| when(mapping.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(mapping.getId()).thenReturn(500L); |
| when(dnsZoneNetworkMapDao.findByNetworkId(NETWORK_ID)).thenReturn(mapping); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| |
| when(dnsZoneNetworkMapDao.remove(500L)).thenReturn(true); |
| boolean result = manager.disassociateZoneFromNetwork(cmd); |
| assertTrue(result); |
| verify(dnsZoneNetworkMapDao).remove(500L); |
| } |
| |
| @Test |
| public void testCreateDnsRecordResponse() { |
| DnsRecord record = new DnsRecord("www.example.com", DnsRecord.RecordType.A, Arrays.asList("1.2.3.4"), 300); |
| DnsRecordResponse response = manager.createDnsRecordResponse(record); |
| assertNotNull(response); |
| } |
| |
| @Test |
| public void testCreateDnsServerResponseFromJoinVO() { |
| DnsServerJoinVO join = mock(DnsServerJoinVO.class); |
| when(join.getUuid()).thenReturn("uuid-1"); |
| when(join.getName()).thenReturn("pdns"); |
| when(join.getUrl()).thenReturn("http://pdns:8081"); |
| when(join.getPort()).thenReturn(8081); |
| when(join.getProviderType()).thenReturn(DnsProviderType.PowerDNS.toString()); |
| when(join.isPublicServer()).thenReturn(false); |
| when(join.getNameServers()).thenReturn(Collections.emptyList()); |
| when(join.getPublicDomainSuffix()).thenReturn(null); |
| when(join.getAccountName()).thenReturn("admin"); |
| when(join.getDomainUuid()).thenReturn("domain-uuid"); |
| when(join.getDomainName()).thenReturn("ROOT"); |
| when(join.getState()).thenReturn(DnsServer.State.Enabled); |
| DnsServerResponse response = manager.createDnsServerResponse(join); |
| assertNotNull(response); |
| } |
| |
| @Test |
| public void testCreateDnsZoneResponseFromJoinVO() { |
| DnsZoneJoinVO join = mock(DnsZoneJoinVO.class); |
| when(join.getUuid()).thenReturn("zone-uuid"); |
| when(join.getName()).thenReturn("example.com"); |
| when(join.getDnsServerUuid()).thenReturn("server-uuid"); |
| when(join.getAccountName()).thenReturn("admin"); |
| when(join.getDomainUuid()).thenReturn("domain-uuid"); |
| when(join.getDomainName()).thenReturn("ROOT"); |
| when(join.getDnsServerName()).thenReturn("pdns"); |
| when(join.getDnsServerAccountName()).thenReturn("admin"); |
| when(join.getState()).thenReturn(DnsZone.State.Active); |
| when(join.getDescription()).thenReturn("Test zone"); |
| DnsZoneResponse response = manager.createDnsZoneResponse(join); |
| assertNotNull(response); |
| } |
| |
| @Test |
| public void testCheckDnsServerPermissionOwner() { |
| // owner has same accountId as server |
| when(callerMock.getId()).thenReturn(ACCOUNT_ID); |
| Mockito.doReturn(ACCOUNT_ID).when(serverVO).getAccountId(); |
| // Should not throw |
| manager.checkDnsServerPermission(callerMock, serverVO); |
| } |
| |
| @Test(expected = PermissionDeniedException.class) |
| public void testCheckDnsServerPermissionNonOwnerPrivate() { |
| when(callerMock.getId()).thenReturn(ACCOUNT_ID + 1); |
| Mockito.doReturn(ACCOUNT_ID).when(serverVO).getAccountId(); |
| Mockito.doReturn(false).when(serverVO).getPublicServer(); |
| manager.checkDnsServerPermission(callerMock, serverVO); |
| } |
| |
| @Test(expected = PermissionDeniedException.class) |
| public void testCheckDnsServerPermissionNonOwnerPublicOutsideDomain() { |
| AccountVO serverOwner = mock(AccountVO.class); |
| when(callerMock.getId()).thenReturn(ACCOUNT_ID + 1); |
| Mockito.doReturn(ACCOUNT_ID).when(serverVO).getAccountId(); |
| Mockito.doReturn(true).when(serverVO).getPublicServer(); |
| when(serverOwner.getDomainId()).thenReturn(20L); |
| when(callerMock.getDomainId()).thenReturn(DOMAIN_ID); |
| ReflectionTestUtils.setField(manager, "accountDao", Mockito.mock(AccountDao.class)); |
| AccountDao accountDaoMock = (AccountDao) ReflectionTestUtils |
| .getField(manager, "accountDao"); |
| when(accountDaoMock.findByIdIncludingRemoved(ACCOUNT_ID)).thenReturn(serverOwner); |
| when(domainDao.isChildDomain(20L, DOMAIN_ID)).thenReturn(false); |
| manager.checkDnsServerPermission(callerMock, serverVO); |
| } |
| |
| @Test |
| public void testCheckDnsZonePermissionOwner() { |
| when(callerMock.getId()).thenReturn(ACCOUNT_ID); |
| Mockito.doReturn(ACCOUNT_ID).when(zoneVO).getAccountId(); |
| // Should not throw |
| manager.checkDnsZonePermission(callerMock, zoneVO); |
| } |
| |
| @Test(expected = PermissionDeniedException.class) |
| public void testCheckDnsZonePermissionNonOwner() { |
| when(callerMock.getId()).thenReturn(ACCOUNT_ID + 1); |
| Mockito.doReturn(ACCOUNT_ID).when(zoneVO).getAccountId(); |
| manager.checkDnsZonePermission(callerMock, zoneVO); |
| } |
| |
| @Test |
| public void testGetCommandsReturnsNonEmptyList() { |
| List<Class<?>> commands = manager.getCommands(); |
| assertNotNull(commands); |
| assertFalse(commands.isEmpty()); |
| assertTrue(commands.size() > 5); |
| } |
| |
| @Test |
| public void testStartWithNoProviders() { |
| manager.setDnsProviders(Collections.emptyList()); |
| assertTrue(manager.start()); |
| } |
| |
| @Test |
| public void testStartWithProviders() { |
| assertTrue(manager.start()); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testAssociateZoneToNetworkZoneNotFound() { |
| AssociateDnsZoneToNetworkCmd cmd = mock(AssociateDnsZoneToNetworkCmd.class); |
| when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(null); |
| manager.associateZoneToNetwork(cmd); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testAssociateZoneToNetworkNetworkNotFound() { |
| AssociateDnsZoneToNetworkCmd cmd = mock(AssociateDnsZoneToNetworkCmd.class); |
| when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(cmd.getNetworkId()).thenReturn(NETWORK_ID); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| when(networkDao.findById(NETWORK_ID)).thenReturn(null); |
| manager.associateZoneToNetwork(cmd); |
| } |
| |
| @Test(expected = CloudRuntimeException.class) |
| public void testAssociateZoneToNetworkNonSharedNetwork() { |
| AssociateDnsZoneToNetworkCmd cmd = mock(AssociateDnsZoneToNetworkCmd.class); |
| when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(cmd.getNetworkId()).thenReturn(NETWORK_ID); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| NetworkVO network = mock(NetworkVO.class); |
| when(network.getGuestType()).thenReturn(NetworkVO.GuestType.Isolated); |
| when(networkDao.findById(NETWORK_ID)).thenReturn(network); |
| manager.associateZoneToNetwork(cmd); |
| } |
| |
| @Test |
| public void testAssociateZoneToNetworkSuccess() { |
| AssociateDnsZoneToNetworkCmd cmd = mock(AssociateDnsZoneToNetworkCmd.class); |
| when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(cmd.getNetworkId()).thenReturn(NETWORK_ID); |
| |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| Mockito.doReturn("zone-uuid").when(zoneVO).getUuid(); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| NetworkVO network = mock(NetworkVO.class); |
| when(network.getGuestType()).thenReturn(NetworkVO.GuestType.Shared); |
| |
| when(networkDao.findById(NETWORK_ID)).thenReturn(network); |
| DnsZoneNetworkMapVO savedMapping = mock(DnsZoneNetworkMapVO.class); |
| when(dnsZoneNetworkMapDao.persist(any(DnsZoneNetworkMapVO.class))).thenReturn(savedMapping); |
| DnsZoneNetworkMapResponse response = manager.associateZoneToNetwork(cmd); |
| assertNotNull(response); |
| verify(dnsZoneNetworkMapDao).persist(any(DnsZoneNetworkMapVO.class)); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testAssociateZoneToNetworkAlreadyAssociated() { |
| AssociateDnsZoneToNetworkCmd cmd = mock(AssociateDnsZoneToNetworkCmd.class); |
| when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(cmd.getNetworkId()).thenReturn(NETWORK_ID); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| NetworkVO network = mock(NetworkVO.class); |
| when(network.getGuestType()).thenReturn(NetworkVO.GuestType.Shared); |
| when(network.getId()).thenReturn(NETWORK_ID); |
| when(networkDao.findById(NETWORK_ID)).thenReturn(network); |
| when(dnsZoneNetworkMapDao.findByNetworkId(NETWORK_ID)).thenReturn(mock(DnsZoneNetworkMapVO.class)); |
| manager.associateZoneToNetwork(cmd); |
| } |
| |
| @Test |
| public void testCreateDnsRecordSuccess() throws Exception { |
| CreateDnsRecordCmd cmd = mock(CreateDnsRecordCmd.class); |
| when(cmd.getName()).thenReturn("www"); |
| when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(cmd.getType()).thenReturn(DnsRecord.RecordType.A); |
| when(cmd.getContents()).thenReturn(Collections.singletonList("1.2.3.4")); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(anyLong())).thenReturn(serverVO); |
| when(dnsProviderMock.addRecord(any(), any(), any())).thenReturn("www.example.com"); |
| |
| DnsRecordResponse res = manager.createDnsRecord(cmd); |
| assertNotNull(res); |
| verify(dnsProviderMock).addRecord(any(), any(), any()); |
| } |
| |
| @Test |
| public void testDeleteDnsRecordSuccess() throws Exception { |
| DeleteDnsRecordCmd cmd = mock(DeleteDnsRecordCmd.class); |
| when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(cmd.getName()).thenReturn("www"); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(anyLong())).thenReturn(serverVO); |
| when(dnsProviderMock.deleteRecord(any(), any(), any())).thenReturn("www.example.com"); |
| |
| boolean res = manager.deleteDnsRecord(cmd); |
| assertTrue(res); |
| verify(dnsProviderMock).deleteRecord(any(), any(), any()); |
| } |
| |
| @Test |
| public void testConfigure() throws Exception { |
| assertTrue(manager.configure("dnsProviderManagerImpl", Collections.emptyMap())); |
| verify(messageBus, times(3)).subscribe(anyString(), any()); |
| } |
| |
| @Test |
| public void testAddDnsServerSuccess() throws Exception { |
| AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); |
| when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); |
| when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); |
| when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); |
| when(dnsProviderMock.validateAndResolveServer(any())).thenReturn("resolved-id"); |
| when(dnsServerDao.persist(any())).thenReturn(serverVO); |
| DnsServer result = manager.addDnsServer(cmd); |
| assertNotNull(result); |
| verify(dnsServerDao).persist(any()); |
| } |
| |
| @Test |
| public void testListDnsServers() { |
| ListDnsServersCmd cmd = mock(ListDnsServersCmd.class); |
| when(domainDao.getDomainParentIds(anyLong())).thenReturn(Collections.emptySet()); |
| List<DnsServerVO> servers = Collections.singletonList(serverVO); |
| Pair<List<DnsServerVO>, Integer> searchPair = new Pair<>(servers, 1); |
| when(dnsServerDao.searchDnsServer(any(), anyLong(), any(), any(), any(), any())).thenReturn(searchPair); |
| |
| DnsServerJoinVO joinVO = mock(DnsServerJoinVO.class); |
| when(joinVO.getProviderType()).thenReturn(DnsProviderType.PowerDNS.toString()); |
| when(joinVO.getState()).thenReturn(DnsServer.State.Enabled); |
| when(dnsServerJoinDao.listByUuids(any())).thenReturn(Collections.singletonList(joinVO)); |
| |
| ListResponse<DnsServerResponse> res = manager.listDnsServers(cmd); |
| assertEquals(1, res.getCount().intValue()); |
| } |
| |
| @Test |
| public void testUpdateDnsServer() throws Exception { |
| UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); |
| when(cmd.getId()).thenReturn(SERVER_ID); |
| when(cmd.getName()).thenReturn("updated-name"); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| when(dnsServerDao.update(eq(SERVER_ID), any())).thenReturn(true); |
| DnsServer res = manager.updateDnsServer(cmd); |
| assertNotNull(res); |
| verify(dnsServerDao).update(eq(SERVER_ID), any()); |
| } |
| |
| @Test |
| public void testListDnsZones() { |
| ListDnsZonesCmd cmd = mock(ListDnsZonesCmd.class); |
| when(cmd.getId()).thenReturn(null); |
| when(cmd.getDnsServerId()).thenReturn(null); |
| when(dnsServerDao.listDnsServerIdsByAccountId(anyLong())).thenReturn(Collections.emptyList()); |
| List<DnsZoneVO> zones = Collections.singletonList(zoneVO); |
| Pair<List<DnsZoneVO>, Integer> searchPair = new Pair<>(zones, 1); |
| when(dnsZoneDao.searchZones(any(), anyLong(), any(), any(), any(), any())).thenReturn(searchPair); |
| |
| DnsZoneJoinVO joinVO = mock(DnsZoneJoinVO.class); |
| when(dnsZoneJoinDao.listByUuids(any())).thenReturn(Collections.singletonList(joinVO)); |
| |
| ListResponse<DnsZoneResponse> res = manager.listDnsZones(cmd); |
| assertEquals(1, res.getCount().intValue()); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testAddDnsServerAlreadyExists() { |
| AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); |
| when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); |
| when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(serverVO); |
| manager.addDnsServer(cmd); |
| } |
| |
| @Test |
| public void testAddDnsServerTrimsUrlBeforeDuplicateCheckAndPersistence() throws Exception { |
| AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); |
| when(cmd.getUrl()).thenReturn(" http://192.0.2.1:8081 "); |
| when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); |
| when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); |
| when(dnsProviderMock.validateAndResolveServer(any())).thenReturn("resolved-id"); |
| when(dnsServerDao.persist(any())).thenReturn(serverVO); |
| |
| manager.addDnsServer(cmd); |
| |
| verify(dnsServerDao).findByUrlAndAccount(eq("http://192.0.2.1:8081"), anyLong()); |
| verify(dnsServerDao).persist(Mockito.argThat(s -> "http://192.0.2.1:8081".equals(((DnsServerVO) s).getUrl()))); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testAddDnsServerRejectsLoopbackUrl() { |
| AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); |
| when(cmd.getUrl()).thenReturn("http://127.0.0.1:8081"); |
| manager.addDnsServer(cmd); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testAddDnsServerRejectsUrlWithoutScheme() { |
| AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); |
| when(cmd.getUrl()).thenReturn("192.0.2.1:8081"); |
| manager.addDnsServer(cmd); |
| } |
| |
| @Test |
| public void testAddDnsServerAllowsPrivateAddressForRootAdmin() throws Exception { |
| AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); |
| when(cmd.getUrl()).thenReturn("http://192.168.1.1:8081"); |
| when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); |
| when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); |
| when(dnsProviderMock.validateAndResolveServer(any())).thenReturn("resolved-id"); |
| when(dnsServerDao.persist(any())).thenReturn(serverVO); |
| |
| DnsServer result = manager.addDnsServer(cmd); |
| assertNotNull(result); |
| verify(dnsServerDao).persist(any()); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testAddDnsServerPublicWithoutSuffixRejected() { |
| AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); |
| when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); |
| when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); |
| when(cmd.isPublic()).thenReturn(true); |
| when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); |
| manager.addDnsServer(cmd); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testUpdateDnsServerPublicWithoutSuffixRejected() { |
| UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); |
| when(cmd.getId()).thenReturn(SERVER_ID); |
| when(cmd.isPublic()).thenReturn(true); |
| when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| manager.updateDnsServer(cmd); |
| } |
| |
| @Test(expected = CloudRuntimeException.class) |
| public void testAddDnsServerValidationFailure() throws Exception { |
| AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); |
| when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); |
| when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); |
| when(cmd.getNameServers()).thenReturn(Collections.emptyList()); |
| when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); |
| when(dnsProviderMock.validateAndResolveServer(any())).thenThrow(new CloudRuntimeException("Validation failed")); |
| manager.addDnsServer(cmd); |
| } |
| |
| @Test(expected = PermissionDeniedException.class) |
| public void testAddDnsServerNormalUser() throws Exception { |
| AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); |
| when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(false); |
| manager.addDnsServer(cmd); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testUpdateDnsServerUrlDuplicate() { |
| UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); |
| when(cmd.getId()).thenReturn(SERVER_ID); |
| when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); |
| DnsServerVO existingServer = mock(DnsServerVO.class); |
| when(existingServer.getId()).thenReturn(SERVER_ID + 1); // Different ID implies duplicate |
| |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| Mockito.doReturn("http://original:8081").when(serverVO).getUrl(); |
| when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(existingServer); |
| |
| manager.updateDnsServer(cmd); |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testUpdateDnsServerRejectsLoopbackUrl() { |
| UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); |
| when(cmd.getId()).thenReturn(SERVER_ID); |
| when(cmd.getUrl()).thenReturn("http://127.0.0.1:8081"); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| Mockito.doReturn("http://original:8081").when(serverVO).getUrl(); |
| |
| manager.updateDnsServer(cmd); |
| } |
| |
| @Test |
| public void testUpdateDnsServerAllowsPrivateAddressForRootAdmin() throws Exception { |
| UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); |
| when(cmd.getId()).thenReturn(SERVER_ID); |
| when(cmd.getUrl()).thenReturn("http://192.168.1.1:8081"); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| Mockito.doReturn("http://original:8081").when(serverVO).getUrl(); |
| Mockito.doReturn(DnsProviderType.PowerDNS).when(serverVO).getProviderType(); |
| when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); |
| doNothing().when(dnsProviderMock).validate(any()); |
| when(dnsServerDao.update(anyLong(), any())).thenReturn(true); |
| |
| DnsServer result = manager.updateDnsServer(cmd); |
| assertNotNull(result); |
| verify(dnsProviderMock).validate(any()); |
| } |
| |
| @Test |
| public void testUpdateDnsServerTreatsWhitespaceOnlyUrlChangeAsUnchanged() throws Exception { |
| UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); |
| Integer unchangedPort = serverVO.getPort(); |
| when(cmd.getId()).thenReturn(SERVER_ID); |
| when(cmd.getUrl()).thenReturn(" http://192.0.2.1:8081 "); |
| when(cmd.getPort()).thenReturn(unchangedPort); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| Mockito.doReturn("http://192.0.2.1:8081").when(serverVO).getUrl(); |
| when(dnsServerDao.update(anyLong(), any())).thenReturn(true); |
| |
| DnsServer result = manager.updateDnsServer(cmd); |
| assertNotNull(result); |
| verify(dnsProviderMock, never()).validate(any()); |
| verify(serverVO, never()).setUrl(anyString()); |
| } |
| |
| @Test |
| public void testUpdateDnsServerUrlValid() throws Exception { |
| UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); |
| when(cmd.getId()).thenReturn(SERVER_ID); |
| when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| |
| Mockito.doReturn("http://original:8081").when(serverVO).getUrl(); |
| Mockito.doReturn(DnsProviderType.PowerDNS).when(serverVO).getProviderType(); |
| when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); |
| doNothing().when(dnsProviderMock).validate(any()); |
| when(dnsServerDao.update(anyLong(), any())).thenReturn(true); |
| |
| DnsServer result = manager.updateDnsServer(cmd); |
| assertNotNull(result); |
| verify(dnsProviderMock).validate(any()); // Changing URL triggers validationRequired |
| } |
| |
| @Test(expected = InvalidParameterValueException.class) |
| public void testUpdateDnsServerValidationException() throws Exception { |
| UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); |
| when(cmd.getId()).thenReturn(SERVER_ID); |
| when(cmd.getDnsApiKey()).thenReturn("new-api-key"); |
| |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| Mockito.doReturn("old-api-key").when(serverVO).getDnsApiKey(); |
| Mockito.doReturn("http://original:8081").when(serverVO).getUrl(); |
| Mockito.doReturn(DnsProviderType.PowerDNS).when(serverVO).getProviderType(); |
| |
| Mockito.doThrow(new CloudRuntimeException("Validation failed")).when(dnsProviderMock).validate(any()); |
| |
| manager.updateDnsServer(cmd); |
| } |
| |
| @Test |
| public void testVmLifecycleSubscriberStateUnchanged() { |
| DnsProviderManagerImpl.VmLifecycleSubscriber subscriber = manager.new VmLifecycleSubscriber(); |
| Map<String, Object> event = new HashMap<>(); |
| event.put(ApiConstants.OLD_STATE, VirtualMachine.State.Running); |
| event.put(ApiConstants.NEW_STATE, VirtualMachine.State.Running); |
| event.put(ApiConstants.INSTANCE_ID, 10L); |
| |
| subscriber.onPublishMessage("sender", "subject", event); |
| verify(vmInstanceDao, never()).findByIdIncludingRemoved(anyLong()); |
| } |
| |
| @Test |
| public void testVmLifecycleSubscriberRunning() { |
| DnsProviderManagerImpl.VmLifecycleSubscriber subscriber = manager.new VmLifecycleSubscriber(); |
| Map<String, Object> event = new HashMap<>(); |
| event.put(ApiConstants.OLD_STATE, VirtualMachine.State.Starting); |
| event.put(ApiConstants.NEW_STATE, VirtualMachine.State.Running); |
| event.put(ApiConstants.INSTANCE_ID, 12L); |
| |
| // Expect handleVmEvent to be called, which accesses |
| // vmInstanceDao.findByIdIncludingRemoved |
| when(vmInstanceDao.findById(12L)).thenReturn(null); |
| |
| subscriber.onPublishMessage("sender", "subject", event); |
| verify(vmInstanceDao, times(1)).findById(12L); |
| } |
| |
| @Test |
| public void testVmLifecycleSubscriberDestroyed() { |
| DnsProviderManagerImpl.VmLifecycleSubscriber subscriber = manager.new VmLifecycleSubscriber(); |
| Map<String, Object> event = new HashMap<>(); |
| event.put(ApiConstants.OLD_STATE, VirtualMachine.State.Running); |
| event.put(ApiConstants.NEW_STATE, VirtualMachine.State.Destroyed); |
| event.put(ApiConstants.INSTANCE_ID, 15L); |
| when(nicDnsJoinDao.listIncludingRemovedByVmId(15L)).thenReturn(null); |
| subscriber.onPublishMessage("sender", "subject", event); |
| verify(nicDnsJoinDao, times(1)).listIncludingRemovedByVmId(15L); |
| } |
| |
| @Test |
| public void testVmLifecycleSubscriberUnsupportedState() { |
| DnsProviderManagerImpl.VmLifecycleSubscriber subscriber = manager.new VmLifecycleSubscriber(); |
| Map<String, Object> event = new HashMap<>(); |
| event.put(ApiConstants.OLD_STATE, VirtualMachine.State.Running); |
| event.put(ApiConstants.NEW_STATE, VirtualMachine.State.Starting); |
| event.put(ApiConstants.INSTANCE_ID, 20L); |
| |
| subscriber.onPublishMessage("sender", "subject", event); |
| verify(vmInstanceDao, never()).findByIdIncludingRemoved(anyLong()); |
| } |
| |
| @Test |
| public void testVmLifecycleSubscriberException() { |
| DnsProviderManagerImpl.VmLifecycleSubscriber subscriber = manager.new VmLifecycleSubscriber(); |
| // Passing invalid args to trigger ClassCastException or similar |
| subscriber.onPublishMessage("sender", "subject", "not a map"); |
| // Should not throw exception upstream |
| verify(vmInstanceDao, never()).findByIdIncludingRemoved(anyLong()); |
| } |
| |
| @Test |
| public void testNicLifecycleSubscriberCreate() { |
| DnsProviderManagerImpl.NicLifecycleSubscriber subscriber = manager.new NicLifecycleSubscriber(); |
| Map<String, Object> event = new HashMap<>(); |
| event.put(ApiConstants.EVENT_TYPE, EventTypes.EVENT_NIC_CREATE); |
| event.put(ApiConstants.NIC_ID, 100L); |
| event.put(ApiConstants.INSTANCE_ID, 200L); |
| |
| when(vmInstanceDao.findById(200L)).thenReturn(null); // Short circuits handleNicEvent |
| |
| subscriber.onPublishMessage("sender", "subject", event); |
| verify(vmInstanceDao, times(1)).findById(200L); |
| } |
| |
| @Test |
| public void testNicLifecycleSubscriberDelete() { |
| DnsProviderManagerImpl.NicLifecycleSubscriber subscriber = manager.new NicLifecycleSubscriber(); |
| Map<String, Object> event = new HashMap<>(); |
| event.put(ApiConstants.EVENT_TYPE, EventTypes.EVENT_NIC_DELETE); |
| event.put(ApiConstants.NIC_ID, 101L); |
| event.put(ApiConstants.INSTANCE_ID, 201L); |
| when(nicDnsJoinDao.findByIdIncludingRemoved(101L)).thenReturn(null); |
| subscriber.onPublishMessage("sender", "subject", event); |
| verify(nicDnsJoinDao, times(1)).findByIdIncludingRemoved(101L); |
| } |
| |
| @Test |
| public void testNicLifecycleSubscriberMissingData() { |
| DnsProviderManagerImpl.NicLifecycleSubscriber subscriber = manager.new NicLifecycleSubscriber(); |
| Map<String, Object> event = new HashMap<>(); |
| event.put(ApiConstants.EVENT_TYPE, EventTypes.EVENT_NIC_CREATE); |
| // Missing NIC_ID and INSTANCE_ID |
| |
| subscriber.onPublishMessage("sender", "subject", event); |
| verify(vmInstanceDao, never()).findById(anyLong()); |
| } |
| |
| @Test |
| public void testNicLifecycleSubscriberUnsupportedEvent() { |
| DnsProviderManagerImpl.NicLifecycleSubscriber subscriber = manager.new NicLifecycleSubscriber(); |
| Map<String, Object> event = new HashMap<>(); |
| event.put(ApiConstants.EVENT_TYPE, "unsupported-event"); |
| event.put(ApiConstants.NIC_ID, 102L); |
| event.put(ApiConstants.INSTANCE_ID, 202L); |
| |
| subscriber.onPublishMessage("sender", "subject", event); |
| verify(vmInstanceDao, never()).findById(anyLong()); |
| } |
| |
| @Test |
| public void testNicLifecycleSubscriberException() { |
| DnsProviderManagerImpl.NicLifecycleSubscriber subscriber = manager.new NicLifecycleSubscriber(); |
| subscriber.onPublishMessage("sender", "subject", "not a map"); |
| // Should catch and not throw |
| verify(vmInstanceDao, never()).findById(anyLong()); |
| } |
| |
| @Test |
| public void testPrepareDnsRecordUrlNullSubdomain() { |
| String result = manager.prepareDnsRecordUrl("myvm", null, "example.com"); |
| assertEquals("myvm.example.com", result); |
| } |
| |
| @Test |
| public void testPrepareDnsRecordUrlBlankSubdomain() { |
| String result = manager.prepareDnsRecordUrl("myvm", " ", "example.com"); |
| assertEquals("myvm.example.com", result); |
| } |
| |
| @Test |
| public void testPrepareDnsRecordUrlTrimsSubdomain() { |
| String result = manager.prepareDnsRecordUrl("myvm", " sub ", "example.com"); |
| assertEquals("myvm.sub.example.com", result); |
| } |
| |
| @Test |
| public void testCreateDnsRecordAlreadyExistsThrowsCloudRuntimeException() throws Exception { |
| CreateDnsRecordCmd cmd = mock(CreateDnsRecordCmd.class); |
| when(cmd.getName()).thenReturn("www"); |
| when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(cmd.getType()).thenReturn(DnsRecord.RecordType.A); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(anyLong())).thenReturn(serverVO); |
| when(dnsProviderMock.dnsRecordExists(any(), any(), anyString(), anyString())).thenReturn(true); |
| |
| boolean threw = false; |
| try { |
| manager.createDnsRecord(cmd); |
| } catch (CloudRuntimeException ex) { |
| threw = true; |
| } |
| assertTrue(threw); |
| } |
| |
| @Test |
| public void testDeleteDnsRecordProviderReturnsNullReturnsFalse() throws Exception { |
| DeleteDnsRecordCmd cmd = mock(DeleteDnsRecordCmd.class); |
| when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(cmd.getName()).thenReturn("www"); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(anyLong())).thenReturn(serverVO); |
| when(dnsProviderMock.deleteRecord(any(), any(), any())).thenReturn(null); |
| |
| boolean result = manager.deleteDnsRecord(cmd); |
| assertFalse(result); |
| } |
| |
| @Test |
| public void testSyncDnsRecordsStateNoIpv4AndNoIpv6DeletesBothRecords() throws Exception { |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| when(nicDnsJoinDao.listActiveByVmIdZoneAndDnsRecord(anyLong(), anyLong(), anyString())) |
| .thenReturn(Collections.emptyList()); |
| |
| manager.syncDnsRecordsState(1L, "myvm.example.com", ZONE_ID); |
| |
| verify(dnsProviderMock, times(2)).deleteRecord(eq(serverVO), eq(zoneVO), any(DnsRecord.class)); |
| verify(dnsProviderMock, never()).addRecord(any(), any(), any()); |
| } |
| |
| @Test |
| public void testSyncDnsRecordsStateOnlyIpv4AddsAAndDeletesAAAA() throws Exception { |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| |
| NicDnsJoinVO nic = mock(NicDnsJoinVO.class); |
| when(nic.getIp4Address()).thenReturn("10.0.0.1"); |
| when(nic.getIp6Address()).thenReturn(null); |
| when(nicDnsJoinDao.listActiveByVmIdZoneAndDnsRecord(anyLong(), anyLong(), anyString())) |
| .thenReturn(Collections.singletonList(nic)); |
| |
| manager.syncDnsRecordsState(1L, "myvm.example.com", ZONE_ID); |
| |
| verify(dnsProviderMock, times(1)).addRecord(eq(serverVO), eq(zoneVO), |
| Mockito.argThat(r -> r.getType() == DnsRecord.RecordType.A)); |
| verify(dnsProviderMock, times(1)).deleteRecord(eq(serverVO), eq(zoneVO), |
| Mockito.argThat(r -> r.getType() == DnsRecord.RecordType.AAAA)); |
| } |
| |
| @Test |
| public void testHandleVmCreateEventFoundButNoActiveNics() throws DnsProviderException { |
| VMInstanceVO instanceMock = mock(VMInstanceVO.class); |
| when(vmInstanceDao.findById(30L)).thenReturn(instanceMock); |
| when(nicDnsJoinDao.listActiveByVmId(30L)).thenReturn(Collections.emptyList()); |
| |
| manager.handleVmCreateEvent(30L); |
| |
| verify(dnsProviderMock, never()).addRecord(any(), any(), any()); |
| verify(dnsProviderMock, never()).deleteRecord(any(), any(), any()); |
| } |
| |
| @Test |
| public void testHandleVmDestroyEventNicWithNullDnsUrlIsSkipped() throws DnsProviderException { |
| NicDnsJoinVO nicMock = mock(NicDnsJoinVO.class); |
| when(nicMock.getNicDnsName()).thenReturn(null); |
| when(nicDnsJoinDao.listIncludingRemovedByVmId(31L)) |
| .thenReturn(Collections.singletonList(nicMock)); |
| |
| manager.handleVmDestroyEvent(31L); |
| |
| verify(dnsProviderMock, never()).deleteRecord(any(), any(), any()); |
| } |
| |
| @Test |
| public void testHandleVmDestroyEventWithValidDnsUrlTriggersCleanup() throws Exception { |
| NicDnsJoinVO nicMock = mock(NicDnsJoinVO.class); |
| when(nicMock.getNicDnsName()).thenReturn("myvm.example.com"); |
| when(nicMock.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(nicDnsJoinDao.listIncludingRemovedByVmId(32L)) |
| .thenReturn(Collections.singletonList(nicMock)); |
| |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| when(nicDnsJoinDao.listActiveByVmIdZoneAndDnsRecord(eq(32L), eq(ZONE_ID), anyString())) |
| .thenReturn(Collections.emptyList()); |
| |
| try (MockedStatic<Transaction> txMock = |
| Mockito.mockStatic(Transaction.class)) { |
| txMock.when(() -> Transaction.execute( |
| any(TransactionCallbackWithExceptionNoReturn.class))) |
| .thenAnswer(invocation -> { |
| TransactionCallbackWithExceptionNoReturn<?> cb = |
| invocation.getArgument(0); |
| try { |
| cb.doInTransactionWithoutResult(null); |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| return null; |
| }); |
| |
| manager.handleVmDestroyEvent(32L); |
| |
| verify(nicDetailsDao).removeDetail(nicMock.getId(), ApiConstants.NIC_DNS_NAME); |
| verify(dnsProviderMock, times(2)).deleteRecord(eq(serverVO), eq(zoneVO), any(DnsRecord.class)); |
| } |
| } |
| |
| @Test |
| public void testHandleNicPlugVmNotRunningExitsEarly() throws DnsProviderException { |
| VMInstanceVO instanceMock = mock(VMInstanceVO.class); |
| when(instanceMock.getState()).thenReturn(VirtualMachine.State.Destroyed); |
| when(vmInstanceDao.findById(33L)).thenReturn(instanceMock); |
| manager.handleNicPlug(33L, 500L); |
| verify(nicDnsJoinDao, never()).findById(anyLong()); |
| verify(dnsProviderMock, never()).addRecord(any(), any(), any()); |
| } |
| |
| @Test |
| public void testHandleNicUnplugNicHasValidDnsUrlTriggersSyncCleanup() throws Exception { |
| NicDnsJoinVO nicMock = mock(NicDnsJoinVO.class); |
| when(nicMock.getNicDnsName()).thenReturn("myvm.example.com"); |
| when(nicMock.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(nicDnsJoinDao.findByIdIncludingRemoved(600L)).thenReturn(nicMock); |
| |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| when(nicDnsJoinDao.listActiveByVmIdZoneAndDnsRecord(eq(34L), eq(ZONE_ID), anyString())) |
| .thenReturn(Collections.emptyList()); |
| |
| try (MockedStatic<Transaction> txMock = |
| Mockito.mockStatic(Transaction.class)) { |
| txMock.when(() -> Transaction.execute( |
| any(TransactionCallbackWithExceptionNoReturn.class))) |
| .thenAnswer(invocation -> { |
| TransactionCallbackWithExceptionNoReturn<?> cb = |
| invocation.getArgument(0); |
| try { |
| cb.doInTransactionWithoutResult(null); |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| return null; |
| }); |
| |
| manager.handleNicUnplug(34L, 600L); |
| |
| verify(nicDetailsDao).removeDetail(600L, ApiConstants.NIC_DNS_NAME); |
| verify(dnsProviderMock, times(2)).deleteRecord(eq(serverVO), eq(zoneVO), any(DnsRecord.class)); |
| } |
| } |
| |
| @Test |
| public void testHandleVmHostnameChangedVmFoundButNoActiveNicsExitsEarly() throws DnsProviderException { |
| VMInstanceVO instanceMock = mock(VMInstanceVO.class); |
| when(vmInstanceDao.findById(35L)).thenReturn(instanceMock); |
| when(nicDnsJoinDao.listActiveByVmId(35L)).thenReturn(Collections.emptyList()); |
| |
| manager.handleVmHostnameChanged(35L, "newname"); |
| |
| verify(dnsZoneDao, never()).findById(anyLong()); |
| verify(dnsProviderMock, never()).addRecord(any(), any(), any()); |
| } |
| |
| @Test |
| public void testIsDnsCollisionReturnsTrueForDifferentInstance() { |
| NicDnsJoinVO existing = mock(NicDnsJoinVO.class); |
| when(existing.getInstanceId()).thenReturn(99L); |
| when(nicDnsJoinDao.findActiveByDnsRecordAndZone(ZONE_ID, "vm.example.com")).thenReturn(existing); |
| |
| try (MockedStatic<ActionEventUtils> aeMock = |
| Mockito.mockStatic(ActionEventUtils.class)) { |
| aeMock.when(() -> ActionEventUtils.onActionEvent( |
| anyLong(), anyLong(), anyLong(), anyString(), anyString(), anyLong(), anyString())) |
| .thenReturn(1L); |
| boolean result = (boolean) ReflectionTestUtils.invokeMethod( |
| manager, "isDnsCollision", "vm.example.com", ZONE_ID, 42L); |
| assertTrue(result); |
| } |
| } |
| |
| @Test |
| public void testIsDnsCollisionReturnsFalseWhenNoExistingRecord() { |
| when(nicDnsJoinDao.findActiveByDnsRecordAndZone(ZONE_ID, "vm.example.com")).thenReturn(null); |
| boolean result = (boolean) ReflectionTestUtils.invokeMethod( |
| manager, "isDnsCollision", "vm.example.com", ZONE_ID, 42L); |
| assertFalse(result); |
| } |
| |
| @Test |
| public void testIsDnsCollisionReturnsFalseWhenSameInstance() { |
| NicDnsJoinVO existing = |
| mock(NicDnsJoinVO.class); |
| when(existing.getInstanceId()).thenReturn(42L); |
| when(nicDnsJoinDao.findActiveByDnsRecordAndZone(ZONE_ID, "vm.example.com")).thenReturn(existing); |
| boolean result = (boolean) ReflectionTestUtils.invokeMethod( |
| manager, "isDnsCollision", "vm.example.com", ZONE_ID, 42L); |
| assertFalse(result); |
| } |
| |
| @Test |
| public void testHandleNicPlugRunningVmNicFoundButZoneNullExitsGracefully() throws DnsProviderException { |
| VMInstanceVO instanceMock = mock(VMInstanceVO.class); |
| when(instanceMock.getState()).thenReturn(VirtualMachine.State.Running); |
| when(vmInstanceDao.findById(40L)).thenReturn(instanceMock); |
| |
| NicDnsJoinVO nicMock = |
| mock(NicDnsJoinVO.class); |
| when(nicMock.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(nicDnsJoinDao.findById(700L)).thenReturn(nicMock); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(null); // zone missing → exit after NIC lookup |
| |
| manager.handleNicPlug(40L, 700L); |
| |
| verify(nicDnsJoinDao, times(1)).findById(700L); |
| verify(dnsProviderMock, never()).addRecord(any(), any(), any()); |
| } |
| |
| @Test |
| public void testHandleVmHostnameChangedNonEmptyNicsAllZonesMissingSkipsTransactions() |
| throws DnsProviderException { |
| VMInstanceVO instanceMock = mock(VMInstanceVO.class); |
| when(vmInstanceDao.findById(41L)).thenReturn(instanceMock); |
| |
| NicDnsJoinVO nicMock = |
| mock(NicDnsJoinVO.class); |
| when(nicMock.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(nicDnsJoinDao.listActiveByVmId(41L)).thenReturn(Collections.singletonList(nicMock)); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(null); // zone null → NIC skipped → empty map |
| |
| manager.handleVmHostnameChanged(41L, "newname"); |
| |
| verify(dnsZoneDao, times(1)).findById(ZONE_ID); |
| verify(dnsProviderMock, never()).addRecord(any(), any(), any()); |
| } |
| |
| @Test |
| public void testHandleVmCreateEventNonEmptyNicsAllZonesMissingSkipsSync() throws DnsProviderException { |
| VMInstanceVO instanceMock = mock(VMInstanceVO.class); |
| when(vmInstanceDao.findById(42L)).thenReturn(instanceMock); |
| |
| NicDnsJoinVO nicMock = |
| mock(NicDnsJoinVO.class); |
| when(nicMock.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(nicDnsJoinDao.listActiveByVmId(42L)).thenReturn(Collections.singletonList(nicMock)); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(null); // zone null → NIC skipped → empty outer map |
| |
| manager.handleVmCreateEvent(42L); |
| |
| verify(dnsZoneDao, times(1)).findById(ZONE_ID); |
| verify(dnsProviderMock, never()).addRecord(any(), any(), any()); |
| } |
| |
| @Test |
| public void testVmRenameSubscriberInvalidPayloadIsSwallowed() { |
| DnsProviderManagerImpl.VmRenameActionSubscriber subscriber = |
| manager.new VmRenameActionSubscriber(); |
| subscriber.onPublishMessage("sender", "topic", "not-a-map"); |
| verify(vmInstanceDao, never()).findById(anyLong()); |
| } |
| |
| @Test |
| public void testVmRenameSubscriberMissingInstanceIdSwallowsNpe() { |
| DnsProviderManagerImpl.VmRenameActionSubscriber subscriber = |
| manager.new VmRenameActionSubscriber(); |
| Map<String, Object> event = new HashMap<>(); |
| event.put(ApiConstants.EVENT_TYPE, EventTypes.EVENT_VM_UPDATE); |
| event.put(ApiConstants.HOST_NAME, "newvm"); |
| event.put(ApiConstants.OLD_HOST_NAME, "oldvm"); |
| // INSTANCE_ID intentionally absent → (long) null → NullPointerException → caught internally |
| subscriber.onPublishMessage("sender", "topic", event); |
| verify(vmInstanceDao, never()).findById(anyLong()); |
| } |
| |
| // ─── handleVmRunningState ────────────────────────────────────────────────── |
| |
| @Test |
| public void testHandleVmCreateEventInstanceNullExitsEarly() throws DnsProviderException { |
| when(vmInstanceDao.findById(50L)).thenReturn(null); |
| manager.handleVmCreateEvent(50L); |
| verify(nicDnsJoinDao, never()).listActiveByVmId(anyLong()); |
| } |
| |
| @Test |
| public void testHandleVmCreateEventFullSyncNoCollision() throws Exception { |
| VMInstanceVO instanceMock = mock(VMInstanceVO.class); |
| when(instanceMock.getHostName()).thenReturn("myvm"); |
| when(vmInstanceDao.findById(51L)).thenReturn(instanceMock); |
| |
| NicDnsJoinVO nicMock = |
| mock(NicDnsJoinVO.class); |
| when(nicMock.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(nicMock.getSubDomain()).thenReturn(null); |
| when(nicDnsJoinDao.listActiveByVmId(51L)).thenReturn(Collections.singletonList(nicMock)); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| |
| // no collision |
| when(nicDnsJoinDao.findActiveByDnsRecordAndZone(eq(ZONE_ID), anyString())).thenReturn(null); |
| // sync: no IPs → delete both |
| when(nicDnsJoinDao.listActiveByVmIdZoneAndDnsRecord(eq(51L), eq(ZONE_ID), anyString())) |
| .thenReturn(Collections.emptyList()); |
| |
| try (MockedStatic<Transaction> txMock = |
| Mockito.mockStatic(Transaction.class)) { |
| txMock.when(() -> Transaction.execute( |
| any(TransactionCallbackWithExceptionNoReturn.class))) |
| .thenAnswer(invocation -> { |
| TransactionCallbackWithExceptionNoReturn<?> cb = |
| invocation.getArgument(0); |
| try { cb.doInTransactionWithoutResult(null); } |
| catch (Exception e) { throw new RuntimeException(e); } |
| return null; |
| }); |
| |
| manager.handleVmCreateEvent(51L); |
| |
| verify(nicDetailsDao).addDetail(anyLong(), |
| eq(ApiConstants.NIC_DNS_NAME), anyString(), eq(true)); |
| verify(dnsProviderMock, times(2)).deleteRecord(eq(serverVO), eq(zoneVO), any(DnsRecord.class)); |
| } |
| } |
| |
| @Test |
| public void testHandleVmCreateEventCollisionSkipsAddDetail() throws Exception { |
| VMInstanceVO instanceMock = mock(VMInstanceVO.class); |
| when(instanceMock.getHostName()).thenReturn("myvm"); |
| when(vmInstanceDao.findById(52L)).thenReturn(instanceMock); |
| |
| NicDnsJoinVO nicMock = |
| mock(NicDnsJoinVO.class); |
| when(nicMock.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(nicMock.getSubDomain()).thenReturn(null); |
| when(nicDnsJoinDao.listActiveByVmId(52L)).thenReturn(Collections.singletonList(nicMock)); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| |
| // collision: different instance owns the record |
| NicDnsJoinVO colliding = |
| mock(NicDnsJoinVO.class); |
| when(colliding.getInstanceId()).thenReturn(999L); |
| when(nicDnsJoinDao.findActiveByDnsRecordAndZone(eq(ZONE_ID), anyString())).thenReturn(colliding); |
| |
| try (MockedStatic<Transaction> txMock = |
| Mockito.mockStatic(Transaction.class); |
| MockedStatic<ActionEventUtils> aeMock = |
| Mockito.mockStatic(ActionEventUtils.class)) { |
| aeMock.when(() -> ActionEventUtils.onActionEvent( |
| anyLong(), anyLong(), anyLong(), anyString(), anyString(), anyLong(), anyString())) |
| .thenReturn(1L); |
| txMock.when(() -> Transaction.execute( |
| any(TransactionCallbackWithExceptionNoReturn.class))) |
| .thenAnswer(invocation -> { |
| TransactionCallbackWithExceptionNoReturn<?> cb = |
| invocation.getArgument(0); |
| try { cb.doInTransactionWithoutResult(null); } |
| catch (Exception e) { throw new RuntimeException(e); } |
| return null; |
| }); |
| |
| manager.handleVmCreateEvent(52L); |
| |
| verify(nicDetailsDao, never()).addDetail(anyLong(), anyString(), anyString(), eq(true)); |
| verify(dnsProviderMock, never()).addRecord(any(), any(), any()); |
| } |
| } |
| |
| // ─── handleVmHostnameChanged ─────────────────────────────────────────────── |
| |
| @Test |
| public void testHandleVmHostnameChangedInstanceNullExitsEarly() throws DnsProviderException { |
| when(vmInstanceDao.findById(60L)).thenReturn(null); |
| manager.handleVmHostnameChanged(60L, "newname"); |
| verify(nicDnsJoinDao, never()).listActiveByVmId(anyLong()); |
| } |
| |
| @Test |
| public void testHandleVmHostnameChangedFqdnUnchangedSkipsNic() throws DnsProviderException { |
| VMInstanceVO instanceMock = mock(VMInstanceVO.class); |
| when(vmInstanceDao.findById(61L)).thenReturn(instanceMock); |
| |
| NicDnsJoinVO nicMock = |
| mock(NicDnsJoinVO.class); |
| when(nicMock.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(nicMock.getSubDomain()).thenReturn(null); |
| // old URL already equals the new computed URL → continue (skip) |
| when(nicMock.getNicDnsName()).thenReturn("newname.example.com"); |
| when(nicDnsJoinDao.listActiveByVmId(61L)).thenReturn(Collections.singletonList(nicMock)); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| |
| manager.handleVmHostnameChanged(61L, "newname"); |
| |
| // map stays empty → no Transaction executed |
| verify(nicDetailsDao, never()).removeDetail(anyLong(), anyString()); |
| verify(dnsProviderMock, never()).addRecord(any(), any(), any()); |
| } |
| |
| @Test |
| public void testHandleVmHostnameChangedFullRenamePath() throws Exception { |
| VMInstanceVO instanceMock = mock(VMInstanceVO.class); |
| when(vmInstanceDao.findById(62L)).thenReturn(instanceMock); |
| |
| NicDnsJoinVO nicMock = |
| mock(NicDnsJoinVO.class); |
| when(nicMock.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(nicMock.getSubDomain()).thenReturn(null); |
| when(nicMock.getNicDnsName()).thenReturn("oldvm.example.com"); // differs from new FQDN |
| when(nicDnsJoinDao.listActiveByVmId(62L)).thenReturn(Collections.singletonList(nicMock)); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| |
| // no collision for new record |
| when(nicDnsJoinDao.findActiveByDnsRecordAndZone(eq(ZONE_ID), anyString())).thenReturn(null); |
| // sync always returns empty → deleteRecord called |
| when(nicDnsJoinDao.listActiveByVmIdZoneAndDnsRecord(eq(62L), eq(ZONE_ID), anyString())) |
| .thenReturn(Collections.emptyList()); |
| |
| try (MockedStatic<Transaction> txMock = |
| Mockito.mockStatic(Transaction.class)) { |
| txMock.when(() -> Transaction.execute( |
| any(TransactionCallbackWithExceptionNoReturn.class))) |
| .thenAnswer(invocation -> { |
| TransactionCallbackWithExceptionNoReturn<?> cb = |
| invocation.getArgument(0); |
| try { cb.doInTransactionWithoutResult(null); } |
| catch (Exception e) { throw new RuntimeException(e); } |
| return null; |
| }); |
| |
| manager.handleVmHostnameChanged(62L, "newvm"); |
| |
| // Tx1: old URL removed from nic_details |
| verify(nicDetailsDao).removeDetail(anyLong(), |
| eq(ApiConstants.NIC_DNS_NAME)); |
| // Tx2: new URL written to nic_details |
| verify(nicDetailsDao).addDetail(anyLong(), |
| eq(ApiConstants.NIC_DNS_NAME), anyString(), eq(true)); |
| // deleteRecord called for both old-sync (A+AAAA) and new-sync (A+AAAA) = 4 total |
| verify(dnsProviderMock, times(4)).deleteRecord(eq(serverVO), eq(zoneVO), any(DnsRecord.class)); |
| } |
| } |
| |
| @Test |
| public void testHandleVmHostnameChangedCollisionOnNewUrlSkipsAddDetail() { |
| VMInstanceVO instanceMock = mock(VMInstanceVO.class); |
| when(vmInstanceDao.findById(63L)).thenReturn(instanceMock); |
| |
| NicDnsJoinVO nicMock = |
| mock(NicDnsJoinVO.class); |
| when(nicMock.getDnsZoneId()).thenReturn(ZONE_ID); |
| when(nicMock.getSubDomain()).thenReturn(null); |
| when(nicMock.getNicDnsName()).thenReturn("oldvm.example.com"); |
| when(nicDnsJoinDao.listActiveByVmId(63L)).thenReturn(Collections.singletonList(nicMock)); |
| when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); |
| when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); |
| |
| // collision on the new FQDN |
| NicDnsJoinVO colliding = |
| mock(NicDnsJoinVO.class); |
| when(colliding.getInstanceId()).thenReturn(999L); |
| when(nicDnsJoinDao.findActiveByDnsRecordAndZone(eq(ZONE_ID), anyString())).thenReturn(colliding); |
| when(nicDnsJoinDao.listActiveByVmIdZoneAndDnsRecord(eq(63L), eq(ZONE_ID), anyString())) |
| .thenReturn(Collections.emptyList()); |
| |
| try (MockedStatic<Transaction> txMock = |
| Mockito.mockStatic(Transaction.class); |
| MockedStatic<ActionEventUtils> aeMock = |
| Mockito.mockStatic(ActionEventUtils.class)) { |
| aeMock.when(() -> ActionEventUtils.onActionEvent( |
| anyLong(), anyLong(), anyLong(), anyString(), anyString(), anyLong(), anyString())) |
| .thenReturn(1L); |
| txMock.when(() -> Transaction.execute( |
| any(TransactionCallbackWithExceptionNoReturn.class))) |
| .thenAnswer(invocation -> { |
| TransactionCallbackWithExceptionNoReturn<?> cb = |
| invocation.getArgument(0); |
| try { cb.doInTransactionWithoutResult(null); } |
| catch (Exception e) { throw new RuntimeException(e); } |
| return null; |
| }); |
| |
| manager.handleVmHostnameChanged(63L, "newvm"); |
| |
| // Tx2 collision → addDetail never called for new URL |
| verify(nicDetailsDao, never()).addDetail(anyLong(), anyString(), anyString(), eq(true)); |
| } |
| } |
| } |