2020-04-05 00:13:40 +00:00
|
|
|
import { module, test } from 'qunit';
|
|
|
|
import { currentURL } from '@ember/test-helpers';
|
2020-03-25 12:51:26 +00:00
|
|
|
import { setupApplicationTest } from 'ember-qunit';
|
|
|
|
import { setupMirage } from 'ember-cli-mirage/test-support';
|
2020-07-28 17:59:14 +00:00
|
|
|
import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
|
2020-04-05 00:13:40 +00:00
|
|
|
import moment from 'moment';
|
|
|
|
import { formatBytes } from 'nomad-ui/helpers/format-bytes';
|
|
|
|
import VolumeDetail from 'nomad-ui/tests/pages/storage/volumes/detail';
|
|
|
|
|
|
|
|
const assignWriteAlloc = (volume, alloc) => {
|
|
|
|
volume.writeAllocs.add(alloc);
|
2020-11-25 21:44:06 +00:00
|
|
|
volume.allocations.add(alloc);
|
2020-04-05 00:13:40 +00:00
|
|
|
volume.save();
|
|
|
|
};
|
|
|
|
|
|
|
|
const assignReadAlloc = (volume, alloc) => {
|
|
|
|
volume.readAllocs.add(alloc);
|
2020-11-25 21:44:06 +00:00
|
|
|
volume.allocations.add(alloc);
|
2020-04-05 00:13:40 +00:00
|
|
|
volume.save();
|
|
|
|
};
|
2020-03-25 12:51:26 +00:00
|
|
|
|
|
|
|
module('Acceptance | volume detail', function(hooks) {
|
|
|
|
setupApplicationTest(hooks);
|
|
|
|
setupMirage(hooks);
|
|
|
|
|
2020-04-05 00:13:40 +00:00
|
|
|
let volume;
|
|
|
|
|
|
|
|
hooks.beforeEach(function() {
|
|
|
|
server.create('node');
|
|
|
|
server.create('csi-plugin', { createVolumes: false });
|
|
|
|
volume = server.create('csi-volume');
|
|
|
|
});
|
|
|
|
|
2020-07-28 17:59:14 +00:00
|
|
|
test('it passes an accessibility audit', async function(assert) {
|
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
2020-08-25 15:56:02 +00:00
|
|
|
await a11yAudit(assert);
|
2020-07-28 17:59:14 +00:00
|
|
|
});
|
|
|
|
|
2020-05-05 21:28:15 +00:00
|
|
|
test('/csi/volumes/:id should have a breadcrumb trail linking back to Volumes and Storage', async function(assert) {
|
2020-04-05 00:13:40 +00:00
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
|
|
|
|
assert.equal(VolumeDetail.breadcrumbFor('csi.index').text, 'Storage');
|
|
|
|
assert.equal(VolumeDetail.breadcrumbFor('csi.volumes').text, 'Volumes');
|
|
|
|
assert.equal(VolumeDetail.breadcrumbFor('csi.volumes.volume').text, volume.name);
|
|
|
|
});
|
|
|
|
|
|
|
|
test('/csi/volumes/:id should show the volume name in the title', async function(assert) {
|
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
|
|
|
|
assert.equal(document.title, `CSI Volume ${volume.name} - Nomad`);
|
|
|
|
assert.equal(VolumeDetail.title, volume.name);
|
|
|
|
});
|
|
|
|
|
|
|
|
test('/csi/volumes/:id should list additional details for the volume below the title', async function(assert) {
|
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
|
|
|
|
assert.ok(VolumeDetail.health.includes(volume.schedulable ? 'Schedulable' : 'Unschedulable'));
|
|
|
|
assert.ok(VolumeDetail.provider.includes(volume.provider));
|
|
|
|
assert.ok(VolumeDetail.externalId.includes(volume.externalId));
|
|
|
|
assert.notOk(
|
|
|
|
VolumeDetail.hasNamespace,
|
|
|
|
'Namespace is omitted when there is only one namespace'
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
test('/csi/volumes/:id should list all write allocations the volume is attached to', async function(assert) {
|
|
|
|
const writeAllocations = server.createList('allocation', 2);
|
|
|
|
const readAllocations = server.createList('allocation', 3);
|
|
|
|
writeAllocations.forEach(alloc => assignWriteAlloc(volume, alloc));
|
|
|
|
readAllocations.forEach(alloc => assignReadAlloc(volume, alloc));
|
|
|
|
|
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
|
|
|
|
assert.equal(VolumeDetail.writeAllocations.length, writeAllocations.length);
|
|
|
|
writeAllocations
|
|
|
|
.sortBy('modifyIndex')
|
|
|
|
.reverse()
|
|
|
|
.forEach((allocation, idx) => {
|
|
|
|
assert.equal(allocation.id, VolumeDetail.writeAllocations.objectAt(idx).id);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
test('/csi/volumes/:id should list all read allocations the volume is attached to', async function(assert) {
|
|
|
|
const writeAllocations = server.createList('allocation', 2);
|
|
|
|
const readAllocations = server.createList('allocation', 3);
|
|
|
|
writeAllocations.forEach(alloc => assignWriteAlloc(volume, alloc));
|
|
|
|
readAllocations.forEach(alloc => assignReadAlloc(volume, alloc));
|
2020-03-25 12:51:26 +00:00
|
|
|
|
2020-04-05 00:13:40 +00:00
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
2020-03-25 12:51:26 +00:00
|
|
|
|
2020-04-05 00:13:40 +00:00
|
|
|
assert.equal(VolumeDetail.readAllocations.length, readAllocations.length);
|
|
|
|
readAllocations
|
|
|
|
.sortBy('modifyIndex')
|
|
|
|
.reverse()
|
|
|
|
.forEach((allocation, idx) => {
|
|
|
|
assert.equal(allocation.id, VolumeDetail.readAllocations.objectAt(idx).id);
|
|
|
|
});
|
|
|
|
});
|
2020-03-25 12:51:26 +00:00
|
|
|
|
2020-04-05 00:13:40 +00:00
|
|
|
test('each allocation should have high-level details for the allocation', async function(assert) {
|
|
|
|
const allocation = server.create('allocation', { clientStatus: 'running' });
|
|
|
|
assignWriteAlloc(volume, allocation);
|
2020-03-25 12:51:26 +00:00
|
|
|
|
2020-04-05 00:13:40 +00:00
|
|
|
const allocStats = server.db.clientAllocationStats.find(allocation.id);
|
|
|
|
const taskGroup = server.db.taskGroups.findBy({
|
|
|
|
name: allocation.taskGroup,
|
|
|
|
jobId: allocation.jobId,
|
|
|
|
});
|
2020-03-25 12:51:26 +00:00
|
|
|
|
2020-04-05 00:13:40 +00:00
|
|
|
const tasks = taskGroup.taskIds.map(id => server.db.tasks.find(id));
|
2020-10-12 22:26:54 +00:00
|
|
|
const cpuUsed = tasks.reduce((sum, task) => sum + task.resources.CPU, 0);
|
|
|
|
const memoryUsed = tasks.reduce((sum, task) => sum + task.resources.MemoryMB, 0);
|
2020-04-05 00:13:40 +00:00
|
|
|
|
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
|
|
|
|
VolumeDetail.writeAllocations.objectAt(0).as(allocationRow => {
|
|
|
|
assert.equal(allocationRow.shortId, allocation.id.split('-')[0], 'Allocation short ID');
|
|
|
|
assert.equal(
|
|
|
|
allocationRow.createTime,
|
|
|
|
moment(allocation.createTime / 1000000).format('MMM DD HH:mm:ss ZZ'),
|
|
|
|
'Allocation create time'
|
|
|
|
);
|
|
|
|
assert.equal(
|
|
|
|
allocationRow.modifyTime,
|
|
|
|
moment(allocation.modifyTime / 1000000).fromNow(),
|
|
|
|
'Allocation modify time'
|
|
|
|
);
|
|
|
|
assert.equal(allocationRow.status, allocation.clientStatus, 'Client status');
|
|
|
|
assert.equal(allocationRow.job, server.db.jobs.find(allocation.jobId).name, 'Job name');
|
|
|
|
assert.ok(allocationRow.taskGroup, 'Task group name');
|
|
|
|
assert.ok(allocationRow.jobVersion, 'Job Version');
|
|
|
|
assert.equal(
|
|
|
|
allocationRow.client,
|
|
|
|
server.db.nodes.find(allocation.nodeId).id.split('-')[0],
|
|
|
|
'Node ID'
|
|
|
|
);
|
|
|
|
assert.equal(
|
|
|
|
allocationRow.cpu,
|
|
|
|
Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks) / cpuUsed,
|
|
|
|
'CPU %'
|
|
|
|
);
|
|
|
|
assert.equal(
|
|
|
|
allocationRow.cpuTooltip,
|
|
|
|
`${Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks)} / ${cpuUsed} MHz`,
|
|
|
|
'Detailed CPU information is in a tooltip'
|
|
|
|
);
|
|
|
|
assert.equal(
|
|
|
|
allocationRow.mem,
|
|
|
|
allocStats.resourceUsage.MemoryStats.RSS / 1024 / 1024 / memoryUsed,
|
|
|
|
'Memory used'
|
|
|
|
);
|
|
|
|
assert.equal(
|
|
|
|
allocationRow.memTooltip,
|
|
|
|
`${formatBytes([allocStats.resourceUsage.MemoryStats.RSS])} / ${memoryUsed} MiB`,
|
|
|
|
'Detailed memory information is in a tooltip'
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
test('each allocation should link to the allocation detail page', async function(assert) {
|
|
|
|
const allocation = server.create('allocation');
|
|
|
|
assignWriteAlloc(volume, allocation);
|
|
|
|
|
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
await VolumeDetail.writeAllocations.objectAt(0).visit();
|
|
|
|
|
|
|
|
assert.equal(currentURL(), `/allocations/${allocation.id}`);
|
|
|
|
});
|
|
|
|
|
|
|
|
test('when there are no write allocations, the table presents an empty state', async function(assert) {
|
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
|
|
|
|
assert.ok(VolumeDetail.writeTableIsEmpty);
|
|
|
|
assert.equal(VolumeDetail.writeEmptyState.headline, 'No Write Allocations');
|
|
|
|
});
|
|
|
|
|
|
|
|
test('when there are no read allocations, the table presents an empty state', async function(assert) {
|
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
|
|
|
|
assert.ok(VolumeDetail.readTableIsEmpty);
|
|
|
|
assert.equal(VolumeDetail.readEmptyState.headline, 'No Read Allocations');
|
|
|
|
});
|
2020-05-02 05:26:19 +00:00
|
|
|
|
|
|
|
test('the constraints table shows access mode and attachment mode', async function(assert) {
|
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
|
|
|
|
|
|
|
assert.equal(VolumeDetail.constraints.accessMode, volume.accessMode);
|
|
|
|
assert.equal(VolumeDetail.constraints.attachmentMode, volume.attachmentMode);
|
|
|
|
});
|
2020-04-05 00:13:40 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Namespace test: details shows the namespace
|
|
|
|
module('Acceptance | volume detail (with namespaces)', function(hooks) {
|
|
|
|
setupApplicationTest(hooks);
|
|
|
|
setupMirage(hooks);
|
2020-03-25 12:51:26 +00:00
|
|
|
|
2020-04-05 00:13:40 +00:00
|
|
|
let volume;
|
2020-03-25 12:51:26 +00:00
|
|
|
|
2020-04-05 00:13:40 +00:00
|
|
|
hooks.beforeEach(function() {
|
|
|
|
server.createList('namespace', 2);
|
|
|
|
server.create('node');
|
|
|
|
server.create('csi-plugin', { createVolumes: false });
|
|
|
|
volume = server.create('csi-volume');
|
|
|
|
});
|
2020-03-25 12:51:26 +00:00
|
|
|
|
2020-04-05 00:13:40 +00:00
|
|
|
test('/csi/volumes/:id detail ribbon includes the namespace of the volume', async function(assert) {
|
|
|
|
await VolumeDetail.visit({ id: volume.id });
|
2020-03-25 12:51:26 +00:00
|
|
|
|
2020-04-05 00:13:40 +00:00
|
|
|
assert.ok(VolumeDetail.hasNamespace);
|
|
|
|
assert.ok(VolumeDetail.namespace.includes(volume.namespaceId || 'default'));
|
|
|
|
});
|
2020-03-25 12:51:26 +00:00
|
|
|
});
|