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 620b7365c2 NIFI-10851 Corrected removal of Controller Service
references on property updates
620b7365c2 is described below
commit 620b7365c2ed0bc741920d5208d733f540795b16
Author: Paul Grey <[email protected]>
AuthorDate: Mon Nov 21 12:10:53 2022 -0500
NIFI-10851 Corrected removal of Controller Service references on property
updates
- Convert unit test to JUnit 5
This closes #6695
Signed-off-by: David Handermann <[email protected]>
---
.../nifi/controller/AbstractComponentNode.java | 6 +-
.../nifi/controller/TestAbstractComponentNode.java | 144 +++++++++++++++------
2 files changed, 106 insertions(+), 44 deletions(-)
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractComponentNode.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractComponentNode.java
index 6a930f4272..162d421db8 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractComponentNode.java
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractComponentNode.java
@@ -462,17 +462,17 @@ public abstract class AbstractComponentNode implements
ComponentNode {
// Keep setProperty/removeProperty private so that all calls go through
setProperties
private void setProperty(final PropertyDescriptor descriptor, final
PropertyConfiguration propertyConfiguration, final Function<PropertyDescriptor,
PropertyConfiguration> valueToCompareFunction) {
// Remove current PropertyDescriptor to force updated instance
references
- properties.remove(descriptor);
+ final PropertyConfiguration removed = properties.remove(descriptor);
final PropertyConfiguration propertyModComparisonValue =
valueToCompareFunction.apply(descriptor);
- final PropertyConfiguration oldConfiguration =
properties.put(descriptor, propertyConfiguration);
+ properties.put(descriptor, propertyConfiguration);
final String effectiveValue =
propertyConfiguration.getEffectiveValue(getParameterContext());
// If the property references a Controller Service, we need to
register this component & property descriptor as a reference.
// If it previously referenced a Controller Service, we need to also
remove that reference.
// It is okay if the new & old values are the same - we just
unregister the component/descriptor and re-register it.
if (descriptor.getControllerServiceDefinition() != null) {
- Optional.ofNullable(oldConfiguration)
+ Optional.ofNullable(removed)
.map(_oldConfiguration ->
_oldConfiguration.getEffectiveValue(getParameterContext()))
.map(oldEffectiveValue ->
serviceProvider.getControllerServiceNode(oldEffectiveValue))
.ifPresent(oldNode -> oldNode.removeReference(this,
descriptor));
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/test/java/org/apache/nifi/controller/TestAbstractComponentNode.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/test/java/org/apache/nifi/controller/TestAbstractComponentNode.java
index f562516423..d417456f24 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/test/java/org/apache/nifi/controller/TestAbstractComponentNode.java
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/test/java/org/apache/nifi/controller/TestAbstractComponentNode.java
@@ -40,7 +40,9 @@ import org.apache.nifi.parameter.ParameterDescriptor;
import org.apache.nifi.parameter.ParameterLookup;
import org.apache.nifi.parameter.ParameterUpdate;
import org.apache.nifi.registry.ComponentVariableRegistry;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+import org.mockito.Answers;
import org.mockito.Mockito;
import java.net.URL;
@@ -56,11 +58,19 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
public class TestAbstractComponentNode {
@@ -68,9 +78,10 @@ public class TestAbstractComponentNode {
private static final String PROPERTY_VALUE = "abstract-property-value";
- @Test(timeout = 5000)
+ @Timeout(5)
+ @Test
public void testGetValidationStatusWithTimeout() {
- final ValidationControlledAbstractComponentNode node = new
ValidationControlledAbstractComponentNode(5000,
Mockito.mock(ValidationTrigger.class));
+ final ValidationControlledAbstractComponentNode node = new
ValidationControlledAbstractComponentNode(5000, mock(ValidationTrigger.class));
final ValidationStatus status = node.getValidationStatus(1,
TimeUnit.MILLISECONDS);
assertEquals(ValidationStatus.VALIDATING, status);
}
@@ -99,7 +110,7 @@ public class TestAbstractComponentNode {
}
};
- final ParameterContext context = Mockito.mock(ParameterContext.class);
+ final ParameterContext context = mock(ParameterContext.class);
final ParameterDescriptor paramDescriptor = new
ParameterDescriptor.Builder()
.name("abc")
.description("")
@@ -133,7 +144,7 @@ public class TestAbstractComponentNode {
public void testMismatchedSensitiveFlags() {
final LocalComponentNode node = new LocalComponentNode();
- final ParameterContext context = Mockito.mock(ParameterContext.class);
+ final ParameterContext context = mock(ParameterContext.class);
final ParameterDescriptor paramDescriptor = new
ParameterDescriptor.Builder()
.name("abc")
.description("")
@@ -156,12 +167,12 @@ public class TestAbstractComponentNode {
node.verifyCanUpdateProperties(properties);
node.setProperties(properties, false, Collections.emptySet());
- final ValidationContext validationContext =
Mockito.mock(ValidationContext.class);
-
Mockito.when(validationContext.getProperties()).thenReturn(Collections.singletonMap(propertyDescriptor,
propertyValue));
-
Mockito.when(validationContext.getAllProperties()).thenReturn(properties);
-
Mockito.when(validationContext.isDependencySatisfied(Mockito.any(PropertyDescriptor.class),
Mockito.any(Function.class))).thenReturn(true);
-
Mockito.when(validationContext.getReferencedParameters(Mockito.anyString())).thenReturn(Collections.singleton("abc"));
-
Mockito.when(validationContext.isParameterDefined("abc")).thenReturn(true);
+ final ValidationContext validationContext =
mock(ValidationContext.class);
+
when(validationContext.getProperties()).thenReturn(Collections.singletonMap(propertyDescriptor,
propertyValue));
+ when(validationContext.getAllProperties()).thenReturn(properties);
+
when(validationContext.isDependencySatisfied(any(PropertyDescriptor.class),
any(Function.class))).thenReturn(true);
+
when(validationContext.getReferencedParameters(Mockito.anyString())).thenReturn(Collections.singleton("abc"));
+ when(validationContext.isParameterDefined("abc")).thenReturn(true);
final ValidationState validationState =
node.performValidation(validationContext);
assertSame(ValidationStatus.INVALID, validationState.getStatus());
@@ -173,7 +184,8 @@ public class TestAbstractComponentNode {
assertTrue(result.getExplanation().toLowerCase().contains("sensitivity"));
}
- @Test(timeout = 10000)
+ @Timeout(10)
+ @Test
public void testValidationTriggerPaused() throws InterruptedException {
final AtomicLong validationCount = new AtomicLong(0L);
@@ -206,26 +218,76 @@ public class TestAbstractComponentNode {
@Test
public void testValidateControllerServicesValid() {
- final ControllerServiceProvider serviceProvider =
Mockito.mock(ControllerServiceProvider.class);
+ final ControllerServiceProvider serviceProvider =
mock(ControllerServiceProvider.class);
final ValidationContext context =
getServiceValidationContext(ControllerServiceState.ENABLED, serviceProvider);
- final ValidationControlledAbstractComponentNode componentNode = new
ValidationControlledAbstractComponentNode(0,
Mockito.mock(ValidationTrigger.class), serviceProvider);
+ final ValidationControlledAbstractComponentNode componentNode = new
ValidationControlledAbstractComponentNode(0, mock(ValidationTrigger.class),
serviceProvider);
final Collection<ValidationResult> results =
componentNode.validateReferencedControllerServices(context);
- assertTrue(String.format("Validation Failed %s", results),
results.isEmpty());
+ assertTrue(results.isEmpty(), String.format("Validation Failed %s",
results));
+ }
+
+ @Test
+ public void testUpdateProcessorControllerServiceReference() {
+ final String controllerServiceIdA = "controllerServiceIdA";
+ final ControllerService controllerServiceA =
mock(ControllerService.class);
+
when(controllerServiceA.getIdentifier()).thenReturn(controllerServiceIdA);
+ final ControllerServiceNode controllerServiceNodeA =
mock(ControllerServiceNode.class);
+
+ final String controllerServiceIdB = "controllerServiceIdB";
+ final ControllerService controllerServiceB =
mock(ControllerService.class);
+
when(controllerServiceB.getIdentifier()).thenReturn(controllerServiceIdB);
+ final ControllerServiceNode controllerServiceNodeB =
mock(ControllerServiceNode.class);
+
+ final String propertyName = "record-reader";
+ final ConfigurableComponent processor =
mock(ConfigurableComponent.class);
+ final PropertyDescriptor propertyDescriptorReader = new
PropertyDescriptor.Builder()
+ .name(propertyName)
+ .identifiesControllerService(ControllerService.class)
+ .build();
+
when(processor.getPropertyDescriptor(eq(propertyName))).thenReturn(propertyDescriptorReader);
+
+ final ControllerServiceProvider serviceProvider =
mock(ControllerServiceProvider.class);
+
when(serviceProvider.getControllerService(eq(controllerServiceIdA))).thenReturn(controllerServiceA);
+
when(serviceProvider.getControllerServiceNode(eq(controllerServiceIdA))).thenReturn(controllerServiceNodeA);
+
when(serviceProvider.getControllerService(eq(controllerServiceIdB))).thenReturn(controllerServiceB);
+
when(serviceProvider.getControllerServiceNode(eq(controllerServiceIdB))).thenReturn(controllerServiceNodeB);
+
+ final AbstractComponentNode componentNode =
mock(AbstractComponentNode.class, withSettings()
+ .defaultAnswer(Answers.CALLS_REAL_METHODS)
+ .useConstructor(
+ "id", mock(ValidationContextFactory.class),
+ serviceProvider, "componentType", "componentClass",
+ mock(ComponentVariableRegistry.class),
mock(ReloadComponent.class),
+ mock(ExtensionManager.class),
mock(ValidationTrigger.class), false));
+
when(componentNode.getControllerServiceProvider()).thenReturn(serviceProvider);
+ when(componentNode.getComponent()).thenReturn(processor);
+
+ final Map<String, String> properties = new HashMap<>();
+ properties.put("record-reader", controllerServiceIdA);
+ componentNode.setProperties(properties);
+ verify(controllerServiceNodeA).addReference(eq(componentNode),
eq(propertyDescriptorReader));
+ verifyNoMoreInteractions(controllerServiceNodeA);
+
+ properties.put("record-reader", controllerServiceIdB);
+ componentNode.setProperties(properties);
+ verify(controllerServiceNodeA).removeReference(eq(componentNode),
eq(propertyDescriptorReader));
+ verifyNoMoreInteractions(controllerServiceNodeA);
+ verify(controllerServiceNodeB).addReference(eq(componentNode),
eq(propertyDescriptorReader));
+ verifyNoMoreInteractions(controllerServiceNodeB);
}
@Test
public void testValidateControllerServicesEnablingInvalid() {
- final ControllerServiceProvider serviceProvider =
Mockito.mock(ControllerServiceProvider.class);
+ final ControllerServiceProvider serviceProvider =
mock(ControllerServiceProvider.class);
final ValidationContext context =
getServiceValidationContext(ControllerServiceState.ENABLING, serviceProvider);
- final ValidationControlledAbstractComponentNode componentNode = new
ValidationControlledAbstractComponentNode(0,
Mockito.mock(ValidationTrigger.class), serviceProvider);
+ final ValidationControlledAbstractComponentNode componentNode = new
ValidationControlledAbstractComponentNode(0, mock(ValidationTrigger.class),
serviceProvider);
final Collection<ValidationResult> results =
componentNode.validateReferencedControllerServices(context);
final Optional<ValidationResult> firstResult =
results.stream().findFirst();
- assertTrue("Validation Result not found", firstResult.isPresent());
+ assertTrue(firstResult.isPresent(), "Validation Result not found");
final ValidationResult validationResult = firstResult.get();
- assertTrue("Enabling Service Validation Result not found",
validationResult instanceof EnablingServiceValidationResult);
+ assertInstanceOf(EnablingServiceValidationResult.class,
validationResult);
}
@Test
@@ -298,13 +360,13 @@ public class TestAbstractComponentNode {
}
private ValidationContext getServiceValidationContext(final
ControllerServiceState serviceState, final ControllerServiceProvider
serviceProvider) {
- final ValidationContext context =
Mockito.mock(ValidationContext.class);
+ final ValidationContext context = mock(ValidationContext.class);
final String serviceIdentifier = MockControllerService.class.getName();
- final ControllerServiceNode serviceNode =
Mockito.mock(ControllerServiceNode.class);
-
Mockito.when(serviceProvider.getControllerServiceNode(serviceIdentifier)).thenReturn(serviceNode);
- Mockito.when(serviceNode.getState()).thenReturn(serviceState);
- Mockito.when(serviceNode.isActive()).thenReturn(true);
+ final ControllerServiceNode serviceNode =
mock(ControllerServiceNode.class);
+
when(serviceProvider.getControllerServiceNode(serviceIdentifier)).thenReturn(serviceNode);
+ when(serviceNode.getState()).thenReturn(serviceState);
+ when(serviceNode.isActive()).thenReturn(true);
final PropertyDescriptor property = new PropertyDescriptor.Builder()
.name(MockControllerService.class.getSimpleName())
@@ -313,11 +375,11 @@ public class TestAbstractComponentNode {
.build();
final Map<PropertyDescriptor, String> properties =
Collections.singletonMap(property, serviceIdentifier);
- Mockito.when(context.getProperties()).thenReturn(properties);
- final PropertyValue propertyValue = Mockito.mock(PropertyValue.class);
- Mockito.when(propertyValue.getValue()).thenReturn(serviceIdentifier);
-
Mockito.when(context.getProperty(Mockito.eq(property))).thenReturn(propertyValue);
-
Mockito.when(context.isDependencySatisfied(Mockito.any(PropertyDescriptor.class),
Mockito.any(Function.class))).thenReturn(true);
+ when(context.getProperties()).thenReturn(properties);
+ final PropertyValue propertyValue = mock(PropertyValue.class);
+ when(propertyValue.getValue()).thenReturn(serviceIdentifier);
+
when(context.getProperty(Mockito.eq(property))).thenReturn(propertyValue);
+ when(context.isDependencySatisfied(any(PropertyDescriptor.class),
any(Function.class))).thenReturn(true);
return context;
}
@@ -325,13 +387,13 @@ public class TestAbstractComponentNode {
private volatile ParameterContext paramContext = null;
public LocalComponentNode() {
- this(Mockito.mock(ControllerServiceProvider.class),
Mockito.mock(ValidationTrigger.class));
+ this(mock(ControllerServiceProvider.class),
mock(ValidationTrigger.class));
}
public LocalComponentNode(final ControllerServiceProvider
controllerServiceProvider, final ValidationTrigger validationTrigger) {
- super("id", Mockito.mock(ValidationContextFactory.class),
controllerServiceProvider, "unit test component",
-
ValidationControlledAbstractComponentNode.class.getCanonicalName(),
Mockito.mock(ComponentVariableRegistry.class),
Mockito.mock(ReloadComponent.class),
- Mockito.mock(ExtensionManager.class), validationTrigger,
false);
+ super("id", mock(ValidationContextFactory.class),
controllerServiceProvider, "unit test component",
+
ValidationControlledAbstractComponentNode.class.getCanonicalName(),
mock(ComponentVariableRegistry.class), mock(ReloadComponent.class),
+ mock(ExtensionManager.class), validationTrigger, false);
}
@Override
@@ -345,8 +407,8 @@ public class TestAbstractComponentNode {
@Override
public ConfigurableComponent getComponent() {
- final ConfigurableComponent component =
Mockito.mock(ConfigurableComponent.class);
-
Mockito.when(component.getPropertyDescriptor(Mockito.anyString())).thenAnswer(invocation
-> {
+ final ConfigurableComponent component =
mock(ConfigurableComponent.class);
+
when(component.getPropertyDescriptor(Mockito.anyString())).thenAnswer(invocation
-> {
final String propertyName = invocation.getArgument(0,
String.class);
return new PropertyDescriptor.Builder()
.name(propertyName)
@@ -427,7 +489,7 @@ public class TestAbstractComponentNode {
private final long pauseMillis;
public ValidationControlledAbstractComponentNode(final long
pauseMillis, final ValidationTrigger validationTrigger) {
- this(pauseMillis, validationTrigger,
Mockito.mock(ControllerServiceProvider.class));
+ this(pauseMillis, validationTrigger,
mock(ControllerServiceProvider.class));
}
public ValidationControlledAbstractComponentNode(final long
pauseMillis, final ValidationTrigger validationTrigger, final
ControllerServiceProvider controllerServiceProvider) {