text: /*
   *    (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
   *
   * Licensed 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.
   */
  (function () {
    'use strict';
  
    describe('Launch Instance Flavor Step', function () {
      describe('LaunchInstanceFlavorController', function () {
        var ctrl, scope, model, defaults;
  
        beforeEach(module('horizon.dashboard.project.workflow.launch-instance'));
  
        beforeEach(inject(function ($controller, $rootScope) {
          scope = $rootScope.$new();
  
          // Track calls to $watch and $watchCollection, and let them
          // do their real work to register watch listeners. Remember
          // that $watchCollection makes its own call to $watch.
          spyOn(scope, '$watch').and.callThrough();
          spyOn(scope, '$watchCollection').and.callThrough();
  
          scope.launchInstanceFlavorForm = {'allocated-flavor': {}};
  
          model = { newInstanceSpec: { },
                    novaLimits: { },
                    cinderLimits: { },
                    flavors: []
                  };
          defaults = { usageLabel: "label",
                       usageColorClass: "class1",
                       addedLabel: "label2",
                       addedColorClass: "class2",
                       remainingLabel: "label3",
                       remainingColorClass: "class3"
                     };
  
          ctrl = $controller('LaunchInstanceFlavorController as selectFlavorCtrl',
                             { $scope:scope,
                               'horizon.framework.widgets.charts.quotaChartDefaults': defaults,
                               launchInstanceModel: model });
        }));
  
        it('defines expected labels', function () {
          var props = [
            'chartTotalInstancesLabel',
            'chartTotalVcpusLabel',
            'chartTotalRamLabel',
            'chartTotalVolumeLabel',
            'chartTotalVolumeStorageLabel'
          ];
  
          props.forEach(function (prop) {
            expect(ctrl[prop]).toBeDefined();
            expect(angular.isString(ctrl[prop]));
          });
        });
  
        it('has a chart data function that returns expected values', function () {
          var data = ctrl.getChartData('fakeTitle', 1, 2, 3);
  
          expect(data.title).toBe('fakeTitle');
          expect(data.label).toBe('100%');
          data.data.forEach(function (cData) {
            expect(angular.isString(cData.label));
            expect(angular.isNumber(cData.value));
            expect(angular.isString(cData.color));
          });
        });
  
        it('includes the unit if it is provided', function () {
          var data = ctrl.getChartData('fakeTitle', 1, 2, 3, "MB");
          expect(data.unit).toBe('MB');
        });
  
        describe("watches", function () {
  
          beforeEach(function() {
            // Mock out calls made by the watch listeners to minimize
            // the amount of mock data needed to successfully trigger
            // a watch listener
            spyOn(ctrl, 'updateFlavorFacades').and.returnValue();
            spyOn(ctrl, 'validateFlavor').and.returnValue();
  
            // Initialize the watchers with default data
            scope.$apply();
  
            // Reset the spies now that we have initialized the watchers
            // so that tests don't see the calls made during setup
            ctrl.updateFlavorFacades.calls.reset();
            ctrl.validateFlavor.calls.reset();
          });
  
          it("establishes seven watches", function () {
            // Count calls to $watch (note: $watchCollection
            // also calls $watch)
            expect(scope.$watch.calls.count()).toBe(7);
          });
  
          it("establishes four watch collections", function () {
            expect(scope.$watchCollection.calls.count()).toBe(4);
          });
  
          describe("novaLimits watch", function () {
  
            it("sets the control novaLimits", function () {
              model.novaLimits = "new";
              scope.$apply();
              expect(ctrl.novaLimits).toBe('new');
            });
  
            it("calls updateFlavorFacades()", function () {
              model.novaLimits = "new";
              scope.$apply();
              expect(ctrl.updateFlavorFacades.calls.count()).toBe(1);
            });
          });
  
          describe("instance count watch", function () {
  
            it("sets the control instanceCount when 1 or more", function () {
              model.newInstanceSpec.instance_count = 5;
              scope.$apply();
              expect(ctrl.instanceCount).toBe(5);
            });
  
            it("sets the control instanceCount to 1 when less than 1", function () {
              model.newInstanceSpec.instance_count = 0;
              scope.$apply();
              expect(ctrl.instanceCount).toBe(1);
            });
  
            it("does nothing when new value is not defined", function () {
              delete model.newInstanceSpec.instance_count;
              scope.$apply();
              expect(ctrl.instanceCount).toBe(1);
              expect(ctrl.updateFlavorFacades).not.toHaveBeenCalled();
              expect(ctrl.validateFlavor).not.toHaveBeenCalled();
            });
  
            it("calls updateFlavorFacades()", function () {
              model.newInstanceSpec.instance_count = 5;
              scope.$apply();
              expect(ctrl.updateFlavorFacades.calls.count()).toBe(1);
            });
  
            it("calls validateFlavor()", function () {
              model.newInstanceSpec.instance_count = 5;
              scope.$apply();
              expect(ctrl.validateFlavor.calls.count()).toBe(1);
            });
          });
  
          describe("model.flavors", function () {
  
            it("sets the control flavors", function () {
              model.flavors = "new";
              scope.$apply();
              expect(ctrl.flavors).toBe("new");
            });
  
            it("calls updateFlavorFacades", function () {
              model.flavors = "new";
              scope.$apply();
              expect(ctrl.updateFlavorFacades.calls.count()).toBe(1);
            });
          });
  
          describe("selectFlavorCtrl.allocatedFlavorFacades", function () {
  
            it("deletes flavor if falsy facade", function () {
              model.newInstanceSpec.flavor = "to be removed";
              ctrl.allocatedFlavorFacades = false;
              scope.$apply();
              expect(model.newInstanceSpec.flavor).not.toBeDefined();
            });
  
            it("deletes flavor if empty facade", function () {
              // First set a non-empty allocated facade
              ctrl.allocatedFlavorFacades = [{flavor: "non-empty", enabled: "true"}];
              scope.$apply();
              expect(model.newInstanceSpec.flavor).toBe("non-empty");
  
              // Now set the empty allocated facade and prove that the flavor is
              // cleaned up.
              model.newInstanceSpec.flavor = "to be removed";
              ctrl.allocatedFlavorFacades = [];
              scope.$apply();
              expect(model.newInstanceSpec.flavor).not.toBeDefined();
            });
  
            it("sets the model's flavor", function () {
              ctrl.allocatedFlavorFacades = [{flavor: "raspberry", enabled: "true"}];
              scope.$apply();
              expect(model.newInstanceSpec.flavor).toEqual("raspberry");
            });
  
            it("calls validateFlavor", function () {
              ctrl.allocatedFlavorFacades = [{flavor: "non-empty", enabled: "true"}];
              scope.$apply();
              expect(ctrl.validateFlavor.calls.count()).toBe(1);
            });
          });
  
          describe("instance spec source changes", function () {
  
            it("sets the source", function () {
              model.newInstanceSpec.source = ["new"];
              scope.$apply();
              expect(ctrl.source).toBe("new");
            });
  
            it("sets the source to null if not provided", function () {
              model.newInstanceSpec.source = ["new"];
              scope.$apply();
              expect(ctrl.source).toBe("new");
  
              delete model.newInstanceSpec.source;
              scope.$apply();
              expect(ctrl.source).toBeNull();
            });
  
            it("sets the source to null if not an array", function () {
              model.newInstanceSpec.source = ["new"];
              scope.$apply();
              expect(ctrl.source).toBe("new");
  
              model.newInstanceSpec.source = 1;
              scope.$apply();
              expect(ctrl.source).toBeNull();
            });
  
            it("calls updateFlavorFacades", function () {
              model.newInstanceSpec.source = ["new"];
              scope.$apply();
              expect(ctrl.updateFlavorFacades.calls.count()).toBe(1);
            });
  
            it("calls validateFlavor", function () {
              model.newInstanceSpec.source = ["new"];
              scope.$apply();
              expect(ctrl.validateFlavor.calls.count()).toBe(1);
            });
          });
  
          describe("cinderLimits watcher", function () {
  
            it("initializes cinderLimits", function () {
              expect(ctrl.cinderLimits).toEqual({});
            });
  
            it("should call updateFlavorFacades", function () {
              model.cinderLimits = {test: "test"};
              scope.$apply();
              expect(ctrl.cinderLimits).toEqual({test: "test"});
              expect(ctrl.updateFlavorFacades.calls.count()).toBe(1);
            });
          });
  
          describe("volume size watcher", function () {
  
            it("should call updateFlavorFacades when source type is changed", function () {
              model.newInstanceSpec.source_type = "image";
              scope.$apply();
              expect(ctrl.updateFlavorFacades.calls.count()).toBe(1);
            });
  
            it("should call updateFlavorFacades when volume size is changed", function () {
              model.newInstanceSpec.vol_size = 10;
              scope.$apply();
              expect(ctrl.updateFlavorFacades.calls.count()).toBe(1);
            });
  
            it("should call updateFlavorFacades when volume create is changed", function () {
              model.newInstanceSpec.vol_create = true;
              scope.$apply();
              expect(ctrl.updateFlavorFacades.calls.count()).toBe(1);
            });
          });
        });
  
        describe("when having allocated flavors", function () {
          beforeEach(function () {
            ctrl.allocatedFlavorFacades = [{enabled: true}];
            scope.launchInstanceFlavorForm['allocated-flavor'] = { $setValidity: function () {} };
            spyOn(scope.launchInstanceFlavorForm['allocated-flavor'], "$setValidity");
          });
          describe("and you call #validateFlavor", function () {
            it("marks the flavor as validated", function () {
              ctrl.validateFlavor();
              expect(scope.launchInstanceFlavorForm['allocated-flavor']
                .$setValidity).toHaveBeenCalledWith('flavor', true);
            });
          });
        });
  
        describe("when not having allocated flavors", function () {
          beforeEach(function () {
            scope.launchInstanceFlavorForm['allocated-flavor'] = { $setValidity: function () {} };
            spyOn(scope.launchInstanceFlavorForm['allocated-flavor'], "$setValidity");
          });
          describe("and you call #validateFlavor", function () {
            it("does not mark the flavor validated", function () {
              ctrl.validateFlavor();
              expect(scope.launchInstanceFlavorForm['allocated-flavor']
                .$setValidity).not.toHaveBeenCalled();
            });
          });
        });
  
        describe("when having flavors defined", function () {
          beforeEach(function () {
            ctrl.flavors = [{name: "flava"}];
          });
          describe("and #buildFlavorFacades", function () {
            it("creates one facade for each flavor entry", function () {
              ctrl.buildFlavorFacades();
              expect(ctrl.availableFlavorFacades.length).toBe(1);
            });
          });
        });
  
        describe("when having no flavors defined", function () {
          describe("and #buildFlavorFacades", function () {
            it("creates no facades", function () {
              ctrl.buildFlavorFacades();
              expect(ctrl.availableFlavorFacades.length).toBe(0);
            });
          });
        });
  
        describe("when flavor definitions change", function () {
          beforeEach(function () {
            ctrl.flavors = [{name: "flava"}];
          });
          describe("and #updateFlavorFacades", function () {
            it("creates no facades", function () {
              ctrl.updateFlavorFacades();
              expect(ctrl.availableFlavorFacades.length).toBe(1);
            });
          });
        });
  
        describe("when checking a flavor for errors", function () {
          beforeEach(function () {
            ctrl.flavors = [{
              name: "flava",
              vcpus: "1",
              ram: 50,
              disk: 50
            }];
            ctrl.source = { min_disk:100, min_ram:100};
            model.newInstanceSpec.source_type = {type: 'image'};
          });
          describe("and there are no cpus available", function () {
            it("presents a vcpu error message", function () {
              expect(ctrl.getErrors(ctrl.flavors[0]).vcpus).toBeDefined();
            });
          });
          describe("and there is not sufficient ram available", function () {
            it("presents a ram error message", function () {
              expect(ctrl.getErrors(ctrl.flavors[0]).ram).toBeDefined();
            });
          });
          describe("and the image source requires more disk available", function () {
            it("presents a disk error message", function () {
              expect(ctrl.getErrors(ctrl.flavors[0]).disk).toBeDefined();
            });
          });
  
          describe("when min disk is less than zero", function () {
            beforeEach(function () {
              ctrl.flavors = [{
                name: "flava",
                vcpus: "1",
                ram: 0, // Modified so doesn't trip other error messages.
                disk: 50
              }];
              ctrl.source = { min_disk:-1, min_ram:-1};
            });
  
            it("doesn't create disk errors", function () {
              expect(ctrl.getErrors(ctrl.flavors[0]).disk).not.toBeDefined();
            });
  
            it("doesn't create ram errors", function () {
              expect(ctrl.getErrors(ctrl.flavors[0]).ram).not.toBeDefined();
            });
          });
  
          describe("when flavor disk has value of zero", function () {
            beforeEach(function () {
              ctrl.flavors = [{
                name: "flava",
                vcpus: "1",
                ram: 100,
                disk: 0
              }];
              ctrl.source = { min_disk:100, min_ram:100};
              model.newInstanceSpec.source_type = {type: 'volume'};
            });
  
            it("doesn't create disk errors", function () {
              expect(ctrl.getErrors(ctrl.flavors[0]).disk).not.toBeDefined();
            });
          });
  
        });
  
        it('initializes empty facades', function () {
          expect(ctrl.availableFlavorFacades).toEqual([]);
          expect(ctrl.displayedAvailableFlavorFacades).toEqual([]);
          expect(ctrl.allocatedFlavorFacades).toEqual([]);
          expect(ctrl.displayedAllocatedFlavorFacades).toEqual([]);
        });
  
        it('initializes empty flavors', function () {
          expect(ctrl.flavors).toEqual([]);
        });
  
        it('initializes nova limits', function () {
          expect(ctrl.novaLimits).toEqual({});
        });
  
        it('initializes instance count to 1', function () {
          expect(ctrl.instanceCount).toBe(1);
        });
  
        it('initializes transfer table model', function () {
          expect(ctrl.transferTableModel).toBeDefined();
          var mod = ctrl.transferTableModel;
          expect(mod.allocated).toBe(ctrl.allocatedFlavorFacades);
          expect(mod.displayedAllocated).toBe(ctrl.displayedAllocatedFlavorFacades);
          expect(mod.available).toBe(ctrl.availableFlavorFacades);
          expect(mod.displayedAvailable).toBe(ctrl.displayedAvailableFlavorFacades);
          expect(Object.keys(mod).length).toBe(4);
        });
  
        it('initializes chart data', function () {
          expect(ctrl.instancesChartData).toEqual({});
        });
  
        it('allows only one allocation', function () {
          expect(ctrl.allocationLimits).toBeDefined();
          expect(ctrl.allocationLimits.maxAllocation).toBe(1);
        });
  
        describe('Functions', function () {
          describe('defaultIfUndefined', function () {
  
            it('returns the given default if value is undefined', function () {
              /*eslint-disable no-undefined */
              expect(ctrl.defaultIfUndefined(undefined, 'defValue')).toBe('defValue');
              /*eslint-enable no-undefined */
            });
  
            it('returns the value if defined', function () {
              expect(ctrl.defaultIfUndefined('myVal', 'defValue')).toBe('myVal');
            });
          });
        });
  
        describe("test updateFlavorFacades", function () {
  
          beforeEach(function () {
            ctrl.flavors = [{name: "tiny"}];
          });
  
          it("should set volumeChartData and volumeStorageChartData", function () {
            ctrl.updateFlavorFacades();
            expect(ctrl.availableFlavorFacades.length).toBe(1);
            expect(ctrl.availableFlavorFacades[0].volumeChartData).toBeDefined();
            expect(ctrl.availableFlavorFacades[0].volumeStorageChartData).toBeDefined();
          });
  
          it("should call getChartData", function() {
            spyOn(ctrl, 'getChartData');
            ctrl.updateFlavorFacades();
            expect(ctrl.getChartData.calls.count()).toBe(5);
          });
        });
  
        describe("test validateFlavor", function () {
  
          it("should call validateFlavor when source type is changed", function () {
            spyOn(ctrl, 'validateFlavor');
            model.newInstanceSpec.source_type = "image";
            scope.$apply();
            expect(ctrl.validateFlavor.calls.count()).toBe(1);
          });
        });
  
      });
  
    });
  
  })();
  