content: /*
   * 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.anyString;
  import static org.easymock.EasyMock.capture;
  import static org.easymock.EasyMock.createMock;
  import static org.easymock.EasyMock.createStrictMock;
  import static org.easymock.EasyMock.eq;
  import static org.easymock.EasyMock.expect;
  import static org.easymock.EasyMock.getCurrentArguments;
  import static org.easymock.EasyMock.newCapture;
  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.assertTrue;
  import static org.junit.Assert.fail;
  
  import java.lang.reflect.Field;
  import java.util.ArrayList;
  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 java.util.TreeMap;
  
  import jakarta.persistence.EntityManager;
  
  import org.apache.ambari.server.controller.AmbariManagementController;
  import org.apache.ambari.server.controller.spi.Predicate;
  import org.apache.ambari.server.controller.spi.Request;
  import org.apache.ambari.server.controller.spi.RequestStatus;
  import org.apache.ambari.server.controller.spi.Resource;
  import org.apache.ambari.server.controller.utilities.PredicateBuilder;
  import org.apache.ambari.server.orm.dao.ArtifactDAO;
  import org.apache.ambari.server.orm.entities.ArtifactEntity;
  import org.apache.ambari.server.state.Cluster;
  import org.apache.ambari.server.state.Clusters;
  import org.easymock.Capture;
  import org.easymock.IAnswer;
  import org.junit.Before;
  import org.junit.Test;
  
  import com.google.gson.Gson;
  
  /**
   * ArtifactResourceProvider unit tests.
   */
  @SuppressWarnings("unchecked")
  public class ArtifactResourceProviderTest {
  
    private ArtifactDAO dao = createStrictMock(ArtifactDAO.class);
    private EntityManager em = createStrictMock(EntityManager.class);
    private AmbariManagementController controller = createStrictMock(AmbariManagementController.class);
    private Request request = createMock(Request.class);
    private Clusters clusters = createMock(Clusters.class);
    private Cluster cluster = createMock(Cluster.class);
    private ArtifactEntity entity = createMock(ArtifactEntity.class);
    private ArtifactEntity entity2 = createMock(ArtifactEntity.class);
  
    ArtifactResourceProvider resourceProvider;
  
    @Before
    public void setUp() throws Exception {
      reset(dao, em, controller, request, clusters, cluster, entity, entity2);
      resourceProvider = new ArtifactResourceProvider(controller);
      setPrivateField(resourceProvider, "artifactDAO", dao);
    }
  
    @Test
    public void testGetResources_instance() throws Exception {
      Set<String> propertyIds = new HashSet<>();
      TreeMap<String, String> foreignKeys = new TreeMap<>();
      foreignKeys.put("cluster", "500");
  
      Map<String, Object> childMap = new TreeMap<>();
      childMap.put("childKey", "childValue");
      Map<String, Object> child2Map = new TreeMap<>();
      childMap.put("child2", child2Map);
      child2Map.put("child2Key", "child2Value");
      Map<String, Object> child3Map = new TreeMap<>();
      child2Map.put("child3", child3Map);
      Map<String, Object> child4Map = new TreeMap<>();
      child3Map.put("child4", child4Map);
      child4Map.put("child4Key", "child4Value");
  
      Map<String, Object> artifact_data = new TreeMap<>();
      artifact_data.put("foo", "bar");
      artifact_data.put("child", childMap);
  
      Map<String, String> responseForeignKeys = new HashMap<>();
      responseForeignKeys.put("cluster", "500");
  
      // expectations
      expect(controller.getClusters()).andReturn(clusters).anyTimes();
      expect(clusters.getCluster("test-cluster")).andReturn(cluster).anyTimes();
      expect(clusters.getClusterById(500L)).andReturn(cluster).anyTimes();
      expect(cluster.getClusterId()).andReturn(500L).anyTimes();
      expect(cluster.getClusterName()).andReturn("test-cluster").anyTimes();
  
      expect(request.getPropertyIds()).andReturn(propertyIds).anyTimes();
  
      expect(dao.findByNameAndForeignKeys(eq("test-artifact"), eq(foreignKeys))).andReturn(entity).once();
      expect(entity.getArtifactName()).andReturn("test-artifact").anyTimes();
      expect(entity.getForeignKeys()).andReturn(responseForeignKeys).anyTimes();
      expect(entity.getArtifactData()).andReturn(artifact_data).anyTimes();
  
      // end of expectation setting
      replay(dao, em, controller, request, clusters, cluster, entity, entity2);
  
      // test
      PredicateBuilder pb = new PredicateBuilder();
      Predicate predicate = pb.begin().property("Artifacts/cluster_name").equals("test-cluster").and().
          property("Artifacts/artifact_name").equals("test-artifact").end().toPredicate();
  
      Set<Resource> response = resourceProvider.getResources(request, predicate);
      assertEquals(1, response.size());
      Resource resource = response.iterator().next();
      Map<String, Map<String, Object>> responseProperties = resource.getPropertiesMap();
      assertEquals(5, responseProperties.size());
  
      Map<String, Object> artifactDataMap = responseProperties.get("artifact_data");
      assertEquals("bar", artifactDataMap.get("foo"));
  
      assertEquals("test-artifact", resource.getPropertyValue("Artifacts/artifact_name"));
      assertEquals("test-cluster", resource.getPropertyValue("Artifacts/cluster_name"));
      assertEquals("bar", resource.getPropertyValue("artifact_data/foo"));
      assertEquals("childValue", resource.getPropertyValue("artifact_data/child/childKey"));
      assertEquals("child2Value", resource.getPropertyValue("artifact_data/child/child2/child2Key"));
      assertEquals("child4Value", resource.getPropertyValue("artifact_data/child/child2/child3/child4/child4Key"));
    }
  
    @Test
    public void testGetResources_collection() throws Exception {
      Set<String> propertyIds = new HashSet<>();
      TreeMap<String, String> foreignKeys = new TreeMap<>();
      foreignKeys.put("cluster", "500");
  
      List<ArtifactEntity> entities = new ArrayList<>();
      entities.add(entity);
      entities.add(entity2);
  
      Map<String, Object> artifact_data = Collections.singletonMap("foo", "bar");
      Map<String, Object> artifact_data2 = Collections.singletonMap("foo2", "bar2");
  
      Map<String, String> responseForeignKeys = new HashMap<>();
      responseForeignKeys.put("cluster", "500");
  
      // expectations
      expect(controller.getClusters()).andReturn(clusters).anyTimes();
      expect(clusters.getCluster("test-cluster")).andReturn(cluster).anyTimes();
      expect(clusters.getClusterById(500L)).andReturn(cluster).anyTimes();
      expect(cluster.getClusterId()).andReturn(500L).anyTimes();
      expect(cluster.getClusterName()).andReturn("test-cluster").anyTimes();
  
      expect(request.getPropertyIds()).andReturn(propertyIds).anyTimes();
  
      expect(dao.findByForeignKeys(eq(foreignKeys))).andReturn(entities).anyTimes();
      expect(entity.getArtifactName()).andReturn("test-artifact").anyTimes();
      expect(entity.getForeignKeys()).andReturn(responseForeignKeys).anyTimes();
      expect(entity.getArtifactData()).andReturn(artifact_data).anyTimes();
      expect(entity2.getArtifactName()).andReturn("test-artifact2").anyTimes();
      expect(entity2.getForeignKeys()).andReturn(responseForeignKeys).anyTimes();
      expect(entity2.getArtifactData()).andReturn(artifact_data2).anyTimes();
  
      // end of expectation setting
      replay(dao, em, controller, request, clusters, cluster, entity, entity2);
  
      // test
      PredicateBuilder pb = new PredicateBuilder();
      Predicate predicate = pb.begin().property("Artifacts/cluster_name").equals("test-cluster").end().toPredicate();
  
      Set<Resource> response = resourceProvider.getResources(request, predicate);
      assertEquals(2, response.size());
  
      boolean artifact1Returned = false;
      boolean artifact2Returned = false;
      for (Resource resource : response) {
        if (resource.getPropertyValue("Artifacts/artifact_name").equals("test-artifact")) {
          artifact1Returned = true;
          assertEquals("bar", resource.getPropertyValue("artifact_data/foo"));
          assertEquals("test-cluster", resource.getPropertyValue("Artifacts/cluster_name"));
        } else if (resource.getPropertyValue("Artifacts/artifact_name").equals("test-artifact2")) {
          artifact2Returned = true;
          assertEquals("bar2", resource.getPropertyValue("artifact_data/foo2"));
          assertEquals("test-cluster", resource.getPropertyValue("Artifacts/cluster_name"));
        } else {
          fail("unexpected artifact name");
        }
      }
      assertTrue(artifact1Returned);
      assertTrue(artifact2Returned);
    }
  
    @Test
    public void testCreateResource() throws Exception {
      Capture<ArtifactEntity> createEntityCapture = newCapture();
      Map<String, Object> outerMap = new TreeMap<>();
      Map<String, Object> childMap = new TreeMap<>();
      outerMap.put("child", childMap);
      childMap.put("childKey", "childValue");
      Map<String, Object> child2Map = new TreeMap<>();
      childMap.put("child2", child2Map);
      child2Map.put("child2Key", "child2Value");
      Map<String, Object> child3Map = new TreeMap<>();
      child2Map.put("child3", child3Map);
      Map<String, Object> child4Map = new TreeMap<>();
      child3Map.put("child4", child4Map);
      child4Map.put("child4Key", "child4Value");
  
      Set<Map<String, Object>> propertySet = new HashSet<>();
      propertySet.add(outerMap);
      propertySet.add(child4Map);
  
      Map<String, Object> artifact_data = new TreeMap<>();
      artifact_data.put("foo", "bar");
      artifact_data.put("child", childMap);
      artifact_data.put("collection", propertySet);
  
      TreeMap<String, String> foreignKeys = new TreeMap<>();
      foreignKeys.put("cluster", "500");
  
      Map<String, String> requestInfoProps = new HashMap<>();
      requestInfoProps.put(Request.REQUEST_INFO_BODY_PROPERTY, bodyJson);
  
  
      // map with flattened properties
      Map<String, Object> properties = new HashMap<>();
      properties.put("Artifacts/artifact_name", "test-artifact");
      properties.put("Artifacts/cluster_name", "test-cluster");
      properties.put("artifact_data/foo", "bar");
      properties.put("artifact_data/child/childKey", "childValue");
      properties.put("artifact_data/child/child2/child2Key", "child2Value");
      properties.put("artifact_data/child/child2/child3/child4/child4Key", "child4Value");
  
      Collection<Object> collectionProperties = new HashSet<>();
      properties.put("artifact_data/collection", collectionProperties);
  
      // collection with maps of flattened properties
      Map<String, Object> map1 = new TreeMap<>();
      collectionProperties.add(map1);
      map1.put("foo", "bar");
      map1.put("child/childKey", "childValue");
      map1.put("child/child2/child2Key", "child2Value");
      map1.put("child/child2/child3/child4/child4Key", "child4Value");
  
      Map<String, Object> map2 = new TreeMap<>();
      collectionProperties.add(map2);
      map2.put("child4Key", "child4Value");
  
      Set<Map<String, Object>> requestProperties = Collections.singleton(properties);
  
      // expectations
      expect(request.getRequestInfoProperties()).andReturn(requestInfoProps).anyTimes();
      expect(request.getProperties()).andReturn(requestProperties).anyTimes();
      expect(controller.getClusters()).andReturn(clusters).anyTimes();
      expect(clusters.getCluster("test-cluster")).andReturn(cluster).anyTimes();
      expect(clusters.getClusterById(500L)).andReturn(cluster).anyTimes();
      expect(cluster.getClusterId()).andReturn(500L).anyTimes();
      expect(cluster.getClusterName()).andReturn("test-cluster").anyTimes();
  
      // check to see if entity already exists
      expect(dao.findByNameAndForeignKeys(eq("test-artifact"), eq(foreignKeys))).andReturn(null).once();
      // create
      dao.create(capture(createEntityCapture));
  
      // end of expectation setting
      replay(dao, em, controller, request, clusters, cluster, entity, entity2);
  
      resourceProvider.createResources(request);
  
      ArtifactEntity createEntity = createEntityCapture.getValue();
      assertEquals("test-artifact", createEntity.getArtifactName());
      Map<String, Object> actualArtifactData = createEntity.getArtifactData();
      // need to decompose actualArtifactData because actualArtifactData.get("collection") returns a set
      // implementation that does not equal an identical(same elements) HashSet instance
      assertEquals(artifact_data.size(), actualArtifactData.size());
      assertEquals(artifact_data.get("foo"), actualArtifactData.get("foo"));
      assertEquals(artifact_data.get("child"), actualArtifactData.get("child"));
      assertEquals(artifact_data.get("collection"), new HashSet(((Collection) actualArtifactData.get("collection"))));
      assertEquals(foreignKeys, createEntity.getForeignKeys());
    }
  
    @Test
    public void testUpdateResources() throws Exception {
      Map<String, String> requestInfoProps = new HashMap<>();
      requestInfoProps.put(Request.REQUEST_INFO_BODY_PROPERTY, bodyJson);
  
      Capture<ArtifactEntity> updateEntityCapture = newCapture();
      Capture<ArtifactEntity> updateEntityCapture2 = newCapture();
      Set<String> propertyIds = new HashSet<>();
      TreeMap<String, String> foreignKeys = new TreeMap<>();
      foreignKeys.put("cluster", "500");
  
      List<ArtifactEntity> entities = new ArrayList<>();
      entities.add(entity);
      entities.add(entity2);
  
      Map<String, Object> artifact_data = Collections.singletonMap("foo", "bar");
      Map<String, Object> artifact_data2 = Collections.singletonMap("foo2", "bar2");
  
      Map<String, String> responseForeignKeys = new HashMap<>();
      responseForeignKeys.put("cluster", "500");
  
      // map with flattened properties
      Map<String, Object> properties = new HashMap<>();
      properties.put("Artifacts/artifact_name", "test-artifact");
      properties.put("Artifacts/cluster_name", "test-cluster");
      properties.put("artifact_data/foo", "bar");
      properties.put("artifact_data/child/childKey", "childValue");
      properties.put("artifact_data/child/child2/child2Key", "child2Value");
      properties.put("artifact_data/child/child2/child3/child4/child4Key", "child4Value");
  
      Set<Map<String, Object>> requestProperties = Collections.singleton(properties);
  
      properties.put("artifact_data/collection", Collections.emptySet());
  
      // expectations
      expect(request.getProperties()).andReturn(requestProperties).anyTimes();
      expect(request.getRequestInfoProperties()).andReturn(requestInfoProps).anyTimes();
      expect(controller.getClusters()).andReturn(clusters).anyTimes();
      expect(clusters.getCluster("test-cluster")).andReturn(cluster).anyTimes();
      expect(clusters.getClusterById(500L)).andReturn(cluster).anyTimes();
      expect(cluster.getClusterId()).andReturn(500L).anyTimes();
      expect(cluster.getClusterName()).andReturn("test-cluster").anyTimes();
  
      expect(request.getPropertyIds()).andReturn(propertyIds).anyTimes();
  
      expect(dao.findByForeignKeys(eq(foreignKeys))).andReturn(entities).anyTimes();
      expect(entity.getArtifactName()).andReturn("test-artifact").anyTimes();
      expect(entity.getForeignKeys()).andReturn(responseForeignKeys).anyTimes();
      expect(entity.getArtifactData()).andReturn(artifact_data).anyTimes();
      expect(entity2.getArtifactName()).andReturn("test-artifact2").anyTimes();
      expect(entity2.getForeignKeys()).andReturn(responseForeignKeys).anyTimes();
      expect(entity2.getArtifactData()).andReturn(artifact_data2).anyTimes();
  
      expect(dao.merge(capture(updateEntityCapture))).andReturn(entity).once();
      expect(dao.merge(capture(updateEntityCapture2))).andReturn(entity2).once();
  
      // end of expectation setting
      replay(dao, em, controller, request, clusters, cluster, entity, entity2);
  
      PredicateBuilder pb = new PredicateBuilder();
      Predicate predicate = pb.begin().property("Artifacts/cluster_name").equals("test-cluster").end().toPredicate();
  
      RequestStatus response = resourceProvider.updateResources(request, predicate);
      ArtifactEntity updateEntity = updateEntityCapture.getValue();
      ArtifactEntity updateEntity2 = updateEntityCapture2.getValue();
  
      Gson serializer = new Gson();
      ArtifactEntity expected = new ArtifactEntity();
      expected.setArtifactData((Map<String, Object>) serializer.<Map<String, Object>>fromJson(
          bodyJson, Map.class).get("artifact_data"));
  
      assertEquals(expected.getArtifactData(), updateEntity.getArtifactData());
      assertEquals(expected.getArtifactData(), updateEntity2.getArtifactData());
  
      if (updateEntity.getArtifactName().equals("test-artifact")) {
        assertEquals("test-artifact2",updateEntity2.getArtifactName() );
      } else if (updateEntity.getArtifactName().equals("test-artifact2")) {
        assertEquals("test-artifact", updateEntity2.getArtifactName());
      } else {
        fail ("Unexpected artifact name: " + updateEntity.getArtifactName());
      }
  
      assertEquals(foreignKeys, updateEntity.getForeignKeys());
      assertEquals(foreignKeys, updateEntity2.getForeignKeys());
  
      assertEquals(RequestStatus.Status.Complete, response.getStatus());
  
      verify(dao, em, controller, request, clusters, cluster, entity, entity2);
    }
  
    @Test
    public void testDeleteResources() throws Exception {
  
      Capture<ArtifactEntity> deleteEntityCapture = newCapture();
      Capture<ArtifactEntity> deleteEntityCapture2 = newCapture();
      TreeMap<String, String> foreignKeys = new TreeMap<>();
      foreignKeys.put("cluster", "500");
  
      List<ArtifactEntity> entities = new ArrayList<>();
      entities.add(entity);
      entities.add(entity2);
  
      Map<String, Object> artifact_data = Collections.singletonMap("foo", "bar");
      Map<String, Object> artifact_data2 = Collections.singletonMap("foo2", "bar2");
  
      Map<String, String> responseForeignKeys = new HashMap<>();
      responseForeignKeys.put("cluster", "500");
  
      // expectations
      expect(controller.getClusters()).andReturn(clusters).anyTimes();
      expect(clusters.getCluster("test-cluster")).andReturn(cluster).anyTimes();
      expect(clusters.getClusterById(500L)).andReturn(cluster).anyTimes();
      expect(cluster.getClusterId()).andReturn(500L).anyTimes();
      expect(cluster.getClusterName()).andReturn("test-cluster").anyTimes();
  
  
      expect(entity.getArtifactName()).andReturn("test-artifact").anyTimes();
      expect(entity.getForeignKeys()).andReturn(responseForeignKeys).anyTimes();
      expect(entity.getArtifactData()).andReturn(artifact_data).anyTimes();
      expect(entity2.getArtifactName()).andReturn("test-artifact2").anyTimes();
      expect(entity2.getForeignKeys()).andReturn(responseForeignKeys).anyTimes();
      expect(entity2.getArtifactData()).andReturn(artifact_data2).anyTimes();
  
      IAnswer<ArtifactEntity> findByNameAndForeignKeys = new IAnswer<ArtifactEntity>() {
        @Override
        public ArtifactEntity answer() throws Throwable {
          String artifactName = (String) getCurrentArguments()[0];
  
          if ("test-artifact".equals(artifactName)) {
            return entity;
          } else if ("test-artifact2".equals(artifactName)) {
            return entity2;
          } else {
            return null;
          }
        }
      };
  
      expect(dao.findByForeignKeys(eq(foreignKeys))).andReturn(entities).once();
      expect(dao.findByNameAndForeignKeys(anyString(), eq(foreignKeys))).andAnswer(findByNameAndForeignKeys).once();
      dao.remove(capture(deleteEntityCapture));
      expect(dao.findByNameAndForeignKeys(anyString(), eq(foreignKeys))).andAnswer(findByNameAndForeignKeys).once();
      dao.remove(capture(deleteEntityCapture2));
  
      // end of expectation setting
      replay(dao, em, controller, request, clusters, cluster, entity, entity2);
  
      PredicateBuilder pb = new PredicateBuilder();
      Predicate predicate = pb.begin().property("Artifacts/cluster_name").equals("test-cluster").end().toPredicate();
  
      RequestStatus response = resourceProvider.deleteResources(new RequestImpl(null, null, null, null), predicate);
      ArtifactEntity deleteEntity = deleteEntityCapture.getValue();
      ArtifactEntity deleteEntity2 = deleteEntityCapture2.getValue();
  
      if (deleteEntity.getArtifactName().equals("test-artifact")) {
        assertEquals("test-artifact2", deleteEntity2.getArtifactName());
      } else if (deleteEntity.getArtifactName().equals("test-artifact2")) {
        assertEquals("test-artifact", deleteEntity2.getArtifactName());
      } else {
        fail ("Unexpected artifact name: " + deleteEntity.getArtifactName());
      }
  
      assertEquals(foreignKeys, deleteEntity.getForeignKeys());
      assertEquals(foreignKeys, deleteEntity2.getForeignKeys());
  
      assertEquals(RequestStatus.Status.Complete, response.getStatus());
  
      verify(dao, em, controller, request, clusters, cluster, entity, entity2);
    }
  
  
    private void setPrivateField(Object o, String field, Object value) throws Exception{
      Class<?> c = o.getClass();
      Field f = c.getDeclaredField(field);
      f.setAccessible(true);
      f.set(o, value);
    }
  
    private String bodyJson =
        "{ " +
            "  \"artifact_data\" : {" +
            "    \"foo\" : \"bar\"," +
            "    \"child\" : {" +
            "      \"childKey\" : \"childValue\"," +
            "      \"child2\" : {" +
            "        \"child2Key\" : \"child2Value\"," +
            "        \"child3\" : {" +
            "          \"child4\" : {" +
            "            \"child4Key\" : \"child4Value\"" +
            "          }" +
            "        }" +
            "      }" +
            "    }," +
            "    \"collection\" : [" +
            "      {" +
            "        \"child\" : {" +
            "          \"childKey\" : \"childValue\"," +
            "          \"child2\" : {" +
            "            \"child2Key\" : \"child2Value\"," +
            "            \"child3\" : {" +
            "              \"child4\" : {" +
            "                \"child4Key\" : \"child4Value\"" +
            "              }" +
            "            }" +
            "          }" +
            "        }" +
            "      }," +
            "      {" +
            "        \"child4Key\" : \"child4Value\"" +
            "      } " +
            "    ]" +
            "  }" +
            "}";
  }
  