github.com/blixtra/nomad@v0.7.2-0.20171221000451-da9a1d7bb050/ui/tests/acceptance/job-deployments-test.js (about)

     1  import { click, findAll, find, visit } from 'ember-native-dom-helpers';
     2  import Ember from 'ember';
     3  import { test } from 'qunit';
     4  import moment from 'moment';
     5  import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance';
     6  
     7  const { get, $ } = Ember;
     8  const sum = (list, key) => list.reduce((sum, item) => sum + get(item, key), 0);
     9  
    10  let job;
    11  let deployments;
    12  let sortedDeployments;
    13  
    14  moduleForAcceptance('Acceptance | job deployments', {
    15    beforeEach() {
    16      server.create('node');
    17      job = server.create('job');
    18      deployments = server.schema.deployments.where({ jobId: job.id });
    19      sortedDeployments = deployments.sort((a, b) => {
    20        const aVersion = server.db.jobVersions.findBy({ jobId: a.jobId, version: a.versionNumber });
    21        const bVersion = server.db.jobVersions.findBy({ jobId: b.jobId, version: b.versionNumber });
    22        if (aVersion.submitTime < bVersion.submitTime) {
    23          return 1;
    24        } else if (aVersion.submitTime > bVersion.submitTime) {
    25          return -1;
    26        }
    27        return 0;
    28      });
    29    },
    30  });
    31  
    32  test('/jobs/:id/deployments should list all job deployments', function(assert) {
    33    visit(`/jobs/${job.id}/deployments`);
    34    andThen(() => {
    35      assert.ok(
    36        findAll('.timeline-object').length,
    37        deployments.length,
    38        'Each deployment gets a row in the timeline'
    39      );
    40    });
    41  });
    42  
    43  test('each deployment mentions the deployment shortId, status, version, and time since it was submitted', function(
    44    assert
    45  ) {
    46    visit(`/jobs/${job.id}/deployments`);
    47  
    48    andThen(() => {
    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 = $(findAll('.timeline-object')[0]);
    55  
    56      assert.ok(deploymentRow.text().includes(deployment.id.split('-')[0]), 'Short ID');
    57      assert.equal(deploymentRow.find('.tag').text(), deployment.status, 'Status');
    58      assert.ok(
    59        deploymentRow.find('.tag').hasClass(classForStatus(deployment.status)),
    60        'Status Class'
    61      );
    62      assert.ok(deploymentRow.text().includes(deployment.versionNumber), 'Version #');
    63      assert.ok(
    64        deploymentRow.text().includes(moment(version.submitTime / 1000000).fromNow()),
    65        'Submit time ago'
    66      );
    67    });
    68  });
    69  
    70  test('when the deployment is running and needs promotion, the deployment item says so', function(
    71    assert
    72  ) {
    73    // Ensure the deployment needs deployment
    74    const deployment = sortedDeployments.models[0];
    75    const taskGroupSummary = deployment.deploymentTaskGroupSummaryIds.map(id =>
    76      server.schema.deploymentTaskGroupSummaries.find(id)
    77    )[0];
    78  
    79    deployment.update('status', 'running');
    80    deployment.save();
    81  
    82    taskGroupSummary.update({
    83      desiredCanaries: 1,
    84      placedCanaries: 0,
    85      promoted: false,
    86    });
    87  
    88    taskGroupSummary.save();
    89  
    90    visit(`/jobs/${job.id}/deployments`);
    91  
    92    andThen(() => {
    93      const deploymentRow = $(findAll('.timeline-object')[0]);
    94      assert.ok(
    95        deploymentRow.find('.badge:contains("Requires Promotion")').length,
    96        'Requires Promotion badge found'
    97      );
    98    });
    99  });
   100  
   101  test('each deployment item can be opened to show details', function(assert) {
   102    let deploymentRow;
   103  
   104    visit(`/jobs/${job.id}/deployments`);
   105  
   106    andThen(() => {
   107      deploymentRow = $(findAll('.timeline-object')[0]);
   108  
   109      assert.ok(deploymentRow.find('.boxed-section-body').length === 0, 'No deployment body');
   110  
   111      click(deploymentRow.find('button').get(0));
   112  
   113      andThen(() => {
   114        assert.ok(deploymentRow.find('.boxed-section-body').length, 'Deployment body found');
   115      });
   116    });
   117  });
   118  
   119  test('when open, a deployment shows the deployment metrics', function(assert) {
   120    visit(`/jobs/${job.id}/deployments`);
   121  
   122    andThen(() => {
   123      const deployment = sortedDeployments.models[0];
   124      const deploymentRow = $(findAll('.timeline-object')[0]);
   125      const taskGroupSummaries = deployment.deploymentTaskGroupSummaryIds.map(id =>
   126        server.db.deploymentTaskGroupSummaries.find(id)
   127      );
   128  
   129      click(deploymentRow.find('button').get(0));
   130  
   131      andThen(() => {
   132        assert.equal(
   133          $('.deployment-metrics .label:contains("Canaries") + .value')
   134            .get(0)
   135            .textContent.trim(),
   136          `${sum(taskGroupSummaries, 'placedCanaries')} / ${sum(
   137            taskGroupSummaries,
   138            'desiredCanaries'
   139          )}`,
   140          'Canaries, both places and desired, are in the metrics'
   141        );
   142  
   143        assert.equal(
   144          $('.deployment-metrics .label:contains("Placed") + .value')
   145            .get(0)
   146            .textContent.trim(),
   147          sum(taskGroupSummaries, 'placedAllocs'),
   148          'Placed allocs aggregates across task groups'
   149        );
   150  
   151        assert.equal(
   152          $('.deployment-metrics .label:contains("Desired") + .value')
   153            .get(0)
   154            .textContent.trim(),
   155          sum(taskGroupSummaries, 'desiredTotal'),
   156          'Desired allocs aggregates across task groups'
   157        );
   158  
   159        assert.equal(
   160          $('.deployment-metrics .label:contains("Healthy") + .value')
   161            .get(0)
   162            .textContent.trim(),
   163          sum(taskGroupSummaries, 'healthyAllocs'),
   164          'Healthy allocs aggregates across task groups'
   165        );
   166  
   167        assert.equal(
   168          $('.deployment-metrics .label:contains("Unhealthy") + .value')
   169            .get(0)
   170            .textContent.trim(),
   171          sum(taskGroupSummaries, 'unhealthyAllocs'),
   172          'Unhealthy allocs aggregates across task groups'
   173        );
   174  
   175        assert.equal(
   176          find('.deployment-metrics .notification').textContent.trim(),
   177          deployment.statusDescription,
   178          'Status description is in the metrics block'
   179        );
   180      });
   181    });
   182  });
   183  
   184  test('when open, a deployment shows a list of all task groups and their respective stats', function(
   185    assert
   186  ) {
   187    visit(`/jobs/${job.id}/deployments`);
   188  
   189    andThen(() => {
   190      const deployment = sortedDeployments.models[0];
   191      const deploymentRow = $(findAll('.timeline-object')[0]);
   192      const taskGroupSummaries = deployment.deploymentTaskGroupSummaryIds.map(id =>
   193        server.db.deploymentTaskGroupSummaries.find(id)
   194      );
   195  
   196      click(deploymentRow.find('button').get(0));
   197  
   198      andThen(() => {
   199        assert.ok(
   200          deploymentRow.find('.boxed-section-head:contains("Task Groups")').length,
   201          'Task groups found'
   202        );
   203  
   204        const taskGroupTable = deploymentRow.find(
   205          '.boxed-section-head:contains("Task Groups") + .boxed-section-body tbody'
   206        );
   207  
   208        assert.equal(
   209          taskGroupTable.find('tr').length,
   210          taskGroupSummaries.length,
   211          'One row per task group'
   212        );
   213  
   214        const taskGroup = taskGroupSummaries[0];
   215        const taskGroupRow = taskGroupTable.find('tr:eq(0)');
   216  
   217        assert.equal(
   218          taskGroupRow
   219            .find('td:eq(0)')
   220            .text()
   221            .trim(),
   222          taskGroup.name,
   223          'Name'
   224        );
   225        assert.equal(
   226          taskGroupRow
   227            .find('td:eq(1)')
   228            .text()
   229            .trim(),
   230          promotionTestForTaskGroup(taskGroup),
   231          'Needs Promotion'
   232        );
   233        assert.equal(
   234          taskGroupRow
   235            .find('td:eq(2)')
   236            .text()
   237            .trim(),
   238          taskGroup.autoRevert ? 'Yes' : 'No',
   239          'Auto Revert'
   240        );
   241        assert.equal(
   242          taskGroupRow
   243            .find('td:eq(3)')
   244            .text()
   245            .trim(),
   246          `${taskGroup.placedCanaries} / ${taskGroup.desiredCanaries}`,
   247          'Canaries'
   248        );
   249        assert.equal(
   250          taskGroupRow
   251            .find('td:eq(4)')
   252            .text()
   253            .trim(),
   254          `${taskGroup.placedAllocs} / ${taskGroup.desiredTotal}`,
   255          'Allocs'
   256        );
   257        assert.equal(
   258          taskGroupRow
   259            .find('td:eq(5)')
   260            .text()
   261            .trim(),
   262          taskGroup.healthyAllocs,
   263          'Healthy Allocs'
   264        );
   265        assert.equal(
   266          taskGroupRow
   267            .find('td:eq(6)')
   268            .text()
   269            .trim(),
   270          taskGroup.unhealthyAllocs,
   271          'Unhealthy Allocs'
   272        );
   273      });
   274    });
   275  });
   276  
   277  test('when open, a deployment shows a list of all allocations for the deployment', function(
   278    assert
   279  ) {
   280    visit(`/jobs/${job.id}/deployments`);
   281  
   282    andThen(() => {
   283      const deployment = sortedDeployments.models[0];
   284      const deploymentRow = $(findAll('.timeline-object')[0]);
   285  
   286      // TODO: Make this less brittle. This logic is copied from the mirage config,
   287      // since there is no reference to allocations on the deployment model.
   288      const allocations = server.db.allocations.where({ jobId: deployment.jobId }).slice(0, 3);
   289  
   290      click(deploymentRow.find('button').get(0));
   291  
   292      andThen(() => {
   293        assert.ok(
   294          deploymentRow.find('.boxed-section-head:contains("Allocations")').length,
   295          'Allocations found'
   296        );
   297  
   298        const allocationsTable = deploymentRow.find(
   299          '.boxed-section-head:contains("Allocations") + .boxed-section-body tbody'
   300        );
   301  
   302        assert.equal(
   303          allocationsTable.find('tr').length,
   304          allocations.length,
   305          'One row per allocation'
   306        );
   307  
   308        const allocation = allocations[0];
   309        const allocationRow = allocationsTable.find('tr:eq(0)');
   310  
   311        assert.equal(
   312          allocationRow
   313            .find('td:eq(0)')
   314            .text()
   315            .trim(),
   316          allocation.id.split('-')[0],
   317          'Allocation is as expected'
   318        );
   319      });
   320    });
   321  });
   322  
   323  function classForStatus(status) {
   324    const classMap = {
   325      running: 'is-running',
   326      successful: 'is-primary',
   327      paused: 'is-light',
   328      failed: 'is-error',
   329      cancelled: 'is-cancelled',
   330    };
   331  
   332    return classMap[status] || 'is-dark';
   333  }
   334  
   335  function promotionTestForTaskGroup(taskGroup) {
   336    if (taskGroup.desiredCanaries > 0 && taskGroup.promoted === false) {
   337      return 'Yes';
   338    } else if (taskGroup.desiredCanaries > 0) {
   339      return 'No';
   340    }
   341    return 'N/A';
   342  }