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