github.com/hernad/nomad@v1.6.112/ui/tests/acceptance/volume-detail-test.js (about)

     1  /**
     2   * Copyright (c) HashiCorp, Inc.
     3   * SPDX-License-Identifier: MPL-2.0
     4   */
     5  
     6  /* eslint-disable qunit/require-expect */
     7  import { module, test } from 'qunit';
     8  import { currentURL } from '@ember/test-helpers';
     9  import { setupApplicationTest } from 'ember-qunit';
    10  import { setupMirage } from 'ember-cli-mirage/test-support';
    11  import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
    12  import moment from 'moment';
    13  import { formatBytes, formatHertz } from 'nomad-ui/utils/units';
    14  import VolumeDetail from 'nomad-ui/tests/pages/storage/volumes/detail';
    15  import Layout from 'nomad-ui/tests/pages/layout';
    16  
    17  const assignWriteAlloc = (volume, alloc) => {
    18    volume.writeAllocs.add(alloc);
    19    volume.allocations.add(alloc);
    20    volume.save();
    21  };
    22  
    23  const assignReadAlloc = (volume, alloc) => {
    24    volume.readAllocs.add(alloc);
    25    volume.allocations.add(alloc);
    26    volume.save();
    27  };
    28  
    29  module('Acceptance | volume detail', function (hooks) {
    30    setupApplicationTest(hooks);
    31    setupMirage(hooks);
    32  
    33    let volume;
    34  
    35    hooks.beforeEach(function () {
    36      server.create('node-pool');
    37      server.create('node');
    38      server.create('csi-plugin', { createVolumes: false });
    39      volume = server.create('csi-volume');
    40    });
    41  
    42    test('it passes an accessibility audit', async function (assert) {
    43      await VolumeDetail.visit({ id: `${volume.id}@default` });
    44      await a11yAudit(assert);
    45    });
    46  
    47    test('/csi/volumes/:id should have a breadcrumb trail linking back to Volumes and Storage', async function (assert) {
    48      await VolumeDetail.visit({ id: `${volume.id}@default` });
    49  
    50      assert.equal(Layout.breadcrumbFor('csi.index').text, 'Storage');
    51      assert.equal(Layout.breadcrumbFor('csi.volumes').text, 'Volumes');
    52      assert.equal(Layout.breadcrumbFor('csi.volumes.volume').text, volume.name);
    53    });
    54  
    55    test('/csi/volumes/:id should show the volume name in the title', async function (assert) {
    56      await VolumeDetail.visit({ id: `${volume.id}@default` });
    57  
    58      assert.equal(document.title, `CSI Volume ${volume.name} - Nomad`);
    59      assert.equal(VolumeDetail.title, volume.name);
    60    });
    61  
    62    test('/csi/volumes/:id should list additional details for the volume below the title', async function (assert) {
    63      await VolumeDetail.visit({ id: `${volume.id}@default` });
    64  
    65      assert.ok(
    66        VolumeDetail.health.includes(
    67          volume.schedulable ? 'Schedulable' : 'Unschedulable'
    68        )
    69      );
    70      assert.ok(VolumeDetail.provider.includes(volume.provider));
    71      assert.ok(VolumeDetail.externalId.includes(volume.externalId));
    72      assert.notOk(
    73        VolumeDetail.hasNamespace,
    74        'Namespace is omitted when there is only one namespace'
    75      );
    76    });
    77  
    78    test('/csi/volumes/:id should list all write allocations the volume is attached to', async function (assert) {
    79      const writeAllocations = server.createList('allocation', 2);
    80      const readAllocations = server.createList('allocation', 3);
    81      writeAllocations.forEach((alloc) => assignWriteAlloc(volume, alloc));
    82      readAllocations.forEach((alloc) => assignReadAlloc(volume, alloc));
    83  
    84      await VolumeDetail.visit({ id: `${volume.id}@default` });
    85  
    86      assert.equal(VolumeDetail.writeAllocations.length, writeAllocations.length);
    87      writeAllocations
    88        .sortBy('modifyIndex')
    89        .reverse()
    90        .forEach((allocation, idx) => {
    91          assert.equal(
    92            allocation.id,
    93            VolumeDetail.writeAllocations.objectAt(idx).id
    94          );
    95        });
    96    });
    97  
    98    test('/csi/volumes/:id should list all read allocations the volume is attached to', async function (assert) {
    99      const writeAllocations = server.createList('allocation', 2);
   100      const readAllocations = server.createList('allocation', 3);
   101      writeAllocations.forEach((alloc) => assignWriteAlloc(volume, alloc));
   102      readAllocations.forEach((alloc) => assignReadAlloc(volume, alloc));
   103  
   104      await VolumeDetail.visit({ id: `${volume.id}@default` });
   105  
   106      assert.equal(VolumeDetail.readAllocations.length, readAllocations.length);
   107      readAllocations
   108        .sortBy('modifyIndex')
   109        .reverse()
   110        .forEach((allocation, idx) => {
   111          assert.equal(
   112            allocation.id,
   113            VolumeDetail.readAllocations.objectAt(idx).id
   114          );
   115        });
   116    });
   117  
   118    test('each allocation should have high-level details for the allocation', async function (assert) {
   119      const allocation = server.create('allocation', { clientStatus: 'running' });
   120      assignWriteAlloc(volume, allocation);
   121  
   122      const allocStats = server.db.clientAllocationStats.find(allocation.id);
   123      const taskGroup = server.db.taskGroups.findBy({
   124        name: allocation.taskGroup,
   125        jobId: allocation.jobId,
   126      });
   127  
   128      const tasks = taskGroup.taskIds.map((id) => server.db.tasks.find(id));
   129      const cpuUsed = tasks.reduce((sum, task) => sum + task.resources.CPU, 0);
   130      const memoryUsed = tasks.reduce(
   131        (sum, task) => sum + task.resources.MemoryMB,
   132        0
   133      );
   134  
   135      await VolumeDetail.visit({ id: `${volume.id}@default` });
   136  
   137      VolumeDetail.writeAllocations.objectAt(0).as((allocationRow) => {
   138        assert.equal(
   139          allocationRow.shortId,
   140          allocation.id.split('-')[0],
   141          'Allocation short ID'
   142        );
   143        assert.equal(
   144          allocationRow.createTime,
   145          moment(allocation.createTime / 1000000).format('MMM DD HH:mm:ss ZZ'),
   146          'Allocation create time'
   147        );
   148        assert.equal(
   149          allocationRow.modifyTime,
   150          moment(allocation.modifyTime / 1000000).fromNow(),
   151          'Allocation modify time'
   152        );
   153        assert.equal(
   154          allocationRow.status,
   155          allocation.clientStatus,
   156          'Client status'
   157        );
   158        assert.equal(
   159          allocationRow.job,
   160          server.db.jobs.find(allocation.jobId).name,
   161          'Job name'
   162        );
   163        assert.ok(allocationRow.taskGroup, 'Task group name');
   164        assert.ok(allocationRow.jobVersion, 'Job Version');
   165        assert.equal(
   166          allocationRow.client,
   167          server.db.nodes.find(allocation.nodeId).id.split('-')[0],
   168          'Node ID'
   169        );
   170        assert.equal(
   171          allocationRow.clientTooltip.substr(0, 15),
   172          server.db.nodes.find(allocation.nodeId).name.substr(0, 15),
   173          'Node Name'
   174        );
   175        assert.equal(
   176          allocationRow.cpu,
   177          Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks) / cpuUsed,
   178          'CPU %'
   179        );
   180        const roundedTicks = Math.floor(
   181          allocStats.resourceUsage.CpuStats.TotalTicks
   182        );
   183        assert.equal(
   184          allocationRow.cpuTooltip,
   185          `${formatHertz(roundedTicks, 'MHz')} / ${formatHertz(cpuUsed, 'MHz')}`,
   186          'Detailed CPU information is in a tooltip'
   187        );
   188        assert.equal(
   189          allocationRow.mem,
   190          allocStats.resourceUsage.MemoryStats.RSS / 1024 / 1024 / memoryUsed,
   191          'Memory used'
   192        );
   193        assert.equal(
   194          allocationRow.memTooltip,
   195          `${formatBytes(
   196            allocStats.resourceUsage.MemoryStats.RSS
   197          )} / ${formatBytes(memoryUsed, 'MiB')}`,
   198          'Detailed memory information is in a tooltip'
   199        );
   200      });
   201    });
   202  
   203    test('each allocation should link to the allocation detail page', async function (assert) {
   204      const allocation = server.create('allocation');
   205      assignWriteAlloc(volume, allocation);
   206  
   207      await VolumeDetail.visit({ id: `${volume.id}@default` });
   208      await VolumeDetail.writeAllocations.objectAt(0).visit();
   209  
   210      assert.equal(currentURL(), `/allocations/${allocation.id}`);
   211    });
   212  
   213    test('when there are no write allocations, the table presents an empty state', async function (assert) {
   214      await VolumeDetail.visit({ id: `${volume.id}@default` });
   215  
   216      assert.ok(VolumeDetail.writeTableIsEmpty);
   217      assert.equal(VolumeDetail.writeEmptyState.headline, 'No Write Allocations');
   218    });
   219  
   220    test('when there are no read allocations, the table presents an empty state', async function (assert) {
   221      await VolumeDetail.visit({ id: `${volume.id}@default` });
   222  
   223      assert.ok(VolumeDetail.readTableIsEmpty);
   224      assert.equal(VolumeDetail.readEmptyState.headline, 'No Read Allocations');
   225    });
   226  
   227    test('the constraints table shows access mode and attachment mode', async function (assert) {
   228      await VolumeDetail.visit({ id: `${volume.id}@default` });
   229  
   230      assert.equal(VolumeDetail.constraints.accessMode, volume.accessMode);
   231      assert.equal(
   232        VolumeDetail.constraints.attachmentMode,
   233        volume.attachmentMode
   234      );
   235    });
   236  });
   237  
   238  // Namespace test: details shows the namespace
   239  module('Acceptance | volume detail (with namespaces)', function (hooks) {
   240    setupApplicationTest(hooks);
   241    setupMirage(hooks);
   242  
   243    let volume;
   244  
   245    hooks.beforeEach(function () {
   246      server.createList('namespace', 2);
   247      server.create('node-pool');
   248      server.create('node');
   249      server.create('csi-plugin', { createVolumes: false });
   250      volume = server.create('csi-volume');
   251    });
   252  
   253    test('/csi/volumes/:id detail ribbon includes the namespace of the volume', async function (assert) {
   254      await VolumeDetail.visit({ id: `${volume.id}@${volume.namespaceId}` });
   255  
   256      assert.ok(VolumeDetail.hasNamespace);
   257      assert.ok(VolumeDetail.namespace.includes(volume.namespaceId || 'default'));
   258    });
   259  });