This is an automated email from the ASF dual-hosted git repository.

exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 43b5b883d7e NIFI-16082 Fixed Connection update when the current 
destination is running (#11400)
43b5b883d7e is described below

commit 43b5b883d7e65ef73a29572d1ebfde59896ad565
Author: Pierre Villard <[email protected]>
AuthorDate: Fri Aug 14 15:52:35 2026 +0200

    NIFI-16082 Fixed Connection update when the current destination is running 
(#11400)
    
    Signed-off-by: David Handermann <[email protected]>
---
 .../nifi/connectable/StandardConnection.java       |  39 +++---
 .../nifi/connectable/StandardConnectionTest.java   | 129 +++++++++++++++++++
 .../org/apache/nifi/connectable/Connection.java    |   6 +
 .../nifi/web/dao/impl/StandardConnectionDAO.java   |  57 ++++++---
 .../web/dao/impl/StandardConnectionDAOTest.java    | 139 +++++++++++++++++++++
 5 files changed, 337 insertions(+), 33 deletions(-)

diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/connectable/StandardConnection.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/connectable/StandardConnection.java
index fe712ab363d..96359e09ff0 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/connectable/StandardConnection.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/connectable/StandardConnection.java
@@ -292,6 +292,31 @@ public final class StandardConnection implements 
Connection {
             return;
         }
 
+        verifyCanUpdateDestination();
+
+        if (newDestination instanceof Funnel && newDestination.equals(source)) 
{
+            throw new IllegalStateException("Funnels do not support 
self-looping connections.");
+        }
+
+        try {
+            previousDestination.removeConnection(this);
+            this.destination.set(newDestination);
+            getSource().updateConnection(this);
+            newDestination.addConnection(this);
+        } catch (final RuntimeException e) {
+            this.destination.set(previousDestination);
+            throw e;
+        }
+    }
+
+    /**
+     * @throws IllegalStateException if the current destination is running and 
is not exempt, or FlowFiles from this
+     * Connection are currently held by the destination
+     */
+    @Override
+    public void verifyCanUpdateDestination() {
+        final Connectable previousDestination = destination.get();
+
         // Allow destination changes when the current destination is a Funnel, 
a LocalPort, or a
         // RemoteGroupPort. Funnels and LocalPorts cannot be stopped/started 
so they are exempt.
         // RemoteGroupPort represents an S2S ingress point: re-routing its 
incoming connections
@@ -310,20 +335,6 @@ public final class StandardConnection implements 
Connection {
         if (getFlowFileQueue().isUnacknowledgedFlowFile()) {
             throw new IllegalStateException("Cannot change destination of 
Connection because FlowFiles from this Connection are currently held by " + 
previousDestination);
         }
-
-        if (newDestination instanceof Funnel && newDestination.equals(source)) 
{
-            throw new IllegalStateException("Funnels do not support 
self-looping connections.");
-        }
-
-        try {
-            previousDestination.removeConnection(this);
-            this.destination.set(newDestination);
-            getSource().updateConnection(this);
-            newDestination.addConnection(this);
-        } catch (final RuntimeException e) {
-            this.destination.set(previousDestination);
-            throw e;
-        }
     }
 
     @Override
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/connectable/StandardConnectionTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/connectable/StandardConnectionTest.java
new file mode 100644
index 00000000000..e2f9d334973
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/connectable/StandardConnectionTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.nifi.connectable;
+
+import org.apache.nifi.controller.ProcessScheduler;
+import org.apache.nifi.controller.queue.FlowFileQueue;
+import org.apache.nifi.controller.queue.FlowFileQueueFactory;
+import org.apache.nifi.groups.ProcessGroup;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.remote.RemoteGroupPort;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class StandardConnectionTest {
+
+    private FlowFileQueue queue;
+
+    private StandardConnection connectionWithDestination(final Connectable 
source, final Connectable destination) {
+        queue = mock(FlowFileQueue.class);
+        final FlowFileQueueFactory queueFactory = 
mock(FlowFileQueueFactory.class);
+        when(queueFactory.createFlowFileQueue(any(), any(), 
any())).thenReturn(queue);
+
+        return new StandardConnection.Builder(mock(ProcessScheduler.class))
+                .id("connection-1")
+                .source(source)
+                .destination(destination)
+                .processGroup(mock(ProcessGroup.class))
+                .flowFileQueueFactory(queueFactory)
+                .relationships(List.of(Relationship.ANONYMOUS))
+                .build();
+    }
+
+    private StandardConnection connectionWithDestination(final Connectable 
destination) {
+        return connectionWithDestination(mock(Connectable.class), destination);
+    }
+
+    @Test
+    void testVerifyCanUpdateDestinationThrowsWhenCurrentDestinationRunning() {
+        final Connectable runningProcessor = mock(Connectable.class);
+        when(runningProcessor.isRunning()).thenReturn(true);
+        final StandardConnection connection = 
connectionWithDestination(runningProcessor);
+
+        assertThrows(IllegalStateException.class, 
connection::verifyCanUpdateDestination);
+    }
+
+    @Test
+    void testVerifyCanUpdateDestinationAllowsRunningFunnel() {
+        final Funnel runningFunnel = mock(Funnel.class);
+        when(runningFunnel.isRunning()).thenReturn(true);
+        final StandardConnection connection = 
connectionWithDestination(runningFunnel);
+
+        assertDoesNotThrow(connection::verifyCanUpdateDestination);
+    }
+
+    @Test
+    void testVerifyCanUpdateDestinationAllowsRunningLocalPort() {
+        final LocalPort runningLocalPort = mock(LocalPort.class);
+        when(runningLocalPort.isRunning()).thenReturn(true);
+        final StandardConnection connection = 
connectionWithDestination(runningLocalPort);
+
+        assertDoesNotThrow(connection::verifyCanUpdateDestination);
+    }
+
+    @Test
+    void testVerifyCanUpdateDestinationAllowsRunningRemoteGroupPort() {
+        final RemoteGroupPort runningRemotePort = mock(RemoteGroupPort.class);
+        when(runningRemotePort.isRunning()).thenReturn(true);
+        final StandardConnection connection = 
connectionWithDestination(runningRemotePort);
+
+        assertDoesNotThrow(connection::verifyCanUpdateDestination);
+    }
+
+    @Test
+    void testVerifyCanUpdateDestinationThrowsWhenFlowFilesHeld() {
+        final Funnel stoppedFunnel = mock(Funnel.class);
+        final StandardConnection connection = 
connectionWithDestination(stoppedFunnel);
+        when(queue.isUnacknowledgedFlowFile()).thenReturn(true);
+
+        assertThrows(IllegalStateException.class, 
connection::verifyCanUpdateDestination);
+    }
+
+    @Test
+    void 
testVerifyCanUpdateDestinationAllowsStoppedDestinationWithNoUnacknowledgedFlowFiles()
 {
+        final Connectable stoppedProcessor = mock(Connectable.class);
+        final StandardConnection connection = 
connectionWithDestination(stoppedProcessor);
+        when(queue.isUnacknowledgedFlowFile()).thenReturn(false);
+
+        assertDoesNotThrow(connection::verifyCanUpdateDestination);
+    }
+
+    @Test
+    void testSetDestinationNoOpWhenDestinationUnchanged() {
+        final Connectable runningProcessor = mock(Connectable.class);
+        when(runningProcessor.isRunning()).thenReturn(true);
+        final StandardConnection connection = 
connectionWithDestination(runningProcessor);
+
+        assertDoesNotThrow(() -> connection.setDestination(runningProcessor));
+    }
+
+    @Test
+    void testSetDestinationRejectsSelfLoopingFunnel() {
+        final Funnel funnel = mock(Funnel.class);
+        final Connectable currentDestination = mock(Connectable.class);
+        final StandardConnection connection = 
connectionWithDestination(funnel, currentDestination);
+
+        assertThrows(IllegalStateException.class, () -> 
connection.setDestination(funnel));
+    }
+}
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/connectable/Connection.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/connectable/Connection.java
index 423f52d4cec..1b801576c33 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/connectable/Connection.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/connectable/Connection.java
@@ -68,6 +68,12 @@ public interface Connection extends Authorizable, 
VersionedComponent {
 
     void setDestination(final Connectable newDestination);
 
+    /**
+     * Verifies that this Connection's destination may be changed, based 
solely on the current (existing) destination
+     * and the FlowFiles the Connection is holding.
+     */
+    void verifyCanUpdateDestination();
+
     void setProcessGroup(ProcessGroup processGroup);
 
     ProcessGroup getProcessGroup();
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
index a6d845d5f72..4a5691d7567 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
@@ -519,12 +519,16 @@ public class StandardConnectionDAO extends ComponentDAO 
implements ConnectionDAO
                 throw new ValidationException(validationErrors);
             }
 
-            // If destination is changing, ensure that current destination is 
not running. This check is done here, rather than
-            // in the Connection object itself because the Connection object 
itself does not know which updates are to occur and
-            // we don't want to prevent updating things like the connection 
name or backpressure just because the destination is running
-            final Connectable destination = connection.getDestination();
-            if (destination != null && destination.isRunning() && 
destination.getConnectableType() != ConnectableType.FUNNEL && 
destination.getConnectableType() != ConnectableType.INPUT_PORT) {
-                throw new 
ValidationException(Collections.singletonList("Cannot change the destination of 
connection because the current destination is running"));
+            // If the destination is changing, ensure the current destination 
is in a state that allows it to be changed.
+            // The check lives on the Connection (shared with setDestination) 
so the verify phase rejects exactly what the
+            // subsequent mutation would reject. It is applied only for an 
actual destination change so that other updates
+            // (name, backpressure, etc.) are not blocked merely because the 
current destination is running.
+            if (isDestinationChanging(connection, 
connectionDTO.getDestination())) {
+                try {
+                    connection.verifyCanUpdateDestination();
+                } catch (final IllegalStateException e) {
+                    throw new 
ValidationException(Collections.singletonList(e.getMessage()));
+                }
             }
 
             // verify that this connection supports modification
@@ -532,6 +536,32 @@ public class StandardConnectionDAO extends ComponentDAO 
implements ConnectionDAO
         }
     }
 
+    /**
+     * Determines whether the proposed destination represents an actual change 
from the Connection's current destination.
+     * Mirrors the change-detection used by {@link 
#updateConnection(ConnectionDTO)} so the verify and commit phases agree
+     * on what constitutes a destination change — including a remote input 
port re-pointed to a different remote process group.
+     */
+    boolean isDestinationChanging(final Connection connection, final 
ConnectableDTO proposedDestination) {
+        if (proposedDestination == null) {
+            return false;
+        }
+
+        final Connectable currentDestination = connection.getDestination();
+        if 
(!proposedDestination.getId().equals(currentDestination.getIdentifier())) {
+            return true;
+        }
+
+        // Same destination id: for a remote input port, a different remote 
process group id is also a change.
+        if 
(ConnectableType.REMOTE_INPUT_PORT.name().equals(proposedDestination.getType())
+                && currentDestination.getConnectableType() == 
ConnectableType.REMOTE_INPUT_PORT) {
+            final RemoteGroupPort remotePort = (RemoteGroupPort) 
currentDestination;
+            return proposedDestination.getGroupId() != null
+                    && 
!proposedDestination.getGroupId().equals(remotePort.getRemoteProcessGroup().getIdentifier());
+        }
+
+        return false;
+    }
+
     @Override
     public Connection updateConnection(final ConnectionDTO connectionDTO) {
         final Connection connection = locateConnection(connectionDTO.getId());
@@ -571,8 +601,6 @@ public class StandardConnectionDAO extends ComponentDAO 
implements ConnectionDAO
         // determine if the destination changed
         final ConnectableDTO proposedDestination = 
connectionDTO.getDestination();
         if (proposedDestination != null) {
-            final Connectable currentDestination = connection.getDestination();
-
             // handle remote input port differently
             if 
(ConnectableType.REMOTE_INPUT_PORT.name().equals(proposedDestination.getType()))
 {
                 // the group id must be specified
@@ -580,17 +608,8 @@ public class StandardConnectionDAO extends ComponentDAO 
implements ConnectionDAO
                     throw new IllegalArgumentException("When the destination 
is a remote input port its group id is required.");
                 }
 
-                // if the current destination is a remote input port
-                boolean isDifferentRemoteProcessGroup = false;
-                if (currentDestination.getConnectableType() == 
ConnectableType.REMOTE_INPUT_PORT) {
-                    RemoteGroupPort remotePort = (RemoteGroupPort) 
currentDestination;
-                    if 
(!proposedDestination.getGroupId().equals(remotePort.getRemoteProcessGroup().getIdentifier()))
 {
-                        isDifferentRemoteProcessGroup = true;
-                    }
-                }
-
                 // if the destination is changing or the previous destination 
was a different remote process group
-                if 
(!proposedDestination.getId().equals(currentDestination.getIdentifier()) || 
isDifferentRemoteProcessGroup) {
+                if (isDestinationChanging(connection, proposedDestination)) {
                     final ProcessGroup destinationParentGroup = 
locateProcessGroup(flowController, group.getIdentifier());
                     final RemoteProcessGroup remoteProcessGroup = 
destinationParentGroup.getRemoteProcessGroup(proposedDestination.getGroupId());
 
@@ -615,7 +634,7 @@ public class StandardConnectionDAO extends ComponentDAO 
implements ConnectionDAO
                 }
             } else {
                 // if there is a different destination id
-                if 
(!proposedDestination.getId().equals(currentDestination.getIdentifier())) {
+                if (isDestinationChanging(connection, proposedDestination)) {
                     // if the destination connectable's group id has not been 
set, its inferred to be the current group
                     if (proposedDestination.getGroupId() == null) {
                         proposedDestination.setGroupId(group.getIdentifier());
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/StandardConnectionDAOTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/StandardConnectionDAOTest.java
index f368ffc2e28..70e2e1a78d5 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/StandardConnectionDAOTest.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/dao/impl/StandardConnectionDAOTest.java
@@ -21,11 +21,18 @@ import 
org.apache.nifi.components.connector.ConnectorRepository;
 import org.apache.nifi.components.connector.ConnectorState;
 import org.apache.nifi.components.connector.ConnectorSyncMode;
 import org.apache.nifi.components.connector.FrameworkFlowContext;
+import org.apache.nifi.connectable.Connectable;
+import org.apache.nifi.connectable.ConnectableType;
 import org.apache.nifi.connectable.Connection;
 import org.apache.nifi.controller.FlowController;
+import org.apache.nifi.controller.exception.ValidationException;
 import org.apache.nifi.controller.flow.FlowManager;
 import org.apache.nifi.groups.ProcessGroup;
+import org.apache.nifi.groups.RemoteProcessGroup;
+import org.apache.nifi.remote.RemoteGroupPort;
 import org.apache.nifi.web.ResourceNotFoundException;
+import org.apache.nifi.web.api.dto.ConnectableDTO;
+import org.apache.nifi.web.api.dto.ConnectionDTO;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -37,11 +44,15 @@ import org.mockito.quality.Strictness;
 import java.util.List;
 import java.util.Optional;
 
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 @ExtendWith(MockitoExtension.class)
@@ -81,6 +92,14 @@ class StandardConnectionDAOTest {
     private static final String ROOT_CONNECTION_ID = "root-connection-id";
     private static final String CONNECTOR_CONNECTION_ID = 
"connector-connection-id";
     private static final String NON_EXISTENT_ID = "non-existent-id";
+    private static final String PROCESS_GROUP_ID = "group-id";
+    private static final String CURRENT_DESTINATION_ID = 
"current-destination-id";
+    private static final String NEW_DESTINATION_ID = "new-destination-id";
+    private static final String DESTINATION_ID = "dest-1";
+    private static final String ALTERNATIVE_DESTINATION_ID = "dest-2";
+    private static final String REMOTE_PORT_ID = "port-1";
+    private static final String REMOTE_PROCESS_GROUP_ID = "rpg-A";
+    private static final String ALTERNATIVE_REMOTE_PROCESS_GROUP_ID = "rpg-B";
 
     @BeforeEach
     void setUp() {
@@ -200,4 +219,124 @@ class StandardConnectionDAOTest {
 
         assertEquals(connectionInSecondConnector, result);
     }
+
+    private ConnectionDTO connectionDtoWithNameOnly() {
+        final ConnectionDTO dto = new ConnectionDTO();
+        dto.setId(ROOT_CONNECTION_ID);
+        dto.setName("renamed-connection");
+        return dto;
+    }
+
+    private ConnectionDTO connectionDtoChangingDestination(final String 
newDestinationId) {
+        final ConnectionDTO dto = new ConnectionDTO();
+        dto.setId(ROOT_CONNECTION_ID);
+        final ConnectableDTO newDestination = new ConnectableDTO();
+        newDestination.setId(newDestinationId);
+        newDestination.setType(ConnectableType.PROCESSOR.name());
+        dto.setDestination(newDestination);
+        return dto;
+    }
+
+    private void stubRootConnectionDestination(final String destinationId) {
+        final ProcessGroup group = mock(ProcessGroup.class);
+        when(group.getIdentifier()).thenReturn(PROCESS_GROUP_ID);
+        when(rootConnection.getProcessGroup()).thenReturn(group);
+
+        final Connectable currentDestination = mock(Connectable.class);
+        when(currentDestination.getIdentifier()).thenReturn(destinationId);
+        when(currentDestination.isRunning()).thenReturn(true);
+        
when(currentDestination.getConnectableType()).thenReturn(ConnectableType.PROCESSOR);
+        when(rootConnection.getDestination()).thenReturn(currentDestination);
+    }
+
+    @Test
+    void testVerifyUpdateDoesNotCheckDestinationForNonDestinationEdit() {
+        stubRootConnectionDestination(CURRENT_DESTINATION_ID);
+
+        assertDoesNotThrow(() -> 
connectionDAO.verifyUpdate(connectionDtoWithNameOnly()));
+        verify(rootConnection, never()).verifyCanUpdateDestination();
+    }
+
+    @Test
+    void 
testVerifyUpdateWrapsIllegalStateFromDestinationGuardAsValidationException() {
+        stubRootConnectionDestination(CURRENT_DESTINATION_ID);
+        final String guardMessage = "Cannot change destination of Connection 
because the current destination ([proc]) is running";
+        doThrow(new IllegalStateException(guardMessage))
+                .when(rootConnection).verifyCanUpdateDestination();
+
+        final ValidationException thrown = 
assertThrows(ValidationException.class,
+                () -> 
connectionDAO.verifyUpdate(connectionDtoChangingDestination(NEW_DESTINATION_ID)));
+        assertTrue(thrown.getValidationErrors().contains(guardMessage),
+                "ValidationException should carry the guard's message; was: " 
+ thrown.getValidationErrors());
+    }
+
+    @Test
+    void testVerifyUpdateChecksDestinationGuardWhenDestinationChanges() {
+        stubRootConnectionDestination(CURRENT_DESTINATION_ID);
+
+        assertDoesNotThrow(() -> 
connectionDAO.verifyUpdate(connectionDtoChangingDestination(NEW_DESTINATION_ID)));
+        verify(rootConnection).verifyCanUpdateDestination();
+    }
+
+    @Test
+    void testIsDestinationChangingReturnsFalseForSameDestinationId() {
+        final Connectable currentDestination = mock(Connectable.class);
+        when(currentDestination.getIdentifier()).thenReturn(DESTINATION_ID);
+        when(rootConnection.getDestination()).thenReturn(currentDestination);
+
+        final ConnectableDTO proposed = new ConnectableDTO();
+        proposed.setId(DESTINATION_ID);
+        proposed.setType(ConnectableType.PROCESSOR.name());
+
+        assertFalse(connectionDAO.isDestinationChanging(rootConnection, 
proposed));
+    }
+
+    @Test
+    void testIsDestinationChangingReturnsTrueForDifferentDestinationId() {
+        final Connectable currentDestination = mock(Connectable.class);
+        when(currentDestination.getIdentifier()).thenReturn(DESTINATION_ID);
+        when(rootConnection.getDestination()).thenReturn(currentDestination);
+
+        final ConnectableDTO proposed = new ConnectableDTO();
+        proposed.setId(ALTERNATIVE_DESTINATION_ID);
+        proposed.setType(ConnectableType.PROCESSOR.name());
+
+        assertTrue(connectionDAO.isDestinationChanging(rootConnection, 
proposed));
+    }
+
+    @Test
+    void testIsDestinationChangingRemoteInputPortSameGroupIsNotChanging() {
+        final RemoteGroupPort currentRemotePort = mock(RemoteGroupPort.class);
+        final RemoteProcessGroup currentRpg = mock(RemoteProcessGroup.class);
+        when(currentRemotePort.getIdentifier()).thenReturn(REMOTE_PORT_ID);
+        
when(currentRemotePort.getConnectableType()).thenReturn(ConnectableType.REMOTE_INPUT_PORT);
+        when(currentRemotePort.getRemoteProcessGroup()).thenReturn(currentRpg);
+        when(currentRpg.getIdentifier()).thenReturn(REMOTE_PROCESS_GROUP_ID);
+        when(rootConnection.getDestination()).thenReturn(currentRemotePort);
+
+        final ConnectableDTO proposed = new ConnectableDTO();
+        proposed.setId(REMOTE_PORT_ID);
+        proposed.setType(ConnectableType.REMOTE_INPUT_PORT.name());
+        proposed.setGroupId(REMOTE_PROCESS_GROUP_ID);
+
+        assertFalse(connectionDAO.isDestinationChanging(rootConnection, 
proposed));
+    }
+
+    @Test
+    void testIsDestinationChangingRemoteInputPortDifferentGroupIsChanging() {
+        final RemoteGroupPort currentRemotePort = mock(RemoteGroupPort.class);
+        final RemoteProcessGroup currentRpg = mock(RemoteProcessGroup.class);
+        when(currentRemotePort.getIdentifier()).thenReturn(REMOTE_PORT_ID);
+        
when(currentRemotePort.getConnectableType()).thenReturn(ConnectableType.REMOTE_INPUT_PORT);
+        when(currentRemotePort.getRemoteProcessGroup()).thenReturn(currentRpg);
+        when(currentRpg.getIdentifier()).thenReturn(REMOTE_PROCESS_GROUP_ID);
+        when(rootConnection.getDestination()).thenReturn(currentRemotePort);
+
+        final ConnectableDTO proposed = new ConnectableDTO();
+        proposed.setId(REMOTE_PORT_ID);
+        proposed.setType(ConnectableType.REMOTE_INPUT_PORT.name());
+        proposed.setGroupId(ALTERNATIVE_REMOTE_PROCESS_GROUP_ID);
+
+        assertTrue(connectionDAO.isDestinationChanging(rootConnection, 
proposed));
+    }
 }

Reply via email to