/*
 * 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.ambari.server.controller.internal;

import static org.easymock.EasyMock.anyBoolean;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.createStrictMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.controller.AmbariManagementController;
import org.apache.ambari.server.controller.internal.BlueprintResourceProvider.BlueprintConfigPopulationStrategy;
import org.apache.ambari.server.controller.internal.BlueprintResourceProvider.BlueprintConfigPopulationStrategyV1;
import org.apache.ambari.server.controller.internal.BlueprintResourceProvider.BlueprintConfigPopulationStrategyV2;
import org.apache.ambari.server.controller.predicate.EqualsPredicate;
import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
import org.apache.ambari.server.controller.spi.NoSuchResourceException;
import org.apache.ambari.server.controller.spi.Predicate;
import org.apache.ambari.server.controller.spi.Request;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
import org.apache.ambari.server.controller.spi.ResourceProvider;
import org.apache.ambari.server.controller.spi.SystemException;
import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
import org.apache.ambari.server.orm.dao.BlueprintDAO;
import org.apache.ambari.server.orm.dao.StackDAO;
import org.apache.ambari.server.orm.dao.TopologyRequestDAO;
import org.apache.ambari.server.orm.entities.BlueprintConfigEntity;
import org.apache.ambari.server.orm.entities.BlueprintConfiguration;
import org.apache.ambari.server.orm.entities.BlueprintEntity;
import org.apache.ambari.server.orm.entities.BlueprintSettingEntity;
import org.apache.ambari.server.orm.entities.HostGroupComponentEntity;
import org.apache.ambari.server.orm.entities.HostGroupEntity;
import org.apache.ambari.server.orm.entities.StackEntity;
import org.apache.ambari.server.orm.entities.TopologyRequestEntity;
import org.apache.ambari.server.state.PropertyInfo;
import org.apache.ambari.server.state.StackInfo;
import org.apache.ambari.server.topology.Blueprint;
import org.apache.ambari.server.topology.BlueprintFactory;
import org.apache.ambari.server.topology.InvalidTopologyException;
import org.apache.ambari.server.topology.SecurityConfiguration;
import org.apache.ambari.server.topology.SecurityConfigurationFactory;
import org.apache.ambari.server.topology.Setting;
import org.apache.ambari.server.utils.StageUtils;
import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import com.google.common.collect.ImmutableList;
import com.google.gson.Gson;

/**
 * BlueprintResourceProvider unit tests.
 */
@SuppressWarnings("unchecked")
public class BlueprintResourceProviderTest {

  private static String BLUEPRINT_NAME = "test-blueprint";

  @Rule
  public ExpectedException expectedException = ExpectedException.none();

  private final static BlueprintDAO blueprintDao = createStrictMock(BlueprintDAO.class);
  private final static TopologyRequestDAO topologyRequestDAO = createMock(TopologyRequestDAO.class);
  private final static StackDAO stackDAO = createNiceMock(StackDAO.class);
  private final static BlueprintEntity entity = createStrictMock(BlueprintEntity.class);
  private final static Blueprint blueprint = createMock(Blueprint.class);
  private final static AmbariMetaInfo metaInfo = createMock(AmbariMetaInfo.class);
  private final static BlueprintFactory blueprintFactory = createMock(BlueprintFactory.class);
  private final static SecurityConfigurationFactory securityFactory = createMock(SecurityConfigurationFactory.class);
  private final static BlueprintResourceProvider provider = createProvider();
  private final static Gson gson = new Gson();

  @BeforeClass
  public static void initClass() {
    BlueprintResourceProvider.init(blueprintFactory, blueprintDao, topologyRequestDAO, securityFactory, gson, metaInfo);

    StackEntity stackEntity = new StackEntity();
    stackEntity.setStackName("test-stack-name");
    stackEntity.setStackVersion("test-stack-version");

    expect(
        stackDAO.find(anyObject(String.class),
          anyObject(String.class))).andReturn(stackEntity).anyTimes();
    replay(stackDAO);

  }

  private Map<String, Set<HashMap<String, String>>> getSettingProperties() {
    return new HashMap<>();
  }

  @Before
  public void resetGlobalMocks() {
    reset(blueprintDao, topologyRequestDAO, metaInfo, blueprintFactory, securityFactory, blueprint, entity);
  }

  @Test
  public void testCreateResources() throws Exception {

    AmbariManagementController managementController = createMock(AmbariManagementController.class);
    Request request = createMock(Request.class);
    Setting setting = createStrictMock(Setting.class);

    Set<Map<String, Object>> setProperties = getBlueprintTestProperties();
    Map<String, String> requestInfoProperties = getTestRequestInfoProperties();
    Map<String, Set<HashMap<String, String>>> settingProperties = getSettingProperties();

    // set expectations
    expect(blueprintFactory.createBlueprint(setProperties.iterator().next(), null)).andReturn(blueprint).once();
    expect(securityFactory.createSecurityConfigurationFromRequest(null, true)).andReturn(null).anyTimes();
    blueprint.validateRequiredProperties();
    blueprint.validateTopology();
    expect(blueprint.getSetting()).andReturn(setting).anyTimes();
    expect(setting.getProperties()).andReturn(settingProperties).anyTimes();
    expect(blueprint.toEntity()).andReturn(entity);
    expect(blueprint.getName()).andReturn(BLUEPRINT_NAME).atLeastOnce();
    expect(request.getProperties()).andReturn(setProperties);
    expect(request.getRequestInfoProperties()).andReturn(requestInfoProperties);
    expect(blueprintDao.findByName(BLUEPRINT_NAME)).andReturn(null);
    blueprintDao.create(entity);

    replay(blueprintDao, entity, metaInfo, blueprintFactory, securityFactory, blueprint, setting, request, managementController);
    // end expectations

    ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
        Resource.Type.Blueprint,
        managementController);

    AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
    ((ObservableResourceProvider)provider).addObserver(observer);

    provider.createResources(request);

    ResourceProviderEvent lastEvent = observer.getLastEvent();
    assertNotNull(lastEvent);
    assertEquals(Resource.Type.Blueprint, lastEvent.getResourceType());
    assertEquals(ResourceProviderEvent.Type.Create, lastEvent.getType());
    assertEquals(request, lastEvent.getRequest());
    assertNull(lastEvent.getPredicate());

    verify(blueprintDao, entity, blueprintFactory, securityFactory, metaInfo, request, managementController);
  }

  @Test()
  public void testCreateResources_ReqestBodyIsEmpty() throws Exception {
    AmbariManagementController managementController = createMock(AmbariManagementController.class);
    Request request = createMock(Request.class);

    Set<Map<String, Object>> setProperties = getBlueprintTestProperties();
    Map<String, String> requestInfoProperties = new HashMap<>();
    requestInfoProperties.put(Request.REQUEST_INFO_BODY_PROPERTY, null);

    // set expectations
    expect(request.getProperties()).andReturn(setProperties);
    expect(request.getRequestInfoProperties()).andReturn(requestInfoProperties);

    replay(request, managementController);
    // end expectations

    ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
      Resource.Type.Blueprint,
      managementController);

    try {
      provider.createResources(request);
      fail("Exception expected");
    } catch (IllegalArgumentException e) {
      //expected exception
      assertEquals(BlueprintResourceProvider.REQUEST_BODY_EMPTY_ERROR_MESSAGE, e.getMessage());
    }
    verify(request, managementController);
  }

  @Test
  public void testCreateResources_NoValidation() throws Exception {

    AmbariManagementController managementController = createMock(AmbariManagementController.class);
    Request request = createMock(Request.class);
    Setting setting = createStrictMock(Setting.class);

    Map<String, Set<HashMap<String, String>>> settingProperties = getSettingProperties();
    Set<Map<String, Object>> setProperties = getBlueprintTestProperties();
    Map<String, String> requestInfoProperties = getTestRequestInfoProperties();
    requestInfoProperties.put("validate_topology", "false");

    // set expectations
    expect(blueprintFactory.createBlueprint(setProperties.iterator().next(), null)).andReturn(blueprint).once();
    blueprint.validateRequiredProperties();
    expect(blueprint.getSetting()).andReturn(setting).anyTimes();
    expect(setting.getProperties()).andReturn(settingProperties).anyTimes();
    expect(blueprint.toEntity()).andReturn(entity);
    expect(blueprint.getName()).andReturn(BLUEPRINT_NAME).atLeastOnce();
    expect(request.getProperties()).andReturn(setProperties);
    expect(request.getRequestInfoProperties()).andReturn(requestInfoProperties);
    expect(blueprintDao.findByName(BLUEPRINT_NAME)).andReturn(null);
    blueprintDao.create(entity);

    replay(blueprintDao, entity, metaInfo, blueprintFactory, blueprint, setting, request, managementController);
    // end expectations

    ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
        Resource.Type.Blueprint,
        managementController);

    AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
    ((ObservableResourceProvider)provider).addObserver(observer);

    provider.createResources(request);

    ResourceProviderEvent lastEvent = observer.getLastEvent();
    assertNotNull(lastEvent);
    assertEquals(Resource.Type.Blueprint, lastEvent.getResourceType());
    assertEquals(ResourceProviderEvent.Type.Create, lastEvent.getType());
    assertEquals(request, lastEvent.getRequest());
    assertNull(lastEvent.getPredicate());

    verify(blueprintDao, entity, blueprintFactory, metaInfo, request, managementController);
  }

  @Test
  public void testCreateResources_TopologyValidationFails() throws Exception {

    Request request = createMock(Request.class);
    Set<Map<String, Object>> setProperties = getBlueprintTestProperties();
    Map<String, String> requestInfoProperties = getTestRequestInfoProperties();

    // set expectations
    expect(blueprintFactory.createBlueprint(setProperties.iterator().next(), null)).andReturn(blueprint).once();
    blueprint.validateRequiredProperties();
    expect(blueprint.getName()).andReturn(BLUEPRINT_NAME).atLeastOnce();
    blueprint.validateTopology();
    expectLastCall().andThrow(new InvalidTopologyException("test"));

    expect(request.getProperties()).andReturn(setProperties);
    expect(request.getRequestInfoProperties()).andReturn(requestInfoProperties);
    expect(blueprintDao.findByName(BLUEPRINT_NAME)).andReturn(null);

    replay(blueprintDao, entity, metaInfo, blueprintFactory, blueprint, request);
    // end expectations

    ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
        Resource.Type.Blueprint,
        createMock(AmbariManagementController.class));

    AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
    ((ObservableResourceProvider)provider).addObserver(observer);

    try {
      provider.createResources(request);
      fail("Expected exception due to topology validation error");
    } catch (IllegalArgumentException e) {
      // expected
    }

    verify(blueprintDao, entity, blueprintFactory, metaInfo, request);
  }


  @Test
  public void testCreateResources_withConfiguration() throws Exception {

    Set<Map<String, Object>> setProperties = getBlueprintTestProperties();
    setConfigurationProperties(setProperties);
    AmbariManagementController managementController = createMock(AmbariManagementController.class);
    Map<String, String> requestInfoProperties = getTestRequestInfoProperties();
    Request request = createMock(Request.class);
    Setting setting = createStrictMock(Setting.class);
    Map<String, Set<HashMap<String, String>>> settingProperties = getSettingProperties();

    // set expectations
    expect(blueprintFactory.createBlueprint(setProperties.iterator().next(), null)).andReturn(blueprint).once();
    blueprint.validateRequiredProperties();
    blueprint.validateTopology();
    expect(blueprint.getSetting()).andReturn(setting).anyTimes();
    expect(setting.getProperties()).andReturn(settingProperties).anyTimes();
    expect(blueprint.toEntity()).andReturn(entity);
    expect(blueprint.getName()).andReturn(BLUEPRINT_NAME).atLeastOnce();
    expect(request.getProperties()).andReturn(setProperties);
    expect(request.getRequestInfoProperties()).andReturn(requestInfoProperties);
    expect(blueprintDao.findByName(BLUEPRINT_NAME)).andReturn(null);
    blueprintDao.create(entity);

    replay(blueprintDao, entity, metaInfo, blueprintFactory, blueprint, setting, request, managementController);
    // end expectations

    ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
        Resource.Type.Blueprint,
        managementController);

    AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
    ((ObservableResourceProvider)provider).addObserver(observer);

    provider.createResources(request);

    ResourceProviderEvent lastEvent = observer.getLastEvent();
    assertNotNull(lastEvent);
    assertEquals(Resource.Type.Blueprint, lastEvent.getResourceType());
    assertEquals(ResourceProviderEvent.Type.Create, lastEvent.getType());
    assertEquals(request, lastEvent.getRequest());
    assertNull(lastEvent.getPredicate());

    verify(blueprintDao, entity, blueprintFactory, metaInfo, request, managementController);
  }

  @Test
  public void testCreateResource_BlueprintFactoryThrowsException() throws Exception
  {
    Request request = createMock(Request.class);

    Set<Map<String, Object>> setProperties = getBlueprintTestProperties();
    setProperties.iterator().next().remove(BlueprintResourceProvider.HOST_GROUP_PROPERTY_ID);

    Map<String, String> requestInfoProperties = getTestRequestInfoProperties();

    // set expectations
    expect(blueprintFactory.createBlueprint(setProperties.iterator().next(), null)).andThrow(
      new IllegalArgumentException("Blueprint name must be provided"));
    expect(securityFactory.createSecurityConfigurationFromRequest(null,true)).andReturn(null).anyTimes();
    expect(request.getProperties()).andReturn(setProperties);
    expect(request.getRequestInfoProperties()).andReturn(requestInfoProperties);

    replay(blueprintDao, entity, metaInfo, blueprintFactory, securityFactory, blueprint, request);
    // end expectations

    try {
      provider.createResources(request);
      fail("Exception expected");
    } catch (IllegalArgumentException e) {
      // expected
    }
    verify(blueprintDao, entity, blueprintFactory, metaInfo, request);
  }

  @Test
  public void testCreateResources_withSecurityConfiguration() throws Exception {
    AmbariManagementController managementController = createMock(AmbariManagementController.class);
    Request request = createMock(Request.class);
    Setting setting = createStrictMock(Setting.class);

    Set<Map<String, Object>> setProperties = getBlueprintTestProperties();
    Map<String, String> requestInfoProperties = getTestRequestInfoProperties();
    Map<String, Set<HashMap<String, String>>> settingProperties = getSettingProperties();
    SecurityConfiguration securityConfiguration = SecurityConfiguration.withReference("testRef");

    // set expectations
    expect(securityFactory.createSecurityConfigurationFromRequest(EasyMock.anyObject(), anyBoolean())).andReturn
      (securityConfiguration).once();
    expect(blueprintFactory.createBlueprint(setProperties.iterator().next(), securityConfiguration)).andReturn(blueprint).once();
    blueprint.validateRequiredProperties();
    blueprint.validateTopology();
    expect(blueprint.getSetting()).andReturn(setting).anyTimes();
    expect(setting.getProperties()).andReturn(settingProperties).anyTimes();
    expect(blueprint.toEntity()).andReturn(entity);
    expect(blueprint.getName()).andReturn(BLUEPRINT_NAME).atLeastOnce();
    expect(request.getProperties()).andReturn(setProperties);
    expect(request.getRequestInfoProperties()).andReturn(requestInfoProperties);
    expect(blueprintDao.findByName(BLUEPRINT_NAME)).andReturn(null);
    blueprintDao.create(entity);

    replay(blueprintDao, entity, metaInfo, blueprintFactory, securityFactory, blueprint, setting, request, managementController);
    // end expectations

    ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
      Resource.Type.Blueprint,
      managementController);

    AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
    ((ObservableResourceProvider)provider).addObserver(observer);

    provider.createResources(request);

    ResourceProviderEvent lastEvent = observer.getLastEvent();
    assertNotNull(lastEvent);
    assertEquals(Resource.Type.Blueprint, lastEvent.getResourceType());
    assertEquals(ResourceProviderEvent.Type.Create, lastEvent.getType());
    assertEquals(request, lastEvent.getRequest());
    assertNull(lastEvent.getPredicate());

    verify(blueprintDao, entity, blueprintFactory, metaInfo, request, managementController);
  }

  @Test
  public void testGetResourcesNoPredicate() throws SystemException, UnsupportedPropertyException,
                                                   NoSuchParentResourceException, NoSuchResourceException {
    Request request = createNiceMock(Request.class);

    BlueprintEntity entity = createEntity(getBlueprintTestProperties().iterator().next());

    List<BlueprintEntity> results = new ArrayList<>();
    results.add(entity);

    // set expectations
    expect(blueprintDao.findAll()).andReturn(results);
    replay(blueprintDao, request);

    Set<Resource> setResults = provider.getResources(request, null);
    assertEquals(1, setResults.size());

    verify(blueprintDao);
    validateResource(setResults.iterator().next(), false);
  }

  @Test
  public void testGetResourcesNoPredicate_withConfiguration() throws SystemException, UnsupportedPropertyException,
      NoSuchParentResourceException, NoSuchResourceException, AmbariException {

    StackInfo info = createMock(StackInfo.class);
    expect(info.getConfigPropertiesTypes("core-site")).andReturn(new HashMap<>()).anyTimes();
    expect(metaInfo.getStack("test-stack-name", "test-stack-version")).andReturn(info).anyTimes();
    replay(info, metaInfo);
    Request request = createNiceMock(Request.class);

    Set<Map<String, Object>> testProperties = getBlueprintTestProperties();
    setConfigurationProperties(testProperties);
    BlueprintEntity entity = createEntity(testProperties.iterator().next());

    List<BlueprintEntity> results = new ArrayList<>();
    results.add(entity);

    // set expectations
    expect(blueprintDao.findAll()).andReturn(results);
    replay(blueprintDao, request);

    Set<Resource> setResults = provider.getResources(request, null);
    assertEquals(1, setResults.size());

    verify(blueprintDao);
    validateResource(setResults.iterator().next(), true);
  }


  @Test
  public void testDeleteResources() throws SystemException, UnsupportedPropertyException,
                                           NoSuchParentResourceException, NoSuchResourceException {

    BlueprintEntity blueprintEntity = createEntity(getBlueprintTestProperties().iterator().next());

    // set expectations
    expect(blueprintDao.findByName(BLUEPRINT_NAME)).andReturn(blueprintEntity);
    blueprintDao.removeByName(blueprintEntity.getBlueprintName());
    expectLastCall();
    expect(topologyRequestDAO.findAllProvisionRequests()).andReturn(ImmutableList.of());
    replay(blueprintDao, topologyRequestDAO);

    Predicate predicate = new EqualsPredicate<>(
      BlueprintResourceProvider.BLUEPRINT_NAME_PROPERTY_ID, BLUEPRINT_NAME);

    AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
    provider.addObserver(observer);

    provider.deleteResources(new RequestImpl(null, null, null, null), predicate);

    ResourceProviderEvent lastEvent = observer.getLastEvent();
    assertNotNull(lastEvent);
    assertEquals(Resource.Type.Blueprint, lastEvent.getResourceType());
    assertEquals(ResourceProviderEvent.Type.Delete, lastEvent.getType());
    assertNotNull(lastEvent.getPredicate());

    verify(blueprintDao);
  }

  @Test(expected = IllegalArgumentException.class)
  public void testDeleteResources_clusterAlreadyProvisioned() throws SystemException, UnsupportedPropertyException,
    NoSuchParentResourceException, NoSuchResourceException {

    BlueprintEntity blueprintEntity = createEntity(getBlueprintTestProperties().iterator().next());

    // set expectations
    expect(blueprintDao.findByName(BLUEPRINT_NAME)).andReturn(blueprintEntity);
    blueprintDao.removeByName(blueprintEntity.getBlueprintName());
    expectLastCall();
    TopologyRequestEntity topologyRequestEntity = new TopologyRequestEntity();
    topologyRequestEntity.setBlueprintName(BLUEPRINT_NAME);
    expect(topologyRequestDAO.findAllProvisionRequests()).andReturn(ImmutableList.of(topologyRequestEntity));
    replay(blueprintDao, topologyRequestDAO);


    Predicate predicate = new EqualsPredicate<>(
      BlueprintResourceProvider.BLUEPRINT_NAME_PROPERTY_ID, BLUEPRINT_NAME);

    AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
    provider.addObserver(observer);

    provider.deleteResources(new RequestImpl(null, null, null, null), predicate);
  }


  @Test
  public void testCreateResources_withEmptyConfiguration() throws Exception {

    Set<Map<String, Object>> setProperties = getBlueprintTestProperties();
    setConfigurationProperties(setProperties);
    AmbariManagementController managementController = createMock(AmbariManagementController.class);
    Map<String, String> requestInfoProperties = new HashMap<>();
    Map<String, Set<HashMap<String, String>>> settingProperties = getSettingProperties();
    requestInfoProperties.put(Request.REQUEST_INFO_BODY_PROPERTY, "{\"configurations\":[]}");
    Request request = createMock(Request.class);
    Setting setting = createStrictMock(Setting.class);

    // set expectations
    expect(blueprintFactory.createBlueprint(setProperties.iterator().next(), null)).andReturn(blueprint).once();
    blueprint.validateRequiredProperties();
    blueprint.validateTopology();
    expect(blueprint.getSetting()).andReturn(setting).anyTimes();
    expect(setting.getProperties()).andReturn(settingProperties).anyTimes();
    expect(blueprint.toEntity()).andReturn(entity);
    expect(blueprint.getName()).andReturn(BLUEPRINT_NAME).atLeastOnce();
    expect(request.getProperties()).andReturn(setProperties);
    expect(request.getRequestInfoProperties()).andReturn(requestInfoProperties);
    expect(blueprintDao.findByName(BLUEPRINT_NAME)).andReturn(null);
    blueprintDao.create(entity);

    replay(blueprintDao, entity, metaInfo, blueprintFactory, blueprint, setting, request, managementController);
    // end expectations

    ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
        Resource.Type.Blueprint,
        managementController);

    AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
    ((ObservableResourceProvider)provider).addObserver(observer);

    provider.createResources(request);

    ResourceProviderEvent lastEvent = observer.getLastEvent();
    assertNotNull(lastEvent);
    assertEquals(Resource.Type.Blueprint, lastEvent.getResourceType());
    assertEquals(ResourceProviderEvent.Type.Create, lastEvent.getType());
    assertEquals(request, lastEvent.getRequest());
    assertNull(lastEvent.getPredicate());

    verify(blueprintDao, entity, blueprintFactory, metaInfo, request, managementController);
  }

  @Test
  public void testCreateResources_withSingleConfigurationType() throws Exception {

    Set<Map<String, Object>> setProperties = getBlueprintTestProperties();
    setConfigurationProperties(setProperties);
    AmbariManagementController managementController = createMock(AmbariManagementController.class);
    Map<String, String> requestInfoProperties = new HashMap<>();
    Map<String, Set<HashMap<String, String>>> settingProperties = getSettingProperties();
    requestInfoProperties.put(Request.REQUEST_INFO_BODY_PROPERTY, "{\"configurations\":[{\"configuration-type\":{\"properties\":{\"property\":\"value\"}}}]}");
    Request request = createMock(Request.class);
    Setting setting = createStrictMock(Setting.class);

    // set expectations
    expect(blueprintFactory.createBlueprint(setProperties.iterator().next(), null)).andReturn(blueprint).once();
    blueprint.validateRequiredProperties();
    blueprint.validateTopology();
    expect(blueprint.getSetting()).andReturn(setting).anyTimes();
    expect(setting.getProperties()).andReturn(settingProperties).anyTimes();
    expect(blueprint.toEntity()).andReturn(entity);
    expect(blueprint.getName()).andReturn(BLUEPRINT_NAME).atLeastOnce();
    expect(request.getProperties()).andReturn(setProperties);
    expect(request.getRequestInfoProperties()).andReturn(requestInfoProperties);
    expect(blueprintDao.findByName(BLUEPRINT_NAME)).andReturn(null);
    blueprintDao.create(entity);

    replay(blueprintDao, entity, metaInfo, blueprintFactory, blueprint, setting, request, managementController);
    // end expectations

    ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
        Resource.Type.Blueprint,
        managementController);

    AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
    ((ObservableResourceProvider)provider).addObserver(observer);

    provider.createResources(request);

    ResourceProviderEvent lastEvent = observer.getLastEvent();
    assertNotNull(lastEvent);
    assertEquals(Resource.Type.Blueprint, lastEvent.getResourceType());
    assertEquals(ResourceProviderEvent.Type.Create, lastEvent.getType());
    assertEquals(request, lastEvent.getRequest());
    assertNull(lastEvent.getPredicate());

    verify(blueprintDao, entity, blueprintFactory, metaInfo, request, managementController);
  }

  @Test
  public void testCreateResources_wrongConfigurationsStructure_withWrongConfigMapSize() throws ResourceAlreadyExistsException, SystemException,
      UnsupportedPropertyException, NoSuchParentResourceException
  {
    Request request = createMock(Request.class);

    Set<Map<String, Object>> setProperties = getBlueprintTestProperties();

    Map<String, String> requestInfoProperties = new HashMap<>();
    String configurationData = "{\"configurations\":[{\"config-type1\":{\"properties\" :{\"property\":\"property-value\"}},"
        + "\"config-type2\" : {\"properties_attributes\" : {\"property\" : \"property-value\"}, \"properties\" : {\"property\" : \"property-value\"}}}"
        + "]}";
    requestInfoProperties.put(Request.REQUEST_INFO_BODY_PROPERTY, configurationData);

    // set expectations
    expect(request.getProperties()).andReturn(setProperties);
    expect(request.getRequestInfoProperties()).andReturn(requestInfoProperties);

    replay(blueprintDao, metaInfo, request);
    // end expectations

    try {
      provider.createResources(request);
      fail("Exception expected");
    } catch (IllegalArgumentException e) {
      //expected exception
      assertEquals(BlueprintResourceProvider.CONFIGURATION_MAP_SIZE_CHECK_ERROR_MESSAGE, e.getMessage());
    }
    verify(blueprintDao, metaInfo, request);
  }

  @Test
  public void testCreateResources_wrongConfigurationStructure_withoutConfigMaps() throws ResourceAlreadyExistsException, SystemException,
    UnsupportedPropertyException, NoSuchParentResourceException {

    Request request = createMock(Request.class);

    Set<Map<String, Object>> setProperties = getBlueprintTestProperties();

    Map<String, String> requestInfoProperties = new HashMap<>();
    String configurationData = "{\"configurations\":[\"config-type1\", \"config-type2\"]}";
    requestInfoProperties.put(Request.REQUEST_INFO_BODY_PROPERTY, configurationData);

    // set expectations
    expect(request.getProperties()).andReturn(setProperties);
    expect(request.getRequestInfoProperties()).andReturn(requestInfoProperties);

    replay(blueprintDao, metaInfo, request);
    // end expectations

    try {
      provider.createResources(request);
      fail("Exception expected");
    } catch (IllegalArgumentException e) {
      //expected exception
      assertEquals(BlueprintResourceProvider.CONFIGURATION_MAP_CHECK_ERROR_MESSAGE, e.getMessage());
    }
    verify(blueprintDao, metaInfo, request);
  }

  @Test
  public void testCreateResources_wrongConfigurationStructure_withoutConfigsList() throws ResourceAlreadyExistsException, SystemException,
    UnsupportedPropertyException, NoSuchParentResourceException {

    Request request = createMock(Request.class);

    Set<Map<String, Object>> setProperties = getBlueprintTestProperties();

    Map<String, String> requestInfoProperties = new HashMap<>();
    String configurationData = "{\"configurations\":{\"config-type1\": \"properties\", \"config-type2\": \"properties\"}}";
    requestInfoProperties.put(Request.REQUEST_INFO_BODY_PROPERTY, configurationData);

    // set expectations
    expect(request.getProperties()).andReturn(setProperties);
    expect(request.getRequestInfoProperties()).andReturn(requestInfoProperties);

    replay(blueprintDao, metaInfo, request);
    // end expectations

    try {
      provider.createResources(request);
      fail("Exception expected");
    } catch (IllegalArgumentException e) {
      //expected exception
      assertEquals(BlueprintResourceProvider.CONFIGURATION_LIST_CHECK_ERROR_MESSAGE, e.getMessage());
    }
    verify(blueprintDao, metaInfo, request);
  }

  public static Set<Map<String, Object>> getBlueprintTestProperties() {
    Map<String, String> mapHostGroupComponentProperties = new HashMap<>();
    mapHostGroupComponentProperties.put(BlueprintResourceProvider.COMPONENT_NAME_PROPERTY_ID, "component1");

    Map<String, String> mapHostGroupComponentProperties2 = new HashMap<>();
    mapHostGroupComponentProperties2.put(BlueprintResourceProvider.COMPONENT_NAME_PROPERTY_ID, "component2");

