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