/**
 * 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('views/common/controls_view');
var validator = require('utils/validator');
var testHelpers = require('test/helpers');

describe('App.ServiceConfigRadioButtons', function () {

  var view;

  beforeEach(function () {
    view = App.ServiceConfigRadioButtons.create();
  });

  describe('#handleDBConnectionProperty', function () {

    var cases = [
        {
          dbType: 'mysql',
          driver: 'mysql-connector-java.jar',
          serviceConfig: {
            name: 'hive_database',
            value: 'New MySQL Database',
            serviceName: 'HIVE'
          },
          controller: Em.Object.create({
            selectedService: {
              configs: [
                Em.Object.create({
                  name: 'javax.jdo.option.ConnectionURL',
                  displayName: 'Database URL'
                }),
                Em.Object.create({
                  name: 'hive_database',
                  displayName: 'Hive Database'
                })
              ]
            }
          }),
          currentStackVersion: 'HDP-2.2',
          rangerVersion: '0.4.0',
          propertyAppendTo1: 'javax.jdo.option.ConnectionURL',
          propertyAppendTo2: 'hive_database',
          isAdditionalView1Null: true,
          isAdditionalView2Null: true,
          title: 'Hive, embedded database'
        },
        {
          dbType: 'postgres',
          driver: 'postgresql.jar',
          serviceConfig: {
            name: 'hive_database',
            value: 'Existing PostgreSQL Database',
            serviceName: 'HIVE'
          },
          controller: Em.Object.create({
            selectedService: {
              configs: [
                Em.Object.create({
                  name: 'javax.jdo.option.ConnectionURL',
                  displayName: 'Database URL'
                }),
                Em.Object.create({
                  name: 'hive_database',
                  displayName: 'Hive Database'
                })
              ]
            }
          }),
          currentStackVersion: 'HDP-2.2',
          rangerVersion: '0.4.0',
          propertyAppendTo1: 'javax.jdo.option.ConnectionURL',
          propertyAppendTo2: 'hive_database',
          isAdditionalView1Null: false,
          isAdditionalView2Null: false,
          title: 'Hive, external database'
        },
        {
          dbType: 'derby',
          driver: 'driver.jar',
          serviceConfig: {
            name: 'oozie_database',
            value: 'New Derby Database',
            serviceName: 'OOZIE'
          },
          controller: Em.Object.create({
            selectedService: {
              configs: [
                Em.Object.create({
                  name: 'oozie.service.JPAService.jdbc.url',
                  displayName: 'Database URL'
                }),
                Em.Object.create({
                  name: 'oozie_database',
                  displayName: 'Oozie Database'
                })
              ]
            }
          }),
          currentStackVersion: 'HDP-2.2',
          rangerVersion: '0.4.0',
          propertyAppendTo1: 'oozie.service.JPAService.jdbc.url',
          propertyAppendTo2: 'oozie_database',
          isAdditionalView1Null: true,
          isAdditionalView2Null: true,
          title: 'Oozie, embedded database'
        },
        {
          dbType: 'oracle',
          driver: 'ojdbc6.jar',
          serviceConfig: {
            name: 'oozie_database',
            value: 'Existing Oracle Database',
            serviceName: 'OOZIE'
          },
          controller: Em.Object.create({
            selectedService: {
              configs: [
                Em.Object.create({
                  name: 'oozie.service.JPAService.jdbc.url',
                  displayName: 'Database URL'
                }),
                Em.Object.create({
                  name: 'oozie_database',
                  displayName: 'Oozie Database'
                })
              ]
            }
          }),
          currentStackVersion: 'HDP-2.2',
          rangerVersion: '0.4.0',
          propertyAppendTo1: 'oozie.service.JPAService.jdbc.url',
          propertyAppendTo2: 'oozie_database',
          isAdditionalView1Null: false,
          isAdditionalView2Null: false,
          title: 'Oozie, external database'
        },
        {
          dbType: 'mysql',
          driver: 'mysql-connector-java.jar',
          serviceConfig: {
            name: 'DB_FLAVOR',
            value: 'MYSQL',
            serviceName: 'RANGER'
          },
          controller: Em.Object.create({
            selectedService: {
              configs: [
                Em.Object.create({
                  name: 'ranger.jpa.jdbc.url'
                }),
                Em.Object.create({
                  name: 'DB_FLAVOR'
                })
              ]
            }
          }),
          currentStackVersion: 'HDP-2.2',
          rangerVersion: '0.4.0',
          propertyAppendTo1: 'ranger.jpa.jdbc.url',
          propertyAppendTo2: 'DB_FLAVOR',
          isAdditionalView1Null: true,
          isAdditionalView2Null: true,
          title: 'Ranger, HDP 2.2, external database'
        },
        {
          dbType: 'mssql',
          driver: 'sqljdbc4.jar',
          serviceConfig: {
            name: 'DB_FLAVOR',
            value: 'MSSQL',
            serviceName: 'RANGER'
          },
          controller: Em.Object.create({
            selectedService: {
              configs: [
                Em.Object.create({
                  name: 'ranger.jpa.jdbc.url'
                }),
                Em.Object.create({
                  name: 'DB_FLAVOR'
                })
              ]
            }
          }),
          currentStackVersion: 'HDP-2.3',
          rangerVersion: '0.5.0',
          propertyAppendTo1: 'ranger.jpa.jdbc.url',
          propertyAppendTo2: 'DB_FLAVOR',
          isAdditionalView1Null: false,
          isAdditionalView2Null: false,
          title: 'Ranger, HDP 2.3, external database'
        }
      ];
    var rangerVersion = '';

    before(function () {
      sinon.stub(Em.run, 'next', function (arg) {
        arg();
      });
    });

    beforeEach(function () {
      sinon.stub(view, 'sendRequestRorDependentConfigs', Em.K);
      this.stub = sinon.stub(App, 'get');
      this.stub.withArgs('currentStackName').returns('HDP');
      sinon.stub(App.StackService, 'find', function() {
        return [Em.Object.create({
          serviceName: 'RANGER',
          serviceVersion: rangerVersion || ''
        })];
      });
    });

    afterEach(function () {
      App.get.restore();
      App.StackService.find.restore();
      view.sendRequestRorDependentConfigs.restore();
    });

    after(function () {
      Em.run.next.restore();
    });

    cases.forEach(function (item) {
      describe(item.title, function () {

        var additionalView1, additionalView2;
        beforeEach(function () {
          this.stub.withArgs('currentStackVersion').returns(item.currentStackVersion);
          rangerVersion = item.rangerVersion;
          view.reopen({controller: item.controller});
          view.setProperties({
            categoryConfigsAll: item.controller.get('selectedService.configs'),
            serviceConfig: item.serviceConfig
          });

          additionalView1 = view.get('categoryConfigsAll').findProperty('name', item.propertyAppendTo1).get('additionalView');
          additionalView2 = view.get('categoryConfigsAll').findProperty('name', item.propertyAppendTo2).get('additionalView');
        });

        it('additionalView1 is ' + (item.isAdditionalView1Null ? '' : 'not') + ' null', function () {
          expect(Em.isNone(additionalView1)).to.equal(item.isAdditionalView1Null);
        });

        it('additionalView2 is ' + (item.isAdditionalView2Null ? '' : 'not') + ' null', function () {
          expect(Em.isNone(additionalView2)).to.equal(item.isAdditionalView2Null);
        });

        if (!item.isAdditionalView2Null) {
          it('additionalView2.message is valid', function () {
            expect(additionalView2.create().get('message')).to.equal(Em.I18n.t('services.service.config.database.msg.jdbcSetup').format(item.dbType, item.driver));
          });
        }

      });
    });

  });

  describe('#options', function () {

    var options = [
        {
          displayName: 'MySQL'
        },
        {
          displayName: 'New PostgreSQL Database'
        },
        {
          displayName: 'existing postgres db'
        },
        {
          displayName: 'sqla database: existing'
        },
        {
          displayName: 'SQL Anywhere database (New)'
        },
        {
          displayName: 'displayName'
        }
      ],
      classNames = ['mysql', 'new-postgres', 'postgres', 'sqla', 'new-sqla', undefined];

    beforeEach(function () {
      view.reopen({
        serviceConfig: Em.Object.create({
          options: options
        })
      });
    });

    it('should set class names for options', function () {
      expect(view.get('options').mapProperty('displayName')).to.eql(options.mapProperty('displayName'));
      expect(view.get('options').mapProperty('className')).to.eql(classNames);
    });

  });

  describe('#name', function () {

    var cases = [
      {
        serviceConfig: {
          radioName: 'n0',
          isOriginalSCP: true,
          isComparison: false
        },
        name: 'n0',
        title: 'original value'
      },
      {
        serviceConfig: {
          radioName: 'n1',
          isOriginalSCP: false,
          isComparison: true,
          compareConfigs: []
        },
        controller: {
          selectedVersion: 1
        },
        name: 'n1-v1',
        title: 'comparison view, original value'
      },
      {
        serviceConfig: {
          radioName: 'n2',
          isOriginalSCP: false,
          isComparison: true,
          compareConfigs: null
        },
        version: 2,
        name: 'n2-v2',
        title: 'comparison view, value to be compared with'
      },
      {
        serviceConfig: {
          radioName: 'n3',
          isOriginalSCP: false,
          isComparison: false,
          group: {
            name: 'g'
          }
        },
        name: 'n3-g',
        title: 'override value'
      }
    ];

    beforeEach(function () {
      view.reopen({
        serviceConfig: Em.Object.create()
      });
    });

    cases.forEach(function (item) {
      it(item.title, function () {
        if (item.controller) {
          view.reopen({
            controller: item.controller
          });
        }
        view.set('version', item.version);
        view.get('serviceConfig').setProperties(item.serviceConfig);
        expect(view.get('name')).to.equal(item.name);
      });
    });

  });

  describe('#dontUseHandleDbConnection', function () {
    var rangerService = Em.Object.create({
      serviceName: 'RANGER'
    });
    beforeEach(function () {
      sinon.stub(App.StackService, 'find', function () {
        return [rangerService];
      });
    });

    afterEach(function () {
      App.StackService.find.restore();
    });

    var cases = [
      {
        title: 'Should return properties for old version of Ranger',
        version: '0.1',
        result: ['DB_FLAVOR', 'authentication_method']
      },
      {
        title: 'Should return properties for old version of Ranger',
        version: '0.4.0',
        result: ['DB_FLAVOR', 'authentication_method']
      },
      {
        title: 'Should return properties for old version of Ranger',
        version: '0.4.9',
        result: ['DB_FLAVOR', 'authentication_method']
      },
      {
        title: 'Should return properties for new version of Ranger',
        version: '0.5.0',
        result: ['ranger.authentication.method']
      },
      {
        title: 'Should return properties for new version of Ranger',
        version: '1.0.0',
        result: ['ranger.authentication.method']
      },
      {
        title: 'Should return properties for new version of Ranger',
        version: '0.5.0.1',
        result: ['ranger.authentication.method']
      }
    ];

    cases.forEach(function (test) {
      it(test.title, function () {
        rangerService.set('serviceVersion', test.version);
        expect(view.get('dontUseHandleDbConnection')).to.eql(test.result);
      });
    });
  });

});

describe('App.ServiceConfigRadioButton', function () {

  var view;

  beforeEach(function () {
    view = App.ServiceConfigRadioButton.create({
      parentView: Em.Object.create({
        serviceConfig: Em.Object.create()
      }),
      controller: Em.Object.create({
        wizardController: Em.Object.create({
          name: null
        })
      })
    })
  });

  describe('#disabled', function () {

    var cases = [
      {
        wizardControllerName: 'addServiceController',
        value: 'New MySQL Database',
        title: 'Add Service Wizard, new database',
        disabled: false
      },
      {
        wizardControllerName: 'installerController',
        value: 'New MySQL Database',
        title: 'Install Wizard, new database',
        disabled: false
      },
      {
        wizardControllerName: 'addServiceController',
        value: 'Existing MySQL Database',
        title: 'Add Service Wizard, existing database',
        disabled: false
      },
      {
        wizardControllerName: 'installerController',
        value: 'Existing MySQL Database',
        title: 'Install Wizard, existing database',
        disabled: false
      },
      {
        wizardControllerName: null,
        value: 'New MySQL Database',
        title: 'No installer, new database',
        disabled: true
      },
      {
        wizardControllerName: null,
        value: 'Existing MySQL Database',
        title: 'No installer, existing database',
        disabled: false
      }
    ];

    cases.forEach(function (item) {
      it(item.title, function () {
        view.setProperties({
          'value': item.value,
          'controller.wizardController.name': item.wizardControllerName,
          'parentView.serviceConfig.isEditable': true
        });
        expect(view.get('disabled')).to.equal(item.disabled);
      });
    });

    it('parent view is disabled', function () {
      view.set('parentView.serviceConfig.isEditable', false);
      expect(view.get('disabled')).to.be.true;
    });

  });

  describe('#onChecked', function () {

    var cases = [
      {
        clicked: true,
        value: 'v1',
        sendRequestRorDependentConfigsCallCount: 1,
        updateForeignKeysCallCount: 1,
        title: 'invoked with click'
      },
      {
        clicked: false,
        value: 'v0',
        sendRequestRorDependentConfigsCallCount: 0,
        updateForeignKeysCallCount: 0,
        title: 'not invoked with click'
      }
    ];

    cases.forEach(function (item) {

      describe(item.title, function () {

        beforeEach(function () {
          sinon.stub(Em.run, 'next', function (context, callback) {
            callback.call(context);
          });
          sinon.stub(view, 'sendRequestRorDependentConfigs', Em.K);
          sinon.stub(view, 'updateForeignKeys', Em.K);
          sinon.stub(view, 'updateCheck', Em.K);
          view.setProperties({
            'clicked': item.clicked,
            'parentView.serviceConfig.value': 'v0',
            'value': 'v1'
          });
          view.propertyDidChange('checked');
        });

        afterEach(function () {
          Em.run.next.restore();
          view.sendRequestRorDependentConfigs.restore();
          view.updateForeignKeys.restore();
          view.updateCheck.restore();
        });

        it('property value', function () {
          expect(view.get('parentView.serviceConfig.value')).to.equal(item.value);
        });

        it('dependent configs request', function () {
          expect(view.sendRequestRorDependentConfigs.callCount).to.equal(item.sendRequestRorDependentConfigsCallCount);
        });

        if (item.sendRequestRorDependentConfigsCallCount) {
          it('config object for dependent configs request', function () {
            expect(view.sendRequestRorDependentConfigs.firstCall.args).to.eql([
              Em.Object.create({
                value: item.value
              })
            ]);
          });
        }

        it('clicked flag reset', function () {
          expect(view.get('clicked')).to.be.false;
        });

        it('update foreign keys', function () {
          expect(view.updateForeignKeys.callCount).to.equal(item.updateForeignKeysCallCount);
        });

      });

    });

  });

});

describe('App.CheckDBConnectionView', function () {

  describe('#masterHostName', function () {

    var cases = [
        {
          serviceName: 'OOZIE',
          value: 'h0'
        },
        {
          serviceName: 'KERBEROS',
          value: 'h1'
        },
        {
          serviceName: 'HIVE',
          value: 'h2'
        },
        {
          serviceName: 'RANGER',
          value: 'h3'
        }
      ],
      categoryConfigsAll = [
        Em.Object.create({
          name: 'oozie_server_hosts',
          value: 'h0'
        }),
        Em.Object.create({
          name: 'kdc_hosts',
          value: 'h1'
        }),
        Em.Object.create({
          name: 'hive_metastore_hosts',
          value: 'h2'
        }),
        Em.Object.create({
          name: 'ranger_server_hosts',
          value: 'h3'
        })
      ];

    cases.forEach(function (item) {
      it(item.serviceName, function () {
        var view = App.CheckDBConnectionView.create({
          parentView: {
            service: {
              serviceName: item.serviceName
            },
            categoryConfigsAll: categoryConfigsAll
          }
        });
        expect(view.get('masterHostName')).to.equal(item.value);
      });
    });

  });

  describe('#setResponseStatus', function () {

    var view,
      cases = [
        {
          isSuccess: 'success',
          logsPopupBefore: null,
          logsPopup: null,
          responseCaption: Em.I18n.t('services.service.config.database.connection.success'),
          isConnectionSuccess: true,
          title: 'success, no popup displayed'
        },
        {
          isSuccess: 'success',
          logsPopupBefore: {},
          logsPopup: {
            header: Em.I18n.t('services.service.config.connection.logsPopup.header').format('MySQL', Em.I18n.t('common.success'))
          },
          responseCaption: Em.I18n.t('services.service.config.database.connection.success'),
          isConnectionSuccess: true,
          title: 'success, popup is displayed'
        },
        {
          isSuccess: 'error',
          logsPopupBefore: {},
          logsPopup: {
            header: Em.I18n.t('services.service.config.connection.logsPopup.header').format('MySQL', Em.I18n.t('common.error'))
          },
          responseCaption: Em.I18n.t('services.service.config.database.connection.failed'),
          isConnectionSuccess: false,
          title: 'error, popup is displayed'
        }
      ];

    beforeEach(function () {
      view = App.CheckDBConnectionView.create({
        databaseName: 'MySQL'
      });
      sinon.stub(view, 'setConnectingStatus', Em.K);
    });

    afterEach(function () {
      view.setConnectingStatus.restore();
    });

    cases.forEach(function (item) {

      describe(item.title, function () {

        beforeEach(function () {
          view.set('logsPopup', item.logsPopupBefore);
          view.setResponseStatus(item.isSuccess);
        });

        it('isRequestResolved is true', function () {
          expect(view.get('isRequestResolved')).to.be.true;
        });

        it('setConnectingStatus is called with valid arguments', function () {
          expect(view.setConnectingStatus.calledOnce).to.be.true;
          expect(view.setConnectingStatus.calledWith(false)).to.be.true;
        });

        it('responseCaption is valid', function () {
          expect(view.get('responseCaption')).to.equal(item.responseCaption);
        });

        it('isConnectionSuccess is valid', function () {
          expect(view.get('isConnectionSuccess')).to.equal(item.isConnectionSuccess);
        });

        it('logsPopup is valid', function () {
          expect(view.get('logsPopup')).to.eql(item.logsPopup);
        });

      });
    });

  });

  describe('#showLogsPopup', function () {

    var view;

    beforeEach(function () {
      view = App.CheckDBConnectionView.create({
        databaseName: 'MySQL'
      });
      sinon.spy(App, 'showAlertPopup');
    });

    afterEach(function () {
      App.showAlertPopup.restore();
    });

    it('successful connection', function () {
      view.set('isConnectionSuccess', true);
      view.showLogsPopup();
      expect(App.showAlertPopup.callCount).to.equal(0);
    });

    describe('failed connection without output data, popup dismissed with Close button', function () {

      beforeEach(function () {
        view.set('isConnectionSuccess', false);
        view.set('isRequestResolved', true);
        view.set('responseFromServer', 'fail');
        view.showLogsPopup();
      });

      it('showAlertPopup is called once', function () {
        expect(App.showAlertPopup.callCount).to.equal(1);
      });
      it('logsPopup.header is valid', function () {
        expect(view.get('logsPopup.header')).to.equal(Em.I18n.t('services.service.config.connection.logsPopup.header').format('MySQL', Em.I18n.t('common.error')));
      });
      it('logsPopup.body is valid', function () {
        expect(view.get('logsPopup.body')).to.equal('fail');
      });
      it('logsPopup is null after close', function () {
        view.get('logsPopup').onClose();
        expect(view.get('logsPopup')).to.be.null;
      });
    });

    describe('check in progress with output data, popup dismissed with OK button', function () {
      var response = {
        stderr: 'stderr',
        stdout: 'stdout',
        structuredOut: 'structuredOut'
      };
      beforeEach(function () {
        view.set('isConnectionSuccess', false);
        view.set('isRequestResolved', false);
        view.set('responseFromServer', response);
        view.showLogsPopup();
      });

      it('showAlertPopup is called once', function () {
        expect(App.showAlertPopup.callCount).to.equal(1);
      });
      it('logsPopup.header is valid', function () {
        expect(view.get('logsPopup.header')).to.equal(Em.I18n.t('services.service.config.connection.logsPopup.header').format('MySQL', Em.I18n.t('common.testing')));
      });
      it('logsPopup.bodyClass is valid', function () {
        expect(view.get('logsPopup.bodyClass').create().get('openedTask')).to.eql(response);
      });
      it('logsPopup is null after primary click', function () {
        view.get('logsPopup').onPrimary();
        expect(view.get('logsPopup')).to.be.null;
      });
    });

  });

  describe("#createCustomAction()", function() {
    var view;
    beforeEach(function () {
      view = App.CheckDBConnectionView.create({
        databaseName: 'MySQL',
        getConnectionProperty: Em.K,
        masterHostName: 'host1'
      });
      this.mock = sinon.stub(App.Service, 'find');
    });
    afterEach(function () {
      this.mock.restore();
    });

    it("service not installed", function() {
      this.mock.returns(Em.Object.create({isLoaded: false}));
      view.createCustomAction();
      var args = testHelpers.findAjaxRequest('name', 'custom_action.create');
      expect(args[0]).exists;
    });
    it("service is installed", function() {
      this.mock.returns(Em.Object.create({isLoaded: true}));
      view.createCustomAction();
      var args = testHelpers.findAjaxRequest('name', 'cluster.custom_action.create');
      expect(args[0]).exists;
    });
  });
});

describe('App.BaseUrlTextField', function () {

  var view = App.BaseUrlTextField.create({
    repository: Em.Object.create({
      baseUrl: 'val'
    }),
    parentView: Em.Object.create({
      uiValidation: Em.K
    })
  });

  describe('#valueWasChanged', function () {
    it('should be recalculated after value is changed', function () {
      view.setProperties({
        value: 'val',
        defaultValue: 'val'
      });
      expect(view.get('valueWasChanged')).to.be.false;
      view.set('value', 'newVal');
      expect(view.get('valueWasChanged')).to.be.true;
    });
  });

  describe('#restoreValue()', function () {
    it('should unset value', function () {
      view.setProperties({
        value: 'valNew',
        defaultValue: 'val'
      });
      view.restoreValue();
      expect(view.get('value')).to.equal('val');
    });
  });

  describe('#didInsertElement()', function () {
    it('should set defaultValue', function () {
      view.setProperties({
        value: 'valNew',
        defaultValue: 'val'
      });
      view.didInsertElement();
      expect(view.get('defaultValue')).to.equal('valNew');
    });
  });

  describe('#validate()', function () {
    beforeEach(function(){
      sinon.stub(view.get('parentView'), 'uiValidation', Em.K);
      sinon.stub(validator, 'isValidBaseUrl').returns(true);
    });
    afterEach(function(){
      view.get('parentView').uiValidation.restore();
      validator.isValidBaseUrl.restore();
    });
    it('skip validation', function () {
      view.set('repository', Em.Object.create({
        skipValidation: true
      }));
      expect(view.get('repository.hasError')).to.be.false;
    });
    it('apply validation', function () {
      view.set('repository', Em.Object.create({
          skipValidation: false
      }));
      expect(view.get('repository.hasError')).to.be.false;
      expect(validator.isValidBaseUrl.calledOnce).to.be.true;
    });
  });
});
