github.com/ferranbt/nomad@v0.9.3-0.20190607002617-85c449b7667c/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/setup-mirage';
     5  import Task from 'nomad-ui/tests/pages/allocations/task/detail';
     6  import moment from 'moment';
     7  
     8  let allocation;
     9  let task;
    10  
    11  module('Acceptance | task detail', function(hooks) {
    12    setupApplicationTest(hooks);
    13    setupMirage(hooks);
    14  
    15    hooks.beforeEach(async function() {
    16      server.create('agent');
    17      server.create('node');
    18      server.create('job', { createAllocations: false });
    19      allocation = server.create('allocation', 'withTaskWithPorts', { clientStatus: 'running' });
    20      task = server.db.taskStates.where({ allocationId: allocation.id })[0];
    21  
    22      await Task.visit({ id: allocation.id, name: task.name });
    23    });
    24  
    25    test('/allocation/:id/:task_name should name the task and list high-level task information', async function(assert) {
    26      assert.ok(Task.title.includes(task.name), 'Task name');
    27      assert.ok(Task.state.includes(task.state), 'Task state');
    28  
    29      assert.ok(
    30        Task.startedAt.includes(moment(task.startedAt).format("MMM DD, 'YY HH:mm:ss ZZ")),
    31        'Task started at'
    32      );
    33    });
    34  
    35    test('breadcrumbs match jobs / job / task group / allocation / task', async function(assert) {
    36      const { jobId, taskGroup } = allocation;
    37      const job = server.db.jobs.find(jobId);
    38  
    39      const shortId = allocation.id.split('-')[0];
    40  
    41      assert.equal(Task.breadcrumbFor('jobs.index').text, 'Jobs', 'Jobs is the first breadcrumb');
    42      assert.equal(
    43        Task.breadcrumbFor('jobs.job.index').text,
    44        job.name,
    45        'Job is the second breadcrumb'
    46      );
    47      assert.equal(
    48        Task.breadcrumbFor('jobs.job.task-group').text,
    49        taskGroup,
    50        'Task Group is the third breadcrumb'
    51      );
    52      assert.equal(
    53        Task.breadcrumbFor('allocations.allocation').text,
    54        shortId,
    55        'Allocation short id is the fourth breadcrumb'
    56      );
    57      assert.equal(
    58        Task.breadcrumbFor('allocations.allocation.task').text,
    59        task.name,
    60        'Task name is the fifth breadcrumb'
    61      );
    62  
    63      await Task.breadcrumbFor('jobs.index').visit();
    64      assert.equal(currentURL(), '/jobs', 'Jobs breadcrumb links correctly');
    65  
    66      await Task.visit({ id: allocation.id, name: task.name });
    67      await Task.breadcrumbFor('jobs.job.index').visit();
    68      assert.equal(currentURL(), `/jobs/${job.id}`, 'Job breadcrumb links correctly');
    69  
    70      await Task.visit({ id: allocation.id, name: task.name });
    71      await Task.breadcrumbFor('jobs.job.task-group').visit();
    72      assert.equal(
    73        currentURL(),
    74        `/jobs/${job.id}/${taskGroup}`,
    75        'Task Group breadcrumb links correctly'
    76      );
    77  
    78      await Task.visit({ id: allocation.id, name: task.name });
    79      await Task.breadcrumbFor('allocations.allocation').visit();
    80      assert.equal(
    81        currentURL(),
    82        `/allocations/${allocation.id}`,
    83        'Allocations breadcrumb links correctly'
    84      );
    85    });
    86  
    87    test('/allocation/:id/:task_name should include resource utilization graphs', async function(assert) {
    88      assert.equal(Task.resourceCharts.length, 2, 'Two resource utilization graphs');
    89      assert.equal(Task.resourceCharts.objectAt(0).name, 'CPU', 'First chart is CPU');
    90      assert.equal(Task.resourceCharts.objectAt(1).name, 'Memory', 'Second chart is Memory');
    91    });
    92  
    93    test('the addresses table lists all reserved and dynamic ports', async function(assert) {
    94      const taskResources = allocation.taskResourceIds
    95        .map(id => server.db.taskResources.find(id))
    96        .find(resources => resources.name === task.name);
    97      const reservedPorts = taskResources.resources.Networks[0].ReservedPorts;
    98      const dynamicPorts = taskResources.resources.Networks[0].DynamicPorts;
    99      const addresses = reservedPorts.concat(dynamicPorts);
   100  
   101      assert.equal(Task.addresses.length, addresses.length, 'All addresses are listed');
   102    });
   103  
   104    test('each address row shows the label and value of the address', async function(assert) {
   105      const taskResources = allocation.taskResourceIds
   106        .map(id => server.db.taskResources.find(id))
   107        .findBy('name', task.name);
   108      const networkAddress = taskResources.resources.Networks[0].IP;
   109      const reservedPorts = taskResources.resources.Networks[0].ReservedPorts;
   110      const dynamicPorts = taskResources.resources.Networks[0].DynamicPorts;
   111      const address = reservedPorts.concat(dynamicPorts).sortBy('Label')[0];
   112  
   113      const addressRow = Task.addresses.objectAt(0);
   114      assert.equal(
   115        addressRow.isDynamic,
   116        reservedPorts.includes(address) ? 'No' : 'Yes',
   117        'Dynamic port is denoted as such'
   118      );
   119      assert.equal(addressRow.name, address.Label, 'Label');
   120      assert.equal(addressRow.address, `${networkAddress}:${address.Value}`, 'Value');
   121    });
   122  
   123    test('the events table lists all recent events', async function(assert) {
   124      const events = server.db.taskEvents.where({ taskStateId: task.id });
   125  
   126      assert.equal(Task.events.length, events.length, `Lists ${events.length} events`);
   127    });
   128  
   129    test('each recent event should list the time, type, and description of the event', async function(assert) {
   130      const event = server.db.taskEvents.where({ taskStateId: task.id })[0];
   131      const recentEvent = Task.events.objectAt(Task.events.length - 1);
   132  
   133      assert.equal(
   134        recentEvent.time,
   135        moment(event.time / 1000000).format("MMM DD, 'YY HH:mm:ss ZZ"),
   136        'Event timestamp'
   137      );
   138      assert.equal(recentEvent.type, event.type, 'Event type');
   139      assert.equal(recentEvent.message, event.displayMessage, 'Event message');
   140    });
   141  
   142    test('when the allocation is not found, the application errors', async function(assert) {
   143      await Task.visit({ id: 'not-a-real-allocation', name: task.name });
   144  
   145      assert.equal(
   146        server.pretender.handledRequests.findBy('status', 404).url,
   147        '/v1/allocation/not-a-real-allocation',
   148        'A request to the nonexistent allocation is made'
   149      );
   150      assert.equal(
   151        currentURL(),
   152        `/allocations/not-a-real-allocation/${task.name}`,
   153        'The URL persists'
   154      );
   155      assert.ok(Task.error.isPresent, 'Error message is shown');
   156      assert.equal(Task.error.title, 'Not Found', 'Error message is for 404');
   157    });
   158  
   159    test('when the allocation is found but the task is not, the application errors', async function(assert) {
   160      await Task.visit({ id: allocation.id, name: 'not-a-real-task-name' });
   161  
   162      assert.ok(
   163        server.pretender.handledRequests
   164          .filterBy('status', 200)
   165          .mapBy('url')
   166          .includes(`/v1/allocation/${allocation.id}`),
   167        'A request to the allocation is made successfully'
   168      );
   169      assert.equal(
   170        currentURL(),
   171        `/allocations/${allocation.id}/not-a-real-task-name`,
   172        'The URL persists'
   173      );
   174      assert.ok(Task.error.isPresent, 'Error message is shown');
   175      assert.equal(Task.error.title, 'Not Found', 'Error message is for 404');
   176    });
   177  
   178    test('task can be restarted', async function(assert) {
   179      await Task.restart.idle();
   180      await Task.restart.confirm();
   181  
   182      const request = server.pretender.handledRequests.findBy('method', 'PUT');
   183      assert.equal(
   184        request.url,
   185        `/v1/client/allocation/${allocation.id}/restart`,
   186        'Restart request is made for the allocation'
   187      );
   188  
   189      assert.deepEqual(
   190        JSON.parse(request.requestBody),
   191        { TaskName: task.name },
   192        'Restart request is made for the correct task'
   193      );
   194    });
   195  
   196    test('when task restart fails, an error message is shown', async function(assert) {
   197      server.pretender.put('/v1/client/allocation/:id/restart', () => [403, {}, '']);
   198  
   199      await Task.restart.idle();
   200      await Task.restart.confirm();
   201  
   202      assert.ok(Task.inlineError.isShown, 'Inline error is shown');
   203      assert.ok(Task.inlineError.title.includes('Could Not Restart Task'), 'Title is descriptive');
   204      assert.ok(
   205        /ACL token.+?allocation lifecycle/.test(Task.inlineError.message),
   206        'Message mentions ACLs and the appropriate permission'
   207      );
   208  
   209      await Task.inlineError.dismiss();
   210  
   211      assert.notOk(Task.inlineError.isShown, 'Inline error is no longer shown');
   212    });
   213  });
   214  
   215  module('Acceptance | task detail (no addresses)', function(hooks) {
   216    setupApplicationTest(hooks);
   217    setupMirage(hooks);
   218  
   219    hooks.beforeEach(async function() {
   220      server.create('agent');
   221      server.create('node');
   222      server.create('job');
   223      allocation = server.create('allocation', 'withoutTaskWithPorts', { clientStatus: 'running' });
   224      task = server.db.taskStates.where({ allocationId: allocation.id })[0];
   225  
   226      await Task.visit({ id: allocation.id, name: task.name });
   227    });
   228  
   229    test('when the task has no addresses, the addresses table is not shown', async function(assert) {
   230      assert.notOk(Task.hasAddresses, 'No addresses table');
   231    });
   232  });
   233  
   234  module('Acceptance | task detail (different namespace)', function(hooks) {
   235    setupApplicationTest(hooks);
   236    setupMirage(hooks);
   237  
   238    hooks.beforeEach(async function() {
   239      server.create('agent');
   240      server.create('node');
   241      server.create('namespace');
   242      server.create('namespace', { id: 'other-namespace' });
   243      server.create('job', { createAllocations: false, namespaceId: 'other-namespace' });
   244      allocation = server.create('allocation', 'withTaskWithPorts', { clientStatus: 'running' });
   245      task = server.db.taskStates.where({ allocationId: allocation.id })[0];
   246  
   247      await Task.visit({ id: allocation.id, name: task.name });
   248    });
   249  
   250    test('breadcrumbs match jobs / job / task group / allocation / task', async function(assert) {
   251      const { jobId, taskGroup } = allocation;
   252      const job = server.db.jobs.find(jobId);
   253  
   254      await Task.breadcrumbFor('jobs.index').visit();
   255      assert.equal(
   256        currentURL(),
   257        '/jobs?namespace=other-namespace',
   258        'Jobs breadcrumb links correctly'
   259      );
   260  
   261      await Task.visit({ id: allocation.id, name: task.name });
   262      await Task.breadcrumbFor('jobs.job.index').visit();
   263      assert.equal(
   264        currentURL(),
   265        `/jobs/${job.id}?namespace=other-namespace`,
   266        'Job breadcrumb links correctly'
   267      );
   268  
   269      await Task.visit({ id: allocation.id, name: task.name });
   270      await Task.breadcrumbFor('jobs.job.task-group').visit();
   271      assert.equal(
   272        currentURL(),
   273        `/jobs/${job.id}/${taskGroup}?namespace=other-namespace`,
   274        'Task Group breadcrumb links correctly'
   275      );
   276  
   277      await Task.visit({ id: allocation.id, name: task.name });
   278      await Task.breadcrumbFor('allocations.allocation').visit();
   279      assert.equal(
   280        currentURL(),
   281        `/allocations/${allocation.id}`,
   282        'Allocations breadcrumb links correctly'
   283      );
   284    });
   285  });
   286  
   287  module('Acceptance | task detail (not running)', function(hooks) {
   288    setupApplicationTest(hooks);
   289    setupMirage(hooks);
   290  
   291    hooks.beforeEach(async function() {
   292      server.create('agent');
   293      server.create('node');
   294      server.create('namespace');
   295      server.create('namespace', { id: 'other-namespace' });
   296      server.create('job', { createAllocations: false, namespaceId: 'other-namespace' });
   297      allocation = server.create('allocation', 'withTaskWithPorts', { clientStatus: 'complete' });
   298      task = server.db.taskStates.where({ allocationId: allocation.id })[0];
   299  
   300      await Task.visit({ id: allocation.id, name: task.name });
   301    });
   302  
   303    test('when the allocation for a task is not running, the resource utilization graphs are replaced by an empty message', async function(assert) {
   304      assert.equal(Task.resourceCharts.length, 0, 'No resource charts');
   305      assert.equal(Task.resourceEmptyMessage, "Task isn't running", 'Empty message is appropriate');
   306    });
   307  });