Undeploy support for native rules PDP-D policies.

Issue-ID: POLICY-2388
Signed-off-by: jhh <jorge.hernandez-herrero@att.com>
Change-Id: Iaaf5e13a5db286928952bcb3e4cc0f526b0b133c
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeController.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeController.java
index ebc44e9..2d9ca51 100644
--- a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeController.java
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeController.java
@@ -26,9 +26,9 @@
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
 
-@Getter
 public class PolicyTypeNativeController implements PolicyTypeController {
 
+    @Getter
     protected final ToscaPolicyTypeIdentifier policyType;
 
     @GsonJsonIgnore
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeRulesController.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeRulesController.java
index 8e928aa..878907b 100644
--- a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeRulesController.java
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeRulesController.java
@@ -23,12 +23,22 @@
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import lombok.Getter;
 import org.onap.policy.common.gson.annotation.GsonJsonIgnore;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.drools.controller.DroolsControllerConstants;
+import org.onap.policy.drools.domain.models.nativ.rules.NativeDroolsPolicy;
+import org.onap.policy.drools.domain.models.nativ.rules.NativeDroolsRulesArtifact;
+import org.onap.policy.drools.protocol.configuration.DroolsConfiguration;
+import org.onap.policy.drools.system.PolicyController;
+import org.onap.policy.drools.system.PolicyControllerConstants;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-@Getter
 public class PolicyTypeRulesController implements PolicyTypeController {
+    private static final Logger logger = LoggerFactory.getLogger(PolicyTypeRulesController.class);
 
+    @Getter
     protected final ToscaPolicyTypeIdentifier policyType;
 
     @GsonJsonIgnore
@@ -48,7 +58,39 @@
 
     @Override
     public boolean undeploy(ToscaPolicy policy) {
-        // TODO
+        PolicyController controller;
+        try {
+            controller = getPolicyController(
+                    fsm.getDomainMaker().convertTo(policy, NativeDroolsPolicy.class));
+        } catch (RuntimeException | CoderException e) {
+            logger.warn("Invalid Policy: {}", policy);
+            return false;
+        }
+
+        DroolsConfiguration noConfig =
+                new DroolsConfiguration(
+                        DroolsControllerConstants.NO_ARTIFACT_ID,
+                        DroolsControllerConstants.NO_GROUP_ID,
+                        DroolsControllerConstants.NO_VERSION);
+
+        PolicyControllerConstants.getFactory().patch(controller, noConfig);
         return true;
     }
+
+    private PolicyController getPolicyController(NativeDroolsPolicy domainPolicy) {
+        /*
+         * If the controller is present, it must have a name (schema validated) and as such, the
+         * controller must exist (via previously deployed controller policy).
+         */
+        if (domainPolicy.getProperties().getController() != null) {
+            return PolicyControllerConstants.getFactory().get(domainPolicy.getProperties().getController().getName());
+        }
+
+        /*
+         * Attempt to get the controller from the rules coordinates (excluding the version).
+         * The rules coordinates are mandatory (per schema validation).
+         */
+        NativeDroolsRulesArtifact rules = domainPolicy.getProperties().getRulesArtifact();
+        return PolicyControllerConstants.getFactory().get(rules.getGroupId(), rules.getArtifactId());
+    }
 }
diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/domain/models/DomainPolicyTypesTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/domain/models/DomainPolicyTypesTest.java
index 945b00b..17bb9d4 100644
--- a/feature-lifecycle/src/test/java/org/onap/policy/drools/domain/models/DomainPolicyTypesTest.java
+++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/domain/models/DomainPolicyTypesTest.java
@@ -140,8 +140,8 @@
 
         assertTrue(domainMaker.isConformant(toscaPolicy));
         NativeDroolsPolicy domainDroolsPolicy = domainMaker.convertTo(toscaPolicy, NativeDroolsPolicy.class);
-        assertEquals("org.onap.policy.controlloop", domainDroolsPolicy.getProperties().getRulesArtifact().getGroupId());
-        assertEquals("example", domainDroolsPolicy.getProperties().getRulesArtifact().getArtifactId());
+        assertEquals("org.onap.policy.drools.test", domainDroolsPolicy.getProperties().getRulesArtifact().getGroupId());
+        assertEquals("lifecycle", domainDroolsPolicy.getProperties().getRulesArtifact().getArtifactId());
         assertEquals("1.0.0", domainDroolsPolicy.getProperties().getRulesArtifact().getVersion());
 
         String policyId = toscaPolicy.getMetadata().remove("policy-id");
diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateRunningTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateRunningTest.java
index 06fd08c..b80a98c 100644
--- a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateRunningTest.java
+++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateRunningTest.java
@@ -61,6 +61,7 @@
      */
     @AfterClass
     public static void tearDown() {
+        controllerSupport.destroyController();
         try {
             Files.deleteIfExists(Paths.get(SystemPersistenceConstants.getManager().getConfigurationPath().toString(),
                                      CONTROLLER_NAME + "-controller.properties.bak"));
diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/PolicyTypeRulesControllerTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/PolicyTypeRulesControllerTest.java
new file mode 100644
index 0000000..17a4ebe
--- /dev/null
+++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/PolicyTypeRulesControllerTest.java
@@ -0,0 +1,140 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.drools.controller.DroolsControllerConstants;
+import org.onap.policy.drools.controller.internal.NullDroolsController;
+import org.onap.policy.drools.domain.models.nativ.rules.NativeDroolsPolicy;
+import org.onap.policy.drools.system.PolicyControllerConstants;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
+
+/**
+ * Rules Controller Test.
+ */
+public class PolicyTypeRulesControllerTest extends LifecycleStateRunningTest {
+    private static final StandardCoder coder = new StandardCoder();
+
+    // Native Drools Policy
+    private static final String EXAMPLE_NATIVE_DROOLS_POLICY_NAME = "example";
+    private static final String EXAMPLE_NATIVE_DROOLS_POLICY_JSON =
+            "src/test/resources/example.policy.native.drools.tosca.json";
+
+    /**
+     * Test Set initialization.
+     */
+    @Before
+    public void init() {
+        fsm = makeFsmWithPseudoTime();
+    }
+
+    @Test
+    public void testDeploy() {
+        // TODO
+    }
+
+    @Test
+    public void testUndeploy() throws IOException, CoderException {
+        assertTrue(controllerSupport.getController().getDrools().isBrained());
+        assertFalse(controllerSupport.getController().isAlive());
+        assertFalse(controllerSupport.getController().getDrools().isAlive());
+        assertSame(controllerSupport.getController(), PolicyControllerConstants.getFactory().get("lifecycle"));
+
+        ToscaPolicy policy = getPolicyFromFile(EXAMPLE_NATIVE_DROOLS_POLICY_JSON, EXAMPLE_NATIVE_DROOLS_POLICY_NAME);
+        NativeDroolsPolicy nativePolicy = fsm.getDomainMaker().convertTo(policy, NativeDroolsPolicy.class);
+
+        assertSame(controllerSupport.getController(),
+                PolicyControllerConstants.getFactory().get(
+                        nativePolicy.getProperties().getRulesArtifact().getGroupId(),
+                        nativePolicy.getProperties().getRulesArtifact().getArtifactId()));
+        assertEquals(controllerSupport.getController().getDrools().getGroupId(),
+                nativePolicy.getProperties().getRulesArtifact().getGroupId());
+        assertEquals(controllerSupport.getController().getDrools().getArtifactId(),
+                nativePolicy.getProperties().getRulesArtifact().getArtifactId());
+        assertEquals(controllerSupport.getController().getDrools().getVersion(),
+                nativePolicy.getProperties().getRulesArtifact().getVersion());
+
+        assertTrue(controllerSupport.getController().start());
+
+        assertTrue(controllerSupport.getController().isAlive());
+        assertTrue(controllerSupport.getController().getDrools().isAlive());
+        assertTrue(controllerSupport.getController().getDrools().isBrained());
+        assertSame(controllerSupport.getController(), PolicyControllerConstants.getFactory().get("lifecycle"));
+        assertSame(controllerSupport.getController(),
+                PolicyControllerConstants.getFactory().get(
+                        nativePolicy.getProperties().getRulesArtifact().getGroupId(),
+                        nativePolicy.getProperties().getRulesArtifact().getArtifactId()));
+        assertEquals(controllerSupport.getController().getDrools().getGroupId(),
+                nativePolicy.getProperties().getRulesArtifact().getGroupId());
+        assertEquals(controllerSupport.getController().getDrools().getArtifactId(),
+                nativePolicy.getProperties().getRulesArtifact().getArtifactId());
+        assertEquals(controllerSupport.getController().getDrools().getVersion(),
+                nativePolicy.getProperties().getRulesArtifact().getVersion());
+
+        PolicyTypeRulesController controller =
+                new PolicyTypeRulesController(makeFsmWithPseudoTime(),
+                        new ToscaPolicyTypeIdentifier("onap.policies.native.Drools", "1.0.0"));
+        assertTrue(controller.undeploy(policy));
+
+        assertFalse(controllerSupport.getController().getDrools().isBrained());
+        assertFalse(controllerSupport.getController().getDrools().isAlive());
+        assertTrue(controllerSupport.getController().isAlive());
+        assertSame(controllerSupport.getController(), PolicyControllerConstants.getFactory().get("lifecycle"));
+        assertThatIllegalArgumentException().isThrownBy(() -> PolicyControllerConstants.getFactory().get(
+                        nativePolicy.getProperties().getRulesArtifact().getGroupId(),
+                        nativePolicy.getProperties().getRulesArtifact().getArtifactId()));
+        assertTrue(controllerSupport.getController().getDrools() instanceof NullDroolsController);
+        assertEquals(DroolsControllerConstants.NO_GROUP_ID, controllerSupport.getController().getDrools().getGroupId());
+        assertEquals(DroolsControllerConstants.NO_ARTIFACT_ID,
+                controllerSupport.getController().getDrools().getArtifactId());
+        assertEquals(DroolsControllerConstants.NO_VERSION, controllerSupport.getController().getDrools().getVersion());
+
+        assertTrue(controller.undeploy(policy));
+        PolicyControllerConstants.getFactory().destroy("lifecycle");
+        assertThatIllegalArgumentException().isThrownBy(() -> PolicyControllerConstants.getFactory().get("lifecycle"));
+    }
+
+    @Test
+    public void testGetPolicyType() {
+        // TODO
+    }
+
+    private ToscaPolicy getPolicyFromFile(String filePath, String policyName) throws CoderException, IOException {
+        String policyJson = new String(Files.readAllBytes(Paths.get(filePath)));
+        ToscaServiceTemplate serviceTemplate = coder.decode(policyJson, ToscaServiceTemplate.class);
+        return serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
+    }
+
+}
\ No newline at end of file
diff --git a/feature-lifecycle/src/test/resources/example.policy.native.drools.tosca.json b/feature-lifecycle/src/test/resources/example.policy.native.drools.tosca.json
index 0313b2e..f3f34b6 100644
--- a/feature-lifecycle/src/test/resources/example.policy.native.drools.tosca.json
+++ b/feature-lifecycle/src/test/resources/example.policy.native.drools.tosca.json
@@ -13,12 +13,12 @@
                     },
                     "properties": {
                         "rulesArtifact": {
-                            "groupId": "org.onap.policy.controlloop",
-                            "artifactId": "example",
+                            "groupId": "org.onap.policy.drools.test",
+                            "artifactId": "lifecycle",
                             "version": "1.0.0"
                         },
                         "controller": {
-                            "name": "example",
+                            "name": "lifecycle",
                             "version": "1.0.0"
                         }
                     }
diff --git a/feature-lifecycle/src/test/resources/schemas/onap.policies.controlloop.Operational-1.0.0.schema.json b/feature-lifecycle/src/test/resources/schemas/onap.policies.controlloop.Operational-1.0.0.schema.json
index e89238d..801859b 100644
--- a/feature-lifecycle/src/test/resources/schemas/onap.policies.controlloop.Operational-1.0.0.schema.json
+++ b/feature-lifecycle/src/test/resources/schemas/onap.policies.controlloop.Operational-1.0.0.schema.json
@@ -7,6 +7,7 @@
     "required": [
         "type",
         "type_version",
+        "name",
         "version",
         "properties"
     ],
diff --git a/feature-lifecycle/src/test/resources/schemas/onap.policies.controlloop.operational.common.Drools-1.0.0.schema.json b/feature-lifecycle/src/test/resources/schemas/onap.policies.controlloop.operational.common.Drools-1.0.0.schema.json
index c98e25b..5d032f0 100644
--- a/feature-lifecycle/src/test/resources/schemas/onap.policies.controlloop.operational.common.Drools-1.0.0.schema.json
+++ b/feature-lifecycle/src/test/resources/schemas/onap.policies.controlloop.operational.common.Drools-1.0.0.schema.json
@@ -201,6 +201,18 @@
                                                     "VNF"
                                                 ],
                                                 "pattern": "^(.+)$"
+                                            },
+                                            "entityIds": {
+                                                "$id": "#/properties/properties/properties/operations/items/properties/operation/properties/target/properties/entityIds",
+                                                "type": "object",
+                                                "title": "Arbitrary Resource Identities",
+                                                "examples": [
+                                                    "Eace933104d443b496b8.nodes.heat.vpg"
+                                                ],
+                                                "additionalProperties": {
+                                                    "type": "string",
+                                                    "pattern": "^(.+)$"
+                                                }
                                             }
                                         }
                                     },
@@ -209,7 +221,8 @@
                                         "type": "object",
                                         "title": "Payload",
                                         "additionalProperties": {
-                                            "type": "string"
+                                            "type": "string",
+                                            "pattern": "^(.+)$"
                                         }
                                     }
                                 }
diff --git a/policy-management/src/main/java/org/onap/policy/drools/controller/IndexedDroolsControllerFactory.java b/policy-management/src/main/java/org/onap/policy/drools/controller/IndexedDroolsControllerFactory.java
index a4c546f..e5128bb 100644
--- a/policy-management/src/main/java/org/onap/policy/drools/controller/IndexedDroolsControllerFactory.java
+++ b/policy-management/src/main/java/org/onap/policy/drools/controller/IndexedDroolsControllerFactory.java
@@ -95,7 +95,6 @@
         }
 
         List<TopicCoderFilterConfiguration> topics2DecodedClasses2Filters = codersAndFilters(properties, eventSources);
-
         List<TopicCoderFilterConfiguration> topics2EncodedClasses2Filters = codersAndFilters(properties, eventSinks);
 
         return this.build(groupId, artifactId, version, topics2DecodedClasses2Filters, topics2EncodedClasses2Filters);
diff --git a/policy-management/src/main/java/org/onap/policy/drools/system/IndexedPolicyControllerFactory.java b/policy-management/src/main/java/org/onap/policy/drools/system/IndexedPolicyControllerFactory.java
index 0d7806c..528f1e0 100644
--- a/policy-management/src/main/java/org/onap/policy/drools/system/IndexedPolicyControllerFactory.java
+++ b/policy-management/src/main/java/org/onap/policy/drools/system/IndexedPolicyControllerFactory.java
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * ONAP
  * ================================================================================
- * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2019-2020 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -122,7 +122,7 @@
      * {@inheritDoc}.
      */
     @Override
-    public void patch(PolicyController controller, DroolsConfiguration droolsConfig) {
+    public synchronized void patch(PolicyController controller, DroolsConfiguration droolsConfig) {
 
         if (controller == null) {
             throw new IllegalArgumentException("Not a valid controller:  null");
@@ -132,20 +132,22 @@
             throw new IllegalArgumentException("Invalid Drools Configuration");
         }
 
+        if (controller.getDrools().isBrained()) {
+            this.coordinates2Controller.remove(
+                    toKey(controller.getDrools().getGroupId(), controller.getDrools().getArtifactId()));
+        }
+
         if (!controller.updateDrools(droolsConfig)) {
             logger.warn("Cannot update drools configuration: {} on {}", droolsConfig, this);
             throw new IllegalArgumentException("Cannot update drools configuration Drools Configuration");
         }
 
         logger.info("UPDATED drools configuration: {} on {}", droolsConfig, this);
-
-        String coordinates = toKey(controller.getDrools().getGroupId(),
-                                   controller.getDrools().getArtifactId());
+        String coordinates = toKey(controller.getDrools().getGroupId(), controller.getDrools().getArtifactId());
 
         if (controller.getDrools().isBrained()) {
             this.coordinates2Controller.put(coordinates, controller);
         }
-
     }
 
     /**
diff --git a/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java b/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java
index aa57aba..1fd4254 100644
--- a/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java
+++ b/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java
@@ -1,8 +1,8 @@
 /*
  * ============LICENSE_START=======================================================
- * policy-management
+ * ONAP
  * ================================================================================
- * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2020 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -226,6 +226,12 @@
             return true;
         }
 
+        if (droolsController.isBrained()
+            && (newDroolsConfiguration.getArtifactId() == null
+                || DroolsControllerConstants.NO_ARTIFACT_ID.equals(newDroolsConfiguration.getArtifactId()))) {
+            DroolsControllerConstants.getFactory().destroy(this.droolsController);
+        }
+
         try {
             /* Drools Controller created, update initialization properties for restarts */
 
diff --git a/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/EventProtocolCoderTest.java b/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/EventProtocolCoderTest.java
index 6f34c2c..2fc1467 100644
--- a/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/EventProtocolCoderTest.java
+++ b/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/EventProtocolCoderTest.java
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * ONAP
  * ================================================================================
- * Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2018-2020 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -90,7 +90,7 @@
                         .modelClassLoaderHash(DroolsConfiguration.class.getName().hashCode()));
 
         final String json = EventProtocolCoderConstants.getManager().encode(NOOP_TOPIC,
-                new DroolsConfiguration(ENCODER_GROUP, ENCODER_ARTIFACT, ENCODER_VERSION));
+                new DroolsConfiguration(ENCODER_ARTIFACT, ENCODER_GROUP, ENCODER_VERSION));
 
         assertTrue(json.contains(ENCODER_GROUP));
         assertTrue(json.contains(ENCODER_ARTIFACT));
diff --git a/policy-management/src/test/java/org/onap/policy/drools/system/PolicyEngineTest.java b/policy-management/src/test/java/org/onap/policy/drools/system/PolicyEngineTest.java
index 997fc03..5f4815f 100644
--- a/policy-management/src/test/java/org/onap/policy/drools/system/PolicyEngineTest.java
+++ b/policy-management/src/test/java/org/onap/policy/drools/system/PolicyEngineTest.java
@@ -1,8 +1,8 @@
 /*-
  * ============LICENSE_START=======================================================
- * policy-management
+ * ONAP
  * ================================================================================
- * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2020 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -223,7 +223,7 @@
                         .modelClassLoaderHash(DroolsConfiguration.class.getName().hashCode()));
 
         assertTrue(PolicyEngineConstants.getManager().deliver(NOOP_TOPIC,
-                new DroolsConfiguration(ENCODER_GROUP, ENCODER_ARTIFACT, ENCODER_VERSION)));
+                new DroolsConfiguration(ENCODER_ARTIFACT, ENCODER_GROUP, ENCODER_VERSION)));
 
         final TopicSink sink = NoopTopicFactories.getSinkFactory().get(NOOP_TOPIC);
         assertTrue(sink.getRecentEvents()[0].contains(ENCODER_GROUP));