You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by at...@apache.org on 2017/04/20 13:46:25 UTC

ambari git commit: AMBARI-20770 Cover assign master controller with unit tests. (atkach)

Repository: ambari
Updated Branches:
  refs/heads/trunk 1c37ffc43 -> cebbff057


AMBARI-20770 Cover assign master controller with unit tests. (atkach)


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

Branch: refs/heads/trunk
Commit: cebbff057baf9b30359aea108533feb8d9cce9ed
Parents: 1c37ffc
Author: Andrii Tkach <at...@apache.org>
Authored: Fri Apr 14 16:37:36 2017 +0300
Committer: Andrii Tkach <at...@apache.org>
Committed: Thu Apr 20 16:36:53 2017 +0300

----------------------------------------------------------------------
 .../wizard/step7/assign_master_controller.js    | 275 ++++---
 .../step7/assign_master_controller_test.js      | 780 ++++++++++++++++---
 2 files changed, 840 insertions(+), 215 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/cebbff05/ambari-web/app/controllers/wizard/step7/assign_master_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step7/assign_master_controller.js b/ambari-web/app/controllers/wizard/step7/assign_master_controller.js
index 8bdab28..099931a 100644
--- a/ambari-web/app/controllers/wizard/step7/assign_master_controller.js
+++ b/ambari-web/app/controllers/wizard/step7/assign_master_controller.js
@@ -65,7 +65,7 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
     
     switch (action) {
       case 'ADD':
-        if (hostComponent.componentName == "HIVE_SERVER_INTERACTIVE") {
+        if (hostComponent.componentName === "HIVE_SERVER_INTERACTIVE") {
           this.getPendingBatchRequests(hostComponent);  
         } else {
           this.showPopup(hostComponent);
@@ -94,9 +94,9 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
 
   pendingBatchRequestsAjaxError: function(data) {
     var error = Em.I18n.t('services.service.actions.run.yarnRefreshQueues.error');
-    if(data && data.responseText){
+    if (data && data.responseText) {
       try {
-        var json = $.parseJSON(data.responseText);
+        var json = JSON.parse(data.responseText);
         error += json.message;
       } catch (err) {}
     }
@@ -105,21 +105,11 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
 
   pendingBatchRequestsAjaxSuccess : function(data, opt, params) {
     var self = this;
-    var showAlert = false;
-    if (data.hasOwnProperty('items') && data.items.length > 0) {
-      data.items.forEach( function(_item) {
-        _item.RequestSchedule.batch.batch_requests.forEach( function(batchRequest) {
-          // Check if a DELETE request on HIVE_SERVER_INTERACTIVE is in progress
-          if (batchRequest.request_type == "DELETE" && batchRequest.request_uri.indexOf("HIVE_SERVER_INTERACTIVE") > -1) {
-            showAlert = true;
-          }
-        });
-      });
-    }
-    if (showAlert) {
-      App.showAlertPopup(Em.I18n.t('services.service.actions.hsi.alertPopup.header'), Em.I18n.t('services.service.actions.hsi.alertPopup.body'), function() {
+    if (this.shouldShowAlertOnBatchRequest(data)) {
+      App.showAlertPopup(Em.I18n.t('services.service.actions.hsi.alertPopup.header'),
+        Em.I18n.t('services.service.actions.hsi.alertPopup.body'), function() {
         var configWidgetContext = self.get('configWidgetContext');
-        var config = self.get('configWidgetContext.config');
+        var config = configWidgetContext.get('config');
         configWidgetContext.toggleProperty('controller.forceUpdateBoundaries');
         var value = config.get('initialValue');
         config.set('value', value);
@@ -131,6 +121,21 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
       this.showPopup(params.hostComponent);
     }
   },
+
+  shouldShowAlertOnBatchRequest: function(data) {
+    var showAlert = false;
+    if (data.hasOwnProperty('items') && data.items.length > 0) {
+      data.items.forEach( function(_item) {
+        _item.RequestSchedule.batch.batch_requests.forEach( function(batchRequest) {
+          // Check if a DELETE request on HIVE_SERVER_INTERACTIVE is in progress
+          if (batchRequest.request_type === "DELETE" && batchRequest.request_uri.indexOf("HIVE_SERVER_INTERACTIVE") > -1) {
+            showAlert = true;
+          }
+        });
+      });
+    }
+    return showAlert;
+  },
   
   showPopup: function(hostComponent) {
     var missingDependentServices = this.getAllMissingDependentServices();
@@ -461,7 +466,6 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
       var configActionComponent = self.get('configActionComponent');
       var componentHostName = self.getSelectedHostName(configActionComponent.componentName);
       var config = self.get('configWidgetContext.config');
-      var oldValueKey = context.get('controller.wizardController.name') === 'installerController' ? 'initialValue' : 'savedValue';
 
       // TODO remove after stack advisor is able to handle this case
       // workaround for hadoop.proxyuser.{{hiveUser}}.hosts after adding Hive Server Interactive from Install Wizard
@@ -484,114 +488,159 @@ App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App
       configActionComponent.hostName = componentHostName;
       config.set('configActionComponent', configActionComponent);
       /* TODO uncomment after stack advisor is able to handle this case
-      context.get('controller').loadConfigRecommendations([{
+       var oldValueKey = context.get('controller.wizardController.name') === 'installerController' ? 'initialValue' : 'savedValue';
+       context.get('controller').loadConfigRecommendations([{
         type: App.config.getConfigTagFromFileName(config.get('fileName')),
         name: config.get('name'),
         old_value: config.get(oldValueKey)
       }]);
       */
+      self.resolveDependencies(dependencies, serviceConfigs, context);
+    });
+  },
 
-      // TODO remove after stack advisor is able to handle this case
-      // workaround for hadoop.proxyuser.{{hiveUser}}.hosts after adding Hive Server Interactive
-      if (dependencies) {
-        var foreignKeys = {};
-        if (dependencies.foreignKeys) {
-          dependencies.foreignKeys.forEach(function (dependency) {
-            var matchingProperty = serviceConfigs.find(function (property) {
-              return property.get('filename') === App.config.getOriginalFileName(dependency.fileName) && property.get('name') === dependency.propertyName;
+  /**
+   * TODO remove after stack advisor is able to handle this case
+   * workaround for hadoop.proxyuser.{{hiveUser}}.hosts after adding Hive Server Interactive
+   * @param {object} dependencies
+   * @param {array} serviceConfigs
+   * @param {Em.Object} context
+   */
+  resolveDependencies: function(dependencies, serviceConfigs, context) {
+    if (dependencies) {
+      var foreignKeys = this.getDependenciesForeignKeys(dependencies, serviceConfigs);
+
+      if (dependencies.properties && dependencies.initializer) {
+        var initializer = App.get(dependencies.initializer.name);
+        var setup = Em.getProperties(foreignKeys, dependencies.initializer.setupKeys);
+        initializer.setup(setup);
+        var blueprintObject = {};
+        dependencies.properties.forEach(function (property) {
+          var propertyObject = Em.getProperties(property, ['name', 'fileName']);
+          if (property.nameTemplate) {
+            var name = property.nameTemplate;
+            Em.keys(foreignKeys).forEach(function (key) {
+              name = name.replace('{{' + key + '}}', foreignKeys[key]);
             });
-            if (matchingProperty) {
-              foreignKeys[dependency.key] = matchingProperty.get('value');
-            }
+            propertyObject.name = name;
+          }
+          if (!blueprintObject[property.fileName]) {
+            blueprintObject[property.fileName] = {
+              properties: {}
+            };
+          }
+          var result = initializer.initialValue(propertyObject, {
+            masterComponentHosts: this.getMasterComponents(dependencies, context)
           });
-        }
-        if (dependencies.properties && dependencies.initializer) {
-          var initializer = App.get(dependencies.initializer.name);
-          var setup = Em.getProperties(foreignKeys, dependencies.initializer.setupKeys);
-          initializer.setup(setup);
-          var blueprintObject = {};
-          dependencies.properties.forEach(function (property) {
-            var propertyObject = Em.getProperties(property, ['name', 'fileName']);
-            if (property.nameTemplate) {
-              var name = property.nameTemplate;
-              Em.keys(foreignKeys).forEach(function (key) {
-                name = name.replace('{{' + key + '}}', foreignKeys[key]);
-              });
-              propertyObject.name = name;
-            }
-            if (!blueprintObject[property.fileName]) {
-              blueprintObject[property.fileName] = {
-                properties: {}
-              };
-            }
-            var masterComponents = [];
-            if (self.get('content.controllerName')) {
-              var savedMasterComponents = context.get('controller.content.masterComponentHosts').filter(function (componentObject) {
-                return dependencies.initializer.componentNames.contains(componentObject.component);
-              });
-              masterComponents = savedMasterComponents.map(function (componentObject) {
-                var masterComponent = Em.getProperties(componentObject, ['component', 'hostName']);
-                masterComponent.isInstalled = true;
-                return masterComponent;
-              });
-            } else {
-              var hostsMap = blueprintUtils.getComponentForHosts();
-              Em.keys(hostsMap).forEach(function (hostName) {
-                hostsMap[hostName].forEach(function (componentName) {
-                  if (dependencies.initializer.componentNames.contains(componentName)) {
-                    masterComponents.push({
-                      component: componentName,
-                      hostName: hostName,
-                      isInstalled: true
-                    });
-                  }
-                });
+
+          var propertiesMap = blueprintObject[propertyObject.fileName].properties;
+          propertiesMap[propertyObject.name] = result.value;
+
+          if (property.isHostsList) {
+            var service = App.config.get('serviceByConfigTypeMap')[propertyObject.fileName];
+            if (service) {
+              var serviceName = service.get('serviceName');
+              var configs = serviceName === context.get('controller.selectedService.serviceName') ? serviceConfigs :
+                context.get('controller.stepConfigs').findProperty('serviceName', serviceName).get('configs');
+              var originalFileName = App.config.getOriginalFileName(propertyObject.fileName);
+              var currentProperty = configs.find(function (configProperty) {
+                return configProperty.get('filename') === originalFileName && configProperty.get('name') === propertyObject.name;
               });
-            }
-            var result = initializer.initialValue(propertyObject, {
-              masterComponentHosts: masterComponents
-            });
-            var propertiesMap = blueprintObject[propertyObject.fileName].properties;
-            propertiesMap[propertyObject.name] = result.value;
-            if (property.isHostsList) {
-              var service = App.config.get('serviceByConfigTypeMap')[propertyObject.fileName];
-              if (service) {
-                var serviceName = service.get('serviceName');
-                var configs = serviceName === context.get('controller.selectedService.serviceName') ? serviceConfigs :
-                  context.get('controller.stepConfigs').findProperty('serviceName', serviceName).get('configs');
-                var originalFileName = App.config.getOriginalFileName(propertyObject.fileName);
-                var currentProperty = configs.find(function (configProperty) {
-                  return configProperty.get('filename') === originalFileName && configProperty.get('name') === propertyObject.name;
-                });
-                if (currentProperty) {
-                  propertiesMap[propertyObject.name] = currentProperty.get('value');
-                  App.config.updateHostsListValue(propertiesMap, propertyObject.fileName, propertyObject.name, propertyObject.value, property.isHostsArray);
-                }
+              if (currentProperty) {
+                propertiesMap[propertyObject.name] = currentProperty.get('value');
+                App.config.updateHostsListValue(propertiesMap, propertyObject.fileName, propertyObject.name, propertyObject.value, property.isHostsArray);
               }
             }
-            context.get('controller').loadRecommendationsSuccess({
-              resources: [
-                {
-                  recommendations: {
-                    blueprint: {
-                      configurations: blueprintObject
-                    }
-                  }
-                }
-              ]
-            }, null, {
-              dataToSend: {
-                changed_configurations: [{
-                  type: App.config.getConfigTagFromFileName(config.get('fileName')),
-                  name: config.get('name'),
-                  old_value: config.get(oldValueKey)
-                }]
-              }
-            });
-            initializer.cleanup();
-          });
+          }
+          this.saveRecommendations(context, blueprintObject);
+          initializer.cleanup();
+        }, this);
+      }
+    }
+  },
+
+  /**
+   *
+   * @param {Em.Object} context
+   * @param {object} blueprintObject
+   */
+  saveRecommendations: function(context, blueprintObject) {
+    var oldValueKey = context.get('controller.wizardController.name') === 'installerController' ? 'initialValue' : 'savedValue';
+    var config = this.get('configWidgetContext.config');
+
+    context.get('controller').loadRecommendationsSuccess({
+      resources: [
+        {
+          recommendations: {
+            blueprint: {
+              configurations: blueprintObject
+            }
+          }
         }
+      ]
+    }, null, {
+      dataToSend: {
+        changed_configurations: [{
+          type: App.config.getConfigTagFromFileName(config.get('fileName')),
+          name: config.get('name'),
+          old_value: config.get(oldValueKey)
+        }]
       }
     });
+  },
+
+  /**
+   *
+   * @param dependencies
+   * @param serviceConfigs
+   * @returns {{}}
+   */
+  getDependenciesForeignKeys: function(dependencies, serviceConfigs) {
+    var foreignKeys = {};
+    if (dependencies.foreignKeys) {
+      dependencies.foreignKeys.forEach(function (dependency) {
+        var matchingProperty = serviceConfigs.find(function (property) {
+          return property.get('filename') === App.config.getOriginalFileName(dependency.fileName) && property.get('name') === dependency.propertyName;
+        });
+        if (matchingProperty) {
+          foreignKeys[dependency.key] = matchingProperty.get('value');
+        }
+      });
+    }
+    return foreignKeys;
+  },
+
+  /**
+   *
+   * @param dependencies
+   * @param context
+   * @returns {Array}
+   */
+  getMasterComponents: function(dependencies, context) {
+    var masterComponents = [];
+    if (this.get('content.controllerName')) {
+      var savedMasterComponents = context.get('controller.content.masterComponentHosts').filter(function (componentObject) {
+        return dependencies.initializer.componentNames.contains(componentObject.component);
+      });
+      masterComponents = savedMasterComponents.map(function (componentObject) {
+        var masterComponent = Em.getProperties(componentObject, ['component', 'hostName']);
+        masterComponent.isInstalled = true;
+        return masterComponent;
+      });
+    } else {
+      var hostsMap = blueprintUtils.getComponentForHosts();
+      Em.keys(hostsMap).forEach(function (hostName) {
+        hostsMap[hostName].forEach(function (componentName) {
+          if (dependencies.initializer.componentNames.contains(componentName)) {
+            masterComponents.push({
+              component: componentName,
+              hostName: hostName,
+              isInstalled: true
+            });
+          }
+        });
+      });
+    }
+    return masterComponents;
   }
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cebbff05/ambari-web/test/controllers/wizard/step7/assign_master_controller_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/wizard/step7/assign_master_controller_test.js b/ambari-web/test/controllers/wizard/step7/assign_master_controller_test.js
index e70055b..1380ef2 100644
--- a/ambari-web/test/controllers/wizard/step7/assign_master_controller_test.js
+++ b/ambari-web/test/controllers/wizard/step7/assign_master_controller_test.js
@@ -19,6 +19,8 @@
 var App = require('app');
 var stringUtils = require('utils/string_utils');
 var numberUtils = require('utils/number_utils');
+var blueprintUtils = require('utils/blueprint');
+var testHelpers = require('test/helpers');
 require('models/stack_service_component');
 
 describe('App.AssignMasterOnStep7Controller', function () {
@@ -50,58 +52,51 @@ describe('App.AssignMasterOnStep7Controller', function () {
   describe("#execute()", function () {
     var context = Em.Object.create({
       controller: {
-        content: Em.Object.create({
-          controllerName: ""
-        })
+        content: {}
       }
     });
 
     beforeEach(function() {
-      this.mock = sinon.stub(view, 'getAllMissingDependentServices');
-      sinon.stub(view, 'showInstallServicesPopup');
-      sinon.stub(view, 'showAssignComponentPopup');
+      sinon.stub(view, 'showPopup');
       sinon.stub(view, 'removeMasterComponent');
-      view.reopen({
-        content: Em.Object.create()
-      });
-    });
+      sinon.stub(view, 'getPendingBatchRequests');
+     });
 
     afterEach(function() {
-      this.mock.restore();
-      view.showInstallServicesPopup.restore();
-      view.showAssignComponentPopup.restore();
+      view.getPendingBatchRequests.restore();
+      view.showPopup.restore();
       view.removeMasterComponent.restore();
     });
 
-    it("ADD action, controllerName is empty", function() {
-      this.mock.returns([{}]);
+    it("should set configWidgetContext", function() {
       view.execute(context, 'ADD', {componentName: 'C1'});
-      expect(view.showInstallServicesPopup.calledOnce).to.be.true;
+      expect(view.get('configWidgetContext')).to.be.eql(context);
     });
 
-    it("ADD action, controllerName is set", function() {
-      context = Em.Object.create({
-        controller: {
-          content: Em.Object.create({
-            controllerName: "ctrl1"
-          })
-        }
-      });
-      this.mock.returns([{}]);
+    it("should set content", function() {
       view.execute(context, 'ADD', {componentName: 'C1'});
-      expect(view.showAssignComponentPopup.calledOnce).to.be.true;
+      expect(view.get('content')).to.be.eql({});
     });
 
-    it("ADD action, no dependent services", function() {
-      this.mock.returns([]);
+    it("should set configActionComponent", function() {
       view.execute(context, 'ADD', {componentName: 'C1'});
-      expect(view.showAssignComponentPopup.calledOnce).to.be.true;
+      expect(view.get('configActionComponent')).to.be.eql({componentName: 'C1'});
     });
 
-    it("DELETE action", function() {
-      this.mock.returns([{}]);
+    it("should call showPopup when action is ADD", function() {
+      view.execute(context, 'ADD', {componentName: 'C1'});
+      expect(view.showPopup.calledWith({componentName: 'C1'})).to.be.true;
+    });
+
+    it("should call getPendingBatchRequests when action is ADD and HIVE_SERVER_INTERACTIVE", function() {
+      view.execute(context, 'ADD', {componentName: 'HIVE_SERVER_INTERACTIVE'});
+      expect(view.getPendingBatchRequests.calledWith({componentName: 'HIVE_SERVER_INTERACTIVE'})).to.be.true;
+    });
+
+    it("should call removeMasterComponent when action is DELETE", function() {
       view.execute(context, 'DELETE', {componentName: 'C1'});
       expect(view.removeMasterComponent.calledOnce).to.be.true;
+      expect(view.get('mastersToCreate')).to.be.eql(['C1']);
     });
   });
 
@@ -186,11 +181,20 @@ describe('App.AssignMasterOnStep7Controller', function () {
     beforeEach(function() {
       sinon.stub(App.router, 'get').returns(mock);
       sinon.stub(mock, 'setDBProperty');
+      sinon.stub(view, 'clearComponentsToBeAdded');
+      sinon.stub(App.HostComponent, 'find').returns([
+        Em.Object.create({
+          componentName: 'C1',
+          hostName: 'host1'
+        })
+      ]);
     });
 
     afterEach(function() {
       App.router.get.restore();
       mock.setDBProperty.restore();
+      view.clearComponentsToBeAdded.restore();
+      App.HostComponent.find.restore();
     });
 
     it("should set masterComponentHosts", function() {
@@ -216,6 +220,22 @@ describe('App.AssignMasterOnStep7Controller', function () {
       expect(view.get('content.masterComponentHosts')).to.be.eql([{component: 'C1'}]);
       expect(view.get('content.recommendationsHostGroups').blueprint).to.be.eql({host_groups: [{name: 'host-group-1', components: [{name: 'C1'}]}]});
     });
+
+    it("should call clearComponentsToBeAdded when controllerName is null", function() {
+      view.setProperties({
+        content: Em.Object.create(),
+        mastersToCreate: ['C1'],
+        configWidgetContext: {
+          config: Em.Object.create()
+        }
+      });
+      view.removeMasterComponent();
+      expect(view.clearComponentsToBeAdded.calledWith('C1')).to.be.true;
+      expect(App.get('componentToBeDeleted')).to.be.eql(Em.Object.create({
+        componentName: 'C1',
+        hostName: 'host1'
+      }));
+    });
   });
 
   describe("#renderHostInfo()", function () {
@@ -318,101 +338,657 @@ describe('App.AssignMasterOnStep7Controller', function () {
     });
   });
 
-  describe("#submit()", function () {
-    var popup = {
-      hide: Em.K
+  describe('#getPendingBatchRequests', function() {
+
+    it('App.ajax.send should be called', function() {
+      view.getPendingBatchRequests({componentName: 'C1'});
+      var args = testHelpers.findAjaxRequest('name', 'request_schedule.get.pending');
+      expect(args[0]).to.be.eql({
+        name : 'request_schedule.get.pending',
+        sender: view,
+        error : 'pendingBatchRequestsAjaxError',
+        success: 'pendingBatchRequestsAjaxSuccess',
+        data: {
+          hostComponent: {componentName: 'C1'}
+        }
+      });
+    });
+  });
+
+  describe('#pendingBatchRequestsAjaxError', function() {
+    beforeEach(function() {
+      sinon.stub(App, 'showAlertPopup');
+    });
+    afterEach(function() {
+      App.showAlertPopup.restore();
+    });
+
+    it('should call showAlertPopup, invalid JSON', function() {
+      view.pendingBatchRequestsAjaxError({responseText: null});
+      expect(App.showAlertPopup.calledWith(
+        Em.I18n.t('services.service.actions.run.yarnRefreshQueues.error'),
+        Em.I18n.t('services.service.actions.run.yarnRefreshQueues.error'),
+        null
+      )).to.be.true;
+    });
+
+    it('should call showAlertPopup, valid JSON', function() {
+      view.pendingBatchRequestsAjaxError({responseText: '{"message":"foo"}'});
+      expect(App.showAlertPopup.calledWith(
+        Em.I18n.t('services.service.actions.run.yarnRefreshQueues.error'),
+        Em.I18n.t('services.service.actions.run.yarnRefreshQueues.error') + 'foo',
+        null
+      )).to.be.true;
+    });
+  });
+
+  describe('#pendingBatchRequestsAjaxSuccess', function() {
+    var configWidgetContext = Em.Object.create({
+      config: Em.Object.create({
+        initialValue: 'iv1',
+        value: 'v1'
+      }),
+      controller: {
+        forceUpdateBoundaries: false
+      },
+      setValue: sinon.spy(),
+      sendRequestRorDependentConfigs: sinon.spy()
+    });
+    beforeEach(function() {
+      this.mock = sinon.stub(view, 'shouldShowAlertOnBatchRequest');
+      sinon.stub(App, 'showAlertPopup', function() {
+        arguments[2].apply({hide: Em.K});
+      });
+      sinon.stub(view, 'showPopup');
+      view.set('configWidgetContext', configWidgetContext);
+    });
+    afterEach(function() {
+      this.mock.restore();
+      App.showAlertPopup.restore();
+      view.showPopup.restore();
+    });
+
+    it('showPopup should be called', function() {
+      this.mock.returns(false);
+      view.pendingBatchRequestsAjaxSuccess({}, {}, {hostComponent: {componentName: 'C1'}});
+      expect(view.showPopup.calledWith({componentName: 'C1'})).to.be.true;
+    });
+
+    it('showAlertPopup should be called', function() {
+      this.mock.returns(true);
+      view.pendingBatchRequestsAjaxSuccess({}, {}, {hostComponent: {componentName: 'C1'}});
+      expect(App.showAlertPopup.calledWith(
+        Em.I18n.t('services.service.actions.hsi.alertPopup.header'),
+        Em.I18n.t('services.service.actions.hsi.alertPopup.body')
+      )).to.be.true;
+      expect(configWidgetContext.get('config.value')).to.be.equal('iv1');
+      expect(configWidgetContext.get('controller.forceUpdateBoundaries')).to.be.true;
+      expect(configWidgetContext.setValue.calledWith('iv1')).to.be.true;
+      expect(configWidgetContext.sendRequestRorDependentConfigs.calledWith(
+        configWidgetContext.get('config')
+      )).to.be.true;
+    });
+  });
+
+  describe('#shouldShowAlertOnBatchRequest', function() {
+    var testCases = [
+      {
+        input: {},
+        expected: false
       },
-      mock = {
-        saveMasterComponentHosts: Em.K,
-        loadMasterComponentHosts: Em.K,
-        setDBProperty: Em.K
+      {
+        input: {
+          items: []
+        },
+        expected: false
+      },
+      {
+        input: {
+          items: [
+            {
+              RequestSchedule: {
+                batch: {
+                  batch_requests: [
+                    {
+                      request_type: 'ADD',
+                      request_uri: ''
+                    }
+                  ]
+                }
+              }
+            }
+          ]
+        },
+        expected: false
       },
-      config = Em.Object.create({
-        filename: 'file1',
-        name: 'conf1'
+      {
+        input: {
+          items: [
+            {
+              RequestSchedule: {
+                batch: {
+                  batch_requests: [
+                    {
+                      request_type: 'DELETE',
+                      request_uri: 'HIVE_SERVER_INTERACTIVE'
+                    }
+                  ]
+                }
+              }
+            }
+          ]
+        },
+        expected: true
+      }
+    ];
+
+    testCases.forEach(function(test) {
+      it('should return ' + test.expected + ' when data = ' + JSON.stringify(test.input), function() {
+        expect(view.shouldShowAlertOnBatchRequest(test.input)).to.be.equal(test.expected);
       });
+    });
+  });
+
+  describe('#updateComponent and showAddControl should be false for component', function() {
 
     beforeEach(function() {
-      sinon.stub(popup, 'hide');
-      sinon.stub(App.router, 'get').returns(mock);
-      sinon.stub(mock, 'saveMasterComponentHosts');
-      sinon.stub(mock, 'loadMasterComponentHosts');
-      sinon.stub(mock, 'setDBProperty');
-      sinon.stub(App.config, 'getConfigTagFromFileName', function (value) {
-        return value;
+      sinon.stub(App.StackServiceComponent, 'find').returns([
+        Em.Object.create({
+          componentName: 'C1',
+          stackService: Em.Object.create({
+            isInstalled: false
+          })
+        })
+      ]);
+    });
+    afterEach(function() {
+      App.StackServiceComponent.find.restore();
+    });
+
+    it('showRemoveControl ', function() {
+      var component = Em.Object.create({
+        component_name: 'C1',
+        showAddControl: true,
+        showRemoveControl: true
       });
+      view.setProperties({
+        mastersToCreate: [],
+        selectedServicesMasters: [ component, {component_name: 'C2'} ]
+      });
+      view.updateComponent('C1');
+      expect(component.get('showAddControl')).to.be.false;
+      expect(component.get('showRemoveControl')).to.be.false;
+    });
+  });
+
+  describe('#saveRecommendationsHostGroups', function() {
+    beforeEach(function() {
+      sinon.stub(view, 'getSelectedHostName').returns('host1');
+    });
+    afterEach(function() {
+      view.getSelectedHostName.restore();
+    });
+
+    it('should add component to recommendations', function() {
+      var recommendationsHostGroups = {
+        blueprint_cluster_binding: {
+          host_groups: [
+            {
+              name: 'g1',
+              hosts: [
+                {
+                  fqdn: 'host1'
+                }
+              ]
+            }
+          ]
+        },
+        blueprint: {
+          host_groups: [
+            {
+              name: 'g1',
+              components: []
+            }
+          ]
+        }
+      };
       view.reopen({
+        mastersToCreate: ['C1'],
         content: Em.Object.create({
-          controllerName: 'ctrl1',
-          componentsFromConfigs: []
-        }),
-        selectedServicesMasters: [
-          {
-            component_name: 'C1',
-            selectedHost: 'host1'
-          }
-        ],
-        popup: popup,
-        configActionComponent: {
-          componentName: 'C1'
-        },
-        configWidgetContext: Em.Object.create({
-          config: Em.Object.create({
-            fileName: 'file1',
-            name: 'conf1',
+          recommendationsHostGroups: recommendationsHostGroups
+        })
+      });
+      view.saveRecommendationsHostGroups();
+      expect(view.get('content.recommendationsHostGroups')).to.be.eql(Object.assign(recommendationsHostGroups, {
+        blueprint: {
+          host_groups: [
+            {
+              name: 'g1',
+              components: [{name: 'C1'}]
+            }
+          ]
+        }
+      }));
+    });
+  });
+
+  describe('#setGlobalComponentToBeAdded', function() {
+
+    it('should set componentToBeAdded', function() {
+      view.setGlobalComponentToBeAdded('C1', 'host1');
+      expect(App.get('componentToBeAdded')).to.be.eql(Em.Object.create({
+        componentName: 'C1',
+        hostNames: ['host1']
+      }));
+    });
+  });
+
+  describe('#clearComponentsToBeDeleted', function() {
+
+    it('should clear componentToBeDeleted', function() {
+      App.set('componentToBeDeleted', Em.Object.create({
+        componentName: 'C1'
+      }));
+      view.clearComponentsToBeDeleted('C1');
+      expect(App.get('componentToBeDeleted')).to.be.empty;
+    });
+  });
+
+  describe('#clearComponentsToBeAdded', function() {
+
+    it('should clear componentToBeAdded', function() {
+      App.set('componentToBeAdded', Em.Object.create({
+        componentName: 'C1'
+      }));
+      view.clearComponentsToBeAdded('C1');
+      expect(App.get('componentToBeAdded')).to.be.empty;
+    });
+  });
+
+  describe('#showPopup', function() {
+    beforeEach(function() {
+      this.mock = sinon.stub(view, 'getAllMissingDependentServices');
+      sinon.stub(view, 'showInstallServicesPopup');
+      sinon.stub(view, 'showAssignComponentPopup');
+    });
+    afterEach(function() {
+      this.mock.restore();
+      view.showInstallServicesPopup.restore();
+      view.showAssignComponentPopup.restore();
+    });
+
+    it('showAssignComponentPopup should be called', function() {
+      this.mock.returns([]);
+      view.showPopup({componentName: 'C1'});
+      expect(view.get('mastersToCreate')).to.be.eql(['C1']);
+      expect(view.showAssignComponentPopup.calledOnce).to.be.true;
+    });
+
+    it('showInstallServicesPopup should be called', function() {
+      this.mock.returns([{}]);
+      view.reopen({
+        content: Em.Object.create()
+      });
+      view.showPopup({componentName: 'C1'});
+      expect(view.showInstallServicesPopup.calledWith([{}])).to.be.true;
+    });
+  });
+
+  describe('#submit', function() {
+    var configWidgetContext = Em.Object.create({
+      controller: {
+        forceUpdateBoundaries: false,
+        stepConfigs: [
+          Em.Object.create({
             serviceName: 'S1',
-            savedValue: 'val1',
-            toggleProperty: Em.K
+            configs: []
           }),
-          controller: Em.Object.create({
-            selectedService: {
-              serviceName: 'S1'
-            },
-            wizardController: {
-              name: 'ctrl'
-            },
-            stepConfigs: [
-              Em.Object.create({
-                serviceName: 'S1',
-                configs: [
-                  config
-                ]
-              }),
-              Em.Object.create({
-                serviceName: 'MISC',
-                configs: [
-                  config
-                ]
-              })
-            ]
+          Em.Object.create({
+            serviceName: 'MISC',
+            configs: []
           })
-        })
+        ],
+        selectedService: {
+          serviceName: 'S1'
+        }
+      },
+      config: Em.Object.create({
+        configAction: {
+          dependencies: []
+        }
+      })
+    });
+    beforeEach(function() {
+      sinon.stub(view, 'resolveDependencies');
+      sinon.stub(view, 'saveMasterComponentHosts');
+      sinon.stub(view, 'saveRecommendationsHostGroups');
+      sinon.stub(view, 'setGlobalComponentToBeAdded');
+      sinon.stub(view, 'clearComponentsToBeDeleted');
+      sinon.stub(App, 'get').returns({
+        getKDCSessionState: Em.clb
+      });
+      sinon.stub(view, 'getSelectedHostName').returns('host1');
+      view.setProperties({
+        configWidgetContext: configWidgetContext,
+        configActionComponent: { componentName: 'C1'},
+        popup: {
+          hide: sinon.spy()
+        }
+      });
+    });
+    afterEach(function() {
+      App.get.restore();
+      view.resolveDependencies.restore();
+      view.clearComponentsToBeDeleted.restore();
+      view.setGlobalComponentToBeAdded.restore();
+      view.saveRecommendationsHostGroups.restore();
+      view.saveMasterComponentHosts.restore();
+      view.getSelectedHostName.restore();
+    });
+
+    it('saveMasterComponentHosts should be called when controllerName defined', function() {
+      view.reopen({
+        content: {
+          controllerName: 'ctrl1'
+        }
+      });
+      view.submit();
+      expect(view.saveMasterComponentHosts.calledOnce).to.be.true;
+    });
+    it('saveRecommendationsHostGroups should be called when controllerName defined', function() {
+      view.reopen({
+        content: {
+          controllerName: 'ctrl1'
+        }
+      });
+      view.submit();
+      expect(view.saveRecommendationsHostGroups.calledOnce).to.be.true;
+    });
+    it('setGlobalComponentToBeAdded should be called when controllerName undefined', function() {
+      view.reopen({
+        content: {
+          controllerName: undefined
+        }
+      });
+      view.submit();
+      expect(view.setGlobalComponentToBeAdded.calledWith('C1', 'host1')).to.be.true;
+    });
+    it('clearComponentsToBeDeleted should be called when controllerName undefined', function() {
+      view.reopen({
+        content: {
+          controllerName: undefined
+        }
       });
       view.submit();
+      expect(view.clearComponentsToBeDeleted.calledWith('C1')).to.be.true;
+    });
+    it('resolveDependencies should be called', function() {
+      view.submit();
+      expect(view.resolveDependencies.calledWith([], [])).to.be.true;
+    });
+    it('hide should be called', function() {
+      view.submit();
+      expect(view.get('popup').hide.calledOnce).to.be.true;
+    });
+    it('configActionComponent should be set', function() {
+      view.submit();
+      expect(view.get('configWidgetContext.config.configActionComponent')).to.be.eql({
+        componentName: 'C1',
+        hostName: 'host1'
+      });
+    });
+  });
+
+  describe('#resolveDependencies', function() {
+    var initializer = {
+      setup: sinon.spy(),
+      initialValue: sinon.stub().returns({value: 'val1'}),
+      cleanup: sinon.spy()
+    };
+    var dependencies = {
+      properties: [
+        {
+          name: 'p1',
+          fileName: 'file1.xml',
+          nameTemplate: '{{bar}}',
+          isHostsList: true,
+          isHostsArray: false
+        }
+      ],
+      initializer: {
+        name: 'i1',
+        setupKeys: ['bar']
+      }
+    };
+    var context = Em.Object.create({
+      controller: {
+        selectedService: {
+          serviceName: 'S1'
+        }
+      }
+    });
+    var serviceConfigs = [
+      Em.Object.create({
+        name: 'foo',
+        filename: 'file1.xml',
+        value: 'val1'
+      })
+    ];
+
+    beforeEach(function() {
+      sinon.stub(view, 'getDependenciesForeignKeys').returns({
+        bar: 'foo'
+      });
+      sinon.stub(App, 'get').returns(initializer);
+      sinon.stub(view, 'getMasterComponents');
+      sinon.stub(view, 'saveRecommendations');
+      sinon.stub(App.config, 'updateHostsListValue');
+      sinon.stub(App.config, 'get').returns({
+        'file1.xml': Em.Object.create({
+          serviceName: 'S1'
+        })
+      });
+      view.resolveDependencies(dependencies, serviceConfigs, context);
+    });
+    afterEach(function() {
+      App.config.get.restore();
+      view.getDependenciesForeignKeys.restore();
+      App.get.restore();
+      view.getMasterComponents.restore();
+      view.saveRecommendations.restore();
+      App.config.updateHostsListValue.restore();
+    });
+
+    it('initializer.setup should be called', function() {
+      expect(initializer.setup.calledWith({bar: 'foo'})).to.be.true;
+    });
+    it('initializer.setup initialValue be called', function() {
+      expect(initializer.initialValue.calledWith({
+        name: 'foo',
+        fileName: 'file1.xml'
+      })).to.be.true;
+    });
+    it('initializer.cleanup should be called', function() {
+      expect(initializer.cleanup.called).to.be.true;
     });
+    it('saveRecommendations should be called', function() {
+      expect(view.saveRecommendations.calledWith(context)).to.be.true;
+    });
+    it('App.config.updateHostsListValue should be called', function() {
+      expect(App.config.updateHostsListValue.getCall(0).args).to.be.eql([
+        {
+          foo: 'val1'
+        },
+        'file1.xml',
+        'foo',
+        undefined,
+        false
+      ]);
+    });
+  });
 
+  describe('#saveMasterComponentHosts', function() {
+    var mockCtrl = {
+      saveMasterComponentHosts: sinon.spy(),
+      loadMasterComponentHosts: sinon.spy()
+    };
+    beforeEach(function() {
+      sinon.stub(App.router, 'get').returns(mockCtrl);
+      view.reopen({
+        content: Em.Object.create({
+          componentsFromConfigs: []
+        })
+      });
+      view.set('mastersToCreate', [
+        {}
+      ]);
+    });
     afterEach(function() {
       App.router.get.restore();
-      popup.hide.restore();
-      mock.saveMasterComponentHosts.restore();
-      mock.loadMasterComponentHosts.restore();
-      mock.setDBProperty.restore();
-      App.config.getConfigTagFromFileName.restore();
     });
 
-    it("saveMasterComponentHosts should be called", function() {
-      expect(mock.saveMasterComponentHosts.calledOnce).to.be.true;
+    it('saveMasterComponentHosts should be called', function() {
+      view.saveMasterComponentHosts();
+      expect(mockCtrl.saveMasterComponentHosts.calledWith(view, true)).to.be.true;
+    });
+    it('loadMasterComponentHosts should be called', function() {
+      view.saveMasterComponentHosts();
+      expect(mockCtrl.loadMasterComponentHosts.calledWith(true)).to.be.true;
     });
+    it('componentsFromConfigs should be set', function() {
+      view.saveMasterComponentHosts();
+      expect(view.get('content.componentsFromConfigs')).to.be.eql([{}]);
+    });
+  });
 
-    it("loadMasterComponentHosts should be called", function() {
-      expect(mock.loadMasterComponentHosts.calledOnce).to.be.true;
+  describe('#getSelectedHostName', function() {
+
+    it('should return host of component', function() {
+      view.set('selectedServicesMasters', [
+        {
+          component_name: 'C1',
+          selectedHost: 'host1'
+        }
+      ]);
+      expect(view.getSelectedHostName('C1')).to.be.equal('host1');
     });
+  });
 
-    it("configActionComponent should be set", function() {
-      expect(view.get('configWidgetContext.config.configActionComponent')).to.be.eql({
-        componentName: 'C1',
-        hostName: 'host1'
+  describe('#saveRecommendations', function() {
+    var mockCtrl = {
+      loadRecommendationsSuccess: sinon.spy(),
+      wizardController: {
+        name: 'installerController'
+      }
+    };
+    var context = Em.Object.create({
+      controller: mockCtrl
+    });
+    it('loadRecommendationsSuccess should be called', function() {
+      view.set('configWidgetContext', {
+        config: Em.Object.create({
+          fileName: 'foo.xml',
+          name: 'bar',
+          initialValue: 'iv1'
+        })
+      });
+      view.saveRecommendations(context, {});
+      expect(mockCtrl.loadRecommendationsSuccess.getCall(0).args).to.be.eql([
+        {
+          resources: [
+            {
+              recommendations: {
+                blueprint: {
+                  configurations: {}
+                }
+              }
+            }
+          ]
+        }, null, {
+          dataToSend: {
+            changed_configurations: [{
+              type: 'foo',
+              name: 'bar',
+              old_value: 'iv1'
+            }]
+          }
+        }
+      ]);
+    });
+  });
+
+  describe('#getDependenciesForeignKeys', function() {
+    var dependencies = {
+      foreignKeys: [
+        {
+          fileName: 'foo.xml',
+          propertyName: 'c1',
+          key: 'k1'
+        }
+      ]
+    };
+    var serviceConfigs = [
+      Em.Object.create({
+        filename: 'foo.xml',
+        name: 'c1',
+        value: 'val1'
+      })
+    ];
+
+    it('should return foreignKeys map', function() {
+      expect(view.getDependenciesForeignKeys(dependencies, serviceConfigs)).to.be.eql({
+        k1: 'val1'
+      });
+    });
+  });
+
+  describe('#getMasterComponents', function() {
+    var dependencies = {
+      initializer: {
+        componentNames: ['C1']
+      }
+    };
+    var context = Em.Object.create({
+      controller: {
+        content: {
+          masterComponentHosts: [
+            {
+              component: 'C1',
+              hostName: 'host2'
+            }
+          ]
+        }
+      }
+    });
+    beforeEach(function() {
+      sinon.stub(blueprintUtils, 'getComponentForHosts').returns({
+        host1: ['C1']
       });
     });
+    afterEach(function() {
+      blueprintUtils.getComponentForHosts.restore();
+    });
+
+    it('should return master components when controllerName undefined', function() {
+      view.set('content.controllerName', undefined);
+      expect(view.getMasterComponents(dependencies, context)).to.be.eql([
+        {
+          component: 'C1',
+          hostName: 'host1',
+          isInstalled: true
+        }
+      ]);
+    });
+
+    it('should return master components when controllerName valid', function() {
+      view.set('content.controllerName', 'ctrl1');
+      expect(view.getMasterComponents(dependencies, context)).to.be.eql([
+        {
+          component: 'C1',
+          hostName: 'host2',
+          isInstalled: true
+        }
+      ]);
+    });
   });
 });
\ No newline at end of file