github.com/hspak/nomad@v0.7.2-0.20180309000617-bc4ae22a39a5/ui/tests/acceptance/job-deployments-test.js (about)

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