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