You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by kd...@apache.org on 2018/09/22 02:11:26 UTC
[27/51] [partial] nifi-registry git commit: NIFIREG-201 Refactoring
project structure to better isolate extensions
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/service/TestRegistryService.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/service/TestRegistryService.java b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/service/TestRegistryService.java
new file mode 100644
index 0000000..95e2d1a
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/service/TestRegistryService.java
@@ -0,0 +1,1236 @@
+/*
+ * 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.registry.service;
+
+import org.apache.nifi.registry.bucket.Bucket;
+import org.apache.nifi.registry.db.entity.BucketEntity;
+import org.apache.nifi.registry.db.entity.FlowEntity;
+import org.apache.nifi.registry.db.entity.FlowSnapshotEntity;
+import org.apache.nifi.registry.diff.ComponentDifference;
+import org.apache.nifi.registry.diff.ComponentDifferenceGroup;
+import org.apache.nifi.registry.diff.VersionedFlowDifference;
+import org.apache.nifi.registry.exception.ResourceNotFoundException;
+import org.apache.nifi.registry.flow.FlowPersistenceProvider;
+import org.apache.nifi.registry.flow.VersionedFlow;
+import org.apache.nifi.registry.flow.VersionedFlowSnapshot;
+import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
+import org.apache.nifi.registry.flow.VersionedProcessGroup;
+import org.apache.nifi.registry.flow.VersionedProcessor;
+import org.apache.nifi.registry.serialization.Serializer;
+import org.apache.nifi.registry.serialization.VersionedProcessGroupSerializer;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.SortedSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class TestRegistryService {
+
+ private MetadataService metadataService;
+ private FlowPersistenceProvider flowPersistenceProvider;
+ private Serializer<VersionedProcessGroup> snapshotSerializer;
+ private Validator validator;
+
+ private RegistryService registryService;
+
+ @Before
+ public void setup() {
+ metadataService = mock(MetadataService.class);
+ flowPersistenceProvider = mock(FlowPersistenceProvider.class);
+ snapshotSerializer = mock(VersionedProcessGroupSerializer.class);
+
+ final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
+ validator = validatorFactory.getValidator();
+
+ registryService = new RegistryService(metadataService, flowPersistenceProvider, snapshotSerializer, validator);
+ }
+
+ // ---------------------- Test Bucket methods ---------------------------------------------
+
+ @Test
+ public void testCreateBucketValid() {
+ final Bucket bucket = new Bucket();
+ bucket.setName("My Bucket");
+ bucket.setDescription("This is my bucket.");
+
+ when(metadataService.getBucketsByName(bucket.getName())).thenReturn(Collections.emptyList());
+
+ doAnswer(createBucketAnswer()).when(metadataService).createBucket(any(BucketEntity.class));
+
+ final Bucket createdBucket = registryService.createBucket(bucket);
+ assertNotNull(createdBucket);
+ assertNotNull(createdBucket.getIdentifier());
+ assertNotNull(createdBucket.getCreatedTimestamp());
+
+ assertEquals(bucket.getName(), createdBucket.getName());
+ assertEquals(bucket.getDescription(), createdBucket.getDescription());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testCreateBucketWithSameName() {
+ final Bucket bucket = new Bucket();
+ bucket.setName("My Bucket");
+ bucket.setDescription("This is my bucket.");
+
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketsByName(bucket.getName())).thenReturn(Collections.singletonList(existingBucket));
+
+ // should throw exception since a bucket with the same name exists
+ registryService.createBucket(bucket);
+ }
+
+ @Test(expected = ConstraintViolationException.class)
+ public void testCreateBucketWithMissingName() {
+ final Bucket bucket = new Bucket();
+ when(metadataService.getBucketsByName(bucket.getName())).thenReturn(Collections.emptyList());
+ registryService.createBucket(bucket);
+ }
+
+ @Test
+ public void testGetExistingBucket() {
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ final Bucket bucket = registryService.getBucket(existingBucket.getId());
+ assertNotNull(bucket);
+ assertEquals(existingBucket.getId(), bucket.getIdentifier());
+ assertEquals(existingBucket.getName(), bucket.getName());
+ assertEquals(existingBucket.getDescription(), bucket.getDescription());
+ assertEquals(existingBucket.getCreated().getTime(), bucket.getCreatedTimestamp());
+ }
+
+ @Test(expected = ResourceNotFoundException.class)
+ public void testGetBucketDoesNotExist() {
+ when(metadataService.getBucketById(any(String.class))).thenReturn(null);
+ registryService.getBucket("does-not-exist");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateBucketWithoutId() {
+ final Bucket bucket = new Bucket();
+ bucket.setName("My Bucket");
+ bucket.setDescription("This is my bucket.");
+ registryService.updateBucket(bucket);
+ }
+
+ @Test(expected = ResourceNotFoundException.class)
+ public void testUpdateBucketDoesNotExist() {
+ final Bucket bucket = new Bucket();
+ bucket.setIdentifier("b1");
+ bucket.setName("My Bucket");
+ bucket.setDescription("This is my bucket.");
+ registryService.updateBucket(bucket);
+
+ when(metadataService.getBucketById(any(String.class))).thenReturn(null);
+ registryService.updateBucket(bucket);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testUpdateBucketWithSameNameAsExistingBucket() {
+ final BucketEntity bucketToUpdate = new BucketEntity();
+ bucketToUpdate.setId("b1");
+ bucketToUpdate.setName("My Bucket");
+ bucketToUpdate.setDescription("This is my bucket");
+ bucketToUpdate.setCreated(new Date());
+
+ when(metadataService.getBucketById(bucketToUpdate.getId())).thenReturn(bucketToUpdate);
+
+ final BucketEntity otherBucket = new BucketEntity();
+ otherBucket.setId("b2");
+ otherBucket.setName("My Bucket #2");
+ otherBucket.setDescription("This is my bucket");
+ otherBucket.setCreated(new Date());
+
+ when(metadataService.getBucketsByName(otherBucket.getName())).thenReturn(Collections.singletonList(otherBucket));
+
+ // should fail because other bucket has the same name
+ final Bucket updatedBucket = new Bucket();
+ updatedBucket.setIdentifier(bucketToUpdate.getId());
+ updatedBucket.setName("My Bucket #2");
+ updatedBucket.setDescription(bucketToUpdate.getDescription());
+
+ registryService.updateBucket(updatedBucket);
+ }
+
+ @Test
+ public void testUpdateBucket() {
+ final BucketEntity bucketToUpdate = new BucketEntity();
+ bucketToUpdate.setId("b1");
+ bucketToUpdate.setName("My Bucket");
+ bucketToUpdate.setDescription("This is my bucket");
+ bucketToUpdate.setCreated(new Date());
+
+ when(metadataService.getBucketById(bucketToUpdate.getId())).thenReturn(bucketToUpdate);
+
+ doAnswer(updateBucketAnswer()).when(metadataService).updateBucket(any(BucketEntity.class));
+
+ final Bucket updatedBucket = new Bucket();
+ updatedBucket.setIdentifier(bucketToUpdate.getId());
+ updatedBucket.setName("Updated Name");
+ updatedBucket.setDescription("Updated Description");
+
+ final Bucket result = registryService.updateBucket(updatedBucket);
+ assertNotNull(result);
+ assertEquals(updatedBucket.getName(), result.getName());
+ assertEquals(updatedBucket.getDescription(), result.getDescription());
+ }
+
+ @Test
+ public void testUpdateBucketPartial() {
+ final BucketEntity bucketToUpdate = new BucketEntity();
+ bucketToUpdate.setId("b1");
+ bucketToUpdate.setName("My Bucket");
+ bucketToUpdate.setDescription("This is my bucket");
+ bucketToUpdate.setCreated(new Date());
+
+ when(metadataService.getBucketById(bucketToUpdate.getId())).thenReturn(bucketToUpdate);
+
+ doAnswer(updateBucketAnswer()).when(metadataService).updateBucket(any(BucketEntity.class));
+
+ final Bucket updatedBucket = new Bucket();
+ updatedBucket.setIdentifier(bucketToUpdate.getId());
+ updatedBucket.setName("Updated Name");
+ updatedBucket.setDescription(null);
+
+ // name should be updated but description should not be changed
+ final Bucket result = registryService.updateBucket(updatedBucket);
+ assertNotNull(result);
+ assertEquals(updatedBucket.getName(), result.getName());
+ assertEquals(bucketToUpdate.getDescription(), result.getDescription());
+ }
+
+ @Test(expected = ResourceNotFoundException.class)
+ public void testDeleteBucketDoesNotExist() {
+ final String bucketId = "b1";
+ when(metadataService.getBucketById(bucketId)).thenReturn(null);
+ registryService.deleteBucket(bucketId);
+ }
+
+ @Test
+ public void testDeleteBucketWithFlows() {
+ final BucketEntity bucketToDelete = new BucketEntity();
+ bucketToDelete.setId("b1");
+ bucketToDelete.setName("My Bucket");
+ bucketToDelete.setDescription("This is my bucket");
+ bucketToDelete.setCreated(new Date());
+
+ final FlowEntity flowToDelete = new FlowEntity();
+ flowToDelete.setId("flow1");
+ flowToDelete.setName("Flow 1");
+ flowToDelete.setDescription("This is flow 1");
+ flowToDelete.setCreated(new Date());
+
+ final List<FlowEntity> flows = new ArrayList<>();
+ flows.add(flowToDelete);
+
+ when(metadataService.getBucketById(bucketToDelete.getId())).thenReturn(bucketToDelete);
+
+ when(metadataService.getFlowsByBucket(bucketToDelete.getId())).thenReturn(flows);
+
+ final Bucket deletedBucket = registryService.deleteBucket(bucketToDelete.getId());
+ assertNotNull(deletedBucket);
+ assertEquals(bucketToDelete.getId(), deletedBucket.getIdentifier());
+
+ verify(flowPersistenceProvider, times(1))
+ .deleteAllFlowContent(eq(bucketToDelete.getId()), eq(flowToDelete.getId()));
+ }
+
+ // ---------------------- Test VersionedFlow methods ---------------------------------------------
+
+ @Test(expected = ConstraintViolationException.class)
+ public void testCreateFlowInvalid() {
+ final VersionedFlow versionedFlow = new VersionedFlow();
+ registryService.createFlow("b1", versionedFlow);
+ }
+
+ @Test(expected = ResourceNotFoundException.class)
+ public void testCreateFlowBucketDoesNotExist() {
+
+ when(metadataService.getBucketById(any(String.class))).thenReturn(null);
+
+ final VersionedFlow versionedFlow = new VersionedFlow();
+ versionedFlow.setName("My Flow");
+ versionedFlow.setBucketIdentifier("b1");
+
+ registryService.createFlow(versionedFlow.getBucketIdentifier(), versionedFlow);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testCreateFlowWithSameName() {
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ // setup a flow with the same name that already exists
+
+ final FlowEntity flowWithSameName = new FlowEntity();
+ flowWithSameName.setId("flow1");
+ flowWithSameName.setName("Flow 1");
+ flowWithSameName.setDescription("This is flow 1");
+ flowWithSameName.setCreated(new Date());
+ flowWithSameName.setModified(new Date());
+
+ when(metadataService.getFlowsByName(existingBucket.getId(), flowWithSameName.getName())).thenReturn(Collections.singletonList(flowWithSameName));
+
+ final VersionedFlow versionedFlow = new VersionedFlow();
+ versionedFlow.setName(flowWithSameName.getName());
+ versionedFlow.setBucketIdentifier("b1");
+
+ registryService.createFlow(versionedFlow.getBucketIdentifier(), versionedFlow);
+ }
+
+ @Test
+ public void testCreateFlowValid() {
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ final VersionedFlow versionedFlow = new VersionedFlow();
+ versionedFlow.setName("My Flow");
+ versionedFlow.setBucketIdentifier("b1");
+
+ doAnswer(createFlowAnswer()).when(metadataService).createFlow(any(FlowEntity.class));
+
+ final VersionedFlow createdFlow = registryService.createFlow(versionedFlow.getBucketIdentifier(), versionedFlow);
+ assertNotNull(createdFlow);
+ assertNotNull(createdFlow.getIdentifier());
+ assertTrue(createdFlow.getCreatedTimestamp() > 0);
+ assertTrue(createdFlow.getModifiedTimestamp() > 0);
+ assertEquals(versionedFlow.getName(), createdFlow.getName());
+ assertEquals(versionedFlow.getBucketIdentifier(), createdFlow.getBucketIdentifier());
+ assertEquals(versionedFlow.getDescription(), createdFlow.getDescription());
+ }
+
+ @Test(expected = ResourceNotFoundException.class)
+ public void testGetFlowDoesNotExist() {
+ when(metadataService.getFlowById(any(String.class))).thenReturn(null);
+ registryService.getFlow("bucket1","flow1");
+ }
+
+ @Test(expected = ResourceNotFoundException.class)
+ public void testGetFlowDirectDoesNotExist() {
+ when(metadataService.getFlowById(any(String.class))).thenReturn(null);
+ registryService.getFlow("flow1");
+ }
+
+ @Test
+ public void testGetFlowExists() {
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ final FlowEntity flowEntity = new FlowEntity();
+ flowEntity.setId("flow1");
+ flowEntity.setName("My Flow");
+ flowEntity.setDescription("This is my flow.");
+ flowEntity.setCreated(new Date());
+ flowEntity.setModified(new Date());
+ flowEntity.setBucketId(existingBucket.getId());
+
+ when(metadataService.getFlowByIdWithSnapshotCounts(flowEntity.getId())).thenReturn(flowEntity);
+
+ final VersionedFlow versionedFlow = registryService.getFlow(existingBucket.getId(), flowEntity.getId());
+ assertNotNull(versionedFlow);
+ assertEquals(flowEntity.getId(), versionedFlow.getIdentifier());
+ assertEquals(flowEntity.getName(), versionedFlow.getName());
+ assertEquals(flowEntity.getDescription(), versionedFlow.getDescription());
+ assertEquals(flowEntity.getBucketId(), versionedFlow.getBucketIdentifier());
+ assertEquals(existingBucket.getName(), versionedFlow.getBucketName());
+ assertEquals(flowEntity.getCreated().getTime(), versionedFlow.getCreatedTimestamp());
+ assertEquals(flowEntity.getModified().getTime(), versionedFlow.getModifiedTimestamp());
+ }
+
+ @Test
+ public void testGetFlowDirectExists() {
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ final FlowEntity flowEntity = new FlowEntity();
+ flowEntity.setId("flow1");
+ flowEntity.setName("My Flow");
+ flowEntity.setDescription("This is my flow.");
+ flowEntity.setCreated(new Date());
+ flowEntity.setModified(new Date());
+ flowEntity.setBucketId(existingBucket.getId());
+
+ when(metadataService.getFlowByIdWithSnapshotCounts(flowEntity.getId())).thenReturn(flowEntity);
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ final VersionedFlow versionedFlow = registryService.getFlow(flowEntity.getId());
+ assertNotNull(versionedFlow);
+ assertEquals(flowEntity.getId(), versionedFlow.getIdentifier());
+ assertEquals(flowEntity.getName(), versionedFlow.getName());
+ assertEquals(flowEntity.getDescription(), versionedFlow.getDescription());
+ assertEquals(flowEntity.getBucketId(), versionedFlow.getBucketIdentifier());
+ assertEquals(existingBucket.getName(), versionedFlow.getBucketName());
+ assertEquals(flowEntity.getCreated().getTime(), versionedFlow.getCreatedTimestamp());
+ assertEquals(flowEntity.getModified().getTime(), versionedFlow.getModifiedTimestamp());
+ }
+
+ @Test(expected = ResourceNotFoundException.class)
+ public void testGetFlowsByBucketDoesNotExist() {
+ when(metadataService.getBucketById(any(String.class))).thenReturn(null);
+ registryService.getFlows("b1");
+ }
+
+ @Test
+ public void testGetFlowsByBucketExists() {
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ final FlowEntity flowEntity1 = new FlowEntity();
+ flowEntity1.setId("flow1");
+ flowEntity1.setName("My Flow");
+ flowEntity1.setDescription("This is my flow.");
+ flowEntity1.setCreated(new Date());
+ flowEntity1.setModified(new Date());
+ flowEntity1.setBucketId(existingBucket.getId());
+
+ final FlowEntity flowEntity2 = new FlowEntity();
+ flowEntity2.setId("flow2");
+ flowEntity2.setName("My Flow 2");
+ flowEntity2.setDescription("This is my flow 2.");
+ flowEntity2.setCreated(new Date());
+ flowEntity2.setModified(new Date());
+ flowEntity2.setBucketId(existingBucket.getId());
+
+ final List<FlowEntity> flows = new ArrayList<>();
+ flows.add(flowEntity1);
+ flows.add(flowEntity2);
+
+ when(metadataService.getFlowsByBucket(eq(existingBucket.getId()))).thenReturn(flows);
+
+ final List<VersionedFlow> allFlows = registryService.getFlows(existingBucket.getId());
+ assertNotNull(allFlows);
+ assertEquals(2, allFlows.size());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFlowWithoutId() {
+ final VersionedFlow versionedFlow = new VersionedFlow();
+ registryService.updateFlow(versionedFlow);
+ }
+
+ @Test(expected = ResourceNotFoundException.class)
+ public void testUpdateFlowDoesNotExist() {
+ final VersionedFlow versionedFlow = new VersionedFlow();
+ versionedFlow.setBucketIdentifier("b1");
+ versionedFlow.setIdentifier("flow1");
+
+ when(metadataService.getFlowById(versionedFlow.getIdentifier())).thenReturn(null);
+
+ registryService.updateFlow(versionedFlow);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testUpdateFlowWithSameNameAsExistingFlow() {
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ final FlowEntity flowToUpdate = new FlowEntity();
+ flowToUpdate.setId("flow1");
+ flowToUpdate.setName("My Flow");
+ flowToUpdate.setDescription("This is my flow.");
+ flowToUpdate.setCreated(new Date());
+ flowToUpdate.setModified(new Date());
+ flowToUpdate.setBucketId(existingBucket.getId());
+
+ when(metadataService.getFlowByIdWithSnapshotCounts(flowToUpdate.getId())).thenReturn(flowToUpdate);
+
+ final FlowEntity otherFlow = new FlowEntity();
+ otherFlow.setId("flow2");
+ otherFlow.setName("My Flow 2");
+ otherFlow.setDescription("This is my flow 2.");
+ otherFlow.setCreated(new Date());
+ otherFlow.setModified(new Date());
+ otherFlow.setBucketId(existingBucket.getId());
+
+ when(metadataService.getFlowsByName(existingBucket.getId(), otherFlow.getName())).thenReturn(Collections.singletonList(otherFlow));
+
+ final VersionedFlow versionedFlow = new VersionedFlow();
+ versionedFlow.setIdentifier(flowToUpdate.getId());
+ versionedFlow.setBucketIdentifier(existingBucket.getId());
+ versionedFlow.setName(otherFlow.getName());
+
+ registryService.updateFlow(versionedFlow);
+ }
+
+ @Test
+ public void testUpdateFlow() throws InterruptedException {
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ final FlowEntity flowToUpdate = new FlowEntity();
+ flowToUpdate.setId("flow1");
+ flowToUpdate.setName("My Flow");
+ flowToUpdate.setDescription("This is my flow.");
+ flowToUpdate.setCreated(new Date());
+ flowToUpdate.setModified(new Date());
+ flowToUpdate.setBucketId(existingBucket.getId());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+ when(metadataService.getFlowByIdWithSnapshotCounts(flowToUpdate.getId())).thenReturn(flowToUpdate);
+ when(metadataService.getFlowsByName(flowToUpdate.getName())).thenReturn(Collections.singletonList(flowToUpdate));
+
+ doAnswer(updateFlowAnswer()).when(metadataService).updateFlow(any(FlowEntity.class));
+
+ final VersionedFlow versionedFlow = new VersionedFlow();
+ versionedFlow.setBucketIdentifier(flowToUpdate.getBucketId());
+ versionedFlow.setIdentifier(flowToUpdate.getId());
+ versionedFlow.setName("New Flow Name");
+ versionedFlow.setDescription("This is a new description");
+
+ Thread.sleep(10);
+
+ final VersionedFlow updatedFlow = registryService.updateFlow(versionedFlow);
+ assertNotNull(updatedFlow);
+ assertEquals(versionedFlow.getIdentifier(), updatedFlow.getIdentifier());
+
+ // name and description should be updated
+ assertEquals(versionedFlow.getName(), updatedFlow.getName());
+ assertEquals(versionedFlow.getDescription(), updatedFlow.getDescription());
+
+ // other fields should not be updated
+ assertEquals(flowToUpdate.getBucketId(), updatedFlow.getBucketIdentifier());
+ assertEquals(flowToUpdate.getCreated().getTime(), updatedFlow.getCreatedTimestamp());
+ }
+
+ @Test(expected = ResourceNotFoundException.class)
+ public void testDeleteFlowDoesNotExist() {
+ when(metadataService.getFlowById(any(String.class))).thenReturn(null);
+ registryService.deleteFlow("b1", "flow1");
+ }
+
+ @Test
+ public void testDeleteFlowWithSnapshots() {
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ final FlowEntity flowToDelete = new FlowEntity();
+ flowToDelete.setId("flow1");
+ flowToDelete.setName("My Flow");
+ flowToDelete.setDescription("This is my flow.");
+ flowToDelete.setCreated(new Date());
+ flowToDelete.setModified(new Date());
+ flowToDelete.setBucketId(existingBucket.getId());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+ when(metadataService.getFlowById(flowToDelete.getId())).thenReturn(flowToDelete);
+ when(metadataService.getFlowsByName(flowToDelete.getName())).thenReturn(Collections.singletonList(flowToDelete));
+
+ final VersionedFlow deletedFlow = registryService.deleteFlow(existingBucket.getId(), flowToDelete.getId());
+ assertNotNull(deletedFlow);
+ assertEquals(flowToDelete.getId(), deletedFlow.getIdentifier());
+
+ verify(flowPersistenceProvider, times(1))
+ .deleteAllFlowContent(flowToDelete.getBucketId(), flowToDelete.getId());
+
+ verify(metadataService, times(1)).deleteFlow(flowToDelete);
+ }
+
+ // ---------------------- Test VersionedFlowSnapshot methods ---------------------------------------------
+
+ private VersionedFlowSnapshot createSnapshot() {
+ final VersionedFlowSnapshotMetadata snapshotMetadata = new VersionedFlowSnapshotMetadata();
+ snapshotMetadata.setFlowIdentifier("flow1");
+ snapshotMetadata.setVersion(1);
+ snapshotMetadata.setComments("This is the first snapshot");
+ snapshotMetadata.setBucketIdentifier("b1");
+ snapshotMetadata.setAuthor("user1");
+
+ final VersionedProcessGroup processGroup = new VersionedProcessGroup();
+ processGroup.setIdentifier("pg1");
+ processGroup.setName("My Process Group");
+
+ final VersionedFlowSnapshot snapshot = new VersionedFlowSnapshot();
+ snapshot.setSnapshotMetadata(snapshotMetadata);
+ snapshot.setFlowContents(processGroup);
+
+ return snapshot;
+ }
+
+ @Test(expected = ConstraintViolationException.class)
+ public void testCreateSnapshotInvalidMetadata() {
+ final VersionedFlowSnapshot snapshot = createSnapshot();
+ snapshot.getSnapshotMetadata().setFlowIdentifier(null);
+ registryService.createFlowSnapshot(snapshot);
+ }
+
+ @Test(expected = ConstraintViolationException.class)
+ public void testCreateSnapshotInvalidFlowContents() {
+ final VersionedFlowSnapshot snapshot = createSnapshot();
+ snapshot.setFlowContents(null);
+ registryService.createFlowSnapshot(snapshot);
+ }
+
+ @Test(expected = ConstraintViolationException.class)
+ public void testCreateSnapshotNullMetadata() {
+ final VersionedFlowSnapshot snapshot = createSnapshot();
+ snapshot.setSnapshotMetadata(null);
+ registryService.createFlowSnapshot(snapshot);
+ }
+
+ @Test(expected = ConstraintViolationException.class)
+ public void testCreateSnapshotNullFlowContents() {
+ final VersionedFlowSnapshot snapshot = createSnapshot();
+ snapshot.setFlowContents(null);
+ registryService.createFlowSnapshot(snapshot);
+ }
+
+ @Test(expected = ResourceNotFoundException.class)
+ public void testCreateSnapshotBucketDoesNotExist() {
+ when(metadataService.getBucketById(any(String.class))).thenReturn(null);
+
+ final VersionedFlowSnapshot snapshot = createSnapshot();
+ registryService.createFlowSnapshot(snapshot);
+ }
+
+ @Test(expected = ResourceNotFoundException.class)
+ public void testCreateSnapshotFlowDoesNotExist() {
+ final VersionedFlowSnapshot snapshot = createSnapshot();
+
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ when(metadataService.getFlowById(snapshot.getSnapshotMetadata().getFlowIdentifier())).thenReturn(null);
+
+ registryService.createFlowSnapshot(snapshot);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testCreateSnapshotVersionAlreadyExists() {
+ final VersionedFlowSnapshot snapshot = createSnapshot();
+
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ // return a flow with the existing snapshot when getFlowById is called
+ final FlowEntity existingFlow = new FlowEntity();
+ existingFlow.setId("flow1");
+ existingFlow.setName("My Flow");
+ existingFlow.setDescription("This is my flow.");
+ existingFlow.setCreated(new Date());
+ existingFlow.setModified(new Date());
+ existingFlow.setBucketId(existingBucket.getId());
+
+ when(metadataService.getFlowById(existingFlow.getId())).thenReturn(existingFlow);
+
+ // make a snapshot that has the same version as the one being created
+ final FlowSnapshotEntity existingSnapshot = new FlowSnapshotEntity();
+ existingSnapshot.setFlowId(snapshot.getSnapshotMetadata().getFlowIdentifier());
+ existingSnapshot.setVersion(snapshot.getSnapshotMetadata().getVersion());
+ existingSnapshot.setComments("This is an existing snapshot");
+ existingSnapshot.setCreated(new Date());
+ existingSnapshot.setCreatedBy("test-user");
+
+ final List<FlowSnapshotEntity> existingSnapshots = Arrays.asList(existingSnapshot);
+ when(metadataService.getSnapshots(existingFlow.getId())).thenReturn(existingSnapshots);
+
+ registryService.createFlowSnapshot(snapshot);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testCreateSnapshotVersionNotNextVersion() {
+ final VersionedFlowSnapshot snapshot = createSnapshot();
+
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ // return a flow with the existing snapshot when getFlowById is called
+ final FlowEntity existingFlow = new FlowEntity();
+ existingFlow.setId("flow1");
+ existingFlow.setName("My Flow");
+ existingFlow.setDescription("This is my flow.");
+ existingFlow.setCreated(new Date());
+ existingFlow.setModified(new Date());
+ existingFlow.setBucketId(existingBucket.getId());
+
+ // make a snapshot that has the same version as the one being created
+ final FlowSnapshotEntity existingSnapshot = new FlowSnapshotEntity();
+ existingSnapshot.setFlowId(snapshot.getSnapshotMetadata().getFlowIdentifier());
+ existingSnapshot.setVersion(snapshot.getSnapshotMetadata().getVersion());
+ existingSnapshot.setComments("This is an existing snapshot");
+ existingSnapshot.setCreated(new Date());
+ existingSnapshot.setCreatedBy("test-user");
+
+ when(metadataService.getFlowById(existingFlow.getId())).thenReturn(existingFlow);
+
+ // set the version to something that is not the next one-up version
+ snapshot.getSnapshotMetadata().setVersion(100);
+ registryService.createFlowSnapshot(snapshot);
+ }
+
+ @Test
+ public void testCreateFirstSnapshot() {
+ final VersionedFlowSnapshot snapshot = createSnapshot();
+
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ // return a flow with the existing snapshot when getFlowById is called
+ final FlowEntity existingFlow = new FlowEntity();
+ existingFlow.setId("flow1");
+ existingFlow.setName("My Flow");
+ existingFlow.setDescription("This is my flow.");
+ existingFlow.setCreated(new Date());
+ existingFlow.setModified(new Date());
+ existingFlow.setBucketId(existingBucket.getId());
+
+ when(metadataService.getFlowById(existingFlow.getId())).thenReturn(existingFlow);
+ when(metadataService.getFlowByIdWithSnapshotCounts(existingFlow.getId())).thenReturn(existingFlow);
+
+ final VersionedFlowSnapshot createdSnapshot = registryService.createFlowSnapshot(snapshot);
+ assertNotNull(createdSnapshot);
+ assertNotNull(createdSnapshot.getSnapshotMetadata());
+ assertNotNull(createdSnapshot.getFlow());
+ assertNotNull(createdSnapshot.getBucket());
+
+ verify(snapshotSerializer, times(1)).serialize(eq(snapshot.getFlowContents()), any(OutputStream.class));
+ verify(flowPersistenceProvider, times(1)).saveFlowContent(any(), any());
+ verify(metadataService, times(1)).createFlowSnapshot(any(FlowSnapshotEntity.class));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testCreateFirstSnapshotWithBadVersion() {
+ final VersionedFlowSnapshot snapshot = createSnapshot();
+
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ // return a flow with the existing snapshot when getFlowById is called
+ final FlowEntity existingFlow = new FlowEntity();
+ existingFlow.setId("flow1");
+ existingFlow.setName("My Flow");
+ existingFlow.setDescription("This is my flow.");
+ existingFlow.setCreated(new Date());
+ existingFlow.setModified(new Date());
+ existingFlow.setBucketId(existingBucket.getId());
+
+ when(metadataService.getFlowById(existingFlow.getId())).thenReturn(existingFlow);
+
+ // set the first version to something other than 1
+ snapshot.getSnapshotMetadata().setVersion(100);
+ registryService.createFlowSnapshot(snapshot);
+ }
+
+ @Test
+ public void testGetFlowSnapshots() {
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ // return a flow with the existing snapshot when getFlowById is called
+ final FlowEntity existingFlow = new FlowEntity();
+ existingFlow.setId("flow1");
+ existingFlow.setName("My Flow");
+ existingFlow.setDescription("This is my flow.");
+ existingFlow.setCreated(new Date());
+ existingFlow.setModified(new Date());
+ existingFlow.setBucketId(existingBucket.getId());
+
+ when(metadataService.getFlowById(existingFlow.getId())).thenReturn(existingFlow);
+
+ final FlowSnapshotEntity existingSnapshot1 = new FlowSnapshotEntity();
+ existingSnapshot1.setVersion(1);
+ existingSnapshot1.setFlowId(existingFlow.getId());
+ existingSnapshot1.setCreatedBy("user1");
+ existingSnapshot1.setCreated(new Date());
+ existingSnapshot1.setComments("This is snapshot 1");
+
+ final FlowSnapshotEntity existingSnapshot2 = new FlowSnapshotEntity();
+ existingSnapshot2.setVersion(2);
+ existingSnapshot2.setFlowId(existingFlow.getId());
+ existingSnapshot2.setCreatedBy("user2");
+ existingSnapshot2.setCreated(new Date());
+ existingSnapshot2.setComments("This is snapshot 2");
+
+ final List<FlowSnapshotEntity> snapshots = new ArrayList<>();
+ snapshots.add(existingSnapshot1);
+ snapshots.add(existingSnapshot2);
+
+ when(metadataService.getSnapshots(existingFlow.getId())).thenReturn(snapshots);
+
+ final SortedSet<VersionedFlowSnapshotMetadata> retrievedSnapshots = registryService.getFlowSnapshots(existingBucket.getId(), existingFlow.getId());
+ assertNotNull(retrievedSnapshots);
+ assertEquals(2, retrievedSnapshots.size());
+ // check that sorted set order is reversed
+ assertEquals(2, retrievedSnapshots.first().getVersion());
+ assertEquals(1, retrievedSnapshots.last().getVersion());
+ }
+
+ @Test
+ public void testGetFlowSnapshotsWhenNoSnapshots() {
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ // return a flow with the existing snapshot when getFlowById is called
+ final FlowEntity existingFlow = new FlowEntity();
+ existingFlow.setId("flow1");
+ existingFlow.setName("My Flow");
+ existingFlow.setDescription("This is my flow.");
+ existingFlow.setCreated(new Date());
+ existingFlow.setModified(new Date());
+ existingFlow.setBucketId(existingBucket.getId());
+
+ final Set<FlowSnapshotEntity> snapshots = new HashSet<>();
+
+ when(metadataService.getFlowById(existingFlow.getId())).thenReturn(existingFlow);
+
+ final SortedSet<VersionedFlowSnapshotMetadata> retrievedSnapshots = registryService.getFlowSnapshots(existingBucket.getId(), existingFlow.getId());
+ assertNotNull(retrievedSnapshots);
+ assertEquals(0, retrievedSnapshots.size());
+ }
+
+ @Test
+ public void testGetLatestSnapshotMetadataWhenVersionsExist() {
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ // return a flow with the existing snapshot when getFlowById is called
+ final FlowEntity existingFlow = new FlowEntity();
+ existingFlow.setId("flow1");
+ existingFlow.setName("My Flow");
+ existingFlow.setDescription("This is my flow.");
+ existingFlow.setCreated(new Date());
+ existingFlow.setModified(new Date());
+ existingFlow.setBucketId(existingBucket.getId());
+
+ when(metadataService.getFlowById(existingFlow.getId())).thenReturn(existingFlow);
+
+ final FlowSnapshotEntity existingSnapshot1 = new FlowSnapshotEntity();
+ existingSnapshot1.setVersion(1);
+ existingSnapshot1.setFlowId(existingFlow.getId());
+ existingSnapshot1.setCreatedBy("user1");
+ existingSnapshot1.setCreated(new Date());
+ existingSnapshot1.setComments("This is snapshot 1");
+
+ when(metadataService.getLatestSnapshot(existingFlow.getId())).thenReturn(existingSnapshot1);
+
+ VersionedFlowSnapshotMetadata latestMetadata = registryService.getLatestFlowSnapshotMetadata(existingBucket.getId(), existingFlow.getId());
+ assertNotNull(latestMetadata);
+ assertEquals(1, latestMetadata.getVersion());
+ }
+
+ @Test
+ public void testGetLatestSnapshotMetadataWhenNoVersionsExist() {
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId("b1");
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+
+ when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
+
+ // return a flow with the existing snapshot when getFlowById is called
+ final FlowEntity existingFlow = new FlowEntity();
+ existingFlow.setId("flow1");
+ existingFlow.setName("My Flow");
+ existingFlow.setDescription("This is my flow.");
+ existingFlow.setCreated(new Date());
+ existingFlow.setModified(new Date());
+ existingFlow.setBucketId(existingBucket.getId());
+
+ when(metadataService.getFlowById(existingFlow.getId())).thenReturn(existingFlow);
+
+ final FlowSnapshotEntity existingSnapshot1 = new FlowSnapshotEntity();
+ existingSnapshot1.setVersion(1);
+ existingSnapshot1.setFlowId(existingFlow.getId());
+ existingSnapshot1.setCreatedBy("user1");
+ existingSnapshot1.setCreated(new Date());
+ existingSnapshot1.setComments("This is snapshot 1");
+
+ when(metadataService.getLatestSnapshot(existingFlow.getId())).thenReturn(null);
+
+ try {
+ registryService.getLatestFlowSnapshotMetadata(existingBucket.getId(), existingFlow.getId());
+ Assert.fail("Should have thrown exception");
+ } catch (ResourceNotFoundException e) {
+ assertEquals("The specified flow ID has no versions", e.getMessage());
+ }
+ }
+
+ @Test(expected = ResourceNotFoundException.class)
+ public void testGetSnapshotDoesNotExistInMetadataProvider() {
+ final String bucketId = "b1";
+ final String flowId = "flow1";
+ final Integer version = 1;
+ when(metadataService.getFlowSnapshot(flowId, version)).thenReturn(null);
+ registryService.getFlowSnapshot(bucketId, flowId, version);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testGetSnapshotDoesNotExistInPersistenceProvider() {
+ final BucketEntity existingBucket = createBucketEntity("b1");
+ final FlowEntity existingFlow = createFlowEntity(existingBucket.getId());
+ final FlowSnapshotEntity existingSnapshot = createFlowSnapshotEntity(existingFlow.getId());
+
+ existingFlow.setSnapshotCount(10);
+
+ when(metadataService.getBucketById(existingBucket.getId()))
+ .thenReturn(existingBucket);
+
+ when(metadataService.getFlowByIdWithSnapshotCounts(existingFlow.getId()))
+ .thenReturn(existingFlow);
+
+ when(metadataService.getFlowSnapshot(existingFlow.getId(), existingSnapshot.getVersion()))
+ .thenReturn(existingSnapshot);
+
+ when(flowPersistenceProvider.getFlowContent(
+ existingBucket.getId(),
+ existingSnapshot.getFlowId(),
+ existingSnapshot.getVersion()
+ )).thenReturn(null);
+
+ registryService.getFlowSnapshot(existingBucket.getId(), existingSnapshot.getFlowId(), existingSnapshot.getVersion());
+ }
+
+ @Test
+ public void testGetSnapshotExists() {
+ final BucketEntity existingBucket = createBucketEntity("b1");
+ final FlowEntity existingFlow = createFlowEntity(existingBucket.getId());
+ final FlowSnapshotEntity existingSnapshot = createFlowSnapshotEntity(existingFlow.getId());
+
+ existingFlow.setSnapshotCount(10);
+
+ when(metadataService.getBucketById(existingBucket.getId()))
+ .thenReturn(existingBucket);
+
+ when(metadataService.getFlowByIdWithSnapshotCounts(existingFlow.getId()))
+ .thenReturn(existingFlow);
+
+ when(metadataService.getFlowSnapshot(existingFlow.getId(), existingSnapshot.getVersion()))
+ .thenReturn(existingSnapshot);
+
+ // return a non-null, non-zero-length array so something gets passed to the serializer
+ when(flowPersistenceProvider.getFlowContent(
+ existingBucket.getId(),
+ existingSnapshot.getFlowId(),
+ existingSnapshot.getVersion()
+ )).thenReturn(new byte[10]);
+
+ final VersionedFlowSnapshot snapshotToDeserialize = createSnapshot();
+ when(snapshotSerializer.deserialize(any(InputStream.class))).thenReturn(snapshotToDeserialize.getFlowContents());
+
+ final VersionedFlowSnapshot returnedSnapshot = registryService.getFlowSnapshot(
+ existingBucket.getId(), existingSnapshot.getFlowId(), existingSnapshot.getVersion());
+ assertNotNull(returnedSnapshot);
+ assertNotNull(returnedSnapshot.getSnapshotMetadata());
+
+ final VersionedFlowSnapshotMetadata snapshotMetadata = returnedSnapshot.getSnapshotMetadata();
+ assertEquals(existingSnapshot.getVersion().intValue(), snapshotMetadata.getVersion());
+ assertEquals(existingBucket.getId(), snapshotMetadata.getBucketIdentifier());
+ assertEquals(existingSnapshot.getFlowId(), snapshotMetadata.getFlowIdentifier());
+ assertEquals(existingSnapshot.getCreated(), new Date(snapshotMetadata.getTimestamp()));
+ assertEquals(existingSnapshot.getCreatedBy(), snapshotMetadata.getAuthor());
+ assertEquals(existingSnapshot.getComments(), snapshotMetadata.getComments());
+
+ final VersionedFlow versionedFlow = returnedSnapshot.getFlow();
+ assertNotNull(versionedFlow);
+ assertNotNull(versionedFlow.getVersionCount());
+ assertTrue(versionedFlow.getVersionCount() > 0);
+
+ final Bucket bucket = returnedSnapshot.getBucket();
+ assertNotNull(bucket);
+ }
+
+ @Test(expected = ResourceNotFoundException.class)
+ public void testDeleteSnapshotDoesNotExist() {
+ final String bucketId = "b1";
+ final String flowId = "flow1";
+ final Integer version = 1;
+ when(metadataService.getFlowSnapshot(flowId, version)).thenReturn(null);
+ registryService.deleteFlowSnapshot(bucketId, flowId, version);
+ }
+
+ @Test
+ public void testDeleteSnapshotExists() {
+ final BucketEntity existingBucket = createBucketEntity("b1");
+ final FlowEntity existingFlow = createFlowEntity(existingBucket.getId());
+ final FlowSnapshotEntity existingSnapshot = createFlowSnapshotEntity(existingFlow.getId());
+
+ when(metadataService.getBucketById(existingBucket.getId()))
+ .thenReturn(existingBucket);
+
+ when(metadataService.getFlowById(existingFlow.getId()))
+ .thenReturn(existingFlow);
+
+ when(metadataService.getFlowSnapshot(existingSnapshot.getFlowId(), existingSnapshot.getVersion()))
+ .thenReturn(existingSnapshot);
+
+ final VersionedFlowSnapshotMetadata deletedSnapshot = registryService.deleteFlowSnapshot(
+ existingBucket.getId(), existingSnapshot.getFlowId(), existingSnapshot.getVersion());
+
+ assertNotNull(deletedSnapshot);
+ assertEquals(existingSnapshot.getFlowId(), deletedSnapshot.getFlowIdentifier());
+
+ verify(flowPersistenceProvider, times(1)).deleteFlowContent(
+ existingBucket.getId(),
+ existingSnapshot.getFlowId(),
+ existingSnapshot.getVersion()
+ );
+
+ verify(metadataService, times(1)).deleteFlowSnapshot(existingSnapshot);
+ }
+
+ private FlowSnapshotEntity createFlowSnapshotEntity(final String flowId) {
+ final FlowSnapshotEntity existingSnapshot = new FlowSnapshotEntity();
+ existingSnapshot.setVersion(1);
+ existingSnapshot.setFlowId(flowId);
+ existingSnapshot.setComments("This is an existing snapshot");
+ existingSnapshot.setCreated(new Date());
+ existingSnapshot.setCreatedBy("test-user");
+ return existingSnapshot;
+ }
+
+ private FlowEntity createFlowEntity(final String bucketId) {
+ final FlowEntity existingFlow = new FlowEntity();
+ existingFlow.setId("flow1");
+ existingFlow.setName("My Flow");
+ existingFlow.setDescription("This is my flow.");
+ existingFlow.setCreated(new Date());
+ existingFlow.setModified(new Date());
+ existingFlow.setBucketId(bucketId);
+ return existingFlow;
+ }
+
+ private BucketEntity createBucketEntity(final String bucketId) {
+ final BucketEntity existingBucket = new BucketEntity();
+ existingBucket.setId(bucketId);
+ existingBucket.setName("My Bucket");
+ existingBucket.setDescription("This is my bucket");
+ existingBucket.setCreated(new Date());
+ return existingBucket;
+ }
+
+ // -----------------Test Flow Diff Service Method---------------------
+ @Test
+ public void testGetDiffReturnsRemovedComponentChanges() {
+ when(flowPersistenceProvider.getFlowContent(
+ anyString(), anyString(), anyInt()
+ )).thenReturn(new byte[10], new byte[10]);
+
+ final VersionedProcessGroup pgA = createVersionedProcessGroupA();
+ final VersionedProcessGroup pgB = createVersionedProcessGroupB();
+ when(snapshotSerializer.deserialize(any())).thenReturn(pgA, pgB);
+
+ final VersionedFlowDifference diff = registryService.getFlowDiff(
+ "bucketIdentifier", "flowIdentifier", 1, 2);
+
+ assertNotNull(diff);
+ Optional<ComponentDifferenceGroup> removedComponent = diff.getComponentDifferenceGroups().stream()
+ .filter(p->p.getComponentId().equals("ID-pg1")).findFirst();
+
+ assertTrue(removedComponent.isPresent());
+ assertTrue(removedComponent.get().getDifferences().iterator().next().getDifferenceType().equals("COMPONENT_REMOVED"));
+ }
+
+ @Test
+ public void testGetDiffReturnsChangesInChronologicalOrder() {
+ when(flowPersistenceProvider.getFlowContent(
+ anyString(), anyString(), anyInt()
+ )).thenReturn(new byte[10], new byte[10]);
+
+ final VersionedProcessGroup pgA = createVersionedProcessGroupA();
+ final VersionedProcessGroup pgB = createVersionedProcessGroupB();
+ when(snapshotSerializer.deserialize(any())).thenReturn(pgA, pgB);
+
+ // getFlowDiff orders the changes in ascending order of version number regardless of param order
+ final VersionedFlowDifference diff = registryService.getFlowDiff(
+ "bucketIdentifier", "flowIdentifier", 2,1);
+
+ assertNotNull(diff);
+ Optional<ComponentDifferenceGroup> nameChangedComponent = diff.getComponentDifferenceGroups().stream()
+ .filter(p->p.getComponentId().equals("ProcessorFirstV1")).findFirst();
+
+ assertTrue(nameChangedComponent.isPresent());
+
+ ComponentDifference nameChangeDifference = nameChangedComponent.get().getDifferences().stream()
+ .filter(d-> d.getDifferenceType().equals("NAME_CHANGED")).findFirst().get();
+
+ assertEquals("ProcessorFirstV1", nameChangeDifference.getValueA());
+ assertEquals("ProcessorFirstV2", nameChangeDifference.getValueB());
+ }
+
+ private VersionedProcessGroup createVersionedProcessGroupA() {
+ VersionedProcessGroup root = new VersionedProcessGroup();
+ root.setProcessGroups(new HashSet<>(Arrays.asList(createProcessGroup("ID-pg1"), createProcessGroup("ID-pg2"))));
+ // Add processors
+ root.setProcessors(new HashSet<>(Arrays.asList(createVersionedProcessor("ProcessorFirstV1"), createVersionedProcessor("ProcessorSecondV1"))));
+ return root;
+ }
+
+ private VersionedProcessGroup createProcessGroup(String identifier){
+ VersionedProcessGroup processGroup = new VersionedProcessGroup();
+ processGroup.setIdentifier(identifier);
+ return processGroup;
+ }
+ private VersionedProcessGroup createVersionedProcessGroupB() {
+ VersionedProcessGroup updated = createVersionedProcessGroupA();
+ // remove a process group
+ updated.getProcessGroups().removeIf(pg->pg.getIdentifier().equals("ID-pg1"));
+ // change the name of a processor
+ updated.getProcessors().stream().forEach(p->p.setPenaltyDuration(p.getName().equals("ProcessorFirstV1") ? "1" : "2"));
+ updated.getProcessors().stream().forEach(p->p.setName(p.getName().equals("ProcessorFirstV1") ? "ProcessorFirstV2" : p.getName()));
+ return updated;
+ }
+
+ private VersionedProcessor createVersionedProcessor(String name){
+ VersionedProcessor processor = new VersionedProcessor();
+ processor.setName(name);
+ processor.setIdentifier(name);
+ processor.setProperties(new HashMap<>());
+ return processor;
+ }
+ // -------------------------------------------------------------------
+
+ private Answer<BucketEntity> createBucketAnswer() {
+ return (InvocationOnMock invocation) -> {
+ BucketEntity bucketEntity = (BucketEntity) invocation.getArguments()[0];
+ return bucketEntity;
+ };
+ }
+
+ private Answer<BucketEntity> updateBucketAnswer() {
+ return (InvocationOnMock invocation) -> {
+ BucketEntity bucketEntity = (BucketEntity) invocation.getArguments()[0];
+ return bucketEntity;
+ };
+ }
+
+ private Answer<FlowEntity> createFlowAnswer() {
+ return (InvocationOnMock invocation) -> {
+ final FlowEntity flowEntity = (FlowEntity) invocation.getArguments()[0];
+ return flowEntity;
+ };
+ }
+
+ private Answer<FlowEntity> updateFlowAnswer() {
+ return (InvocationOnMock invocation) -> {
+ final FlowEntity flowEntity = (FlowEntity) invocation.getArguments()[0];
+ return flowEntity;
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/resources/application.properties
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/application.properties b/nifi-registry-core/nifi-registry-framework/src/test/resources/application.properties
new file mode 100644
index 0000000..1e0d7c9
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/application.properties
@@ -0,0 +1,25 @@
+# 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.
+
+# Properties for Spring Boot tests
+
+# Properties for Spring Boot integration tests
+# Documentation for commoon Spring Boot application properties can be found at:
+# https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
+
+# These verbose log levels can be enabled locally for dev testing, but disable them in the repo to minimize travis logs.
+#logging.level.org.springframework.core.io.support: DEBUG
+#logging.level.org.springframework.context.annotation: DEBUG
+#logging.level.org.springframework.web: DEBUG
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/resources/db/migration/V999999.1__test-setup.sql
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/db/migration/V999999.1__test-setup.sql b/nifi-registry-core/nifi-registry-framework/src/test/resources/db/migration/V999999.1__test-setup.sql
new file mode 100644
index 0000000..8f3cfa8
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/db/migration/V999999.1__test-setup.sql
@@ -0,0 +1,70 @@
+-- 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.
+
+-- test data for buckets
+
+insert into bucket (id, name, description, created)
+ values ('1', 'Bucket 1', 'This is test bucket 1', parsedatetime('2017-09-11 12:51:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'));
+
+insert into bucket (id, name, description, created)
+ values ('2', 'Bucket 2', 'This is test bucket 2', parsedatetime('2017-09-11 12:52:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'));
+
+insert into bucket (id, name, description, created)
+ values ('3', 'Bucket 3', 'This is test bucket 3', parsedatetime('2017-09-11 12:53:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'));
+
+insert into bucket (id, name, description, created)
+ values ('4', 'Bucket 4', 'This is test bucket 4', parsedatetime('2017-09-11 12:54:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'));
+
+insert into bucket (id, name, description, created)
+ values ('5', 'Bucket 5', 'This is test bucket 5', parsedatetime('2017-09-11 12:55:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'));
+
+insert into bucket (id, name, description, created)
+ values ('6', 'Bucket 6', 'This is test bucket 6', parsedatetime('2017-09-11 12:56:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'));
+
+
+-- test data for flows
+
+insert into bucket_item (id, name, description, created, modified, item_type, bucket_id)
+ values ('1', 'Flow 1', 'This is flow 1 bucket 1', parsedatetime('2017-09-11 12:56:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), parsedatetime('2017-09-11 12:56:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), 'FLOW', '1');
+
+insert into flow (id) values ('1');
+
+insert into bucket_item (id, name, description, created, modified, item_type, bucket_id)
+ values ('2', 'Flow 2', 'This is flow 2 bucket 1', parsedatetime('2017-09-11 12:56:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), parsedatetime('2017-09-11 12:56:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), 'FLOW', '1');
+
+insert into flow (id) values ('2');
+
+insert into bucket_item (id, name, description, created, modified, item_type, bucket_id)
+ values ('3', 'Flow 1', 'This is flow 1 bucket 2', parsedatetime('2017-09-11 12:56:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), parsedatetime('2017-09-11 12:56:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), 'FLOW', '2');
+
+insert into flow (id) values ('3');
+
+
+-- test data for flow snapshots
+
+insert into flow_snapshot (flow_id, version, created, created_by, comments)
+ values ('1', 1, parsedatetime('2017-09-11 12:57:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), 'user1', 'This is flow 1 snapshot 1');
+
+insert into flow_snapshot (flow_id, version, created, created_by, comments)
+ values ('1', 2, parsedatetime('2017-09-11 12:58:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), 'user1', 'This is flow 1 snapshot 2');
+
+insert into flow_snapshot (flow_id, version, created, created_by, comments)
+ values ('1', 3, parsedatetime('2017-09-11 12:59:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), 'user1', 'This is flow 1 snapshot 3');
+
+
+-- test data for signing keys
+
+insert into signing_key (id, tenant_identity, key_value)
+ values ('1', 'unit_test_tenant_identity', '0123456789abcdef');
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/resources/nifi-example.ldif
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/nifi-example.ldif b/nifi-registry-core/nifi-registry-framework/src/test/resources/nifi-example.ldif
new file mode 100644
index 0000000..c91feac
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/nifi-example.ldif
@@ -0,0 +1,166 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+version: 1
+
+dn: o=nifi
+objectclass: extensibleObject
+objectclass: top
+objectclass: domain
+dc: nifi
+o: nifi
+
+dn: ou=users,o=nifi
+objectClass: organizationalUnit
+objectClass: top
+ou: users
+
+dn: ou=users-2,o=nifi
+objectClass: organizationalUnit
+objectClass: top
+ou: users-2
+
+dn: cn=User 1,ou=users,o=nifi
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: User 1
+sn: User1
+uid: user1
+
+dn: cn=User 2,ou=users,o=nifi
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: User 2
+sn: User2
+uid: user2
+
+dn: cn=User 3,ou=users,o=nifi
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: User 3
+sn: User3
+uid: user3
+
+## since the embedded ldap does not support memberof, we are using description to simulate
+
+dn: cn=User 4,ou=users,o=nifi
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: User 4
+sn: User4
+description: cn=team1,ou=groups,o=nifi
+uid: user4
+
+dn: cn=User 5,ou=users,o=nifi
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: User 5
+sn: User5
+description: cn=team1,ou=groups,o=nifi
+uid: user5
+
+dn: cn=User 6,ou=users,o=nifi
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: User 6
+sn: User6
+description: cn=team2,ou=groups,o=nifi
+uid: user6
+
+dn: cn=User 7,ou=users,o=nifi
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: User 7
+sn: User7
+description: cn=team2,ou=groups,o=nifi
+uid: user7
+
+dn: cn=User 8,ou=users,o=nifi
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: User 8
+sn: User8
+uid: user8
+
+dn: cn=User 9,ou=users-2,o=nifi
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: User 9
+sn: User9
+description: team3
+uid: user9
+
+dn: ou=groups,o=nifi
+objectClass: organizationalUnit
+objectClass: top
+ou: groups
+
+dn: ou=groups-2,o=nifi
+objectClass: organizationalUnit
+objectClass: top
+ou: groups
+
+dn: cn=admins,ou=groups,o=nifi
+objectClass: groupOfNames
+objectClass: top
+cn: admins
+member: cn=User 1,ou=users,o=nifi
+member: cn=User 3,ou=users,o=nifi
+
+dn: cn=read-only,ou=groups,o=nifi
+objectClass: groupOfNames
+objectClass: top
+cn: read-only
+member: cn=User 2,ou=users,o=nifi
+
+dn: cn=team1,ou=groups,o=nifi
+objectClass: groupOfNames
+objectClass: top
+cn: team1
+member: cn=User 1,ou=users,o=nifi
+
+dn: cn=team2,ou=groups,o=nifi
+objectClass: groupOfNames
+objectClass: top
+cn: team2
+member: cn=User 1,ou=users,o=nifi
+
+## since the embedded ldap requires member to be fqdn, we are simulating using room and description
+
+dn: cn=team3,ou=groups-2,o=nifi
+objectClass: room
+objectClass: top
+cn: team3
+description: user9
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/hook/bad-script-provider.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/hook/bad-script-provider.xml b/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/hook/bad-script-provider.xml
new file mode 100644
index 0000000..568e756
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/hook/bad-script-provider.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ 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.
+-->
+<providers>
+
+ <flowPersistenceProvider>
+ <class>org.apache.nifi.registry.provider.MockFlowPersistenceProvider</class>
+ <property name="Flow Property 1">flow foo</property>
+ <property name="Flow Property 2">flow bar</property>
+ </flowPersistenceProvider>
+
+ <eventHookProvider>
+ <class>org.apache.nifi.registry.provider.hook.ScriptEventHookProvider</class>
+ <property name="Script Path"></property>
+ <property name="Working Directory"></property>
+ </eventHookProvider>
+
+</providers>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-class-not-found.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-class-not-found.xml b/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-class-not-found.xml
new file mode 100644
index 0000000..9adba54
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-class-not-found.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ 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.
+-->
+<providers>
+
+ <flowPersistenceProvider>
+ <class>org.apache.nifi.registry.provider.FlowProviderXXX</class>
+ <property name="Flow Property 1">foo</property>
+ <property name="Flow Property 2">bar</property>
+ </flowPersistenceProvider>
+
+</providers>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-good.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-good.xml b/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-good.xml
new file mode 100644
index 0000000..32414e5
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-good.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ 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.
+-->
+<providers>
+
+ <flowPersistenceProvider>
+ <class>org.apache.nifi.registry.provider.MockFlowPersistenceProvider</class>
+ <property name="Flow Property 1">flow foo</property>
+ <property name="Flow Property 2">flow bar</property>
+ </flowPersistenceProvider>
+
+</providers>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-ap-provider-ids.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-ap-provider-ids.xml b/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-ap-provider-ids.xml
new file mode 100644
index 0000000..dcbc36a
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-ap-provider-ids.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ ~ 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.
+ -->
+<authorizers>
+
+ <userGroupProvider>
+ <identifier>file-user-group-provider</identifier>
+ <class>org.apache.nifi.registry.security.authorization.file.FileUserGroupProvider</class>
+ <property name="Users File">./target/test-classes/security/users.xml</property>
+ </userGroupProvider>
+
+ <accessPolicyProvider>
+ <identifier>file-access-policy-provider</identifier>
+ <class>org.apache.nifi.registry.security.authorization.file.FileAccessPolicyProvider</class>
+ <property name="User Group Provider">file-user-group-provider</property>
+ <property name="Authorizations File">./target/test-classes/security/authorizations1.xml</property>
+ </accessPolicyProvider>
+
+ <accessPolicyProvider>
+ <!-- this is incorrect: identifiers must be unique across all accessPolicyProviders -->
+ <identifier>file-access-policy-provider</identifier>
+ <class>org.apache.nifi.registry.security.authorization.file.FileAccessPolicyProvider</class>
+ <property name="User Group Provider">file-user-group-provider</property>
+ <property name="Authorizations File">./target/test-classes/security/authorizations2.xml</property>
+ </accessPolicyProvider>
+
+ <authorizer>
+ <identifier>managed-authorizer</identifier>
+ <class>org.apache.nifi.registry.security.authorization.StandardManagedAuthorizer</class>
+ <property name="Access Policy Provider">file-access-policy-provider</property>
+ </authorizer>
+
+</authorizers>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-authorizer-ids.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-authorizer-ids.xml b/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-authorizer-ids.xml
new file mode 100644
index 0000000..d31de91
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-authorizer-ids.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ ~ 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.
+ -->
+<authorizers>
+
+ <userGroupProvider>
+ <identifier>file-user-group-provider</identifier>
+ <class>org.apache.nifi.registry.security.authorization.file.FileUserGroupProvider</class>
+ <property name="Users File">./target/test-classes/security/users.xml</property>
+ </userGroupProvider>
+
+ <accessPolicyProvider>
+ <identifier>file-access-policy-provider</identifier>
+ <class>org.apache.nifi.registry.security.authorization.file.FileAccessPolicyProvider</class>
+ <property name="User Group Provider">file-user-group-provider</property>
+ <property name="Authorizations File">./target/test-classes/security/authorizations.xml</property>
+ </accessPolicyProvider>
+
+ <authorizer>
+ <identifier>managed-authorizer</identifier>
+ <class>org.apache.nifi.registry.security.authorization.StandardManagedAuthorizer</class>
+ <property name="Access Policy Provider">file-access-policy-provider</property>
+ </authorizer>
+
+ <authorizer>
+ <!-- this is incorrect: identifiers must be unique across all authorizers -->
+ <identifier>managed-authorizer</identifier>
+ <class>org.apache.nifi.registry.security.authorization.StandardManagedAuthorizer</class>
+ <property name="Access Policy Provider">file-access-policy-provider</property>
+ </authorizer>
+
+</authorizers>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-composite.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-composite.xml b/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-composite.xml
new file mode 100644
index 0000000..1aa96f8
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-composite.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ ~ 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.
+ -->
+<authorizers>
+
+ <userGroupProvider>
+ <identifier>file-user-group-provider-1</identifier>
+ <class>org.apache.nifi.registry.security.authorization.file.FileUserGroupProvider</class>
+ <property name="Users File">./target/test-classes/security/users.xml</property>
+ </userGroupProvider>
+
+ <userGroupProvider>
+ <identifier>file-user-group-provider-2</identifier>
+ <class>org.apache.nifi.registry.security.authorization.file.FileUserGroupProvider</class>
+ <property name="Users File">./target/test-classes/security/read-only-users.xml</property>
+ </userGroupProvider>
+
+ <userGroupProvider>
+ <identifier>composite-user-group-provider</identifier>
+ <class>org.apache.nifi.registry.security.authorization.CompositeUserGroupProvider</class>
+ <property name="User Group Provider 1">file-user-group-provider-1</property>
+ <property name="User Group Provider 2">file-user-group-provider-2</property>
+ <!-- this is incorrect: the list of providers must not contain duplicates -->
+ <property name="User Group Provider 3">file-user-group-provider-2</property>
+ </userGroupProvider>
+
+ <accessPolicyProvider>
+ <identifier>file-access-policy-provider</identifier>
+ <class>org.apache.nifi.registry.security.authorization.file.FileAccessPolicyProvider</class>
+ <property name="User Group Provider">composite-user-group-provider</property>
+ <property name="Authorizations File">./target/test-classes/security/authorizations.xml</property>
+ </accessPolicyProvider>
+
+ <authorizer>
+ <identifier>managed-authorizer</identifier>
+ <class>org.apache.nifi.registry.security.authorization.StandardManagedAuthorizer</class>
+ <property name="Access Policy Provider">file-access-policy-provider</property>
+ </authorizer>
+
+</authorizers>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-configurable-composite.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-configurable-composite.xml b/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-configurable-composite.xml
new file mode 100644
index 0000000..f019345
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-configurable-composite.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ ~ 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.
+ -->
+<authorizers>
+
+ <userGroupProvider>
+ <identifier>file-user-group-provider-1</identifier>
+ <class>org.apache.nifi.registry.security.authorization.file.FileUserGroupProvider</class>
+ <property name="Users File">./target/test-classes/security/users.xml</property>
+ </userGroupProvider>
+
+ <userGroupProvider>
+ <identifier>file-user-group-provider-2</identifier>
+ <class>org.apache.nifi.registry.security.authorization.file.FileUserGroupProvider</class>
+ <property name="Users File">./target/test-classes/security/read-only-users.xml</property>
+ </userGroupProvider>
+
+ <userGroupProvider>
+ <identifier>composite-configurable-user-group-provider</identifier>
+ <class>org.apache.nifi.registry.security.authorization.CompositeConfigurableUserGroupProvider</class>
+ <property name="Configurable User Group Provider">file-user-group-provider-1</property>
+ <!-- this is incorrect: the configurable provider should not be listed in the list of non-configurable providers -->
+ <property name="User Group Provider 1">file-user-group-provider-1</property>
+ <property name="User Group Provider 2">file-user-group-provider-2</property>
+ </userGroupProvider>
+
+ <accessPolicyProvider>
+ <identifier>file-access-policy-provider</identifier>
+ <class>org.apache.nifi.registry.security.authorization.file.FileAccessPolicyProvider</class>
+ <property name="User Group Provider">composite-configurable-user-group-provider</property>
+ <property name="Authorizations File">./target/test-classes/security/authorizations.xml</property>
+ </accessPolicyProvider>
+
+ <authorizer>
+ <identifier>managed-authorizer</identifier>
+ <class>org.apache.nifi.registry.security.authorization.StandardManagedAuthorizer</class>
+ <property name="Access Policy Provider">file-access-policy-provider</property>
+ </authorizer>
+
+</authorizers>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-ug-provider-ids.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-ug-provider-ids.xml b/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-ug-provider-ids.xml
new file mode 100644
index 0000000..a28a36d
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/security/authorizers-bad-ug-provider-ids.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ ~ 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.
+ -->
+<authorizers>
+
+ <userGroupProvider>
+ <identifier>file-user-group-provider</identifier>
+ <class>org.apache.nifi.registry.security.authorization.file.FileUserGroupProvider</class>
+ <property name="Users File">./target/test-classes/security/users1.xml</property>
+ </userGroupProvider>
+
+ <userGroupProvider>
+ <!-- this is incorrect: identifiers must be unique across all userGroupProviders -->
+ <identifier>file-user-group-provider</identifier>
+ <class>org.apache.nifi.registry.security.authorization.file.FileUserGroupProvider</class>
+ <property name="Users File">./target/test-classes/security/users2.xml</property>
+ </userGroupProvider>
+
+ <accessPolicyProvider>
+ <identifier>file-access-policy-provider</identifier>
+ <class>org.apache.nifi.registry.security.authorization.file.FileAccessPolicyProvider</class>
+ <property name="User Group Provider">file-user-group-provider</property>
+ <property name="Authorizations File">./target/test-classes/security/authorizations.xml</property>
+ </accessPolicyProvider>
+
+ <authorizer>
+ <identifier>managed-authorizer</identifier>
+ <class>org.apache.nifi.registry.security.authorization.StandardManagedAuthorizer</class>
+ <property name="Access Policy Provider">file-access-policy-provider</property>
+ </authorizer>
+
+</authorizers>
\ No newline at end of file