github.com/ferranbt/nomad@v0.9.3-0.20190607002617-85c449b7667c/ui/tests/acceptance/job-deployments-test.js (about)

     1  import { currentURL } from '@ember/test-helpers';
     2  import { get } from '@ember/object';
     3  import { module, test } from 'qunit';
     4  import { setupApplicationTest } from 'ember-qunit';
     5  import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
     6  import moment from 'moment';
     7  import Deployments from 'nomad-ui/tests/pages/jobs/job/deployments';
     8  
     9  const sum = (list, key, getter = a => a) =>
    10    list.reduce((sum, item) => sum + getter(get(item, key)), 0);
    11  
    12  let job;
    13  let deployments;
    14  let sortedDeployments;
    15  
    16  module('Acceptance | job deployments', function(hooks) {
    17    setupApplicationTest(hooks);
    18    setupMirage(hooks);
    19  
    20    hooks.beforeEach(function() {
    21      server.create('node');
    22      job = server.create('job');
    23      deployments = server.schema.deployments.where({ jobId: job.id });
    24      sortedDeployments = deployments.sort((a, b) => {
    25        const aVersion = server.db.jobVersions.findBy({ jobId: a.jobId, version: a.versionNumber });
    26        const bVersion = server.db.jobVersions.findBy({ jobId: b.jobId, version: b.versionNumber });
    27        if (aVersion.submitTime < bVersion.submitTime) {
    28          return 1;
    29        } else if (aVersion.submitTime > bVersion.submitTime) {
    30          return -1;
    31        }
    32        return 0;
    33      });
    34    });
    35  
    36    test('/jobs/:id/deployments should list all job deployments', async function(assert) {
    37      await Deployments.visit({ id: job.id });
    38  
    39      assert.ok(
    40        Deployments.deployments.length,
    41        deployments.length,
    42        'Each deployment gets a row in the timeline'
    43      );
    44    });
    45  
    46    test('each deployment mentions the deployment shortId, status, version, and time since it was submitted', async function(assert) {
    47      await Deployments.visit({ id: job.id });
    48  
    49      const deployment = sortedDeployments.models[0];
    50      const version = server.db.jobVersions.findBy({
    51        jobId: deployment.jobId,
    52        version: deployment.versionNumber,
    53      });
    54      const deploymentRow = Deployments.deployments.objectAt(0);
    55  
    56      assert.ok(deploymentRow.text.includes(deployment.id.split('-')[0]), 'Short ID');
    57      assert.equal(deploymentRow.status, deployment.status, 'Status');
    58      assert.ok(
    59        deploymentRow.statusClass.includes(classForStatus(deployment.status)),
    60        'Status Class'
    61      );
    62      assert.ok(deploymentRow.version.includes(deployment.versionNumber), 'Version #');
    63      assert.ok(
    64        deploymentRow.submitTime.includes(moment(version.submitTime / 1000000).fromNow()),
    65        'Submit time ago'
    66      );
    67    });
    68  
    69    test('when the deployment is running and needs promotion, the deployment item says so', async function(assert) {
    70      // Ensure the deployment needs deployment
    71      const deployment = sortedDeployments.models[0];
    72      const taskGroupSummary = deployment.deploymentTaskGroupSummaryIds.map(id =>
    73        server.schema.deploymentTaskGroupSummaries.find(id)
    74      )[0];
    75  
    76      deployment.update('status', 'running');
    77      deployment.save();
    78  
    79      taskGroupSummary.update({
    80        desiredCanaries: 1,
    81        placedCanaries: [],
    82        promoted: false,
    83      });
    84  
    85      taskGroupSummary.save();
    86  
    87      await Deployments.visit({ id: job.id });
    88  
    89      const deploymentRow = Deployments.deployments.objectAt(0);
    90      assert.ok(deploymentRow.promotionIsRequired, 'Requires Promotion badge found');
    91    });
    92  
    93    test('each deployment item can be opened to show details', async function(assert) {
    94      await Deployments.visit({ id: job.id });
    95  
    96      const deploymentRow = Deployments.deployments.objectAt(0);
    97      assert.notOk(deploymentRow.hasDetails, 'No deployment body');
    98  
    99      await deploymentRow.toggle();
   100      assert.ok(deploymentRow.hasDetails, 'Deployment body found');
   101    });
   102  
   103    test('when open, a deployment shows the deployment metrics', async function(assert) {
   104      await Deployments.visit({ id: job.id });
   105  
   106      const deployment = sortedDeployments.models[0];
   107      const deploymentRow = Deployments.deployments.objectAt(0);
   108      const taskGroupSummaries = deployment.deploymentTaskGroupSummaryIds.map(id =>
   109        server.db.deploymentTaskGroupSummaries.find(id)
   110      );
   111  
   112      await deploymentRow.toggle();
   113  
   114      assert.equal(
   115        deploymentRow.metricFor('canaries').text,
   116        `${sum(taskGroupSummaries, 'placedCanaries', a => a.length)} / ${sum(
   117          taskGroupSummaries,
   118          'desiredCanaries'
   119        )}`,
   120        'Canaries, both places and desired, are in the metrics'
   121      );
   122  
   123      assert.equal(
   124        deploymentRow.metricFor('placed').text,
   125        sum(taskGroupSummaries, 'placedAllocs'),
   126        'Placed allocs aggregates across task groups'
   127      );
   128  
   129      assert.equal(
   130        deploymentRow.metricFor('desired').text,
   131        sum(taskGroupSummaries, 'desiredTotal'),
   132        'Desired allocs aggregates across task groups'
   133      );
   134  
   135      assert.equal(
   136        deploymentRow.metricFor('healthy').text,
   137        sum(taskGroupSummaries, 'healthyAllocs'),
   138        'Healthy allocs aggregates across task groups'
   139      );
   140  
   141      assert.equal(
   142        deploymentRow.metricFor('unhealthy').text,
   143        sum(taskGroupSummaries, 'unhealthyAllocs'),
   144        'Unhealthy allocs aggregates across task groups'
   145      );
   146  
   147      assert.equal(
   148        deploymentRow.notification,
   149        deployment.statusDescription,
   150        'Status description is in the metrics block'
   151      );
   152    });
   153  
   154    test('when open, a deployment shows a list of all task groups and their respective stats', async function(assert) {
   155      await Deployments.visit({ id: job.id });
   156  
   157      const deployment = sortedDeployments.models[0];
   158      const deploymentRow = Deployments.deployments.objectAt(0);
   159      const taskGroupSummaries = deployment.deploymentTaskGroupSummaryIds.map(id =>
   160        server.db.deploymentTaskGroupSummaries.find(id)
   161      );
   162  
   163      await deploymentRow.toggle();
   164  
   165      assert.ok(deploymentRow.hasTaskGroups, 'Task groups found');
   166  
   167      assert.equal(
   168        deploymentRow.taskGroups.length,
   169        taskGroupSummaries.length,
   170        'One row per task group'
   171      );
   172  
   173      const taskGroup = taskGroupSummaries[0];
   174      const taskGroupRow = deploymentRow.taskGroups.objectAt(0);
   175  
   176      assert.equal(taskGroupRow.name, taskGroup.name, 'Name');
   177      assert.equal(taskGroupRow.promotion, promotionTestForTaskGroup(taskGroup), 'Needs Promotion');
   178      assert.equal(taskGroupRow.autoRevert, taskGroup.autoRevert ? 'Yes' : 'No', 'Auto Revert');
   179      assert.equal(
   180        taskGroupRow.canaries,
   181        `${taskGroup.placedCanaries.length} / ${taskGroup.desiredCanaries}`,
   182        'Canaries'
   183      );
   184      assert.equal(
   185        taskGroupRow.allocs,
   186        `${taskGroup.placedAllocs} / ${taskGroup.desiredTotal}`,
   187        'Allocs'
   188      );
   189      assert.equal(taskGroupRow.healthy, taskGroup.healthyAllocs, 'Healthy Allocs');
   190      assert.equal(taskGroupRow.unhealthy, taskGroup.unhealthyAllocs, 'Unhealthy Allocs');
   191      assert.equal(
   192        taskGroupRow.progress,
   193        moment(taskGroup.requireProgressBy).format("MMM DD, 'YY HH:mm:ss ZZ"),
   194        'Progress By'
   195      );
   196    });
   197  
   198    test('when open, a deployment shows a list of all allocations for the deployment', async function(assert) {
   199      await Deployments.visit({ id: job.id });
   200  
   201      const deployment = sortedDeployments.models[0];
   202      const deploymentRow = Deployments.deployments.objectAt(0);
   203  
   204      // TODO: Make this less brittle. This logic is copied from the mirage config,
   205      // since there is no reference to allocations on the deployment model.
   206      const allocations = server.db.allocations.where({ jobId: deployment.jobId }).slice(0, 3);
   207      await deploymentRow.toggle();
   208  
   209      assert.ok(deploymentRow.hasAllocations, 'Allocations found');
   210      assert.equal(deploymentRow.allocations.length, allocations.length, 'One row per allocation');
   211  
   212      const allocation = allocations[0];
   213      const allocationRow = deploymentRow.allocations.objectAt(0);
   214  
   215      assert.equal(allocationRow.shortId, allocation.id.split('-')[0], 'Allocation is as expected');
   216    });
   217  
   218    test('when the job for the deployments is not found, an error message is shown, but the URL persists', async function(assert) {
   219      await Deployments.visit({ id: 'not-a-real-job' });
   220  
   221      assert.equal(
   222        server.pretender.handledRequests.findBy('status', 404).url,
   223        '/v1/job/not-a-real-job',
   224        'A request to the nonexistent job is made'
   225      );
   226      assert.equal(currentURL(), '/jobs/not-a-real-job/deployments', 'The URL persists');
   227      assert.ok(Deployments.error.isPresent, 'Error message is shown');
   228      assert.equal(Deployments.error.title, 'Not Found', 'Error message is for 404');
   229    });
   230  
   231    function classForStatus(status) {
   232      const classMap = {
   233        running: 'is-running',
   234        successful: 'is-primary',
   235        paused: 'is-light',
   236        failed: 'is-error',
   237        cancelled: 'is-cancelled',
   238      };
   239  
   240      return classMap[status] || 'is-dark';
   241    }
   242  
   243    function promotionTestForTaskGroup(taskGroup) {
   244      if (taskGroup.desiredCanaries > 0 && taskGroup.promoted === false) {
   245        return 'Yes';
   246      } else if (taskGroup.desiredCanaries > 0) {
   247        return 'No';
   248      }
   249      return 'N/A';
   250    }
   251  });