From 966be69605e9037727a2ffe1f1b39bb4ea6c272a Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Wed, 15 Apr 2026 15:33:55 +0200 Subject: [PATCH] add unit tests --- .../CustomActionResultResponseTest.java | 93 +++++ .../NetworkOrchestratorTest.java | 67 ++++ .../framework/extensions/network/README.md | 1 - .../extensions/api/ListExtensionsCmdTest.java | 41 ++ .../api/UpdateRegisteredExtensionCmdTest.java | 46 +++ .../dao/ExtensionResourceMapDaoImplTest.java | 51 +++ .../manager/ExtensionsManagerImplTest.java | 372 ++++++++++++++++++ .../cloud/network/NetworkModelImplTest.java | 131 ++++++ .../firewall/FirewallManagerImplTest.java | 179 +++++++++ .../cloud/network/vpc/VpcManagerImplTest.java | 49 +++ 10 files changed, 1029 insertions(+), 1 deletion(-) create mode 100644 api/src/test/java/org/apache/cloudstack/extension/CustomActionResultResponseTest.java create mode 100644 server/src/test/java/com/cloud/network/firewall/FirewallManagerImplTest.java diff --git a/api/src/test/java/org/apache/cloudstack/extension/CustomActionResultResponseTest.java b/api/src/test/java/org/apache/cloudstack/extension/CustomActionResultResponseTest.java new file mode 100644 index 00000000000..dfb4e409220 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/extension/CustomActionResultResponseTest.java @@ -0,0 +1,93 @@ +// 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.extension; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +public class CustomActionResultResponseTest { + + private CustomActionResultResponse response; + + @Before + public void setUp() { + response = new CustomActionResultResponse(); + } + + @Test + public void getResultReturnsNullByDefault() { + assertNull(response.getResult()); + } + + @Test + public void getResultReturnsSetValue() { + Map result = Map.of("message", "OK", "details", "All good"); + response.setResult(result); + assertEquals(result, response.getResult()); + assertEquals("OK", response.getResult().get("message")); + } + + @Test + public void isSuccessReturnsFalseWhenSuccessIsNull() { + // success is null by default + assertFalse(response.isSuccess()); + } + + @Test + public void isSuccessReturnsFalseWhenSuccessIsFalse() { + response.setSuccess(false); + assertFalse(response.isSuccess()); + } + + @Test + public void isSuccessReturnsTrueWhenSuccessIsTrue() { + response.setSuccess(true); + assertTrue(response.isSuccess()); + } + + @Test + public void getSuccessReturnsNullByDefault() { + assertNull(response.getSuccess()); + } + + @Test + public void getSuccessReturnsTrueAfterSetSuccessTrue() { + response.setSuccess(true); + assertTrue(response.getSuccess()); + } + + @Test + public void getSuccessReturnsFalseAfterSetSuccessFalse() { + response.setSuccess(false); + assertFalse(response.getSuccess()); + } + + @Test + public void setAndGetResultWithNullResult() { + response.setResult(null); + assertNull(response.getResult()); + } +} + diff --git a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java index 58f3cbe8473..f03affa0728 100644 --- a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java +++ b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.when; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -35,12 +36,15 @@ import com.cloud.dc.DataCenter; import com.cloud.exception.InsufficientVirtualNetworkCapacityException; import com.cloud.network.IpAddressManager; import com.cloud.utils.Pair; +import org.apache.cloudstack.extension.Extension; import org.apache.cloudstack.extension.ExtensionHelper; +import org.apache.cloudstack.framework.extensions.network.NetworkExtensionElement; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import org.springframework.test.util.ReflectionTestUtils; import org.mockito.ArgumentMatchers; import org.mockito.MockedStatic; import org.mockito.Mockito; @@ -69,6 +73,7 @@ import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.dao.RouterNetworkDao; import com.cloud.network.element.DhcpServiceProvider; +import com.cloud.network.element.NetworkElement; import com.cloud.network.guru.GuestNetworkGuru; import com.cloud.network.guru.NetworkGuru; import com.cloud.network.vpc.VpcManager; @@ -106,6 +111,7 @@ public class NetworkOrchestratorTest extends TestCase { private String guruName = "GuestNetworkGuru"; private String dhcpProvider = "VirtualRouter"; private NetworkGuru guru = mock(NetworkGuru.class); + private NetworkExtensionElement networkExtensionElement; NetworkOfferingVO networkOffering = mock(NetworkOfferingVO.class); @@ -137,6 +143,8 @@ public class NetworkOrchestratorTest extends TestCase { testOrchestrator._ipAddrMgr = mock(IpAddressManager.class); testOrchestrator._entityMgr = mock(EntityManager.class); testOrchestrator.extensionHelper = mock(ExtensionHelper.class); + networkExtensionElement = mock(NetworkExtensionElement.class); + ReflectionTestUtils.setField(testOrchestrator, "networkExtensionElement", networkExtensionElement); DhcpServiceProvider provider = mock(DhcpServiceProvider.class); Map capabilities = new HashMap(); @@ -1012,4 +1020,63 @@ public class NetworkOrchestratorTest extends TestCase { assertEquals("testtag", nicProfile.getName()); } } + + // ----------------------------------------------------------------------- + // Tests for getNetworkElementsIncludingExtensions + // ----------------------------------------------------------------------- + + @Test + public void getNetworkElementsIncludingExtensionsReturnsBaseListWhenNoExtensions() { + when(testOrchestrator.extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator)) + .thenReturn(Collections.emptyList()); + + DhcpServiceProvider dhcpProvider = mock(DhcpServiceProvider.class); + List elements = new ArrayList<>(List.of(dhcpProvider)); + testOrchestrator.networkElements = elements; + + @SuppressWarnings("unchecked") + List result = + (List) ReflectionTestUtils + .invokeMethod(testOrchestrator, "getNetworkElementsIncludingExtensions"); + assertNotNull(result); + assertEquals(elements.size(), result.size()); + } + + @Test + public void getNetworkElementsIncludingExtensionsAddsExtensionElements() { + Extension ext = mock(Extension.class); + when(ext.getName()).thenReturn("my-net-ext"); + when(testOrchestrator.extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator)) + .thenReturn(List.of(ext)); + + NetworkExtensionElement extElement = mock(NetworkExtensionElement.class); + when(networkExtensionElement.withProviderName("my-net-ext")).thenReturn(extElement); + + DhcpServiceProvider dhcpProvider = mock(DhcpServiceProvider.class); + testOrchestrator.networkElements = new ArrayList<>(List.of(dhcpProvider)); + + @SuppressWarnings("unchecked") + List result = + (List) ReflectionTestUtils + .invokeMethod(testOrchestrator, "getNetworkElementsIncludingExtensions"); + assertNotNull(result); + assertEquals(2, result.size()); + assertTrue(result.contains(extElement)); + } + + @Test + public void getNetworkElementsIncludingExtensionsReturnsBaseListWhenExtensionHelperReturnsNull() { + when(testOrchestrator.extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator)) + .thenReturn(null); + + DhcpServiceProvider dhcpProvider = mock(DhcpServiceProvider.class); + testOrchestrator.networkElements = new ArrayList<>(List.of(dhcpProvider)); + + @SuppressWarnings("unchecked") + List result = + (List) ReflectionTestUtils + .invokeMethod(testOrchestrator, "getNetworkElementsIncludingExtensions"); + assertNotNull(result); + assertEquals(1, result.size()); + } } diff --git a/framework/extensions/src/main/java/org/apache/cloudstack/framework/extensions/network/README.md b/framework/extensions/src/main/java/org/apache/cloudstack/framework/extensions/network/README.md index 75aaa5d33b3..964677f261b 100644 --- a/framework/extensions/src/main/java/org/apache/cloudstack/framework/extensions/network/README.md +++ b/framework/extensions/src/main/java/org/apache/cloudstack/framework/extensions/network/README.md @@ -1090,4 +1090,3 @@ exit 0 For a full production implementation see https://github.com/apache/cloudstack-extensions/tree/network-namespace/Network-Namespace: - `network-namespace.sh` — management-server entry-point (SSH proxy). - `enetwork-namespace-wrapper.sh` — KVM-host wrapper that implements all commands using Linux network namespaces. - diff --git a/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/api/ListExtensionsCmdTest.java b/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/api/ListExtensionsCmdTest.java index 1ca601293a3..fe597966354 100644 --- a/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/api/ListExtensionsCmdTest.java +++ b/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/api/ListExtensionsCmdTest.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.framework.extensions.api; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.util.Arrays; @@ -89,4 +90,44 @@ public class ListExtensionsCmdTest { setPrivateField("details", detailsList); cmd.getDetails(); } + + // ----------------------------------------------------------------------- + // Tests for new getters: type, resourceId, resourceType + // ----------------------------------------------------------------------- + + @Test + public void testGetTypeReturnsValueWhenSet() { + setPrivateField("type", "NetworkOrchestrator"); + assertEquals("NetworkOrchestrator", cmd.getType()); + } + + @Test + public void testGetTypeReturnsNullWhenUnset() { + setPrivateField("type", null); + assertNull(cmd.getType()); + } + + @Test + public void testGetResourceIdReturnsValueWhenSet() { + setPrivateField("resourceId", "pnet-uuid-123"); + assertEquals("pnet-uuid-123", cmd.getResourceId()); + } + + @Test + public void testGetResourceIdReturnsNullWhenUnset() { + setPrivateField("resourceId", null); + assertNull(cmd.getResourceId()); + } + + @Test + public void testGetResourceTypeReturnsValueWhenSet() { + setPrivateField("resourceType", "PhysicalNetwork"); + assertEquals("PhysicalNetwork", cmd.getResourceType()); + } + + @Test + public void testGetResourceTypeReturnsNullWhenUnset() { + setPrivateField("resourceType", null); + assertNull(cmd.getResourceType()); + } } diff --git a/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/api/UpdateRegisteredExtensionCmdTest.java b/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/api/UpdateRegisteredExtensionCmdTest.java index e13f165c584..023820237f4 100644 --- a/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/api/UpdateRegisteredExtensionCmdTest.java +++ b/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/api/UpdateRegisteredExtensionCmdTest.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.framework.extensions.api; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -31,6 +32,7 @@ import static org.mockito.Mockito.when; import java.util.EnumSet; import java.util.Map; +import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.ServerApiException; @@ -42,6 +44,8 @@ import org.junit.Test; import org.mockito.Mockito; import org.springframework.test.util.ReflectionTestUtils; +import com.cloud.user.Account; + public class UpdateRegisteredExtensionCmdTest { private UpdateRegisteredExtensionCmd cmd; @@ -60,6 +64,48 @@ public class UpdateRegisteredExtensionCmdTest { assertNull(cmd.getExtensionId()); } + @Test + public void extensionIdReturnsValueWhenSet() { + Long extensionId = 42L; + ReflectionTestUtils.setField(cmd, "extensionId", extensionId); + assertEquals(extensionId, cmd.getExtensionId()); + } + + @Test + public void cleanupDetailsReturnsNullWhenUnset() { + ReflectionTestUtils.setField(cmd, "cleanupDetails", null); + assertNull(cmd.isCleanupDetails()); + } + + @Test + public void cleanupDetailsReturnsTrueWhenSet() { + ReflectionTestUtils.setField(cmd, "cleanupDetails", true); + assertTrue(cmd.isCleanupDetails()); + } + + @Test + public void cleanupDetailsReturnsFalseWhenSetToFalse() { + ReflectionTestUtils.setField(cmd, "cleanupDetails", false); + assertFalse(cmd.isCleanupDetails()); + } + + @Test + public void getEntityOwnerIdReturnsSystemAccountId() { + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } + + @Test + public void getApiResourceTypeReturnsExtension() { + assertEquals(ApiCommandResourceType.Extension, cmd.getApiResourceType()); + } + + @Test + public void getApiResourceIdReturnsExtensionId() { + Long extensionId = 99L; + ReflectionTestUtils.setField(cmd, "extensionId", extensionId); + assertEquals(extensionId, cmd.getApiResourceId()); + } + @Test public void resourceIdReturnsValueWhenSet() { String resourceId = "resource-123"; diff --git a/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/dao/ExtensionResourceMapDaoImplTest.java b/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/dao/ExtensionResourceMapDaoImplTest.java index 76a0175e757..9b389411ba8 100644 --- a/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/dao/ExtensionResourceMapDaoImplTest.java +++ b/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/dao/ExtensionResourceMapDaoImplTest.java @@ -83,4 +83,55 @@ public class ExtensionResourceMapDaoImplTest { when(dao.listResourceIdsByExtensionIdAndType(1L, ExtensionResourceMap.ResourceType.Cluster)).thenReturn(expectedIds); assertEquals(expectedIds, dao.listResourceIdsByExtensionIdAndType(1L, ExtensionResourceMap.ResourceType.Cluster)); } + + // ----------------------------------------------------------------------- + // Tests for new methods: listByResourceIdAndType, listResourceIdsByType + // ----------------------------------------------------------------------- + + @Test + public void listByResourceIdAndTypeReturnsEmptyListWhenNoMatch() { + when(dao.listByResourceIdAndType(999L, ExtensionResourceMap.ResourceType.PhysicalNetwork)).thenReturn(List.of()); + assertTrue(dao.listByResourceIdAndType(999L, ExtensionResourceMap.ResourceType.PhysicalNetwork).isEmpty()); + } + + @Test + public void listByResourceIdAndTypeReturnsMatchingEntries() { + ExtensionResourceMapVO map1 = new ExtensionResourceMapVO(); + map1.setResourceId(42L); + map1.setResourceType(ExtensionResourceMap.ResourceType.PhysicalNetwork); + ExtensionResourceMapVO map2 = new ExtensionResourceMapVO(); + map2.setResourceId(42L); + map2.setResourceType(ExtensionResourceMap.ResourceType.PhysicalNetwork); + List expected = List.of(map1, map2); + when(dao.listByResourceIdAndType(42L, ExtensionResourceMap.ResourceType.PhysicalNetwork)).thenReturn(expected); + List result = dao.listByResourceIdAndType(42L, ExtensionResourceMap.ResourceType.PhysicalNetwork); + assertEquals(2, result.size()); + assertEquals(expected, result); + } + + @Test + public void listByResourceIdAndTypeDifferentiatesResourceTypes() { + ExtensionResourceMapVO clusterMap = new ExtensionResourceMapVO(); + clusterMap.setResourceType(ExtensionResourceMap.ResourceType.Cluster); + when(dao.listByResourceIdAndType(10L, ExtensionResourceMap.ResourceType.Cluster)).thenReturn(List.of(clusterMap)); + when(dao.listByResourceIdAndType(10L, ExtensionResourceMap.ResourceType.PhysicalNetwork)).thenReturn(List.of()); + + assertEquals(1, dao.listByResourceIdAndType(10L, ExtensionResourceMap.ResourceType.Cluster).size()); + assertTrue(dao.listByResourceIdAndType(10L, ExtensionResourceMap.ResourceType.PhysicalNetwork).isEmpty()); + } + + @Test + public void listResourceIdsByTypeReturnsEmptyListWhenNoMatch() { + when(dao.listResourceIdsByType(ExtensionResourceMap.ResourceType.PhysicalNetwork)).thenReturn(List.of()); + assertTrue(dao.listResourceIdsByType(ExtensionResourceMap.ResourceType.PhysicalNetwork).isEmpty()); + } + + @Test + public void listResourceIdsByTypeReturnsMatchingIds() { + List expectedIds = List.of(5L, 10L, 15L); + when(dao.listResourceIdsByType(ExtensionResourceMap.ResourceType.PhysicalNetwork)).thenReturn(expectedIds); + List result = dao.listResourceIdsByType(ExtensionResourceMap.ResourceType.PhysicalNetwork); + assertEquals(3, result.size()); + assertEquals(expectedIds, result); + } } diff --git a/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/manager/ExtensionsManagerImplTest.java b/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/manager/ExtensionsManagerImplTest.java index 05290ea9511..7356bffb5a9 100644 --- a/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/manager/ExtensionsManagerImplTest.java +++ b/framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/manager/ExtensionsManagerImplTest.java @@ -63,6 +63,7 @@ import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.extension.CustomActionResultResponse; import org.apache.cloudstack.extension.Extension; import org.apache.cloudstack.extension.ExtensionCustomAction; +import org.apache.cloudstack.extension.ExtensionHelper; import org.apache.cloudstack.extension.ExtensionResourceMap; import org.apache.cloudstack.framework.extensions.api.AddCustomActionCmd; import org.apache.cloudstack.framework.extensions.api.CreateExtensionCmd; @@ -132,6 +133,7 @@ import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkServiceMapDao; import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.element.NetworkElement; import org.apache.cloudstack.extension.NetworkCustomActionProvider; @@ -207,6 +209,9 @@ public class ExtensionsManagerImplTest { @Mock private NetworkModel networkModel; + @Mock + private PhysicalNetworkServiceProviderDao physicalNetworkServiceProviderDao; + @Before public void setUp() { MockitoAnnotations.openMocks(this); @@ -2438,6 +2443,373 @@ public class ExtensionsManagerImplTest { assertTrue(resp.getResult().get(ApiConstants.DETAILS).contains("No network service provider")); } + // ----------------------------------------------------------------------- + // Tests for getExtensionFromResource with Network resource type + // ----------------------------------------------------------------------- + + @Test + public void getExtensionFromResourceReturnsExtensionForNetworkWithProviderMatch() { + Network network = mock(Network.class); + when(network.getId()).thenReturn(10L); + when(network.getPhysicalNetworkId()).thenReturn(5L); + when(entityManager.findByUuid(eq(Network.class), eq("net-uuid"))).thenReturn(network); + + List providers = List.of("my-ext-provider"); + when(networkServiceMapDao.getDistinctProviders(10L)).thenReturn(providers); + + ExtensionVO ext = mock(ExtensionVO.class); + doReturn(ext).when(extensionsManager).getExtensionForPhysicalNetworkAndProvider(5L, "my-ext-provider"); + + Extension result = extensionsManager.getExtensionFromResource(ExtensionCustomAction.ResourceType.Network, "net-uuid"); + assertEquals(ext, result); + } + + @Test + public void getExtensionFromResourceFallsBackToFirstMappingForNetwork() { + Network network = mock(Network.class); + when(network.getId()).thenReturn(10L); + when(network.getPhysicalNetworkId()).thenReturn(5L); + when(entityManager.findByUuid(eq(Network.class), eq("net-uuid"))).thenReturn(network); + + when(networkServiceMapDao.getDistinctProviders(10L)).thenReturn(Collections.emptyList()); + + ExtensionResourceMapVO mapVO = mock(ExtensionResourceMapVO.class); + when(mapVO.getExtensionId()).thenReturn(99L); + when(extensionResourceMapDao.listByResourceIdAndType(5L, ExtensionResourceMap.ResourceType.PhysicalNetwork)) + .thenReturn(List.of(mapVO)); + ExtensionVO ext = mock(ExtensionVO.class); + when(extensionDao.findById(99L)).thenReturn(ext); + + Extension result = extensionsManager.getExtensionFromResource(ExtensionCustomAction.ResourceType.Network, "net-uuid"); + assertEquals(ext, result); + } + + @Test + public void getExtensionFromResourceReturnsNullForNetworkWithNullPhysicalNetworkId() { + Network network = mock(Network.class); + when(network.getId()).thenReturn(10L); + when(network.getPhysicalNetworkId()).thenReturn(null); + when(entityManager.findByUuid(eq(Network.class), eq("net-uuid"))).thenReturn(network); + + Extension result = extensionsManager.getExtensionFromResource(ExtensionCustomAction.ResourceType.Network, "net-uuid"); + assertNull(result); + } + + // ----------------------------------------------------------------------- + // Tests for listExtensions with resourceId + resourceType (PhysicalNetwork) + // ----------------------------------------------------------------------- + + @Test + public void listExtensionsWithPhysicalNetworkResourceReturnsFilteredExtensions() { + ListExtensionsCmd cmd = mock(ListExtensionsCmd.class); + when(cmd.getResourceId()).thenReturn("pnet-uuid"); + when(cmd.getResourceType()).thenReturn(ExtensionResourceMap.ResourceType.PhysicalNetwork.name()); + when(cmd.getExtensionId()).thenReturn(null); + when(cmd.getName()).thenReturn(null); + when(cmd.getType()).thenReturn(null); + when(cmd.getDetails()).thenReturn(null); + + PhysicalNetworkVO physNet = mock(PhysicalNetworkVO.class); + when(physNet.getId()).thenReturn(42L); + when(physicalNetworkDao.findByUuid("pnet-uuid")).thenReturn(physNet); + + ExtensionResourceMapVO mapVO = mock(ExtensionResourceMapVO.class); + when(mapVO.getExtensionId()).thenReturn(100L); + when(extensionResourceMapDao.listByResourceIdAndType(42L, ExtensionResourceMap.ResourceType.PhysicalNetwork)) + .thenReturn(List.of(mapVO)); + + ExtensionVO ext = mock(ExtensionVO.class); + when(ext.getType()).thenReturn(Extension.Type.NetworkOrchestrator); + when(extensionDao.findById(100L)).thenReturn(ext); + + ExtensionResponse resp = mock(ExtensionResponse.class); + doReturn(resp).when(extensionsManager).createExtensionResponse(eq(ext), any()); + + List result = extensionsManager.listExtensions(cmd); + assertEquals(1, result.size()); + assertEquals(resp, result.get(0)); + } + + @Test(expected = InvalidParameterValueException.class) + public void listExtensionsWithInvalidResourceTypeThrows() { + ListExtensionsCmd cmd = mock(ListExtensionsCmd.class); + when(cmd.getResourceId()).thenReturn("resource-uuid"); + when(cmd.getResourceType()).thenReturn("InvalidType"); + when(cmd.getExtensionId()).thenReturn(null); + when(cmd.getName()).thenReturn(null); + when(cmd.getType()).thenReturn(null); + + extensionsManager.listExtensions(cmd); + } + + // ----------------------------------------------------------------------- + // Tests for registerExtensionWithPhysicalNetwork + // ----------------------------------------------------------------------- + + @Test + public void registerExtensionWithPhysicalNetworkSucceeds() { + RegisterExtensionCmd cmd = mock(RegisterExtensionCmd.class); + when(cmd.getResourceType()).thenReturn(ExtensionResourceMap.ResourceType.PhysicalNetwork.name()); + when(cmd.getResourceId()).thenReturn("pnet-uuid"); + when(cmd.getExtensionId()).thenReturn(1L); + when(cmd.getDetails()).thenReturn(null); + + ExtensionVO extension = mock(ExtensionVO.class); + when(extension.getType()).thenReturn(Extension.Type.NetworkOrchestrator); + when(extension.getName()).thenReturn("my-ext"); + when(extension.getId()).thenReturn(1L); + when(extensionDao.findById(1L)).thenReturn(extension); + + PhysicalNetworkVO physNet = mock(PhysicalNetworkVO.class); + when(physNet.getId()).thenReturn(42L); + when(physicalNetworkDao.findByUuid("pnet-uuid")).thenReturn(physNet); + + when(extensionResourceMapDao.listByResourceIdAndType(42L, ExtensionResourceMap.ResourceType.PhysicalNetwork)) + .thenReturn(Collections.emptyList()); + when(extensionDetailsDao.listDetailsKeyPairs(1L)).thenReturn(Collections.emptyMap()); + + ExtensionResourceMapVO savedMap = mock(ExtensionResourceMapVO.class); + when(extensionResourceMapDao.persist(any())).thenReturn(savedMap); + + when(physicalNetworkServiceProviderDao.findByServiceProvider(42L, "my-ext")).thenReturn(null); + + Extension result = extensionsManager.registerExtensionWithResource(cmd); + assertEquals(extension, result); + } + + @Test(expected = InvalidParameterValueException.class) + public void registerExtensionWithPhysicalNetworkFailsForNonNetworkOrchestratorType() { + RegisterExtensionCmd cmd = mock(RegisterExtensionCmd.class); + when(cmd.getResourceType()).thenReturn(ExtensionResourceMap.ResourceType.PhysicalNetwork.name()); + when(cmd.getResourceId()).thenReturn("pnet-uuid"); + when(cmd.getExtensionId()).thenReturn(1L); + + ExtensionVO extension = mock(ExtensionVO.class); + when(extension.getType()).thenReturn(Extension.Type.Orchestrator); + when(extension.getName()).thenReturn("orch-ext"); + when(extensionDao.findById(1L)).thenReturn(extension); + + PhysicalNetworkVO physNet = mock(PhysicalNetworkVO.class); + when(physNet.getId()).thenReturn(42L); + when(physicalNetworkDao.findByUuid("pnet-uuid")).thenReturn(physNet); + + extensionsManager.registerExtensionWithResource(cmd); + } + + // ----------------------------------------------------------------------- + // Tests for getExtensionIdForPhysicalNetwork + // ----------------------------------------------------------------------- + + @Test + public void getExtensionIdForPhysicalNetworkReturnsIdWhenMapped() { + ExtensionResourceMapVO mapVO = mock(ExtensionResourceMapVO.class); + when(mapVO.getExtensionId()).thenReturn(55L); + when(extensionResourceMapDao.listByResourceIdAndType(10L, ExtensionResourceMap.ResourceType.PhysicalNetwork)) + .thenReturn(List.of(mapVO)); + + Long result = extensionsManager.getExtensionIdForPhysicalNetwork(10L); + assertEquals(Long.valueOf(55L), result); + } + + @Test + public void getExtensionIdForPhysicalNetworkReturnsNullWhenNotMapped() { + when(extensionResourceMapDao.listByResourceIdAndType(10L, ExtensionResourceMap.ResourceType.PhysicalNetwork)) + .thenReturn(Collections.emptyList()); + + Long result = extensionsManager.getExtensionIdForPhysicalNetwork(10L); + assertNull(result); + } + + // ----------------------------------------------------------------------- + // Tests for getExtensionForPhysicalNetworkAndProvider + // ----------------------------------------------------------------------- + + @Test + public void getExtensionForPhysicalNetworkAndProviderReturnsMatchingExtension() { + ExtensionResourceMapVO mapVO = mock(ExtensionResourceMapVO.class); + when(mapVO.getExtensionId()).thenReturn(10L); + when(extensionResourceMapDao.listByResourceIdAndType(5L, ExtensionResourceMap.ResourceType.PhysicalNetwork)) + .thenReturn(List.of(mapVO)); + + ExtensionVO ext = mock(ExtensionVO.class); + when(ext.getName()).thenReturn("MyExt"); + when(extensionDao.findById(10L)).thenReturn(ext); + + Extension result = extensionsManager.getExtensionForPhysicalNetworkAndProvider(5L, "myext"); + assertEquals(ext, result); + } + + @Test + public void getExtensionForPhysicalNetworkAndProviderReturnsNullWhenNameDoesNotMatch() { + ExtensionResourceMapVO mapVO = mock(ExtensionResourceMapVO.class); + when(mapVO.getExtensionId()).thenReturn(10L); + when(extensionResourceMapDao.listByResourceIdAndType(5L, ExtensionResourceMap.ResourceType.PhysicalNetwork)) + .thenReturn(List.of(mapVO)); + + ExtensionVO ext = mock(ExtensionVO.class); + when(ext.getName()).thenReturn("OtherExt"); + when(extensionDao.findById(10L)).thenReturn(ext); + + Extension result = extensionsManager.getExtensionForPhysicalNetworkAndProvider(5L, "myext"); + assertNull(result); + } + + @Test + public void getExtensionForPhysicalNetworkAndProviderReturnsNullForNullProviderName() { + Extension result = extensionsManager.getExtensionForPhysicalNetworkAndProvider(5L, null); + assertNull(result); + } + + // ----------------------------------------------------------------------- + // Tests for getAllResourceMapDetailsForExtensionOnPhysicalNetwork + // ----------------------------------------------------------------------- + + @Test + public void getAllResourceMapDetailsForExtensionOnPhysicalNetworkReturnsDetails() { + ExtensionResourceMapVO mapVO = mock(ExtensionResourceMapVO.class); + when(mapVO.getExtensionId()).thenReturn(10L); + when(mapVO.getId()).thenReturn(100L); + when(extensionResourceMapDao.listByResourceIdAndType(5L, ExtensionResourceMap.ResourceType.PhysicalNetwork)) + .thenReturn(List.of(mapVO)); + when(extensionResourceMapDetailsDao.listDetailsKeyPairs(100L)) + .thenReturn(Map.of("host", "192.168.1.1")); + + Map result = extensionsManager.getAllResourceMapDetailsForExtensionOnPhysicalNetwork(5L, 10L); + assertEquals("192.168.1.1", result.get("host")); + } + + @Test + public void getAllResourceMapDetailsForExtensionOnPhysicalNetworkReturnsEmptyWhenNoMapping() { + when(extensionResourceMapDao.listByResourceIdAndType(5L, ExtensionResourceMap.ResourceType.PhysicalNetwork)) + .thenReturn(Collections.emptyList()); + + Map result = extensionsManager.getAllResourceMapDetailsForExtensionOnPhysicalNetwork(5L, 10L); + assertTrue(result.isEmpty()); + } + + // ----------------------------------------------------------------------- + // Tests for isNetworkExtensionProvider + // ----------------------------------------------------------------------- + + @Test + public void isNetworkExtensionProviderReturnsTrueWhenProviderMatchesExtension() { + ExtensionVO ext = mock(ExtensionVO.class); + when(ext.getName()).thenReturn("my-ext"); + when(extensionDao.listByType(Extension.Type.NetworkOrchestrator)).thenReturn(List.of(ext)); + + assertTrue(extensionsManager.isNetworkExtensionProvider("my-ext")); + } + + @Test + public void isNetworkExtensionProviderReturnsFalseWhenNoMatch() { + when(extensionDao.listByType(Extension.Type.NetworkOrchestrator)).thenReturn(Collections.emptyList()); + assertFalse(extensionsManager.isNetworkExtensionProvider("unknown")); + } + + @Test + public void isNetworkExtensionProviderReturnsFalseForNullProvider() { + assertFalse(extensionsManager.isNetworkExtensionProvider(null)); + } + + // ----------------------------------------------------------------------- + // Tests for listExtensionsByType + // ----------------------------------------------------------------------- + + @Test + public void listExtensionsByTypeReturnsExtensionsForType() { + ExtensionVO ext = mock(ExtensionVO.class); + when(extensionDao.listByType(Extension.Type.NetworkOrchestrator)).thenReturn(List.of(ext)); + + List result = extensionsManager.listExtensionsByType(Extension.Type.NetworkOrchestrator); + assertEquals(1, result.size()); + assertEquals(ext, result.get(0)); + } + + @Test + public void listExtensionsByTypeReturnsEmptyForNullType() { + List result = extensionsManager.listExtensionsByType(null); + assertTrue(result.isEmpty()); + } + + @Test + public void listExtensionsByTypeReturnsEmptyWhenNoExtensions() { + when(extensionDao.listByType(Extension.Type.NetworkOrchestrator)).thenReturn(Collections.emptyList()); + List result = extensionsManager.listExtensionsByType(Extension.Type.NetworkOrchestrator); + assertTrue(result.isEmpty()); + } + + // ----------------------------------------------------------------------- + // Tests for getNetworkCapabilitiesForProvider + // ----------------------------------------------------------------------- + + @Test + public void getNetworkCapabilitiesForProviderReturnsCapabilitiesFromExtensionDetails() { + long physNetId = 10L; + String providerName = "my-ext"; + + ExtensionVO ext = mock(ExtensionVO.class); + when(ext.getId()).thenReturn(5L); + doReturn(ext).when(extensionsManager).getExtensionForPhysicalNetworkAndProvider(physNetId, providerName); + + when(extensionDetailsDao.listDetailsKeyPairs(5L)).thenReturn(Map.of( + ExtensionHelper.NETWORK_SERVICES_DETAIL_KEY, + "SourceNat,StaticNat")); + + Map> result = + extensionsManager.getNetworkCapabilitiesForProvider(physNetId, providerName); + assertNotNull(result); + assertTrue(result.containsKey(Network.Service.SourceNat)); + } + + @Test + public void getNetworkCapabilitiesForProviderReturnsEmptyMapForNullProvider() { + Map> result = + extensionsManager.getNetworkCapabilitiesForProvider(10L, null); + assertTrue(result.isEmpty()); + } + + // ----------------------------------------------------------------------- + // Tests for validateNetworkServicesSubset + // ----------------------------------------------------------------------- + + @Test + public void validateNetworkServicesSubsetDoesNothingForBlankServices() { + Extension ext = mock(Extension.class); + extensionsManager.validateNetworkServicesSubset(ext, ""); + // no exception expected + } + + @Test + public void validateNetworkServicesSubsetDoesNothingWhenAllowedIsEmpty() { + Extension ext = mock(Extension.class); + when(ext.getId()).thenReturn(1L); + when(extensionDetailsDao.listDetailsKeyPairs(1L)).thenReturn(Collections.emptyMap()); + extensionsManager.validateNetworkServicesSubset(ext, "SourceNat"); + // no exception - when no services declared, accept any + } + + @Test(expected = InvalidParameterValueException.class) + public void validateNetworkServicesSubsetThrowsForUnsupportedService() { + Extension ext = mock(Extension.class); + when(ext.getId()).thenReturn(1L); + when(ext.getName()).thenReturn("my-ext"); + when(extensionDetailsDao.listDetailsKeyPairs(1L)).thenReturn(Map.of( + ExtensionHelper.NETWORK_SERVICES_DETAIL_KEY, + "SourceNat,Firewall")); + extensionsManager.validateNetworkServicesSubset(ext, "StaticNat"); + } + + @Test + public void validateNetworkServicesSubsetDoesNothingForValidSubset() { + Extension ext = mock(Extension.class); + when(ext.getId()).thenReturn(1L); + when(extensionDetailsDao.listDetailsKeyPairs(1L)).thenReturn(Map.of( + ExtensionHelper.NETWORK_SERVICES_DETAIL_KEY, + "SourceNat,Firewall,StaticNat")); + extensionsManager.validateNetworkServicesSubset(ext, "SourceNat,Firewall"); + // no exception expected + } + @Test public void runNetworkCustomActionFailsWhenProviderReturnsNull() { Network network = mock(Network.class); diff --git a/server/src/test/java/com/cloud/network/NetworkModelImplTest.java b/server/src/test/java/com/cloud/network/NetworkModelImplTest.java index b70dc74bdb9..4a027b2ae2d 100644 --- a/server/src/test/java/com/cloud/network/NetworkModelImplTest.java +++ b/server/src/test/java/com/cloud/network/NetworkModelImplTest.java @@ -18,6 +18,9 @@ package com.cloud.network; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -54,6 +57,9 @@ import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkServiceMapDao; import com.cloud.network.dao.NetworkServiceMapVO; import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderVO; import com.cloud.network.element.NetworkElement; import com.cloud.network.element.VpcVirtualRouterElement; import com.cloud.network.vpc.VpcVO; @@ -67,6 +73,7 @@ import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; import com.cloud.vm.VirtualMachine; import org.apache.cloudstack.extension.ExtensionHelper; +import org.apache.cloudstack.framework.extensions.network.NetworkExtensionElement; @RunWith(MockitoJUnitRunner.class) public class NetworkModelImplTest { @@ -84,6 +91,15 @@ public class NetworkModelImplTest { @Mock private ExtensionHelper extensionHelper; + @Mock + private NetworkExtensionElement networkExtensionElement; + + @Mock + private PhysicalNetworkDao physicalNetworkDao; + + @Mock + private PhysicalNetworkServiceProviderDao physicalNetworkServiceProviderDao; + @Spy @InjectMocks private NetworkModelImpl networkModel = new NetworkModelImpl(); @@ -100,6 +116,9 @@ public class NetworkModelImplTest { networkModel._ntwkSrvcDao = networkServiceMapDao; networkModel._ntwkOfferingSrvcDao = networkOfferingServiceMapDao; ReflectionTestUtils.setField(networkModel, "extensionHelper", extensionHelper); + ReflectionTestUtils.setField(networkModel, "networkExtensionElement", networkExtensionElement); + ReflectionTestUtils.setField(networkModel, "_physicalNetworkDao", physicalNetworkDao); + ReflectionTestUtils.setField(networkModel, "_pNSPDao", physicalNetworkServiceProviderDao); Mockito.lenient().when(extensionHelper.isNetworkExtensionProvider(Mockito.anyString())).thenReturn(false); } @@ -278,4 +297,116 @@ public class NetworkModelImplTest { assertNotNull(result); } + + // ----------------------------------------------------------------------- + // Tests for getElementImplementingProvider with extension provider + // ----------------------------------------------------------------------- + + @Test + public void getElementImplementingProviderReturnsExtensionElementForExtensionProvider() { + String providerName = "my-ext-provider"; + // Provider is not in the static map, so element would be null + ReflectionTestUtils.setField(networkModel, "networkElements", new ArrayList<>()); + when(extensionHelper.isNetworkExtensionProvider(providerName)).thenReturn(true); + NetworkExtensionElement mockElement = mock(NetworkExtensionElement.class); + when(networkExtensionElement.withProviderName(providerName)).thenReturn(mockElement); + + NetworkElement result = networkModel.getElementImplementingProvider(providerName); + // When the element is a NetworkExtensionElement (which is a NetworkElement), result should not be null + assertNotNull(result); + } + + @Test + public void getElementImplementingProviderReturnsNullForUnknownNonExtensionProvider() { + String providerName = "unknown-provider"; + ReflectionTestUtils.setField(networkModel, "networkElements", new ArrayList<>()); + when(extensionHelper.isNetworkExtensionProvider(providerName)).thenReturn(false); + + NetworkElement result = networkModel.getElementImplementingProvider(providerName); + assertNull(result); + } + + // ----------------------------------------------------------------------- + // Tests for resolveProvider + // ----------------------------------------------------------------------- + + @Test + public void resolveProviderReturnsKnownProvider() { + Network.Provider result = networkModel.resolveProvider(Network.Provider.VirtualRouter.getName()); + assertNotNull(result); + assertEquals(Network.Provider.VirtualRouter, result); + } + + @Test + public void resolveProviderReturnsTransientProviderForExtensionProvider() { + String extensionName = "my-ext-network-provider"; + when(extensionHelper.isNetworkExtensionProvider(extensionName)).thenReturn(true); + + Network.Provider result = networkModel.resolveProvider(extensionName); + assertNotNull(result); + assertEquals(extensionName, result.getName()); + } + + @Test + public void resolveProviderReturnsNullForUnknownNonExtensionProvider() { + String providerName = "totally-unknown"; + when(extensionHelper.isNetworkExtensionProvider(providerName)).thenReturn(false); + + Network.Provider result = networkModel.resolveProvider(providerName); + assertNull(result); + } + + // ----------------------------------------------------------------------- + // Tests for canElementEnableIndividualServicesByName + // ----------------------------------------------------------------------- + + @Test + public void canElementEnableIndividualServicesByNameReturnsFalseForNullProvider() { + assertFalse(networkModel.canElementEnableIndividualServicesByName(null)); + } + + @Test + public void canElementEnableIndividualServicesByNameReturnsFalseForUnknownProvider() { + when(extensionHelper.isNetworkExtensionProvider("unknown")).thenReturn(false); + assertFalse(networkModel.canElementEnableIndividualServicesByName("unknown")); + } + + // ----------------------------------------------------------------------- + // Tests for getExternalProviderCapabilities + // ----------------------------------------------------------------------- + + @Test + public void getExternalProviderCapabilitiesCallsExtensionHelper() { + Map> caps = new HashMap<>(); + when(extensionHelper.getNetworkCapabilitiesForProvider(10L, "my-ext")).thenReturn(caps); + + Map> result = + networkModel.getExternalProviderCapabilities(10L, "my-ext"); + assertEquals(caps, result); + } + + // ----------------------------------------------------------------------- + // Tests for isServiceProvidedByNsp (via listSupportedNetworkServiceProviders) + // ----------------------------------------------------------------------- + + @Test + public void listSupportedNetworkServiceProvidersIncludesExtensionBackedProviders() { + com.cloud.network.dao.PhysicalNetworkVO physNet = mock(com.cloud.network.dao.PhysicalNetworkVO.class); + when(physNet.getId()).thenReturn(1L); + when(physicalNetworkDao.listAll()).thenReturn(List.of(physNet)); + + PhysicalNetworkServiceProviderVO nsp = mock(PhysicalNetworkServiceProviderVO.class); + when(nsp.getProviderName()).thenReturn("my-ext"); + when(physicalNetworkServiceProviderDao.listBy(1L)).thenReturn(List.of(nsp)); + when(extensionHelper.isNetworkExtensionProvider("my-ext")).thenReturn(true); + + // networkElements is empty so no standard providers found + ReflectionTestUtils.setField(networkModel, "networkElements", new ArrayList<>()); + + // We call with null service to test the inclusion path (parameter is service name String) + List result = networkModel.listSupportedNetworkServiceProviders(null); + + boolean found = result.stream().anyMatch(p -> "my-ext".equalsIgnoreCase(p.getName())); + assertTrue("Extension-backed provider should be included", found); + } } diff --git a/server/src/test/java/com/cloud/network/firewall/FirewallManagerImplTest.java b/server/src/test/java/com/cloud/network/firewall/FirewallManagerImplTest.java new file mode 100644 index 00000000000..f90b424857c --- /dev/null +++ b/server/src/test/java/com/cloud/network/firewall/FirewallManagerImplTest.java @@ -0,0 +1,179 @@ +// 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.network.firewall; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +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.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 org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.NetworkModel; +import com.cloud.network.dao.NetworkServiceMapDao; +import com.cloud.network.element.FirewallServiceProvider; +import com.cloud.network.element.NetworkElement; +import com.cloud.network.element.PortForwardingServiceProvider; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.PortForwardingRule; + +@RunWith(MockitoJUnitRunner.Silent.class) +public class FirewallManagerImplTest { + + interface MockFwElement extends NetworkElement, FirewallServiceProvider {} + interface MockPfElement extends NetworkElement, PortForwardingServiceProvider {} + + @Spy + @InjectMocks + private FirewallManagerImpl firewallManager; + + @Mock + private NetworkModel _networkModel; + + @Mock + private NetworkServiceMapDao networkServiceMapDao; + + @Before + public void setUp() { + ReflectionTestUtils.setField(firewallManager, "_firewallElements", Collections.emptyList()); + ReflectionTestUtils.setField(firewallManager, "_pfElements", Collections.emptyList()); + ReflectionTestUtils.setField(firewallManager, "_staticNatElements", Collections.emptyList()); + ReflectionTestUtils.setField(firewallManager, "_networkAclElements", Collections.emptyList()); + } + + // ----------------------------------------------------------------------- + // Tests for applyRules with extension-backed Firewall provider + // ----------------------------------------------------------------------- + + @Test + public void applyRulesFirewallHandledByExtensionProvider() throws ResourceUnavailableException { + Network network = mock(Network.class); + when(network.getId()).thenReturn(1L); + + FirewallRule rule = mock(FirewallRule.class); + List rules = List.of(rule); + + // No standard firewall elements handle it + String extProviderName = "my-ext-fw-provider"; + when(networkServiceMapDao.getProviderForServiceInNetwork(1L, Network.Service.Firewall)) + .thenReturn(extProviderName); + + // The element implementing the provider is both NetworkElement and FirewallServiceProvider + MockFwElement element = mock(MockFwElement.class); + when(element.applyFWRules(eq(network), any())).thenReturn(true); + when(_networkModel.getElementImplementingProvider(extProviderName)).thenReturn(element); + + boolean result = firewallManager.applyRules(network, FirewallRule.Purpose.Firewall, rules); + assertTrue(result); + verify(element).applyFWRules(eq(network), any()); + } + + @Test + public void applyRulesFirewallReturnsFalseWhenNoExtensionProviderFound() throws ResourceUnavailableException { + Network network = mock(Network.class); + when(network.getId()).thenReturn(2L); + + FirewallRule rule = mock(FirewallRule.class); + List rules = List.of(rule); + + // No standard provider and no extension provider found + when(networkServiceMapDao.getProviderForServiceInNetwork(2L, Network.Service.Firewall)) + .thenReturn(null); + + boolean result = firewallManager.applyRules(network, FirewallRule.Purpose.Firewall, rules); + assertFalse(result); + verify(_networkModel, never()).getElementImplementingProvider(any()); + } + + // ----------------------------------------------------------------------- + // Tests for applyRules with extension-backed PortForwarding provider + // ----------------------------------------------------------------------- + + @Test + public void applyRulesPortForwardingHandledByExtensionProvider() throws ResourceUnavailableException { + Network network = mock(Network.class); + when(network.getId()).thenReturn(3L); + + PortForwardingRule rule = mock(PortForwardingRule.class); + @SuppressWarnings("unchecked") + List rules = List.of(rule); + + String extProviderName = "my-ext-pf-provider"; + when(networkServiceMapDao.getProviderForServiceInNetwork(3L, Network.Service.PortForwarding)) + .thenReturn(extProviderName); + + MockPfElement element = mock(MockPfElement.class); + when(element.applyPFRules(eq(network), any())).thenReturn(true); + when(_networkModel.getElementImplementingProvider(extProviderName)).thenReturn(element); + + boolean result = firewallManager.applyRules(network, FirewallRule.Purpose.PortForwarding, rules); + assertTrue(result); + verify(element).applyPFRules(eq(network), any()); + } + + @Test + public void applyRulesPortForwardingReturnsFalseWhenNoExtensionProviderFound() throws ResourceUnavailableException { + Network network = mock(Network.class); + when(network.getId()).thenReturn(4L); + + PortForwardingRule rule = mock(PortForwardingRule.class); + List rules = List.of(rule); + + when(networkServiceMapDao.getProviderForServiceInNetwork(4L, Network.Service.PortForwarding)) + .thenReturn(null); + + boolean result = firewallManager.applyRules(network, FirewallRule.Purpose.PortForwarding, rules); + assertFalse(result); + } + + // ----------------------------------------------------------------------- + // Tests for StaticNat (handled by Firewall elements) + // ----------------------------------------------------------------------- + + @Test + public void applyRulesStaticNatReturnsFalseWhenNoProviderFound() throws ResourceUnavailableException { + Network network = mock(Network.class); + when(network.getId()).thenReturn(5L); + + FirewallRule rule = mock(FirewallRule.class); + List rules = List.of(rule); + + when(networkServiceMapDao.getProviderForServiceInNetwork(5L, Network.Service.Firewall)) + .thenReturn(null); + + boolean result = firewallManager.applyRules(network, FirewallRule.Purpose.StaticNat, rules); + assertFalse(result); + } +} + diff --git a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java index a8a77770431..2acad0c2b45 100644 --- a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java +++ b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java @@ -43,6 +43,7 @@ import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.NetworkDao; import com.cloud.network.element.NetworkElement; +import com.cloud.network.element.VpcProvider; import com.cloud.network.router.CommandSetupHelper; import com.cloud.network.router.NetworkHelper; import com.cloud.network.router.VirtualRouter; @@ -72,6 +73,8 @@ import org.apache.cloudstack.api.command.admin.vpc.CreateVPCOfferingCmd; import org.apache.cloudstack.api.command.user.vpc.UpdateVPCCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.extension.Extension; +import org.apache.cloudstack.extension.ExtensionHelper; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; import org.apache.cloudstack.network.RoutedIpv4Manager; @@ -90,6 +93,7 @@ import org.springframework.test.util.ReflectionTestUtils; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -609,4 +613,49 @@ public class VpcManagerImplTest { Assert.assertTrue(manager.isNetworkOnVpcEnabledConserveMode(network)); } + // ----------------------------------------------------------------------- + // Tests for getVpcElements with extension-backed NetworkOrchestrator + // ----------------------------------------------------------------------- + + @Test + public void getVpcElementsIncludesExtensionBackedVpcProvider() { + manager.setVpcElements(null); + + Mockito.when(networkModel.getElementImplementingProvider(Provider.VPCVirtualRouter.getName())).thenReturn(null); + Mockito.when(networkModel.getElementImplementingProvider(Provider.JuniperContrailVpcRouter.getName())).thenReturn(null); + + Extension ext = mock(Extension.class); + Mockito.when(ext.getName()).thenReturn("my-vpc-ext"); + + ExtensionHelper extHelper = mock(ExtensionHelper.class); + Mockito.when(extHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator)) + .thenReturn(List.of(ext)); + manager.extensionHelper = extHelper; + + // The element for the extension also implements VpcProvider + VpcProvider vpcProviderElement = mock(VpcProvider.class); + Mockito.when(networkModel.getElementImplementingProvider("my-vpc-ext")).thenReturn((NetworkElement) vpcProviderElement); + + List result = manager.getVpcElements(); + Assert.assertNotNull(result); + Assert.assertTrue(result.contains(vpcProviderElement)); + } + + @Test + public void getVpcElementsReturnsEmptyListWhenNoStaticNorExtensionProviders() { + manager.setVpcElements(null); + + Mockito.when(networkModel.getElementImplementingProvider(Provider.VPCVirtualRouter.getName())).thenReturn(null); + Mockito.when(networkModel.getElementImplementingProvider(Provider.JuniperContrailVpcRouter.getName())).thenReturn(null); + + ExtensionHelper extHelper = mock(ExtensionHelper.class); + Mockito.when(extHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator)) + .thenReturn(Collections.emptyList()); + manager.extensionHelper = extHelper; + + List result = manager.getVpcElements(); + Assert.assertNotNull(result); + Assert.assertTrue(result.isEmpty()); + } + }