github.com/zoomfoo/nomad@v0.8.5-0.20180907175415-f28fd3a1a056/ui/tests/acceptance/job-deployments-test.js (about)

     1  import { get } from '@ember/object';
     2  import { test } from 'qunit';
     3  import moment from 'moment';
     4  import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance';
     5  import Deployments from 'nomad-ui/tests/pages/jobs/job/deployments';
     6  
     7  const sum = (list, key, getter = a => a) =>
     8    list.reduce((sum, item) => sum + getter(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    Deployments.visit({ id: job.id });
    34  
    35    andThen(() => {
    36      assert.ok(
    37        Deployments.deployments.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    Deployments.visit({ id: job.id });
    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 = Deployments.deployments.objectAt(0);
    54  
    55      assert.ok(deploymentRow.text.includes(deployment.id.split('-')[0]), 'Short ID');
    56      assert.equal(deploymentRow.status, deployment.status, 'Status');
    57      assert.ok(
    58        deploymentRow.statusClass.includes(classForStatus(deployment.status)),
    59        'Status Class'
    60      );
    61      assert.ok(deploymentRow.version.includes(deployment.versionNumber), 'Version #');
    62      assert.ok(
    63        deploymentRow.submitTime.includes(moment(version.submitTime / 1000000).fromNow()),
    64        'Submit time ago'
    65      );
    66    });
    67  });
    68  
    69  test('when the deployment is running and needs promotion, the deployment item says so', 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    Deployments.visit({ id: job.id });
    88  
    89    andThen(() => {
    90      const deploymentRow = Deployments.deployments.objectAt(0);
    91      assert.ok(deploymentRow.promotionIsRequired, 'Requires Promotion badge found');
    92    });
    93  });
    94  
    95  test('each deployment item can be opened to show details', function(assert) {
    96    Deployments.visit({ id: job.id });
    97  
    98    andThen(() => {
    99      const deploymentRow = Deployments.deployments.objectAt(0);
   100  
   101      assert.notOk(deploymentRow.hasDetails, 'No deployment body');
   102  
   103      deploymentRow.toggle();
   104  
   105      andThen(() => {
   106        assert.ok(deploymentRow.hasDetails, 'Deployment body found');
   107      });
   108    });
   109  });
   110  
   111  test('when open, a deployment shows the deployment metrics', function(assert) {
   112    Deployments.visit({ id: job.id });
   113  
   114    andThen(() => {
   115      const deployment = sortedDeployments.models[0];
   116      const deploymentRow = Deployments.deployments.objectAt(0);
   117      const taskGroupSummaries = deployment.deploymentTaskGroupSummaryIds.map(id =>
   118        server.db.deploymentTaskGroupSummaries.find(id)
   119      );
   120  
   121      deploymentRow.toggle();
   122  
   123      andThen(() => {
   124        assert.equal(
   125          deploymentRow.metricFor('canaries').text,
   126          `${sum(taskGroupSummaries, 'placedCanaries', a => a.length)} / ${sum(
   127            taskGroupSummaries,
   128            'desiredCanaries'
   129          )}`,
   130          'Canaries, both places and desired, are in the metrics'
   131        );
   132  
   133        assert.equal(
   134          deploymentRow.metricFor('placed').text,
   135          sum(taskGroupSummaries, 'placedAllocs'),
   136          'Placed allocs aggregates across task groups'
   137        );
   138  
   139        assert.equal(
   140          deploymentRow.metricFor('desired').text,
   141          sum(taskGroupSummaries, 'desiredTotal'),
   142          'Desired allocs aggregates across task groups'
   143        );
   144  
   145        assert.equal(
   146          deploymentRow.metricFor('healthy').text,
   147          sum(taskGroupSummaries, 'healthyAllocs'),
   148          'Healthy allocs aggregates across task groups'
   149        );
   150  
   151        assert.equal(
   152          deploymentRow.metricFor('unhealthy').text,
   153          sum(taskGroupSummaries, 'unhealthyAllocs'),
   154          'Unhealthy allocs aggregates across task groups'
   155        );
   156  
   157        assert.equal(
   158          deploymentRow.notification,
   159          deployment.statusDescription,
   160          'Status description is in the metrics block'
   161        );
   162      });
   163    });
   164  });
   165  
   166  test('when open, a deployment shows a list of all task groups and their respective stats', function(assert) {
   167    Deployments.visit({ id: job.id });
   168  
   169    andThen(() => {
   170      const deployment = sortedDeployments.models[0];
   171      const deploymentRow = Deployments.deployments.objectAt(0);
   172      const taskGroupSummaries = deployment.deploymentTaskGroupSummaryIds.map(id =>
   173        server.db.deploymentTaskGroupSummaries.find(id)
   174      );
   175  
   176      deploymentRow.toggle();
   177  
   178      andThen(() => {
   179        assert.ok(deploymentRow.hasTaskGroups, 'Task groups found');
   180  
   181        assert.equal(
   182          deploymentRow.taskGroups.length,
   183          taskGroupSummaries.length,
   184          'One row per task group'
   185        );
   186  
   187        const taskGroup = taskGroupSummaries[0];
   188        const taskGroupRow = deploymentRow.taskGroups.objectAt(0);
   189  
   190        assert.equal(taskGroupRow.name, taskGroup.name, 'Name');
   191        assert.equal(taskGroupRow.promotion, promotionTestForTaskGroup(taskGroup), 'Needs Promotion');
   192        assert.equal(taskGroupRow.autoRevert, taskGroup.autoRevert ? 'Yes' : 'No', 'Auto Revert');
   193        assert.equal(
   194          taskGroupRow.canaries,
   195          `${taskGroup.placedCanaries.length} / ${taskGroup.desiredCanaries}`,
   196          'Canaries'
   197        );
   198        assert.equal(
   199          taskGroupRow.allocs,
   200          `${taskGroup.placedAllocs} / ${taskGroup.desiredTotal}`,
   201          'Allocs'
   202        );
   203        assert.equal(taskGroupRow.healthy, taskGroup.healthyAllocs, 'Healthy Allocs');
   204        assert.equal(taskGroupRow.unhealthy, taskGroup.unhealthyAllocs, 'Unhealthy Allocs');
   205        assert.equal(
   206          taskGroupRow.progress,
   207          moment(taskGroup.requireProgressBy).format('MM/DD/YY HH:mm:ss'),
   208          'Progress By'
   209        );
   210      });
   211    });
   212  });
   213  
   214  test('when open, a deployment shows a list of all allocations for the deployment', function(assert) {
   215    Deployments.visit({ id: job.id });
   216  
   217    andThen(() => {
   218      const deployment = sortedDeployments.models[0];
   219      const deploymentRow = Deployments.deployments.objectAt(0);
   220  
   221      // TODO: Make this less brittle. This logic is copied from the mirage config,
   222      // since there is no reference to allocations on the deployment model.
   223      const allocations = server.db.allocations.where({ jobId: deployment.jobId }).slice(0, 3);
   224  
   225      deploymentRow.toggle();
   226  
   227      andThen(() => {
   228        assert.ok(deploymentRow.hasAllocations, 'Allocations found');
   229  
   230        assert.equal(deploymentRow.allocations.length, allocations.length, 'One row per allocation');
   231  
   232        const allocation = allocations[0];
   233        const allocationRow = deploymentRow.allocations.objectAt(0);
   234  
   235        assert.equal(allocationRow.shortId, allocation.id.split('-')[0], 'Allocation is as expected');
   236      });
   237    });
   238  });
   239  
   240  function classForStatus(status) {
   241    const classMap = {
   242      running: 'is-running',
   243      successful: 'is-primary',
   244      paused: 'is-light',
   245      failed: 'is-error',
   246      cancelled: 'is-cancelled',
   247    };
   248  
   249    return classMap[status] || 'is-dark';
   250  }
   251  
   252  function promotionTestForTaskGroup(taskGroup) {
   253    if (taskGroup.desiredCanaries > 0 && taskGroup.promoted === false) {
   254      return 'Yes';
   255    } else if (taskGroup.desiredCanaries > 0) {
   256      return 'No';
   257    }
   258    return 'N/A';
   259  }