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.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertNull;
  import static org.junit.Assert.assertTrue;
  
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.Set;
  import java.util.regex.Pattern;
  
  import org.apache.ambari.server.controller.spi.Request;
  import org.apache.ambari.server.controller.spi.Resource;
  import org.apache.ambari.server.controller.utilities.PropertyHelper;
  import org.junit.Test;
  
  /**
   * Base provider tests.
   */
  public class BaseProviderTest {
    @Test
    public void testGetProperties() {
      Set<String> propertyIds = new HashSet<>();
      propertyIds.add("foo");
      propertyIds.add("bar");
      propertyIds.add("cat1/prop1");
      propertyIds.add("cat2/prop2");
      propertyIds.add("cat3/subcat3/prop3");
  
      BaseProvider provider = new TestProvider(propertyIds);
  
      Set<String> supportedPropertyIds = provider.getPropertyIds();
      assertTrue(supportedPropertyIds.containsAll(propertyIds));
    }
  
    @Test
    public void testCheckPropertyIds() {
      Set<String> propertyIds = new HashSet<>();
      propertyIds.add("foo");
      propertyIds.add("bar");
      propertyIds.add("cat1/prop1");
      propertyIds.add("cat2/prop2");
      propertyIds.add("cat3/subcat3/prop3");
      propertyIds.add("cat4/subcat4/map");
  
      BaseProvider provider = new TestProvider(propertyIds);
  
      assertTrue(provider.checkPropertyIds(propertyIds).isEmpty());
  
      assertTrue(provider.checkPropertyIds(Collections.singleton("cat1")).isEmpty());
      assertTrue(provider.checkPropertyIds(Collections.singleton("cat2")).isEmpty());
      assertTrue(provider.checkPropertyIds(Collections.singleton("cat3")).isEmpty());
      assertTrue(provider.checkPropertyIds(Collections.singleton("cat3/subcat3")).isEmpty());
      assertTrue(provider.checkPropertyIds(
          Collections.singleton("cat4/subcat4/map")).isEmpty());
  
      // note that key is not in the set of known property ids.  We allow it if its parent is a known property.
      // this allows for Map type properties where we want to treat the entries as individual properties
      assertTrue(provider.checkPropertyIds(
          Collections.singleton("cat4/subcat4/map/key")).isEmpty());
  
      propertyIds.add("badprop");
      propertyIds.add("badcat");
  
      Set<String> unsupportedPropertyIds = provider.checkPropertyIds(propertyIds);
      assertFalse(unsupportedPropertyIds.isEmpty());
      assertEquals(2, unsupportedPropertyIds.size());
      assertTrue(unsupportedPropertyIds.contains("badprop"));
      assertTrue(unsupportedPropertyIds.contains("badcat"));
    }
  
    @Test
    public void testGetRequestPropertyIds() {
      Set<String> providerPropertyIds = new HashSet<>();
      providerPropertyIds.add("foo");
      providerPropertyIds.add("bar");
      providerPropertyIds.add("cat1/sub1");
  
      BaseProvider provider = new TestProvider(providerPropertyIds);
  
      Request request = PropertyHelper.getReadRequest("foo");
  
      Set<String> requestedPropertyIds = provider.getRequestPropertyIds(request, null);
  
      assertEquals(1, requestedPropertyIds.size());
      assertTrue(requestedPropertyIds.contains("foo"));
  
      request = PropertyHelper.getReadRequest("foo", "bar");
  
      requestedPropertyIds = provider.getRequestPropertyIds(request, null);
  
      assertEquals(2, requestedPropertyIds.size());
      assertTrue(requestedPropertyIds.contains("foo"));
      assertTrue(requestedPropertyIds.contains("bar"));
  
      request = PropertyHelper.getReadRequest("foo", "baz", "bar", "cat", "cat1/prop1");
  
      requestedPropertyIds = provider.getRequestPropertyIds(request, null);
  
      assertEquals(2, requestedPropertyIds.size());
      assertTrue(requestedPropertyIds.contains("foo"));
      assertTrue(requestedPropertyIds.contains("bar"));
  
      // ask for a property that isn't specified as supported, but its category is... the property
      // should end up in the returned set for the case where the category is a Map property
      request = PropertyHelper.getReadRequest("foo", "cat1/sub1/prop1");
  
      requestedPropertyIds = provider.getRequestPropertyIds(request, null);
  
      assertEquals(2, requestedPropertyIds.size());
      assertTrue(requestedPropertyIds.contains("foo"));
      assertTrue(requestedPropertyIds.contains("cat1/sub1/prop1"));
    }
  
    @Test
    public void testSetResourceProperty() {
      Set<String> propertyIds = new HashSet<>();
      propertyIds.add("p1");
      propertyIds.add("foo");
      propertyIds.add("cat1/foo");
      propertyIds.add("cat2/bar");
      propertyIds.add("cat2/baz");
      propertyIds.add("cat3/sub1/bam");
      propertyIds.add("cat4/sub2/sub3/bat");
      propertyIds.add("cat5/sub5");
  
      Resource resource = new ResourceImpl(Resource.Type.Service);
  
      assertNull(resource.getPropertyValue("foo"));
  
      BaseProvider.setResourceProperty(resource, "foo", "value1", propertyIds);
      assertEquals("value1", resource.getPropertyValue("foo"));
  
      BaseProvider.setResourceProperty(resource, "cat2/bar", "value2", propertyIds);
      assertEquals("value2", resource.getPropertyValue("cat2/bar"));
  
      assertNull(resource.getPropertyValue("unsupported"));
      BaseProvider.setResourceProperty(resource, "unsupported", "valueX", propertyIds);
      assertNull(resource.getPropertyValue("unsupported"));
  
      // we should allow anything under the category cat5/sub5
      BaseProvider.setResourceProperty(resource, "cat5/sub5/prop5", "value5", propertyIds);
      assertEquals("value5", resource.getPropertyValue("cat5/sub5/prop5"));
      BaseProvider.setResourceProperty(resource, "cat5/sub5/sub5a/prop5a", "value5", propertyIds);
      assertEquals("value5", resource.getPropertyValue("cat5/sub5/sub5a/prop5a"));
      // we shouldn't allow anything under the category cat5/sub7
      BaseProvider.setResourceProperty(resource, "cat5/sub7/unsupported", "valueX", propertyIds);
      assertNull(resource.getPropertyValue("cat5/sub7/unsupported"));
    }
  
    @Test
    public void testIsPropertyRequested() {
      Set<String> propertyIds = new HashSet<>();
      propertyIds.add("p1");
      propertyIds.add("foo");
      propertyIds.add("cat1/foo");
      propertyIds.add("cat2/bar");
      propertyIds.add("cat2/baz");
      propertyIds.add("cat3/sub1/bam");
      propertyIds.add("cat4/sub2/sub3/bat");
      propertyIds.add("cat5/sub5");
  
      assertTrue(BaseProvider.isPropertyRequested("foo", propertyIds));
  
      assertTrue(BaseProvider.isPropertyRequested("cat2", propertyIds));
  
      assertTrue(BaseProvider.isPropertyRequested("cat2/bar", propertyIds));
  
      assertFalse(BaseProvider.isPropertyRequested("unsupported", propertyIds));
  
      // we should allow anything under the category cat5/sub5
      assertTrue(BaseProvider.isPropertyRequested("cat5/sub5/prop5", propertyIds));
      assertTrue(BaseProvider.isPropertyRequested("cat5/sub5/sub5a/prop5a", propertyIds));
  
      // we shouldn't allow anything under the category cat5/sub7
      assertFalse(BaseProvider.isPropertyRequested("cat5/sub7/unsupported", propertyIds));
    }
  
    @Test
    public void testSetResourcePropertyWithMaps() {
      Set<String> propertyIds = new HashSet<>();
      propertyIds.add("cat1/emptyMapProperty");
      propertyIds.add("cat1/mapProperty");
      propertyIds.add("cat2/mapMapProperty");
      propertyIds.add("cat3/mapProperty3/key2");
      propertyIds.add("cat4/mapMapProperty4/subMap1/key3");
      propertyIds.add("cat4/mapMapProperty4/subMap2");
  
      Resource resource = new ResourceImpl(Resource.Type.Service);
  
      // Adding an empty Map as a property should add the actual Map as a property
      Map<String, String> emptyMapProperty = new HashMap<>();
      BaseProvider.setResourceProperty(resource, "cat1/emptyMapProperty", emptyMapProperty, propertyIds);
      assertTrue(resource.getPropertiesMap().containsKey("cat1/emptyMapProperty"));
  
      Map<String, String> mapProperty = new HashMap<>();
      mapProperty.put("key1", "value1");
      mapProperty.put("key2", "value2");
      mapProperty.put("key3", "value3");
  
      // Adding a property of type Map should add all of its keys as sub properties
      // if the map property was requested
      BaseProvider.setResourceProperty(resource, "cat1/mapProperty", mapProperty, propertyIds);
      assertNull(resource.getPropertyValue("cat1/mapProperty"));
      assertEquals("value1", resource.getPropertyValue("cat1/mapProperty/key1"));
      assertEquals("value2", resource.getPropertyValue("cat1/mapProperty/key2"));
      assertEquals("value3", resource.getPropertyValue("cat1/mapProperty/key3"));
  
      Map<String, Map<String, String>> mapMapProperty = new HashMap<>();
      Map<String, String> mapSubProperty1 = new HashMap<>();
      mapSubProperty1.put("key1", "value11");
      mapSubProperty1.put("key2", "value12");
      mapSubProperty1.put("key3", "value13");
      mapMapProperty.put("subMap1", mapSubProperty1);
      Map<String, String> mapSubProperty2 = new HashMap<>();
      mapSubProperty2.put("key1", "value21");
      mapSubProperty2.put("key2", "value22");
      mapSubProperty2.put("key3", "value23");
      mapMapProperty.put("subMap2", mapSubProperty2);
      Map<String, String> mapSubProperty3 = new HashMap<>();
      mapMapProperty.put("subMap3", mapSubProperty3);
  
      // Map of maps ... adding a property of type Map should add all of its keys as sub properties
      // if the map property was requested
      BaseProvider.setResourceProperty(resource, "cat2/mapMapProperty", mapMapProperty, propertyIds);
      assertNull(resource.getPropertyValue("cat2/mapMapProperty"));
      assertNull(resource.getPropertyValue("cat2/mapMapProperty/subMap1"));
      assertNull(resource.getPropertyValue("cat2/mapMapProperty/subMap2"));
      assertTrue(resource.getPropertiesMap().containsKey(
          "cat2/mapMapProperty/subMap3"));
      assertEquals("value11",
          resource.getPropertyValue("cat2/mapMapProperty/subMap1/key1"));
      assertEquals("value12",
          resource.getPropertyValue("cat2/mapMapProperty/subMap1/key2"));
      assertEquals("value13",
          resource.getPropertyValue("cat2/mapMapProperty/subMap1/key3"));
      assertEquals("value21",
          resource.getPropertyValue("cat2/mapMapProperty/subMap2/key1"));
      assertEquals("value22",
          resource.getPropertyValue("cat2/mapMapProperty/subMap2/key2"));
      assertEquals("value23",
          resource.getPropertyValue("cat2/mapMapProperty/subMap2/key3"));
  
      Map<String, String> mapProperty3 = new HashMap<>();
      mapProperty3.put("key1", "value1");
      mapProperty3.put("key2", "value2");
      mapProperty3.put("key3", "value3");
  
      // Adding a property of type Map shouldn't add the map if it wasn't requested and
      // should only add requested keys as sub properties ...
      // only "cat3/mapProperty3/key2" was requested
      BaseProvider.setResourceProperty(resource, "cat3/mapProperty3", mapProperty3, propertyIds);
      assertNull(resource.getPropertyValue("cat3/mapProperty3"));
      assertNull(resource.getPropertyValue("cat3/mapProperty3/key1"));
      assertEquals("value2", resource.getPropertyValue("cat3/mapProperty3/key2"));
      assertNull(resource.getPropertyValue("cat3/mapProperty3/key3"));
  
      Map<String, Map<String, String>> mapMapProperty4 = new HashMap<>();
      mapMapProperty4.put("subMap1", mapSubProperty1);
      mapMapProperty4.put("subMap2", mapSubProperty2);
      // Map of maps ... adding a property of type Map shouldn't add the map if it wasn't requested and
      // should only add requested keys as sub properties ...
      // only "cat4/mapMapProperty4/subMap1/key3" and "cat4/mapMapProperty4/subMap2" are requested
      BaseProvider.setResourceProperty(resource, "cat4/mapMapProperty4", mapMapProperty4, propertyIds);
      assertNull(resource.getPropertyValue("cat4/mapMapProperty4"));
      assertNull(resource.getPropertyValue("cat4/mapMapProperty4/subMap1"));
      assertNull(resource.getPropertyValue("cat4/mapMapProperty4/subMap2"));
      assertNull(resource.getPropertyValue("cat4/mapMapProperty4/subMap1/key1"));
      assertNull(resource.getPropertyValue("cat4/mapMapProperty4/subMap1/key2"));
      assertEquals("value13",
          resource.getPropertyValue("cat4/mapMapProperty4/subMap1/key3"));
      assertEquals("value21",
          resource.getPropertyValue("cat4/mapMapProperty4/subMap2/key1"));
      assertEquals("value22",
          resource.getPropertyValue("cat4/mapMapProperty4/subMap2/key2"));
      assertEquals("value23",
          resource.getPropertyValue("cat4/mapMapProperty4/subMap2/key3"));
    }
  
    @Test
    public void testRegexpMethods() {
      Set<String> propertyIds = new HashSet<>();
      String regexp = "cat/$1.replaceAll(\\\"([.])\\\",\\\"/\\\")/key";
      String propertyId = "cat/sub/key";
      String regexp2 = "cat/$1.replaceAll(\\\"([.])\\\",\\\"/\\\")/something/$2/key";
      String propertyId2 = "cat/sub/something/sub2/key";
  
      String incorrectPropertyId = "some/property/id";
      propertyIds.add(regexp);
      propertyIds.add(regexp2);
  
      BaseProvider provider = new TestProvider(propertyIds);
      Map.Entry<String, Pattern> regexEntry = provider.getRegexEntry(propertyId);
  
      assertEquals(regexp, regexEntry.getKey());
      assertNull(provider.getRegexEntry(incorrectPropertyId));
      assertEquals("sub", provider.getRegexGroups(regexp, propertyId).get(0));
      assertEquals("sub2", provider.getRegexGroups(regexp2, propertyId2).get(1));
      assertTrue(provider.getRegexGroups(regexp, incorrectPropertyId).isEmpty());
    }
  
    @Test
    public void testComplexMetricParsing() {
      Set<String> propertyIds = new HashSet<>();
      propertyIds.add("metrics/flume/$1.substring(0)/CHANNEL/$2.replaceAll(\"[^-]+\",\"\")EventPutSuccessCount/rate/sum");
      propertyIds.add("metrics/yarn/Queue/$1.replaceAll(\"([.])\",\"/\")/AppsCompleted");
      propertyIds.add("metrics/yarn/Queue/$1.replaceAll(\",q(\\d+)=\",\"/\").substring(1)/AppsFailed");
  
      TestProvider provider = new TestProvider(propertyIds);
      Entry<String, Pattern> entry = provider.getRegexEntry("metrics/flume/flume");
      assertEquals("metrics/flume/$1", entry.getKey());
      assertEquals("metrics/flume/(\\S*)", entry.getValue().pattern());
  
      entry = provider.getRegexEntry("metrics/flume/flume/CHANNEL");
      assertEquals("metrics/flume/$1/CHANNEL", entry.getKey());
      assertEquals("metrics/flume/(\\S*)/CHANNEL", entry.getValue().pattern());
  
      entry = provider.getRegexEntry("metrics/flume/flume/CHANNEL/EventPutSuccessCount");
      assertEquals("metrics/flume/$1/CHANNEL/$2EventPutSuccessCount", entry.getKey());
      assertEquals("metrics/flume/(\\S*)/CHANNEL/(\\S*)EventPutSuccessCount",
          entry.getValue().pattern());
  
      entry = provider.getRegexEntry("metrics/flume/flume/CHANNEL/EventPutSuccessCount/rate");
      assertEquals("metrics/flume/$1/CHANNEL/$2EventPutSuccessCount/rate",
          entry.getKey());
      assertEquals(
          "metrics/flume/(\\S*)/CHANNEL/(\\S*)EventPutSuccessCount/rate",
          entry.getValue().pattern());
  
      entry = provider.getRegexEntry("metrics/yarn/Queue/root/AppsCompleted");
      assertEquals("metrics/yarn/Queue/$1.replaceAll(\"([.])\",\"/\")/AppsCompleted",
          entry.getKey());
      assertEquals(
          "metrics/yarn/Queue/(\\S*)/AppsCompleted",
          entry.getValue().pattern());
  
      entry = provider.getRegexEntry("metrics/yarn/Queue/root/default/AppsCompleted");
      assertEquals("metrics/yarn/Queue/$1.replaceAll(\"([.])\",\"/\")/AppsCompleted",
          entry.getKey());
      assertEquals(
          "metrics/yarn/Queue/(\\S*)/AppsCompleted",
          entry.getValue().pattern());
  
      entry = provider.getRegexEntry("metrics/yarn/Queue/root/default/AppsFailed");
      assertEquals("metrics/yarn/Queue/$1.replaceAll(\",q(\\d+)=\",\"/\").substring(1)/AppsFailed",
          entry.getKey());
      assertEquals(
          "metrics/yarn/Queue/(\\S*)/AppsFailed",
          entry.getValue().pattern());
    }
  
    static class TestProvider extends BaseProvider {
      public TestProvider(Set<String> propertyIds) {
        super(propertyIds);
      }
    }
  }
  