Browse Source

AMBARI-5587 UI unit tests for host controllers. (atkach)

atkach 11 years ago
parent
commit
5c49faf56b

+ 2 - 0
ambari-web/app/assets/test/tests.js

@@ -48,6 +48,8 @@ require('test/controllers/main/charts/heatmap_metrics/heatmap_metric_diskspaceus
 require('test/controllers/main/charts/heatmap_metrics/heatmap_metric_memoryused_test');
 require('test/controllers/main/charts/heatmap_metrics/heatmap_metric_yarn_ResourceUsed_test');
 require('test/controllers/main/host/add_controller_test');
+require('test/controllers/main/host/configs_service_test');
+require('test/controllers/main/host/details_test');
 require('test/controllers/main/service/add_controller_test');
 require('test/controllers/main/service/reassign_controller_test');
 require('test/controllers/main/dashboard_test');

+ 56 - 57
ambari-web/app/controllers/main/host/configs_service.js

@@ -5,9 +5,9 @@
  * 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
@@ -21,6 +21,9 @@ App.MainHostServiceConfigsController = App.MainServiceInfoConfigsController.exte
   name: 'mainHostServiceConfigsController',
   host: null,
   isHostsConfigsPage: true,
+  typeTagToHostMap: null,
+  configKeyToConfigMap: null,
+
   /**
    * On load function
    */
@@ -36,48 +39,68 @@ App.MainHostServiceConfigsController = App.MainServiceInfoConfigsController.exte
    */
   renderServiceConfigs: function (serviceConfigs) {
     var newServiceConfigs = jQuery.extend({}, serviceConfigs);
-    var hostHostComponentNames = [];
+    newServiceConfigs.configCategories = this.filterServiceConfigs(serviceConfigs.configCategories);
+    this._super(newServiceConfigs);
+  },
+  /**
+   * filter config categories by host-component of host
+   * @param configCategories
+   * @return {Array}
+   */
+  filterServiceConfigs: function (configCategories) {
     var hostComponents = this.get('host.hostComponents');
-    if (hostComponents) {
-      hostComponents.forEach(function (hc) {
-        var name = hc.get('componentName');
-        if (!hostHostComponentNames.contains(name)) {
-          hostHostComponentNames.push(name);
-        }
-      });
-    }
-    newServiceConfigs.configCategories = serviceConfigs.configCategories.filter(function (category) {
+    var hostHostComponentNames = (hostComponents) ? hostComponents.mapProperty('componentName') : [];
+
+    return configCategories.filter(function (category) {
       var hcNames = category.get('hostComponentNames');
-      if (hcNames != null && hcNames.length > 0) {
-        var show = false;
-        hcNames.forEach(function (name) {
-          if (hostHostComponentNames.contains(name)) {
-            show = true;
+      if (hcNames && hcNames.length > 0) {
+        for (var i = 0, l = hcNames.length; i < l; i++) {
+          if (hostHostComponentNames.contains(hcNames[i])) {
+            return true;
           }
-        });
-        return show;
+        }
+        return false;
       }
       return true;
     });
-    this._super(newServiceConfigs);
   },
 
-  typeTagToHostMap: null,
-
-  configKeyToConfigMap: null,
-
   /**
    * This method will *not load* the overridden properties. However it will
    * replace the value shown for properties which this host has override for.
+   * @param serviceConfigs
+   * @param loadedGroupToOverrideSiteToTagMap
+   * @param configGroups
    */
   loadServiceConfigHostsOverrides: function (serviceConfigs, loadedGroupToOverrideSiteToTagMap, configGroups) {
-    var thisHostName = this.get('host.hostName');
     var configKeyToConfigMap = {};
     serviceConfigs.forEach(function (item) {
       configKeyToConfigMap[item.name] = item;
     });
+    this.set('configKeyToConfigMap', configKeyToConfigMap);
+    var urlParams = this.constructUrlParams(loadedGroupToOverrideSiteToTagMap, configGroups);
+    if (urlParams.length > 0) {
+      App.ajax.send({
+        name: 'host.service_config_hosts_overrides',
+        sender: this,
+        data: {
+          urlParams: urlParams.join('|')
+        },
+        success: 'loadServiceConfigHostsOverridesSuccessCallback',
+        error: 'loadServiceConfigHostsOverridesErrorCallback'
+      });
+    }
+  },
+  /**
+   * construct URL parameters, based on groups
+   * @param loadedGroupToOverrideSiteToTagMap
+   * @param configGroups
+   * @return {Array}
+   */
+  constructUrlParams: function (loadedGroupToOverrideSiteToTagMap, configGroups) {
     var typeTagToGroupMap = {};
     var urlParams = [];
+    var thisHostName = this.get('host.hostName');
     for (var group in loadedGroupToOverrideSiteToTagMap) {
       var groupObj = configGroups.findProperty('name', group);
       if (groupObj.get('hosts').contains(thisHostName)) {
@@ -90,53 +113,29 @@ App.MainHostServiceConfigsController = App.MainServiceInfoConfigsController.exte
       }
     }
     this.set('typeTagToGroupMap', typeTagToGroupMap);
-    this.set('configKeyToConfigMap', configKeyToConfigMap);
-    if (urlParams.length > 0) {
-      App.ajax.send({
-        name: 'host.service_config_hosts_overrides',
-        sender: this,
-        data: {
-          urlParams: urlParams.join('|')
-        },
-        success: 'loadServiceConfigHostsOverridesSuccessCallback',
-        error: 'loadServiceConfigHostsOverridesErrorCallback'
-      });
-    }
+    return urlParams;
   },
+  /**
+   * handle configs, override their values
+   * @param data
+   */
   loadServiceConfigHostsOverridesSuccessCallback: function (data) {
     var typeTagToGroupMap = this.get('typeTagToGroupMap');
     var configKeyToConfigMap = this.get('configKeyToConfigMap');
     data.items.forEach(function (config) {
       var group = typeTagToGroupMap[config.type + "///" + config.tag];
       var properties = config.properties;
-      for ( var prop in properties) {
+      for (var prop in properties) {
         var serviceConfig = configKeyToConfigMap[prop];
         var hostOverrideValue = properties[prop];
-        if (serviceConfig && serviceConfig.displayType === 'int') {
-          if (/\d+m$/.test(hostOverrideValue)) {
-            hostOverrideValue = hostOverrideValue.slice(0, hostOverrideValue.length - 1);
-          }
-        } else if (serviceConfig && serviceConfig.displayType === 'checkbox') {
-          switch (hostOverrideValue) {
-            case 'true':
-              hostOverrideValue = true;
-              break;
-            case 'false':
-              hostOverrideValue = false;
-              break;
-          }
-        }
         if (serviceConfig) {
-          // Value of this property is different for this host.
-          console.log("loadServiceConfigHostsOverrides(" + group + "): [" + group + "] OVERRODE(" + serviceConfig.name + "): " + serviceConfig.value + " -> " + hostOverrideValue);
           serviceConfig.value = hostOverrideValue;
-          serviceConfig.defaultValue = hostOverrideValue;
           serviceConfig.isOriginalSCP = false;
           serviceConfig.group = group;
+          App.config.handleSpecialProperties(serviceConfig);
         }
       }
     });
-    console.log("loadServiceConfigHostsOverrides(" + this.get('host.hostName') + "): Finished loading.");
   },
   loadServiceConfigHostsOverridesErrorCallback: function (request, ajaxOptions, error) {
     console.log("TRACE: error code status is: " + request.status);
@@ -146,7 +145,7 @@ App.MainHostServiceConfigsController = App.MainServiceInfoConfigsController.exte
    */
   switchHostGroup: function () {
     var self = this;
-    App.config.launchSwitchConfigGroupOfHostDialog(this.get('selectedConfigGroup'), this.get('configGroups'), this.get('host.hostName'), function(newGroup){
+    App.config.launchSwitchConfigGroupOfHostDialog(this.get('selectedConfigGroup'), this.get('configGroups'), this.get('host.hostName'), function (newGroup) {
       self.set('selectedConfigGroup', newGroup);
     })
   }

File diff suppressed because it is too large
+ 355 - 370
ambari-web/app/controllers/main/host/details.js


+ 1 - 0
ambari-web/app/messages.js

@@ -1508,6 +1508,7 @@ Em.I18n.translations = {
   'hosts.host.details.stopAllComponents':'Stop All Components',
   'hosts.host.details.restartAllComponents':'Restart All Components',
   'hosts.host.details.refreshConfigs':'Refresh configs',
+  'hosts.host.details.for.postfix':'{0} for host',
 
   'host.host.componentFilter.master':'Master Components',
   'host.host.componentFilter.slave':'Slave Components',

+ 223 - 0
ambari-web/test/controllers/main/host/configs_service_test.js

@@ -0,0 +1,223 @@
+/**
+ * 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.
+ */
+
+
+var App = require('app');
+require('controllers/main/host/configs_service');
+
+
+describe('App.MainHostServiceConfigsController', function () {
+
+  var controller = App.MainHostServiceConfigsController.create({
+    host: Em.Object.create()
+  });
+
+
+  describe('#filterServiceConfigs()', function () {
+    var testCases = [
+      {
+        title: 'configCategories is empty',
+        content: {
+          configCategories: [],
+          hostComponents: []
+        },
+        result: []
+      },
+      {
+        title: 'Category hostComponentNames is null',
+        content: {
+          configCategories: [
+            Em.Object.create({hostComponentNames: null})
+          ],
+          hostComponents: []
+        },
+        result: [
+          Em.Object.create({hostComponentNames: null})
+        ]
+      },
+      {
+        title: 'Components of host are empty',
+        content: {
+          configCategories: [
+            Em.Object.create({hostComponentNames: ['comp1']})
+          ],
+          hostComponents: []
+        },
+        result: []
+      },
+      {
+        title: 'Host components do not match component of categories',
+        content: {
+          configCategories: [
+            Em.Object.create({hostComponentNames: ['comp1']})
+          ],
+          hostComponents: [
+            {
+              componentName: 'comp2'
+            }
+          ]
+        },
+        result: []
+      },
+      {
+        title: 'Host components match component of categories',
+        content: {
+          configCategories: [
+            Em.Object.create({hostComponentNames: ['comp1']})
+          ],
+          hostComponents: [
+            {
+              componentName: 'comp1'
+            }
+          ]
+        },
+        result: [
+          Em.Object.create({hostComponentNames: ['comp1']})
+        ]
+      }
+    ];
+
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        controller.set('host.hostComponents', test.content.hostComponents);
+        expect(controller.filterServiceConfigs(test.content.configCategories)).to.eql(test.result);
+      });
+    });
+  });
+
+  describe('#constructUrlParams()', function () {
+    it('loadedGroupToOverrideSiteToTagMap is empty', function () {
+      var loadedGroupToOverrideSiteToTagMap = {};
+      var configGroups = [];
+      expect(controller.constructUrlParams(loadedGroupToOverrideSiteToTagMap, configGroups)).to.eql([]);
+      expect(controller.get('typeTagToGroupMap')).to.eql({});
+    });
+    it('Group does not have hosts', function () {
+      var loadedGroupToOverrideSiteToTagMap = {'group1': {}};
+      var configGroups = [
+        Em.Object.create({
+          name: 'group1',
+          hosts: []
+        })
+      ];
+      expect(controller.constructUrlParams(loadedGroupToOverrideSiteToTagMap, configGroups)).to.eql([]);
+      expect(controller.get('typeTagToGroupMap')).to.eql({});
+    });
+    it('Group does not contain current host', function () {
+      controller.set('host.hostName', 'host2');
+      var loadedGroupToOverrideSiteToTagMap = {'group1': {}};
+      var configGroups = [
+        Em.Object.create({
+          name: 'group1',
+          hosts: ['host1']
+        })
+      ];
+      expect(controller.constructUrlParams(loadedGroupToOverrideSiteToTagMap, configGroups)).to.eql([]);
+      expect(controller.get('typeTagToGroupMap')).to.eql({});
+    });
+    it('No type to tags relations in group', function () {
+      var loadedGroupToOverrideSiteToTagMap = {'group1': {}};
+      var configGroups = [
+        Em.Object.create({
+          name: 'group1',
+          hosts: ['host2']
+        })
+      ];
+      expect(controller.constructUrlParams(loadedGroupToOverrideSiteToTagMap, configGroups)).to.eql([]);
+      expect(controller.get('typeTagToGroupMap')).to.eql({});
+    });
+    it('Input params are correct', function () {
+      var loadedGroupToOverrideSiteToTagMap = {
+        'group1': {
+          'type1': 'tag1'
+        }
+      };
+      var configGroups = [
+        Em.Object.create({
+          name: 'group1',
+          hosts: ['host2']
+        })
+      ];
+      expect(controller.constructUrlParams(loadedGroupToOverrideSiteToTagMap, configGroups)).to.eql(['(type=type1&tag=tag1)']);
+      expect(controller.get('typeTagToGroupMap')['type1///tag1'].get('name')).to.equal('group1');
+    });
+  });
+
+  describe('#loadServiceConfigHostsOverrides()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "constructUrlParams", function(){
+        return controller.get('testUrlParams');
+      });
+      sinon.spy(App.ajax, "send");
+    });
+    afterEach(function () {
+      controller.constructUrlParams.restore();
+      App.ajax.send.restore();
+    });
+
+    it('configKeyToConfigMap and urlParams are empty', function () {
+      var serviceConfigs = [];
+      controller.set('testUrlParams', []);
+      controller.loadServiceConfigHostsOverrides(serviceConfigs);
+      expect(controller.get('configKeyToConfigMap')).to.eql({});
+      expect(App.ajax.send.called).to.be.false;
+    });
+
+    it('configKeyToConfigMap and urlParams are correct', function () {
+      var serviceConfigs = [{
+        name: 'config1'
+      }];
+      controller.set('testUrlParams', ['params']);
+      controller.loadServiceConfigHostsOverrides(serviceConfigs);
+      expect(controller.get('configKeyToConfigMap')).to.eql({'config1': {
+        name: 'config1'
+      }});
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#loadServiceConfigHostsOverridesSuccessCallback()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App.config, "handleSpecialProperties");
+    });
+    afterEach(function () {
+      App.config.handleSpecialProperties.restore();
+    });
+    it('data.items is empty', function () {
+      var data = {
+        items: [
+          {
+            type: 'type1',
+            tag: 'tag1',
+            properties: {'prop1': 'value1'}
+          }
+        ]
+      };
+      controller.set('typeTagToGroupMap', {'type1///tag1': {}});
+      controller.set('configKeyToConfigMap', {'prop1': {}});
+      controller.loadServiceConfigHostsOverridesSuccessCallback(data);
+      expect(App.config.handleSpecialProperties.calledWith({
+        value: 'value1',
+        isOriginalSCP: false,
+        group: {}
+      })).to.be.true;
+    });
+  });
+});

+ 1516 - 0
ambari-web/test/controllers/main/host/details_test.js

@@ -0,0 +1,1516 @@
+/**
+ * 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.
+ */
+
+
+var App = require('app');
+require('controllers/main/host/details');
+require('models/service');
+require('models/host_component');
+var batchUtils = require('utils/batch_scheduled_requests');
+
+
+describe('App.MainHostDetailsController', function () {
+
+  var controller = App.MainHostDetailsController.create({
+    getSecurityStatus: function () {
+      return this.get('mockSecurityStatus');
+    },
+    mockSecurityStatus: false
+  });
+
+  describe('#serviceActiveComponents', function () {
+
+    it('No host-components', function () {
+      controller.set('content.hostComponents', []);
+      expect(controller.get('serviceActiveComponents')).to.be.empty;
+    });
+
+    it('No host-components in active state', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        service: {
+          isInPassive: true
+        }
+      })]);
+      expect(controller.get('serviceActiveComponents')).to.be.empty;
+    });
+    it('Host-components in active state', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        service: {
+          isInPassive: false
+        }
+      })]);
+      expect(controller.get('serviceActiveComponents')).to.eql([Em.Object.create({
+        service: {
+          isInPassive: false
+        }
+      })]);
+    });
+  });
+
+  describe('#serviceNonClientActiveComponents', function () {
+
+    it('No active host-components', function () {
+      controller.reopen({
+        serviceActiveComponents: []
+      });
+      controller.set('serviceActiveComponents', []);
+      expect(controller.get('serviceNonClientActiveComponents')).to.be.empty;
+    });
+
+    it('Active host-component is client', function () {
+      controller.set('serviceActiveComponents', [Em.Object.create({
+        isClient: true
+      })]);
+      expect(controller.get('serviceNonClientActiveComponents')).to.be.empty;
+    });
+    it('Active host-component is not client', function () {
+      controller.set('serviceActiveComponents', [Em.Object.create({
+        isClient: false
+      })]);
+      expect(controller.get('serviceNonClientActiveComponents')).to.eql( [Em.Object.create({
+        isClient: false
+      })]);
+    });
+  });
+
+  describe('#startComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(controller, "sendStartComponentCommand", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.sendStartComponentCommand.restore();
+    });
+
+    it('popup should be displayed', function () {
+      var popup = controller.startComponent({context: Em.Object.create({'displayName': 'Comp1'})});
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.sendStartComponentCommand.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#sendStartComponentCommand()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      var component = Em.Object.create({
+        componentName: 'comp1'
+      });
+      controller.sendStartComponentCommand(component, '');
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#getDataToSend()', function () {
+    it('should return correct query info', function () {
+      expect(controller.getDataToSend('STATE1', 'context')).to.eql({
+        RequestInfo: {
+          "context": 'context'
+        },
+        Body: {
+          HostRoles: {
+            state: 'STATE1'
+          }
+        }
+      });
+    });
+  });
+
+  describe('#setComponentToData()', function () {
+    var testCases = [
+      {
+        title: 'dataToSend is null',
+        content: {
+          data: {},
+          dataToSend: null,
+          component: {}
+        },
+        result: {
+          output: false,
+          data: {}
+        }
+      },
+      {
+        title: 'component is null',
+        content: {
+          data: {},
+          dataToSend: {},
+          component: null
+        },
+        result: {
+          output: false,
+          data: {}
+        }
+      },
+      {
+        title: 'data is null',
+        content: {
+          data: null,
+          dataToSend: {},
+          component: {}
+        },
+        result: {
+          output: false,
+          data: null
+        }
+      },
+      {
+        title: 'component is object',
+        content: {
+          data: {},
+          dataToSend: {},
+          component: Em.Object.create({componentName: 'comp1'})
+        },
+        result: {
+          output: true,
+          data: {
+            componentName: 'comp1',
+            data: '{}'
+          }
+        }
+      },
+      {
+        title: 'component is array',
+        content: {
+          data: {},
+          dataToSend: {RequestInfo: {}},
+          component: [Em.Object.create({componentName: 'comp1'})]
+        },
+        result: {
+          output: true,
+          data: {
+            data: '{\"RequestInfo\":{\"query\":\"HostRoles/component_name.in(comp1)\"}}'
+          }
+        }
+      }
+    ];
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        expect(controller.setComponentToData(test.content.data, test.content.dataToSend, test.content.component)).to.equal(test.result.output);
+        expect(test.content.data).to.eql(test.result.data);
+      });
+    });
+  });
+
+  describe('#deleteComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App.ModalPopup, "show");
+    });
+    afterEach(function () {
+      App.ModalPopup.show.restore();
+    });
+
+    it('confirm popup should be displayed', function () {
+      var event = {
+        context: Em.Object.create({})
+      };
+      controller.deleteComponent(event);
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#_doDeleteHostComponent()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('delete request should be sent, component is null', function () {
+      controller.set('_deletedHostComponentResult', null);
+      expect(controller._doDeleteHostComponent()).to.equal(null);
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+    it('delete request should be sent, component is correct', function () {
+      controller.set('_deletedHostComponentResult', {});
+      expect(controller._doDeleteHostComponent(Em.Object.create({componentName: 'COMP1'}))).to.eql({});
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#mimicWorkStatusChange()', function () {
+
+    var clock;
+    beforeEach(function () {
+      clock = sinon.useFakeTimers();
+    });
+    afterEach(function () {
+      clock.restore()
+    });
+
+    it('change status of object', function () {
+      var entity = Em.Object.create({
+        workStatus: ''
+      });
+      controller.mimicWorkStatusChange(entity, 'STATE1', 'STATE2');
+      expect(entity.get('workStatus')).to.equal('STATE1');
+      clock.tick(App.testModeDelayForActions);
+      expect(entity.get('workStatus')).to.equal('STATE2');
+    });
+    it('change status of objects in array', function () {
+      var entity = [Em.Object.create({
+        workStatus: ''
+      })];
+      controller.mimicWorkStatusChange(entity, 'STATE1', 'STATE2');
+      expect(entity[0].get('workStatus')).to.equal('STATE1');
+      clock.tick(App.testModeDelayForActions);
+      expect(entity[0].get('workStatus')).to.equal('STATE2');
+    });
+  });
+
+  describe('#upgradeComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      App.ajax.send.restore();
+    });
+
+    it('confirm popup should be displayed', function () {
+      var popup = controller.upgradeComponent({context: Em.Object.create()});
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#stopComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(controller, "sendStopComponentCommand", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.sendStopComponentCommand.restore();
+    });
+
+    it('popup should be displayed', function () {
+      var popup = controller.stopComponent({context: Em.Object.create({'displayName': 'Comp1'})});
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.sendStopComponentCommand.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#sendStopComponentCommand()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      var component = Em.Object.create({
+        componentName: 'comp1'
+      });
+      controller.sendStopComponentCommand(component, '');
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#restartComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(batchUtils, "restartHostComponents", Em.K);
+    });
+
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      batchUtils.restartHostComponents.restore();
+    });
+
+    it('popup should be displayed', function () {
+      var popup = controller.restartComponent({context: Em.Object.create({'displayName': 'Comp1'})});
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(batchUtils.restartHostComponents.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#addComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(controller, "addClientComponent", Em.K);
+      sinon.stub(controller, "primary", Em.K);
+    });
+
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.addClientComponent.restore();
+      controller.primary.restore();
+    });
+
+    it('add ZOOKEEPER_SERVER', function () {
+      var event = {context: Em.Object.create({
+        componentName: 'ZOOKEEPER_SERVER'
+      })};
+      var popup = controller.addComponent(event);
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.primary.calledWith(Em.Object.create({
+        componentName: 'ZOOKEEPER_SERVER'
+      }))).to.be.true;
+    });
+    it('add slave component, securityEnabled = true', function () {
+      var event = {context: Em.Object.create({
+        componentName: 'COMP1'
+      })};
+      controller.set('mockSecurityStatus', true);
+      var popup = controller.addComponent(event);
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.primary.calledWith(Em.Object.create({
+        componentName: 'COMP1'
+      }))).to.be.true;
+    });
+    it('add slave component, securityEnabled = false', function () {
+      var event = {context: Em.Object.create({
+        componentName: 'COMP1'
+      })};
+      controller.set('mockSecurityStatus', false);
+      controller.addComponent(event);
+      expect(controller.addClientComponent.calledWith(Em.Object.create({
+        componentName: 'COMP1'
+      }))).to.be.true;
+    });
+    it('add CLIENTS', function () {
+      var event = {context: Em.Object.create({
+        componentName: 'CLIENTS'
+      })};
+      controller.set('mockSecurityStatus', true);
+      controller.addComponent(event);
+      expect(controller.addClientComponent.calledWith(Em.Object.create({
+        componentName: 'CLIENTS'
+      }))).to.be.true;
+    });
+  });
+
+  describe('#formatClientsMessage()', function () {
+    var testCases = [
+      {
+        title: 'subComponentNames is null',
+        client: Em.Object.create({
+          subComponentNames: null,
+          displayName: 'CLIENTS'
+        }),
+        result: 'CLIENTS'
+      },
+      {
+        title: 'subComponentNames is empty',
+        client: Em.Object.create({
+          subComponentNames: [],
+          displayName: 'CLIENTS'
+        }),
+        result: 'CLIENTS'
+      },
+      {
+        title: 'displayName is null',
+        client: Em.Object.create({
+          subComponentNames: ['DATANODE'],
+          displayName: null
+        }),
+        result: ' (DataNode)'
+      },
+      {
+        title: 'displayName is CLIENTS',
+        client: Em.Object.create({
+          subComponentNames: ['DATANODE'],
+          displayName: 'CLIENTS'
+        }),
+        result: 'CLIENTS (DataNode)'
+      }
+    ];
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        expect(controller.formatClientsMessage(test.client)).to.equal(test.result);
+      });
+    });
+  });
+
+  describe('#addClientComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App.ModalPopup, "show");
+      sinon.stub(controller, "primary", Em.K);
+    });
+
+    afterEach(function () {
+      App.ModalPopup.show.restore();
+      controller.primary.restore();
+    });
+
+    it('not CLIENT component', function () {
+      var component = Em.Object.create({'componentName': 'Comp1'});
+      var popup = controller.addClientComponent(component);
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.primary.calledWith(Em.Object.create({'componentName': 'Comp1'}))).to.be.true;
+    });
+    it('CLIENT components, with empty subComponentNames', function () {
+      var component = Em.Object.create({
+        componentName: 'CLIENTS',
+        subComponentNames: []
+      });
+      var popup = controller.addClientComponent(component);
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.primary.calledOnce).to.be.false;
+    });
+    it('CLIENT components, with two sub-component', function () {
+      var component = Em.Object.create({
+        componentName: 'CLIENTS',
+        subComponentNames: ['DATANODE', 'TASKTRACKER']
+      });
+      var popup = controller.addClientComponent(component);
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.primary.calledTwice).to.be.true;
+    });
+  });
+
+  describe('#primary()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      var component = Em.Object.create({
+        componentName: 'COMP1',
+        displayName: 'comp1'
+      });
+      controller.primary(component);
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#addNewComponentSuccessCallback()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      controller.addNewComponentSuccessCallback({}, {}, {component: Em.Object.create()});
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#installNewComponentSuccessCallback()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "showBackgroundOperationsPopup", Em.K);
+    });
+    afterEach(function () {
+      controller.showBackgroundOperationsPopup.restore();
+    });
+
+    it('data.Requests is null', function () {
+      var data = {Requests: null};
+      expect(controller.installNewComponentSuccessCallback(data, {}, {})).to.be.false;
+      expect(controller.showBackgroundOperationsPopup.called).to.be.false;
+    });
+    it('data.Requests.id is null', function () {
+      var data = {Requests: {id: null}};
+      expect(controller.installNewComponentSuccessCallback(data, {}, {})).to.be.false;
+      expect(controller.showBackgroundOperationsPopup.called).to.be.false;
+    });
+    it('data.Requests.id is correct', function () {
+      var data = {Requests: {id: 1}};
+      expect(controller.installNewComponentSuccessCallback(data, {}, {component: []})).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#refreshComponentConfigs()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(controller, "sendRefreshComponentConfigsCommand", Em.K);
+    });
+
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.sendRefreshComponentConfigsCommand.restore();
+    });
+
+    it('popup should be displayed', function () {
+      var popup = controller.refreshComponentConfigs({context: Em.Object.create({'displayName': 'Comp1'})});
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.sendRefreshComponentConfigsCommand.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#sendRefreshComponentConfigsCommand()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      var component = Em.Object.create({
+        service: {},
+        componentName: 'COMP1',
+        host: {}
+      });
+      controller.sendRefreshComponentConfigsCommand(component, {});
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#loadConfigs()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      controller.loadConfigs();
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#constructConfigUrlParams()', function () {
+
+    it('URL params should be empty', function () {
+      var data = {};
+      App.Service.find().clear();
+      expect(controller.constructConfigUrlParams(data)).to.eql([]);
+    });
+    it('isHaEnabled = true', function () {
+      var data = {Clusters: {desired_configs: {'core-site': {tag: 1}}}};
+      App.HostComponent.find().clear();
+      App.set('currentStackVersion', 'HDP-2.0.2');
+      expect(controller.constructConfigUrlParams(data)).to.eql(['(type=core-site&tag=1)']);
+      App.store.load(App.HostComponent, {
+        id: 'SECONDARY_NAMENODE_host1',
+        component_name: 'SECONDARY_NAMENODE'
+      });
+      App.set('currentStackVersion', 'HDP-2.0.1');
+    });
+    it('HBASE is installed', function () {
+      App.store.load(App.Service, {
+        id: 'HBASE',
+        service_name: 'HBASE'
+      });
+      var data = {Clusters: {desired_configs: {'hbase-site': {tag: 1}}}};
+      expect(controller.constructConfigUrlParams(data)).to.eql(['(type=hbase-site&tag=1)']);
+      App.Service.find().clear();
+    });
+    it('HIVE is installed', function () {
+      App.store.load(App.Service, {
+        id: 'HIVE',
+        service_name: 'HIVE'
+      });
+      var data = {Clusters: {desired_configs: {'webhcat-site': {tag: 1}}}};
+      expect(controller.constructConfigUrlParams(data)).to.eql(['(type=webhcat-site&tag=1)']);
+      App.Service.find().clear();
+    });
+    it('STORM is installed', function () {
+      App.store.load(App.Service, {
+        id: 'STORM',
+        service_name: 'STORM'
+      });
+      var data = {Clusters: {desired_configs: {'storm-site': {tag: 1}}}};
+      expect(controller.constructConfigUrlParams(data)).to.eql(['(type=storm-site&tag=1)']);
+      App.Service.find().clear();
+    });
+  });
+
+  describe('#loadConfigsSuccessCallback()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+      sinon.stub(controller, "constructConfigUrlParams", function () {
+        return this.get('mockUrlParams');
+      });
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+      controller.constructConfigUrlParams.restore();
+    });
+
+    it('url params is empty', function () {
+      controller.set('mockUrlParams', []);
+      expect(controller.loadConfigsSuccessCallback()).to.be.false;
+      expect(App.ajax.send.called).to.be.false;
+    });
+    it('url params are correct', function () {
+      controller.set('mockUrlParams', ['param1']);
+      expect(controller.loadConfigsSuccessCallback()).to.be.true;
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#saveZkConfigs()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "getZkServerHosts", Em.K);
+      sinon.stub(controller, "concatZkNames", Em.K);
+      sinon.stub(controller, "setZKConfigs", Em.K);
+      sinon.spy(App.ajax, "send");
+    });
+    afterEach(function () {
+      controller.getZkServerHosts.restore();
+      controller.concatZkNames.restore();
+      controller.setZKConfigs.restore();
+      App.ajax.send.restore();
+    });
+
+    it('data.items is empty', function () {
+      var data = {items: []};
+      controller.saveZkConfigs(data);
+      expect(App.ajax.send.called).to.be.false;
+    });
+    it('data.items has one item', function () {
+      var data = {items: [
+        {
+          type: 'type1',
+          properties: {}
+        }
+      ]};
+      controller.saveZkConfigs(data);
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+    it('data.items has two items', function () {
+      var data = {items: [
+        {
+          type: 'type1',
+          properties: {}
+        },
+        {
+          type: 'type2',
+          properties: {}
+        }
+      ]};
+      controller.saveZkConfigs(data);
+      expect(App.ajax.send.calledTwice).to.be.true;
+    });
+  });
+
+  describe('#setZKConfigs()', function () {
+
+    it('configs is null', function () {
+      expect(controller.setZKConfigs(null)).to.be.false;
+    });
+    it('zks is null', function () {
+      expect(controller.setZKConfigs({}, '', null)).to.be.false;
+    });
+    it('isHaEnabled = true', function () {
+      var configs = {'core-site': {}};
+      App.HostComponent.find().clear();
+      App.set('currentStackVersion', 'HDP-2.0.2');
+      expect(controller.setZKConfigs(configs, 'host1:2181', [])).to.be.true;
+      expect(configs).to.eql({"core-site": {
+        "ha.zookeeper.quorum": "host1:2181"
+      }});
+      App.store.load(App.HostComponent, {
+        id: 'SECONDARY_NAMENODE_host1',
+        component_name: 'SECONDARY_NAMENODE'
+      });
+      App.set('currentStackVersion', 'HDP-2.0.1');
+    });
+    it('hbase-site is present', function () {
+      var configs = {'hbase-site': {}};
+      expect(controller.setZKConfigs(configs, '', ['host1', 'host2'])).to.be.true;
+      expect(configs).to.eql({"hbase-site": {
+        "hbase.zookeeper.quorum": "host1,host2"
+      }});
+    });
+    it('webhcat-site is present', function () {
+      var configs = {'webhcat-site': {}};
+      expect(controller.setZKConfigs(configs, 'host1:2181', [])).to.be.true;
+      expect(configs).to.eql({"webhcat-site": {
+        "templeton.zookeeper.hosts": "host1:2181"
+      }});
+    });
+    it('storm-site is present', function () {
+      var configs = {'storm-site': {}};
+      expect(controller.setZKConfigs(configs, '', ["host1", 'host2'])).to.be.true;
+      expect(configs).to.eql({"storm-site": {
+        "storm.zookeeper.servers": "['host1','host2']"
+      }});
+    });
+  });
+
+  describe('#concatZkNames()', function () {
+    it('No ZooKeeper hosts', function () {
+      expect(controller.concatZkNames([])).to.equal('');
+    });
+    it('One ZooKeeper host', function () {
+      expect(controller.concatZkNames(['host1'])).to.equal('host1:2181');
+    });
+    it('Two ZooKeeper hosts', function () {
+      expect(controller.concatZkNames(['host1', 'host2'])).to.equal('host1:2181,host2:2181');
+    });
+  });
+
+  describe('#getZkServerHosts()', function () {
+    controller.set('content', {});
+
+    it('No ZooKeeper hosts, fromDeleteHost = false', function () {
+      App.HostComponent.find().clear();
+      controller.set('fromDeleteHost', false);
+      expect(controller.getZkServerHosts()).to.be.empty;
+    });
+    it('No ZooKeeper hosts, fromDeleteHost = true', function () {
+      controller.set('fromDeleteHost', true);
+      expect(controller.getZkServerHosts()).to.be.empty;
+      expect(controller.get('fromDeleteHost')).to.be.false;
+    });
+    it('One ZooKeeper host, fromDeleteHost = false', function () {
+      controller.set('fromDeleteHost', false);
+      App.store.load(App.HostComponent, {
+        id: 'ZOOKEEPER_SERVER_host1',
+        component_name: 'ZOOKEEPER_SERVER',
+        host_id: 'host1'
+      });
+      expect(controller.getZkServerHosts()).to.eql(['host1']);
+    });
+    it('One ZooKeeper host match current host name, fromDeleteHost = true', function () {
+      controller.set('fromDeleteHost', true);
+      controller.set('content.hostName', 'host1');
+      expect(controller.getZkServerHosts()).to.be.empty;
+      expect(controller.get('fromDeleteHost')).to.be.false;
+    });
+    it('One ZooKeeper host does not match current host name, fromDeleteHost = true', function () {
+      controller.set('fromDeleteHost', true);
+      controller.set('content.hostName', 'host2');
+      expect(controller.getZkServerHosts()[0]).to.equal("host1");
+      expect(controller.get('fromDeleteHost')).to.be.false;
+    });
+  });
+
+  describe('#installComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App.ModalPopup, "show");
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+
+    afterEach(function () {
+      App.ModalPopup.show.restore();
+      App.ajax.send.restore();
+    });
+
+    it('popup should be displayed', function () {
+      var event = {context: Em.Object.create()};
+      var popup = controller.installComponent(event);
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#decommission()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(controller, "runDecommission", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.runDecommission.restore();
+    });
+
+    it('popup should be displayed', function () {
+      var popup = controller.decommission(Em.Object.create({service: {}}));
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.runDecommission.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#runDecommission()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "doDecommission", Em.K);
+      sinon.stub(controller, "warnBeforeDecommission", Em.K);
+      sinon.stub(controller, "showBackgroundOperationsPopup", Em.K);
+    });
+
+    afterEach(function () {
+      controller.doDecommission.restore();
+      controller.warnBeforeDecommission.restore();
+      controller.showBackgroundOperationsPopup.restore();
+    });
+
+    it('HDFS service', function () {
+      controller.runDecommission('host1', 'HDFS');
+      expect(controller.doDecommission.calledWith('host1', 'HDFS', "NAMENODE", "DATANODE")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+    it('YARN service', function () {
+      controller.runDecommission('host1', 'YARN');
+      expect(controller.doDecommission.calledWith('host1', 'YARN', "RESOURCEMANAGER", "NODEMANAGER")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+    it('MAPREDUCE service', function () {
+      controller.runDecommission('host1', 'MAPREDUCE');
+      expect(controller.doDecommission.calledWith('host1', 'MAPREDUCE', "JOBTRACKER", "TASKTRACKER")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+    it('HBASE service', function () {
+      controller.runDecommission('host1', 'HBASE');
+      expect(controller.warnBeforeDecommission.calledWith('host1', 'HBASE', "HBASE_MASTER", "HBASE_REGIONSERVER")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#runRecommission()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "doRecommissionAndStart", Em.K);
+      sinon.stub(controller, "doRecommissionAndRestart", Em.K);
+      sinon.stub(controller, "showBackgroundOperationsPopup", Em.K);
+    });
+
+    afterEach(function () {
+      controller.doRecommissionAndStart.restore();
+      controller.doRecommissionAndRestart.restore();
+      controller.showBackgroundOperationsPopup.restore();
+    });
+
+    it('HDFS service', function () {
+      controller.runRecommission('host1', 'HDFS');
+      expect(controller.doRecommissionAndStart.calledWith('host1', 'HDFS', "NAMENODE", "DATANODE")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+    it('YARN service', function () {
+      controller.runRecommission('host1', 'YARN');
+      expect(controller.doRecommissionAndStart.calledWith('host1', 'YARN', "RESOURCEMANAGER", "NODEMANAGER")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+    it('MAPREDUCE service', function () {
+      controller.runRecommission('host1', 'MAPREDUCE');
+      expect(controller.doRecommissionAndRestart.calledWith('host1', 'MAPREDUCE', "JOBTRACKER", "TASKTRACKER")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+    it('HBASE service', function () {
+      controller.runRecommission('host1', 'HBASE');
+      expect(controller.doRecommissionAndStart.calledWith('host1', 'HBASE', "HBASE_MASTER", "HBASE_REGIONSERVER")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#doDecommission()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      controller.doDecommission('', '', '', '');
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#doDecommissionRegionServer()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      controller.doDecommissionRegionServer('', '', '', '');
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  /**
+   * TODO uncomment test when final rules will be implemented into warnBeforeDecommission function
+   */
+ /* describe('#warnBeforeDecommission()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "doDecommissionRegionServer", Em.K);
+      sinon.stub(App.ModalPopup, "show", Em.K);
+    });
+    afterEach(function () {
+      App.ModalPopup.show.restore();
+      controller.doDecommissionRegionServer.restore();
+    });
+
+    it('Component in passive state', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'HBASE_REGIONSERVER',
+        passiveState: 'ON'
+      })]);
+      controller.warnBeforeDecommission('host1', 'HBASE', 'HBASE_REGIONSERVER', 'SLAVE');
+      expect(App.ModalPopup.show.called).to.be.false;
+      expect(controller.doDecommissionRegionServer.calledWith('host1', 'HBASE', 'HBASE_REGIONSERVER', 'SLAVE')).to.be.true;
+    });
+    it('Component is not in passive state', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'HBASE_REGIONSERVER',
+        passiveState: 'OFF'
+      })]);
+      controller.warnBeforeDecommission('host1', 'HBASE', 'HBASE_REGIONSERVER', 'SLAVE');
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+      expect(controller.doDecommissionRegionServer.called).to.be.false;
+    });
+  });*/
+
+  describe('#doRecommissionAndStart()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      controller.doRecommissionAndStart('', '', '', '');
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#decommissionSuccessCallback()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "showBackgroundOperationsPopup", Em.K);
+    });
+    afterEach(function () {
+      controller.showBackgroundOperationsPopup.restore();
+    });
+
+    it('data is null', function () {
+      expect(controller.decommissionSuccessCallback(null)).to.be.false;
+      expect(controller.showBackgroundOperationsPopup.called).to.be.false;
+    });
+    it('data has Requests', function () {
+      var data = {Requests: []};
+      expect(controller.decommissionSuccessCallback(data)).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+    it('data has resources', function () {
+      var data = {resources: [{RequestSchedule: {}}]};
+      expect(controller.decommissionSuccessCallback(data)).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#doRecommissionAndRestart()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      controller.doRecommissionAndRestart('', '', '', '');
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#doAction()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "validateAndDeleteHost", Em.K);
+      sinon.stub(controller, "doStartAllComponents", Em.K);
+      sinon.stub(controller, "doStopAllComponents", Em.K);
+      sinon.stub(controller, "doRestartAllComponents", Em.K);
+      sinon.stub(controller, "onOffPassiveModeForHost", Em.K);
+    });
+
+    afterEach(function () {
+      controller.validateAndDeleteHost.restore();
+      controller.doStartAllComponents.restore();
+      controller.doStopAllComponents.restore();
+      controller.doRestartAllComponents.restore();
+      controller.onOffPassiveModeForHost.restore();
+    });
+
+    it('"deleteHost" action', function () {
+      var option = {context: {action: "deleteHost"}};
+      controller.doAction(option);
+      expect(controller.validateAndDeleteHost.calledOnce).to.be.true;
+    });
+    it('"startAllComponents" action, isNotHeartBeating = false', function () {
+      var option = {context: {action: "startAllComponents"}};
+      controller.set('content.isNotHeartBeating', false);
+      controller.doAction(option);
+      expect(controller.doStartAllComponents.calledOnce).to.be.true;
+    });
+    it('"startAllComponents" action, isNotHeartBeating = true', function () {
+      var option = {context: {action: "startAllComponents"}};
+      controller.set('content.isNotHeartBeating', true);
+      controller.doAction(option);
+      expect(controller.doStartAllComponents.called).to.be.false;
+    });
+    it('"stopAllComponents" action, isNotHeartBeating = false', function () {
+      var option = {context: {action: "stopAllComponents"}};
+      controller.set('content.isNotHeartBeating', false);
+      controller.doAction(option);
+      expect(controller.doStopAllComponents.calledOnce).to.be.true;
+    });
+    it('"stopAllComponents" action, isNotHeartBeating = true', function () {
+      var option = {context: {action: "stopAllComponents"}};
+      controller.set('content.isNotHeartBeating', true);
+      controller.doAction(option);
+      expect(controller.doStopAllComponents.called).to.be.false;
+    });
+    it('"restartAllComponents" action, isNotHeartBeating = false', function () {
+      var option = {context: {action: "restartAllComponents"}};
+      controller.set('content.isNotHeartBeating', false);
+      controller.doAction(option);
+      expect(controller.doRestartAllComponents.calledOnce).to.be.true;
+    });
+    it('"restartAllComponents" action, isNotHeartBeating = true', function () {
+      var option = {context: {action: "restartAllComponents"}};
+      controller.set('content.isNotHeartBeating', true);
+      controller.doAction(option);
+      expect(controller.doRestartAllComponents.called).to.be.false;
+    });
+    it('"onOffPassiveModeForHost" action', function () {
+      var option = {context: {action: "onOffPassiveModeForHost"}};
+      controller.doAction(option);
+      expect(controller.onOffPassiveModeForHost.calledWith({action: "onOffPassiveModeForHost"})).to.be.true;
+    });
+  });
+
+  describe('#onOffPassiveModeForHost()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(controller, "hostPassiveModeRequest", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.hostPassiveModeRequest.restore();
+    });
+
+    it('popup should be displayed, active = true', function () {
+      var popup = controller.onOffPassiveModeForHost({active: true});
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.hostPassiveModeRequest.calledWith('ON')).to.be.true;
+    });
+    it('popup should be displayed, active = false', function () {
+      var popup = controller.onOffPassiveModeForHost({active: false});
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.hostPassiveModeRequest.calledWith('OFF')).to.be.true;
+    });
+  });
+
+  describe('#hostPassiveModeRequest()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      controller.hostPassiveModeRequest('', '');
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#doStartAllComponents()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App, "showConfirmationPopup", Em.K);
+      sinon.stub(controller, "sendStartComponentCommand", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.sendStartComponentCommand.restore();
+    });
+
+    it('serviceNonClientActiveComponents is null', function () {
+      controller.reopen({
+        serviceNonClientActiveComponents: []
+      });
+      controller.set('serviceNonClientActiveComponents', null);
+      controller.doStartAllComponents();
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('serviceNonClientActiveComponents is empty', function () {
+      controller.set('serviceNonClientActiveComponents', []);
+      controller.doStartAllComponents();
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('serviceNonClientActiveComponents is correct', function () {
+      controller.set('serviceNonClientActiveComponents', [{}]);
+      var popup = controller.doStartAllComponents();
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      /*popup.onPrimary();
+      expect(controller.sendStartComponentCommand.calledWith([{}])).to.be.true;*/
+    });
+  });
+
+  describe('#doStopAllComponents()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App, "showConfirmationPopup", Em.K);
+      sinon.stub(controller, "sendStartComponentCommand", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.sendStartComponentCommand.restore();
+    });
+
+    it('serviceNonClientActiveComponents is null', function () {
+      controller.set('serviceNonClientActiveComponents', null);
+      controller.doStopAllComponents();
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('serviceNonClientActiveComponents is empty', function () {
+      controller.set('serviceNonClientActiveComponents', []);
+      controller.doStopAllComponents();
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('serviceNonClientActiveComponents is correct', function () {
+      controller.set('serviceNonClientActiveComponents', [{}]);
+      var popup = controller.doStopAllComponents();
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      /*popup.onPrimary();
+      expect(controller.sendStopComponentCommand.calledWith([{}])).to.be.true;*/
+    });
+  });
+
+  describe('#doRestartAllComponents()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App, "showConfirmationPopup", Em.K);
+      sinon.stub(controller, "sendStartComponentCommand", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.sendStartComponentCommand.restore();
+    });
+
+    it('serviceActiveComponents is null', function () {
+      controller.set('serviceActiveComponents', null);
+      controller.doRestartAllComponents();
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('serviceActiveComponents is empty', function () {
+      controller.set('serviceActiveComponents', []);
+      controller.doRestartAllComponents();
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('serviceActiveComponents is correct', function () {
+      controller.set('serviceActiveComponents', [{}]);
+      var popup = controller.doRestartAllComponents();
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      /*popup.onPrimary();
+       expect(controller.restartHostComponents.calledWith([{}])).to.be.true;*/
+    });
+  });
+
+  describe('#getHostComponentsInfo()', function () {
+
+    var result = {
+      zkServerInstalled: false,
+      lastComponents: [],
+      masterComponents: [],
+      runningComponents: [],
+      nonDeletableComponents: [],
+      unknownComponents: []
+    };
+
+    it('content.hostComponents is null', function () {
+      controller.set('content.hostComponents', null);
+      expect(controller.getHostComponentsInfo()).to.eql(result);
+    });
+    it('content.hostComponents is empty', function () {
+      controller.set('content.hostComponents', []);
+      expect(controller.getHostComponentsInfo()).to.eql(result);
+    });
+    it('content.hostComponents has ZOOKEEPER_SERVER', function () {
+      App.HostComponent.find().clear();
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'ZOOKEEPER_SERVER',
+        workStatus: 'INIT',
+        isDeletable: true
+      })]);
+      expect(controller.getHostComponentsInfo().zkServerInstalled).to.be.true;
+    });
+    it('content.hostComponents has last component', function () {
+      App.store.load(App.HostComponent, {
+        id: 'TASKTRACKER_host1',
+        component_name: 'TASKTRACKER'
+      });
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'TASKTRACKER',
+        displayName: 'TaskTracker',
+        workStatus: 'INIT',
+        isDeletable: true
+      })]);
+      expect(controller.getHostComponentsInfo().lastComponents).to.eql(['TaskTracker']);
+      App.HostComponent.find().clear();
+    });
+    it('content.hostComponents has master non-deletable component', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'TASKTRACKER',
+        workStatus: 'INIT',
+        isDeletable: false,
+        isMaster: true,
+        displayName: 'ZK1'
+      })]);
+      expect(controller.getHostComponentsInfo().masterComponents).to.eql(['ZK1']);
+      expect(controller.getHostComponentsInfo().nonDeletableComponents).to.eql(['ZK1']);
+    });
+    it('content.hostComponents has running component', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'TASKTRACKER',
+        workStatus: 'STARTED',
+        isDeletable: true,
+        displayName: 'ZK1'
+      })]);
+      expect(controller.getHostComponentsInfo().runningComponents).to.eql(['ZK1']);
+    });
+    it('content.hostComponents has non-deletable component', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'TASKTRACKER',
+        workStatus: 'INIT',
+        isDeletable: false,
+        displayName: 'ZK1'
+      })]);
+      expect(controller.getHostComponentsInfo().nonDeletableComponents).to.eql(['ZK1']);
+    });
+    it('content.hostComponents has component with UNKNOWN state', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'TASKTRACKER',
+        workStatus: 'UNKNOWN',
+        isDeletable: false,
+        displayName: 'ZK1'
+      })]);
+      expect(controller.getHostComponentsInfo().unknownComponents).to.eql(['ZK1']);
+    });
+  });
+
+  describe('#validateAndDeleteHost()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App, "showConfirmationPopup", Em.K);
+      sinon.stub(controller, "getHostComponentsInfo", function(){
+        return this.get('mockHostComponentsInfo');
+      });
+      sinon.stub(controller, "raiseDeleteComponentsError", Em.K);
+      sinon.stub(controller, "_doDeleteHost", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.getHostComponentsInfo.restore();
+      controller.raiseDeleteComponentsError.restore();
+      controller._doDeleteHost.restore();
+    });
+
+    it('App.supports.deleteHost = false', function () {
+      App.supports.deleteHost = false;
+      expect(controller.validateAndDeleteHost()).to.be.false;
+      App.supports.deleteHost = true;
+    });
+    it('masterComponents exist', function () {
+      controller.set('mockHostComponentsInfo', {masterComponents: [{}]});
+      controller.validateAndDeleteHost();
+      expect(controller.raiseDeleteComponentsError.calledWith([{}], 'masterList')).to.be.true;
+    });
+    it('nonDeletableComponents exist', function () {
+      controller.set('mockHostComponentsInfo', {
+        masterComponents: [],
+        nonDeletableComponents: [{}]
+      });
+      controller.validateAndDeleteHost();
+      expect(controller.raiseDeleteComponentsError.calledWith([{}], 'nonDeletableList')).to.be.true;
+    });
+    it('runningComponents exist', function () {
+      controller.set('mockHostComponentsInfo', {
+        masterComponents: [],
+        nonDeletableComponents: [],
+        runningComponents: [{}]
+      });
+      controller.validateAndDeleteHost();
+      expect(controller.raiseDeleteComponentsError.calledWith([{}], 'runningList')).to.be.true;
+    });
+    it('zkServerInstalled = true', function () {
+      controller.set('mockHostComponentsInfo', {
+        masterComponents: [],
+        nonDeletableComponents: [],
+        runningComponents: [],
+        unknownComponents: [],
+        lastComponents: [],
+        zkServerInstalled: true
+      });
+      var popup = controller.validateAndDeleteHost();
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+     /* popup.onPrimary();
+      expect(controller._doDeleteHost.calledWith([], [])).to.be.true;*/
+    });
+    it('zkServerInstalled = false', function () {
+      controller.set('mockHostComponentsInfo', {
+        masterComponents: [],
+        nonDeletableComponents: [],
+        runningComponents: [],
+        unknownComponents: [],
+        lastComponents: [],
+        zkServerInstalled: false
+      });
+      controller.validateAndDeleteHost();
+      expect(controller._doDeleteHost.calledWith([], [])).to.be.true;
+    });
+  });
+
+  describe('#raiseDeleteComponentsError()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ModalPopup, "show", Em.K);
+    });
+    afterEach(function () {
+      App.ModalPopup.show.restore();
+    });
+
+    it('Popup should be displayed', function () {
+      controller.raiseDeleteComponentsError([], '');
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#_doDeleteHost()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ModalPopup, "show", Em.K);
+    });
+    afterEach(function () {
+      App.ModalPopup.show.restore();
+    });
+
+    it('Popup should be displayed', function () {
+      controller._doDeleteHost([], []);
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#restartAllStaleConfigComponents()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(batchUtils, "restartHostComponents", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      batchUtils.restartHostComponents.restore();
+    });
+
+    it('popup should be displayed', function () {
+      controller.set('content.componentsWithStaleConfigs', [{}]);
+      var popup = controller.restartAllStaleConfigComponents();
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(batchUtils.restartHostComponents.calledWith([{}])).to.be.true;
+    });
+  });
+
+  describe('#moveComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+    });
+
+    it('popup should be displayed', function () {
+      var popup = controller.moveComponent();
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#refreshConfigs()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(batchUtils, "restartHostComponents", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      batchUtils.restartHostComponents.restore();
+    });
+
+    it('No components', function () {
+      var event = {context: []};
+      controller.refreshConfigs(event);
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('No components with stale configs', function () {
+      var event = {context: [Em.Object.create({
+        staleConfigs: false
+      })]};
+      controller.refreshConfigs(event);
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('Components with stale configs', function () {
+      var event = {context: [Em.Object.create({
+        staleConfigs: true
+      })]};
+      var popup = controller.refreshConfigs(event);
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(batchUtils.restartHostComponents.calledWith([Em.Object.create({
+        staleConfigs: true
+      })])).to.be.true;
+    });
+  });
+});

Some files were not shown because too many files changed in this diff