You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ab...@apache.org on 2015/06/30 17:31:14 UTC

ambari git commit: AMBARI-12219 Refactor config generation process for Service Config page (do not affects wizards and installer). (ababiichuk)

Repository: ambari
Updated Branches:
  refs/heads/trunk 9b6abb574 -> fa11e3c1b


AMBARI-12219 Refactor config generation process for Service Config page (do not affects wizards and installer). (ababiichuk)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/fa11e3c1
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/fa11e3c1
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/fa11e3c1

Branch: refs/heads/trunk
Commit: fa11e3c1b74979ca826ff88d2f422eb23dc39431
Parents: 9b6abb5
Author: aBabiichuk <ab...@cybervisiontech.com>
Authored: Tue Jun 30 11:18:10 2015 +0300
Committer: aBabiichuk <ab...@cybervisiontech.com>
Committed: Tue Jun 30 18:28:24 2015 +0300

----------------------------------------------------------------------
 .../controllers/main/service/info/configs.js    |   5 +-
 ambari-web/app/utils/config.js                  | 235 +++++++++++-----
 ambari-web/test/utils/config_test.js            | 276 ++++++++++++++++++-
 3 files changed, 451 insertions(+), 65 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/fa11e3c1/ambari-web/app/controllers/main/service/info/configs.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/info/configs.js b/ambari-web/app/controllers/main/service/info/configs.js
index ba88e66..50586c8 100644
--- a/ambari-web/app/controllers/main/service/info/configs.js
+++ b/ambari-web/app/controllers/main/service/info/configs.js
@@ -373,9 +373,10 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ConfigsLoader, A
         configs.findProperty('name', 'kdc_host').set('isRequired', false).set('isVisible', false);
         configs.findProperty('name', 'admin_server_host').set('isRequired', false).set('isVisible', false);
         configs.findProperty('name', 'domains').set('isRequired', false).set('isVisible', false);
+      } else if (kdc_type.get('value') === 'active-directory') {
+        configs.findProperty('name', 'container_dn').set('isVisible', true);
+        configs.findProperty('name', 'ldap_url').set('isVisible', true);
       }
-
-      kdc_type.set('value', App.router.get('mainAdminKerberosController.kdcTypesValues')[kdc_type.get('value')]);
     }
 
     this.set('allConfigs', configs);

http://git-wip-us.apache.org/repos/asf/ambari/blob/fa11e3c1/ambari-web/app/utils/config.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/config.js b/ambari-web/app/utils/config.js
index 250b650..d2cb098 100644
--- a/ambari-web/app/utils/config.js
+++ b/ambari-web/app/utils/config.js
@@ -279,11 +279,20 @@ App.config = Em.Object.create({
   },
 
 
+  /**
+   * generates config objects
+   * @param configCategories
+   * @param advancedConfigs
+   * @param serviceName
+   * @param selectedConfigGroup
+   * @param canEdit
+   * @returns {Array}
+   */
   mergePredefinedWithSaved: function (configCategories, advancedConfigs, serviceName, selectedConfigGroup, canEdit) {
     var configs = [];
     var contentProperties = this.createContentProperties(advancedConfigs);
     var preDefinedConfigs = this.get('preDefinedSiteProperties').concat(contentProperties);
-    var filenameExceptions = this.get('filenameExceptions');
+
     configCategories.forEach(function (siteConfig) {
       var service = this.getServiceByConfigType(siteConfig.type);
       if (service) {
@@ -297,75 +306,177 @@ App.config = Em.Object.create({
       for (var index in properties) {
         var configsPropertyDef = preDefinedConfigs.filterProperty('name', index).findProperty('filename', filename);
         var advancedConfig = advancedConfigs.filterProperty('name', index).findProperty('filename', filename);
-        var isAdvanced = Boolean(advancedConfig);
-        if (!configsPropertyDef) {
-          configsPropertyDef = advancedConfig;
-        }
-        var value = this.parseValue(properties[index], configsPropertyDef, advancedConfig);
-        var serviceConfigObj = Em.Object.create({
-          name: index,
-          displayName: (configsPropertyDef && Em.get(configsPropertyDef, 'displayName')) || index,
-          value: value,
-          savedValue: value,
-          recommendedValue: advancedConfig ? Em.get(advancedConfig, 'recommendedValue') : null,
-          filename: filename,
-          isUserProperty: !configsPropertyDef,
-          isVisible: !!service,
-          isOverridable: true,
-          isReconfigurable: true,
-          isRequired: isAdvanced,
-          isFinal: finalAttributes[index] === "true",
-          savedIsFinal: finalAttributes[index] === "true",
-          recommendedIsFinal: advancedConfig ? Em.get(advancedConfig, 'recommendedIsFinal') : null,
-          showLabel: true,
-          serviceName: serviceName,
-          belongsToService: [],
-          supportsFinal: advancedConfig ? Em.get(advancedConfig, 'supportsFinal') : this.shouldSupportFinal(serviceName, siteConfig.type)
-        });
-        if (configsPropertyDef) {
-          this.setServiceConfigUiAttributes(serviceConfigObj, configsPropertyDef);
-          // check if defined UI config present in config list obtained from server.
-          // in case when config is absent on server and defined UI config is required
-          // by server, this config should be ignored
-          var serverProperty = properties[serviceConfigObj.get('name')];
-          if (Em.isNone(serverProperty) && serviceConfigObj.get('isRequiredByAgent')) {
-            continue;
-          }
-        }
 
-        this.tweakConfigVisibility(serviceConfigObj, properties);
-        if (!this.getBySiteName(serviceConfigObj.get('filename')).someProperty('name', index)) {
-          if (configsPropertyDef) {
-            if (Em.get(configsPropertyDef, 'isRequiredByAgent') === false) {
-              configs.push(App.ServiceConfigProperty.create(serviceConfigObj));
-              continue;
-            }
-            this.handleSpecialProperties(serviceConfigObj);
-          } else {
-            serviceConfigObj.set('displayType', stringUtils.isSingleLine(serviceConfigObj.get('value')) ? 'advanced' : 'multiLine');
-          }
+        var serviceConfigObj = this.mergeStackConfigsWithUI(index, filename, properties[index], finalAttributes[index] === "true", service, advancedConfig, configsPropertyDef);
+
+        if (serviceConfigObj.get('isRequiredByAgent') !== false) {
+          var formattedValue = this.formatPropertyValue(serviceConfigObj);
           serviceConfigObj.setProperties({
-            'id': 'site property',
-            'displayName': configsPropertyDef && Em.get(configsPropertyDef, 'displayName') ? Em.get(configsPropertyDef, 'displayName') : index,
-            'options': configsPropertyDef ? Em.get(configsPropertyDef, 'options') : null,
-            'radioName': configsPropertyDef ? Em.get(configsPropertyDef, 'radioName') : null,
-            'serviceName': configsPropertyDef && Em.get(configsPropertyDef, 'serviceName') ? Em.get(configsPropertyDef, 'serviceName') : serviceName,
-            'belongsToService': configsPropertyDef && Em.get(configsPropertyDef, 'belongsToService') ? Em.get(configsPropertyDef, 'belongsToService') : []
+            'value': formattedValue,
+            'savedValue': formattedValue,
+            'isEditable': this.getIsEditable(serviceConfigObj, selectedConfigGroup, canEdit)
           });
-          this.calculateConfigProperties(serviceConfigObj, isAdvanced, advancedConfig);
-          this.setValueByDisplayType(serviceConfigObj);
-          if (this.get('secureConfigs').mapProperty('name').contains(serviceConfigObj.get('name'))) {
-            serviceConfigObj.set('isSecure', true);
-          }
-          serviceConfigObj.set('isEditable', canEdit && selectedConfigGroup.get('isDefault') && serviceConfigObj.get('isReconfigurable'));
-          var serviceConfigProperty = App.ServiceConfigProperty.create(serviceConfigObj);
-          serviceConfigProperty.validate();
-          configs.push(serviceConfigProperty);
         }
+
+        var serviceConfigProperty = App.ServiceConfigProperty.create(serviceConfigObj);
+        serviceConfigProperty.validate();
+        configs.push(serviceConfigProperty);
       }
     }, this);
     return configs;
   },
+
+  /**
+   * This method merge properties form <code>stackConfigProperty<code> which are taken from stack
+   * with <code>UIConfigProperty<code> which are hardcoded on UI
+   * @param name
+   * @param fileName
+   * @param value
+   * @param isFinal
+   * @param service
+   * @param stackConfigProperty
+   * @param UIConfigProperty
+   */
+  mergeStackConfigsWithUI: function(name, fileName, value, isFinal, service, stackConfigProperty, UIConfigProperty) {
+    return Em.Object.create({
+      /** core properties **/
+      name: name,
+      filename: fileName,
+      value: value,
+      savedValue: value,
+      isFinal: isFinal,
+      savedIsFinal: isFinal,
+      /** UI and Stack properties **/
+      recommendedValue: this.getPropertyIfExists('recommendedValue', null, stackConfigProperty, UIConfigProperty),
+      recommendedIsFinal: this.getPropertyIfExists('recommendedIsFinal', null, stackConfigProperty, UIConfigProperty),
+      displayName: this.getPropertyIfExists('displayName', name, stackConfigProperty, UIConfigProperty),
+      description: this.getPropertyIfExists('description', null, stackConfigProperty, UIConfigProperty),
+      supportsFinal: this.getPropertyIfExists('supportsFinal', false, stackConfigProperty, UIConfigProperty),
+      /** properties with calculations **/
+      displayType: this.getPropertyIfExists('displayType', this.getDefaultDisplayType(value), UIConfigProperty, stackConfigProperty),
+      category: this.getPropertyIfExists('category', this.getDefaultCategory(stackConfigProperty, fileName), UIConfigProperty),
+      isSecureConfig: this.getPropertyIfExists('isSecureConfig', this.getIsSecure(name), UIConfigProperty),
+      serviceName: this.getPropertyIfExists('serviceName', service ? service.get('serviceName') : 'MISC', stackConfigProperty, UIConfigProperty),
+      isVisible: this.getPropertyIfExists('isVisible', !!service, UIConfigProperty),
+      isUserProperty: this.getPropertyIfExists('isUserProperty', !stackConfigProperty, UIConfigProperty),
+      isRequired: this.getPropertyIfExists('isRequired', !!stackConfigProperty, UIConfigProperty),
+      /** UI properties **/
+      id: this.getPropertyIfExists('id', 'site property', UIConfigProperty),
+      isRequiredByAgent: this.getPropertyIfExists('isRequiredByAgent', true, UIConfigProperty),
+      isReconfigurable: this.getPropertyIfExists('isReconfigurable', true, UIConfigProperty),
+      unit: this.getPropertyIfExists('unit', null, UIConfigProperty),
+      isOverridable: this.getPropertyIfExists('isOverridable', true, UIConfigProperty),
+      index: this.getPropertyIfExists('index', null, UIConfigProperty),
+      showLabel: this.getPropertyIfExists('showLabel', true, UIConfigProperty),
+      dependentConfigPattern: this.getPropertyIfExists('dependentConfigPattern', null, UIConfigProperty),
+      options: this.getPropertyIfExists('options', null, UIConfigProperty),
+      radioName: this.getPropertyIfExists('radioName', null, UIConfigProperty),
+      belongsToService: this.getPropertyIfExists('belongsToService', [], UIConfigProperty)
+    });
+  },
+
+  /**
+   * This method using for merging some properties from two objects
+   * if property exists in <code>firstPriority<code> result will be it's property
+   * else if property exists in <code>secondPriority<code> result will be it's property
+   * otherwise <code>defaultValue<code> will be returned
+   * @param {String} propertyName
+   * @param {*} defaultValue=null
+   * @param {Em.Object|Object} firstPriority
+   * @param {Em.Object|Object} [secondPriority=null]
+   * @returns {*}
+   */
+  getPropertyIfExists: function(propertyName, defaultValue, firstPriority, secondPriority) {
+    if (firstPriority && !Em.isNone(Em.get(firstPriority, propertyName))) {
+      return Em.get(firstPriority, propertyName);
+    } else if (secondPriority && !Em.isNone(Em.get(secondPriority, propertyName))) {
+      return Em.get(secondPriority, propertyName);
+    } else {
+      return defaultValue;
+    }
+  },
+
+  /**
+   * Get displayType for properties that has not defined value
+   * @param value
+   * @returns {string}
+   */
+  getDefaultDisplayType: function(value) {
+    return stringUtils.isSingleLine(value) ? 'advanced' : 'multiLine';
+  },
+
+  /**
+   * Get category for properties that has not defined value
+   * @param stackConfigProperty
+   * @param fileName
+   * @returns {string}
+   */
+  getDefaultCategory: function(stackConfigProperty, fileName) {
+    return (stackConfigProperty ? 'Advanced ' : 'Custom ') + this.getConfigTagFromFileName(fileName);
+  },
+
+  /**
+   * Get isSecureConfig for properties that has not defined value
+   * @param propertyName
+   * @returns {boolean}
+   */
+  getIsSecure: function(propertyName) {
+    return this.get('secureConfigs').mapProperty('name').contains(propertyName);
+  },
+
+  /**
+   * Calculate isEditable rely on controller state selected group and config restriction
+   * @param {Em.Object} serviceConfigProperty
+   * @param {Em.Object} selectedConfigGroup
+   * @param {boolean} canEdit
+   * @returns {boolean}
+   */
+  getIsEditable: function(serviceConfigProperty, selectedConfigGroup, canEdit) {
+    return canEdit && selectedConfigGroup.get('isDefault') && serviceConfigProperty.get('isReconfigurable')
+  },
+
+  /**
+   * format property value depending on displayType
+   * and one exception for 'kdc_type'
+   * @param serviceConfigProperty
+   * @returns {*}
+   */
+  formatPropertyValue: function(serviceConfigProperty) {
+    var value = serviceConfigProperty.get('value'), displayType = serviceConfigProperty.get('displayType') || serviceConfigProperty.get('valueAttributes.type'), category = serviceConfigProperty.get('category');
+    switch (displayType) {
+      case 'directories':
+        if (['DataNode', 'NameNode'].contains(category)) {
+          return value.split(',').sort().join(',');//TODO check if this code is used
+        }
+        break;
+      case 'directory':
+        if (['SNameNode'].contains(category)) {
+          return value.split(',').sort()[0];//TODO check if this code is used
+        }
+        break;
+      case 'masterHosts':
+        if (typeof(value) == 'string') {
+          return value.replace(/\[|]|'|&apos;/g, "").split(',');
+        }
+        break;
+      case 'int':
+        if (/\d+m$/.test(value) ) {
+          return value.slice(0, value.length - 1);
+        } else {
+          var int = parseInt(value);
+          return isNaN(int) ? "" : int.toString();
+        }
+        break;
+      case 'float':
+        var float = parseFloat(value);
+        return isNaN(float) ? "" : float.toString();
+    }
+    if (serviceConfigProperty.get('name') === 'kdc_type') {
+      return App.router.get('mainAdminKerberosController.kdcTypesValues')[value];
+    }
+    return value;
+  },
+
   /**
    * return:
    *   configs,

http://git-wip-us.apache.org/repos/asf/ambari/blob/fa11e3c1/ambari-web/test/utils/config_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/utils/config_test.js b/ambari-web/test/utils/config_test.js
index 4df4a19..00cb096 100644
--- a/ambari-web/test/utils/config_test.js
+++ b/ambari-web/test/utils/config_test.js
@@ -1375,6 +1375,280 @@ describe('App.config', function () {
       expect(configProperty.get('overrideValues')).to.be.eql([overridenTemplate2.value]);
       expect(configProperty.get('overrideIsFinalValues')).to.be.eql([overridenTemplate2.isFinal]);
     });
-  })
+  });
+
+  describe('#getIsEditable', function() {
+    [{
+        isDefaultGroup: true,
+        isReconfigurable: true,
+        canEdit: true,
+        res: true,
+        m: "isEditable is true"
+      },
+      {
+        isDefaultGroup: false,
+        isReconfigurable: true,
+        canEdit: true,
+        res: false,
+        m: "isEditable is false; config group is not default"
+      },
+      {
+        isDefaultGroup: true,
+        isReconfigurable: false,
+        canEdit: true,
+        res: false,
+        m: "isEditable is true; config is not reconfigurable"
+      },
+      {
+        isDefaultGroup: true,
+        isReconfigurable: true,
+        canEdit: false,
+        res: false,
+        m: "isEditable is true; edition restricted by controller state"
+    }].forEach(function(t) {
+        it(t.m, function() {
+          var configProperty = Ember.Object.create({isReconfigurable: t.isReconfigurable});
+          var configGroup = Ember.Object.create({isDefault: t.isDefaultGroup});
+          var isEditable = App.config.getIsEditable(configProperty, configGroup, t.canEdit);
+          expect(isEditable).to.equal(t.res);
+        })
+      });
+  });
+
+  describe('#getIsSecure', function() {
+    var secureConfigs = App.config.get('secureConfigs');
+    before(function() {
+      App.config.set('secureConfigs', [{name: 'secureConfig'}]);
+    });
+    after(function() {
+      App.config.set('secureConfigs', secureConfigs);
+    });
+
+    it('config is secure', function() {
+      expect(App.config.getIsSecure('secureConfig')).to.equal(true);
+    });
+    it('config is not secure', function() {
+      expect(App.config.getIsSecure('NotSecureConfig')).to.equal(false);
+    });
+  });
+
+  describe('#getDefaultCategory', function() {
+    it('returns custom category', function() {
+      expect(App.config.getDefaultCategory(null, 'filename.xml')).to.equal('Custom filename');
+    });
+    it('returns advanced category', function() {
+      expect(App.config.getDefaultCategory(Em.Object.create, 'filename.xml')).to.equal('Advanced filename');
+    });
+  });
+
+  describe('#getDefaultDisplayType', function() {
+    it('returns singleLine displayTyepe', function() {
+      expect(App.config.getDefaultDisplayType('v1')).to.equal('advanced');
+    });
+    it('returns multiline displayTyepe', function() {
+      expect(App.config.getDefaultDisplayType('v1\nv2')).to.equal('multiLine');
+    });
+  });
+
+  describe('#formatValue', function() {
+    it('formatValue for masterHosts', function () {
+      var serviceConfigProperty = Em.Object.create({'displayType': 'masterHosts', value: "['h1','h2']"});
+      expect(App.config.formatPropertyValue(serviceConfigProperty)).to.eql(['h1','h2']);
+    });
+
+    it('formatValue for int', function () {
+      var serviceConfigProperty = Em.Object.create({'displayType': 'int', value: '4.0'});
+      expect(App.config.formatPropertyValue(serviceConfigProperty)).to.equal('4');
+    });
+
+    it('formatValue for int with m', function () {
+      var serviceConfigProperty = Em.Object.create({'displayType': 'int', value: '4m'});
+      expect(App.config.formatPropertyValue(serviceConfigProperty)).to.equal('4');
+    });
+
+    it('formatValue for float', function () {
+      var serviceConfigProperty = Em.Object.create({'displayType': 'float', value: '0.40'});
+      expect(App.config.formatPropertyValue(serviceConfigProperty)).to.equal('0.4');
+    });
+
+    it('formatValue for kdc_type', function () {
+      var serviceConfigProperty = Em.Object.create({'name': 'kdc_type', value: 'mit-kdc'});
+      expect(App.config.formatPropertyValue(serviceConfigProperty)).to.equal(Em.I18n.t('admin.kerberos.wizard.step1.option.kdc'));
+    });
+
+    it('don\'t format value', function () {
+      var serviceConfigProperty = Em.Object.create({'name': 'any', displayType: 'any', value: 'any'});
+      expect(App.config.formatPropertyValue(serviceConfigProperty)).to.equal('any');
+    });
+  });
+
+  describe('#getPropertyIfExists', function() {
+    [
+      {
+        propertyName: 'someProperty',
+        defaultValue: 'default',
+        firstObject: { someProperty: '1' },
+        secondObject: { someProperty: '2' },
+        res: '1',
+        m: 'use value from first object'
+      },
+      {
+        propertyName: 'someProperty',
+        defaultValue: 'default',
+        firstObject: { someOtherProperty: '1' },
+        secondObject: { someProperty: '2' },
+        res: '2',
+        m: 'use value from second object'
+      },
+      {
+        propertyName: 'someProperty',
+        defaultValue: 'default',
+        firstObject: { someOtherProperty: '1' },
+        secondObject: { someOtherProperty: '2' },
+        res: 'default',
+        m: 'use default value'
+      },
+      {
+        propertyName: 'someProperty',
+        defaultValue: 'default',
+        res: 'default',
+        m: 'use default value'
+      },
+      {
+        propertyName: 'someProperty',
+        defaultValue: true,
+        firstObject: { someProperty: false },
+        secondObject: { someProperty: true },
+        res: false,
+        m: 'use value from first object, check booleans'
+      },
+      {
+        propertyName: 'someProperty',
+        defaultValue: true,
+        firstObject: { someProperty: 0 },
+        secondObject: { someProperty: 1 },
+        res: 0,
+        m: 'use value from first object, check 0'
+      },
+      {
+        propertyName: 'someProperty',
+        defaultValue: true,
+        firstObject: { someProperty: '' },
+        secondObject: { someProperty: '1' },
+        res: '',
+        m: 'use value from first object, check empty string'
+      }
+    ].forEach(function (t) {
+        it(t.m, function () {
+          expect(App.config.getPropertyIfExists(t.propertyName, t.defaultValue, t.firstObject, t.secondObject)).to.equal(t.res);
+        })
+      });
+  });
+
+  describe('#mergeStackConfigsWithUI', function() {
+    beforeEach(function() {
+      sinon.stub(App.config, 'getPropertyIfExists', function(propertyName, defaultValue) {return defaultValue});
+      sinon.stub(App.config, 'getDefaultCategory', function(stackConfigProperty, filename) {return 'Advanced ' + filename});
+      sinon.stub(App.config, 'getIsSecure', function() {return false});
+      sinon.stub(App.config, 'getDefaultDisplayType', function() {return 'string'});
+    });
+
+    afterEach(function() {
+      App.config.getPropertyIfExists.restore();
+      App.config.getDefaultCategory.restore();
+      App.config.getIsSecure.restore();
+      App.config.getDefaultDisplayType.restore();
+    });
+    var service = Em.Object.create({ serviceName:'sName' });
+    var template =  {
+        name: 'pName',
+        filename: 'pFileName',
+        value: 'v',
+        savedValue: 'v',
+        isFinal: true,
+        savedIsFinal: true,
+        recommendedValue: null,
+        recommendedIsFinal: null,
+        displayName: 'pName',
+        description: null,
+        displayType: 'string',
+        category: 'Advanced pFileName',
+        isSecureConfig: false,
+        serviceName: 'sName',
+        isVisible: true,
+        isUserProperty: false,
+        isRequired: true,
+        id: 'site property',
+        supportsFinal: false,
+        isRequiredByAgent: true,
+        isReconfigurable: true,
+        unit: null,
+        isOverridable: true,
+        index: null,
+        showLabel: true,
+        dependentConfigPattern: null,
+        options: null,
+        radioName: null,
+        belongsToService: []
+    };
+    it('performs merge UI and Stack properties', function() {
+      for (var key in template){
+        expect(App.config.mergeStackConfigsWithUI('pName', 'pFileName', 'v', true, service, Em.Object.create({}), Em.Object.create({}))[key]).to.eql(template[key]);
+      }
+    });
+
+    it('performs merge UI and Stack properties (without stackProperty)', function() {
+      template.isUserProperty = true;
+      template.isRequired = false;
+      for (var key in template){
+        expect(App.config.mergeStackConfigsWithUI('pName', 'pFileName', 'v', true, service, null, Em.Object.create({}))[key]).to.eql(template[key]);
+      }
+    });
+
+    it('performs merge UI and Stack properties (unknown service)', function () {
+      template.serviceName = 'MISC';
+      template.isUserProperty = false;
+      template.isRequired = true;
+      template.isVisible = false;
+      for (var key in template) {
+        expect(App.config.mergeStackConfigsWithUI('pName', 'pFileName', 'v', true, null, Em.Object.create({}), Em.Object.create({}))[key]).to.eql(template[key]);
+      }
+    });
+
+    it('called getPropertyIfExists couple of times', function () {
+      App.config.mergeStackConfigsWithUI('pName', 'pFileName', 'v', true, null, Em.Object.create({}), Em.Object.create({}));
+      expect(App.config.getPropertyIfExists.callCount).to.equal(23);
+    });
+
+    it('called getPropertyIfExists with args', function () {
+      var stackConfigProperty = Em.Object.create({stackProperty: true});
+      var UIConfigProperty = Em.Object.create({UIProperty: true});
+      App.config.mergeStackConfigsWithUI('pName', 'pFileName', 'v', true, service, stackConfigProperty, UIConfigProperty);
+
+      expect(App.config.getPropertyIfExists.calledWith('recommendedValue', null, stackConfigProperty, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('recommendedIsFinal', null, stackConfigProperty, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('displayName', 'pName', stackConfigProperty, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('description', null, stackConfigProperty, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('supportsFinal', false, stackConfigProperty, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('id', 'site property', UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('isRequiredByAgent', true, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('isReconfigurable', true, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('unit', null, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('isOverridable', true, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('index', null, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('showLabel', true, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('dependentConfigPattern', null, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('options', null, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('belongsToService', [], UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('radioName', null, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('displayType', 'string', UIConfigProperty, stackConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('category', 'Advanced pFileName', UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('isSecureConfig', false, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('serviceName', 'sName', stackConfigProperty, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('isVisible', true, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('isUserProperty', false, UIConfigProperty)).to.be.true;
+      expect(App.config.getPropertyIfExists.calledWith('isRequired', true, UIConfigProperty)).to.be.true;
+    });
+  });
 
 });