github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/ui/tests/acceptance/task-detail-test.js (about)

     1  import { currentURL } from '@ember/test-helpers';
     2  import { module, test } from 'qunit';
     3  import { setupApplicationTest } from 'ember-qunit';
     4  import { setupMirage } from 'ember-cli-mirage/test-support';
     5  import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
     6  import Task from 'nomad-ui/tests/pages/allocations/task/detail';
     7  import Layout from 'nomad-ui/tests/pages/layout';
     8  import moment from 'moment';
     9  
    10  let allocation;
    11  let task;
    12  
    13  module('Acceptance | task detail', function(hooks) {
    14    setupApplicationTest(hooks);
    15    setupMirage(hooks);
    16  
    17    hooks.beforeEach(async function() {
    18      server.create('agent');
    19      server.create('node');
    20      server.create('job', { createAllocations: false });
    21      allocation = server.create('allocation', 'withTaskWithPorts', { clientStatus: 'running' });
    22      task = server.db.taskStates.where({ allocationId: allocation.id })[0];
    23  
    24      await Task.visit({ id: allocation.id, name: task.name });
    25    });
    26  
    27    test('it passes an accessibility audit', async function(assert) {
    28      await a11yAudit(assert);
    29    });
    30  
    31    test('/allocation/:id/:task_name should name the task and list high-level task information', async function(assert) {
    32      assert.ok(Task.title.text.includes(task.name), 'Task name');
    33      assert.ok(Task.state.includes(task.state), 'Task state');
    34  
    35      assert.ok(
    36        Task.startedAt.includes(moment(task.startedAt).format("MMM DD, 'YY HH:mm:ss ZZ")),
    37        'Task started at'
    38      );
    39  
    40      const lifecycle = server.db.tasks.where({ name: task.name })[0].Lifecycle;
    41  
    42      let lifecycleName = 'main';
    43      if (lifecycle && (lifecycle.Hook === 'prestart' || lifecycle.Hook === 'poststart')) {
    44        lifecycleName = `${lifecycle.Hook}-${lifecycle.Sidecar ? 'sidecar' : 'ephemeral'}`;
    45      }
    46      if (lifecycle && lifecycle.Hook === 'poststop') {
    47        lifecycleName = 'poststop';
    48      }
    49  
    50      assert.equal(Task.lifecycle, lifecycleName);
    51  
    52      assert.equal(document.title, `Task ${task.name} - Nomad`);
    53    });
    54  
    55    test('breadcrumbs match jobs / job / task group / allocation / task', async function(assert) {
    56      const { jobId, taskGroup } = allocation;
    57      const job = server.db.jobs.find(jobId);
    58  
    59      const shortId = allocation.id.split('-')[0];
    60  
    61      assert.equal(Layout.breadcrumbFor('jobs.index').text, 'Jobs', 'Jobs is the first breadcrumb');
    62      assert.equal(
    63        Layout.breadcrumbFor('jobs.job.index').text,
    64        job.name,
    65        'Job is the second breadcrumb'
    66      );
    67      assert.equal(
    68        Layout.breadcrumbFor('jobs.job.task-group').text,
    69        taskGroup,
    70        'Task Group is the third breadcrumb'
    71      );
    72      assert.equal(
    73        Layout.breadcrumbFor('allocations.allocation').text,
    74        shortId,
    75        'Allocation short id is the fourth breadcrumb'
    76      );
    77      assert.equal(
    78        Layout.breadcrumbFor('allocations.allocation.task').text,
    79        task.name,
    80        'Task name is the fifth breadcrumb'
    81      );
    82  
    83      await Layout.breadcrumbFor('jobs.index').visit();
    84      assert.equal(currentURL(), '/jobs', 'Jobs breadcrumb links correctly');
    85  
    86      await Task.visit({ id: allocation.id, name: task.name });
    87      await Layout.breadcrumbFor('jobs.job.index').visit();
    88      assert.equal(currentURL(), `/jobs/${job.id}`, 'Job breadcrumb links correctly');
    89  
    90      await Task.visit({ id: allocation.id, name: task.name });
    91      await Layout.breadcrumbFor('jobs.job.task-group').visit();
    92      assert.equal(
    93        currentURL(),
    94        `/jobs/${job.id}/${taskGroup}`,
    95        'Task Group breadcrumb links correctly'
    96      );
    97  
    98      await Task.visit({ id: allocation.id, name: task.name });
    99      await Layout.breadcrumbFor('allocations.allocation').visit();
   100      assert.equal(
   101        currentURL(),
   102        `/allocations/${allocation.id}`,
   103        'Allocations breadcrumb links correctly'
   104      );
   105    });
   106  
   107    test('/allocation/:id/:task_name should include resource utilization graphs', async function(assert) {
   108      assert.equal(Task.resourceCharts.length, 2, 'Two resource utilization graphs');
   109      assert.equal(Task.resourceCharts.objectAt(0).name, 'CPU', 'First chart is CPU');
   110      assert.equal(Task.resourceCharts.objectAt(1).name, 'Memory', 'Second chart is Memory');
   111    });
   112  
   113    test('the events table lists all recent events', async function(assert) {
   114      const events = server.db.taskEvents.where({ taskStateId: task.id });
   115  
   116      assert.equal(Task.events.length, events.length, `Lists ${events.length} events`);
   117    });
   118  
   119    test('when a task has volumes, the volumes table is shown', async function(assert) {
   120      const taskGroup = server.schema.taskGroups.where({
   121        jobId: allocation.jobId,
   122        name: allocation.taskGroup,
   123      }).models[0];
   124  
   125      const jobTask = taskGroup.tasks.models.find(m => m.name === task.name);
   126  
   127      assert.ok(Task.hasVolumes);
   128      assert.equal(Task.volumes.length, jobTask.volumeMounts.length);
   129    });
   130  
   131    test('when a task does not have volumes, the volumes table is not shown', async function(assert) {
   132      const job = server.create('job', { createAllocations: false, noHostVolumes: true });
   133      allocation = server.create('allocation', { jobId: job.id, clientStatus: 'running' });
   134      task = server.db.taskStates.where({ allocationId: allocation.id })[0];
   135  
   136      await Task.visit({ id: allocation.id, name: task.name });
   137      assert.notOk(Task.hasVolumes);
   138    });
   139  
   140    test('each volume in the volumes table shows information about the volume', async function(assert) {
   141      const taskGroup = server.schema.taskGroups.where({
   142        jobId: allocation.jobId,
   143        name: allocation.taskGroup,
   144      }).models[0];
   145  
   146      const jobTask = taskGroup.tasks.models.find(m => m.name === task.name);
   147      const volume = jobTask.volumeMounts[0];
   148  
   149      Task.volumes[0].as(volumeRow => {
   150        assert.equal(volumeRow.name, volume.Volume);
   151        assert.equal(volumeRow.destination, volume.Destination);
   152        assert.equal(volumeRow.permissions, volume.ReadOnly ? 'Read' : 'Read/Write');
   153        assert.equal(volumeRow.clientSource, taskGroup.volumes[volume.Volume].Source);
   154      });
   155    });
   156  
   157    test('each recent event should list the time, type, and description of the event', async function(assert) {
   158      const event = server.db.taskEvents.where({ taskStateId: task.id })[0];
   159      const recentEvent = Task.events.objectAt(Task.events.length - 1);
   160  
   161      assert.equal(
   162        recentEvent.time,
   163        moment(event.time / 1000000).format("MMM DD, 'YY HH:mm:ss ZZ"),
   164        'Event timestamp'
   165      );
   166      assert.equal(recentEvent.type, event.type, 'Event type');
   167      assert.equal(recentEvent.message, event.displayMessage, 'Event message');
   168    });
   169  
   170    test('when the allocation is not found, the application errors', async function(assert) {
   171      await Task.visit({ id: 'not-a-real-allocation', name: task.name });
   172  
   173      assert.equal(
   174        server.pretender.handledRequests
   175          .filter(request => !request.url.includes('policy'))
   176          .findBy('status', 404).url,
   177        '/v1/allocation/not-a-real-allocation',
   178        'A request to the nonexistent allocation is made'
   179      );
   180      assert.equal(
   181        currentURL(),
   182        `/allocations/not-a-real-allocation/${task.name}`,
   183        'The URL persists'
   184      );
   185      assert.ok(Task.error.isPresent, 'Error message is shown');
   186      assert.equal(Task.error.title, 'Not Found', 'Error message is for 404');
   187    });
   188  
   189    test('when the allocation is found but the task is not, the application errors', async function(assert) {
   190      await Task.visit({ id: allocation.id, name: 'not-a-real-task-name' });
   191  
   192      assert.ok(
   193        server.pretender.handledRequests
   194          .filterBy('status', 200)
   195          .mapBy('url')
   196          .includes(`/v1/allocation/${allocation.id}`),
   197        'A request to the allocation is made successfully'
   198      );
   199      assert.equal(
   200        currentURL(),
   201        `/allocations/${allocation.id}/not-a-real-task-name`,
   202        'The URL persists'
   203      );
   204      assert.ok(Task.error.isPresent, 'Error message is shown');
   205      assert.equal(Task.error.title, 'Not Found', 'Error message is for 404');
   206    });
   207  
   208    test('task can be restarted', async function(assert) {
   209      await Task.restart.idle();
   210      await Task.restart.confirm();
   211  
   212      const request = server.pretender.handledRequests.findBy('method', 'PUT');
   213      assert.equal(
   214        request.url,
   215        `/v1/client/allocation/${allocation.id}/restart`,
   216        'Restart request is made for the allocation'
   217      );
   218  
   219      assert.deepEqual(
   220        JSON.parse(request.requestBody),
   221        { TaskName: task.name },
   222        'Restart request is made for the correct task'
   223      );
   224    });
   225  
   226    test('when task restart fails (403), an ACL permissions error message is shown', async function(assert) {
   227      server.pretender.put('/v1/client/allocation/:id/restart', () => [403, {}, '']);
   228  
   229      await Task.restart.idle();
   230      await Task.restart.confirm();
   231  
   232      assert.ok(Task.inlineError.isShown, 'Inline error is shown');
   233      assert.ok(Task.inlineError.title.includes('Could Not Restart Task'), 'Title is descriptive');
   234      assert.ok(
   235        /ACL token.+?allocation lifecycle/.test(Task.inlineError.message),
   236        'Message mentions ACLs and the appropriate permission'
   237      );
   238  
   239      await Task.inlineError.dismiss();
   240  
   241      assert.notOk(Task.inlineError.isShown, 'Inline error is no longer shown');
   242    });
   243  
   244    test('when task restart fails (500), the error message from the API is piped through to the alert', async function(assert) {
   245      const message = 'A plaintext error message';
   246      server.pretender.put('/v1/client/allocation/:id/restart', () => [500, {}, message]);
   247  
   248      await Task.restart.idle();
   249      await Task.restart.confirm();
   250  
   251      assert.ok(Task.inlineError.isShown);
   252      assert.ok(Task.inlineError.title.includes('Could Not Restart Task'));
   253      assert.equal(Task.inlineError.message, message);
   254  
   255      await Task.inlineError.dismiss();
   256  
   257      assert.notOk(Task.inlineError.isShown);
   258    });
   259  
   260    test('exec button is present', async function(assert) {
   261      assert.ok(Task.execButton.isPresent);
   262    });
   263  });
   264  
   265  module('Acceptance | task detail (no addresses)', function(hooks) {
   266    setupApplicationTest(hooks);
   267    setupMirage(hooks);
   268  
   269    hooks.beforeEach(async function() {
   270      server.create('agent');
   271      server.create('node');
   272      server.create('job');
   273      allocation = server.create('allocation', 'withoutTaskWithPorts', { clientStatus: 'running' });
   274      task = server.db.taskStates.where({ allocationId: allocation.id })[0];
   275  
   276      await Task.visit({ id: allocation.id, name: task.name });
   277    });
   278  });
   279  
   280  module('Acceptance | task detail (different namespace)', function(hooks) {
   281    setupApplicationTest(hooks);
   282    setupMirage(hooks);
   283  
   284    hooks.beforeEach(async function() {
   285      server.create('agent');
   286      server.create('node');
   287      server.create('namespace');
   288      server.create('namespace', { id: 'other-namespace' });
   289      server.create('job', { createAllocations: false, namespaceId: 'other-namespace' });
   290      allocation = server.create('allocation', 'withTaskWithPorts', { clientStatus: 'running' });
   291      task = server.db.taskStates.where({ allocationId: allocation.id })[0];
   292  
   293      await Task.visit({ id: allocation.id, name: task.name });
   294    });
   295  
   296    test('breadcrumbs match jobs / job / task group / allocation / task', async function(assert) {
   297      const { jobId, taskGroup } = allocation;
   298      const job = server.db.jobs.find(jobId);
   299  
   300      await Layout.breadcrumbFor('jobs.index').visit();
   301      assert.equal(
   302        currentURL(),
   303        '/jobs?namespace=other-namespace',
   304        'Jobs breadcrumb links correctly'
   305      );
   306  
   307      await Task.visit({ id: allocation.id, name: task.name });
   308      await Layout.breadcrumbFor('jobs.job.index').visit();
   309      assert.equal(
   310        currentURL(),
   311        `/jobs/${job.id}?namespace=other-namespace`,
   312        'Job breadcrumb links correctly'
   313      );
   314  
   315      await Task.visit({ id: allocation.id, name: task.name });
   316      await Layout.breadcrumbFor('jobs.job.task-group').visit();
   317      assert.equal(
   318        currentURL(),
   319        `/jobs/${job.id}/${taskGroup}?namespace=other-namespace`,
   320        'Task Group breadcrumb links correctly'
   321      );
   322  
   323      await Task.visit({ id: allocation.id, name: task.name });
   324      await Layout.breadcrumbFor('allocations.allocation').visit();
   325      assert.equal(
   326        currentURL(),
   327        `/allocations/${allocation.id}`,
   328        'Allocations breadcrumb links correctly'
   329      );
   330    });
   331  });
   332  
   333  module('Acceptance | task detail (not running)', function(hooks) {
   334    setupApplicationTest(hooks);
   335    setupMirage(hooks);
   336  
   337    hooks.beforeEach(async function() {
   338      server.create('agent');
   339      server.create('node');
   340      server.create('namespace');
   341      server.create('namespace', { id: 'other-namespace' });
   342      server.create('job', { createAllocations: false, namespaceId: 'other-namespace' });
   343      allocation = server.create('allocation', 'withTaskWithPorts', { clientStatus: 'complete' });
   344      task = server.db.taskStates.where({ allocationId: allocation.id })[0];
   345  
   346      await Task.visit({ id: allocation.id, name: task.name });
   347    });
   348  
   349    test('when the allocation for a task is not running, the resource utilization graphs are replaced by an empty message', async function(assert) {
   350      assert.equal(Task.resourceCharts.length, 0, 'No resource charts');
   351      assert.equal(Task.resourceEmptyMessage, "Task isn't running", 'Empty message is appropriate');
   352    });
   353  
   354    test('exec button is absent', async function(assert) {
   355      assert.notOk(Task.execButton.isPresent);
   356    });
   357  });
   358  
   359  module('Acceptance | proxy task detail', function(hooks) {
   360    setupApplicationTest(hooks);
   361    setupMirage(hooks);
   362  
   363    hooks.beforeEach(async function() {
   364      server.create('agent');
   365      server.create('node');
   366      server.create('job', { createAllocations: false });
   367      allocation = server.create('allocation', 'withTaskWithPorts', { clientStatus: 'running' });
   368  
   369      const taskState = allocation.taskStates.models[0];
   370      const task = server.schema.tasks.findBy({ name: taskState.name });
   371      task.update('kind', 'connect-proxy:task');
   372      task.save();
   373  
   374      await Task.visit({ id: allocation.id, name: taskState.name });
   375    });
   376  
   377    test('a proxy tag is shown', async function(assert) {
   378      assert.ok(Task.title.proxyTag.isPresent);
   379    });
   380  });