2020-05-05 21:28:15 +00:00
|
|
|
import { module, test } from 'qunit';
|
|
|
|
import { currentURL } from '@ember/test-helpers';
|
|
|
|
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-05-05 21:28:15 +00:00
|
|
|
import moment from 'moment';
|
|
|
|
import { formatBytes } from 'nomad-ui/helpers/format-bytes';
|
|
|
|
import PluginDetail from 'nomad-ui/tests/pages/storage/plugins/detail';
|
|
|
|
|
|
|
|
module('Acceptance | plugin detail', function(hooks) {
|
|
|
|
setupApplicationTest(hooks);
|
|
|
|
setupMirage(hooks);
|
|
|
|
|
|
|
|
let plugin;
|
|
|
|
|
|
|
|
hooks.beforeEach(function() {
|
|
|
|
server.create('node');
|
2020-05-18 22:28:41 +00:00
|
|
|
plugin = server.create('csi-plugin', { controllerRequired: true });
|
2020-05-05 21:28:15 +00:00
|
|
|
});
|
|
|
|
|
2020-07-28 17:59:14 +00:00
|
|
|
test('it passes an accessibility audit', async function(assert) {
|
|
|
|
await PluginDetail.visit({ id: plugin.id });
|
|
|
|
await a11yAudit();
|
|
|
|
assert.ok(true, 'a11y audit passes');
|
|
|
|
});
|
|
|
|
|
2020-05-05 21:28:15 +00:00
|
|
|
test('/csi/plugins/:id should have a breadcrumb trail linking back to Plugins and Storage', async function(assert) {
|
|
|
|
await PluginDetail.visit({ id: plugin.id });
|
|
|
|
|
|
|
|
assert.equal(PluginDetail.breadcrumbFor('csi.index').text, 'Storage');
|
|
|
|
assert.equal(PluginDetail.breadcrumbFor('csi.plugins').text, 'Plugins');
|
|
|
|
assert.equal(PluginDetail.breadcrumbFor('csi.plugins.plugin').text, plugin.id);
|
|
|
|
});
|
|
|
|
|
|
|
|
test('/csi/plugins/:id should show the plugin name in the title', async function(assert) {
|
|
|
|
await PluginDetail.visit({ id: plugin.id });
|
|
|
|
|
|
|
|
assert.equal(document.title, `CSI Plugin ${plugin.id} - Nomad`);
|
|
|
|
assert.equal(PluginDetail.title, plugin.id);
|
|
|
|
});
|
|
|
|
|
|
|
|
test('/csi/plugins/:id should list additional details for the plugin below the title', async function(assert) {
|
|
|
|
await PluginDetail.visit({ id: plugin.id });
|
|
|
|
|
|
|
|
assert.ok(
|
|
|
|
PluginDetail.controllerHealth.includes(
|
|
|
|
`${Math.round((plugin.controllersHealthy / plugin.controllersExpected) * 100)}%`
|
|
|
|
)
|
|
|
|
);
|
|
|
|
assert.ok(
|
|
|
|
PluginDetail.controllerHealth.includes(
|
|
|
|
`${plugin.controllersHealthy}/${plugin.controllersExpected}`
|
|
|
|
)
|
|
|
|
);
|
|
|
|
assert.ok(
|
|
|
|
PluginDetail.nodeHealth.includes(
|
|
|
|
`${Math.round((plugin.nodesHealthy / plugin.nodesExpected) * 100)}%`
|
|
|
|
)
|
|
|
|
);
|
|
|
|
assert.ok(PluginDetail.nodeHealth.includes(`${plugin.nodesHealthy}/${plugin.nodesExpected}`));
|
|
|
|
assert.ok(PluginDetail.provider.includes(plugin.provider));
|
|
|
|
});
|
|
|
|
|
|
|
|
test('/csi/plugins/:id should list all the controller plugin allocations for the plugin', async function(assert) {
|
|
|
|
await PluginDetail.visit({ id: plugin.id });
|
|
|
|
|
|
|
|
assert.equal(PluginDetail.controllerAllocations.length, plugin.controllers.length);
|
|
|
|
plugin.controllers.models
|
|
|
|
.sortBy('updateTime')
|
|
|
|
.reverse()
|
|
|
|
.forEach((allocation, idx) => {
|
|
|
|
assert.equal(PluginDetail.controllerAllocations.objectAt(idx).id, allocation.allocID);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
test('/csi/plugins/:id should list all the node plugin allocations for the plugin', async function(assert) {
|
|
|
|
await PluginDetail.visit({ id: plugin.id });
|
|
|
|
|
|
|
|
assert.equal(PluginDetail.nodeAllocations.length, plugin.nodes.length);
|
|
|
|
plugin.nodes.models
|
|
|
|
.sortBy('updateTime')
|
|
|
|
.reverse()
|
|
|
|
.forEach((allocation, idx) => {
|
|
|
|
assert.equal(PluginDetail.nodeAllocations.objectAt(idx).id, allocation.allocID);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
test('each allocation should have high-level details for the allocation', async function(assert) {
|
|
|
|
const controller = plugin.controllers.models.sortBy('updateTime').reverse()[0];
|
|
|
|
const allocation = server.db.allocations.find(controller.allocID);
|
|
|
|
const allocStats = server.db.clientAllocationStats.find(allocation.id);
|
|
|
|
const taskGroup = server.db.taskGroups.findBy({
|
|
|
|
name: allocation.taskGroup,
|
|
|
|
jobId: allocation.jobId,
|
|
|
|
});
|
|
|
|
|
|
|
|
const tasks = taskGroup.taskIds.map(id => server.db.tasks.find(id));
|
|
|
|
const cpuUsed = tasks.reduce((sum, task) => sum + task.Resources.CPU, 0);
|
|
|
|
const memoryUsed = tasks.reduce((sum, task) => sum + task.Resources.MemoryMB, 0);
|
|
|
|
|
|
|
|
await PluginDetail.visit({ id: plugin.id });
|
|
|
|
|
|
|
|
PluginDetail.controllerAllocations.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 D')
|
|
|
|
);
|
|
|
|
assert.equal(
|
|
|
|
allocationRow.createTooltip,
|
|
|
|
moment(allocation.createTime / 1000000).format('MMM DD HH:mm:ss ZZ')
|
|
|
|
);
|
|
|
|
assert.equal(allocationRow.modifyTime, moment(allocation.modifyTime / 1000000).fromNow());
|
|
|
|
assert.equal(allocationRow.health, controller.healthy ? 'Healthy' : 'Unhealthy');
|
|
|
|
assert.equal(
|
|
|
|
allocationRow.client,
|
|
|
|
server.db.nodes.find(allocation.nodeId).id.split('-')[0],
|
|
|
|
'Node ID'
|
|
|
|
);
|
|
|
|
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.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 controller = plugin.controllers.models.sortBy('updateTime').reverse()[0];
|
|
|
|
|
|
|
|
await PluginDetail.visit({ id: plugin.id });
|
|
|
|
await PluginDetail.controllerAllocations.objectAt(0).visit();
|
|
|
|
|
|
|
|
assert.equal(currentURL(), `/allocations/${controller.allocID}`);
|
|
|
|
});
|
|
|
|
|
|
|
|
test('when there are no plugin allocations, the tables present empty states', async function(assert) {
|
|
|
|
const emptyPlugin = server.create('csi-plugin', {
|
2020-05-18 22:28:41 +00:00
|
|
|
controllerRequired: true,
|
2020-05-05 21:28:15 +00:00
|
|
|
controllersHealthy: 0,
|
|
|
|
controllersExpected: 0,
|
|
|
|
nodesHealthy: 0,
|
|
|
|
nodesExpected: 0,
|
|
|
|
});
|
|
|
|
|
|
|
|
await PluginDetail.visit({ id: emptyPlugin.id });
|
|
|
|
|
|
|
|
assert.ok(PluginDetail.controllerTableIsEmpty);
|
|
|
|
assert.equal(PluginDetail.controllerEmptyState.headline, 'No Controller Plugin Allocations');
|
|
|
|
|
|
|
|
assert.ok(PluginDetail.nodeTableIsEmpty);
|
|
|
|
assert.equal(PluginDetail.nodeEmptyState.headline, 'No Node Plugin Allocations');
|
|
|
|
});
|
2020-05-18 22:28:41 +00:00
|
|
|
|
|
|
|
test('when the plugin is node-only, the controller information is omitted', async function(assert) {
|
|
|
|
const nodeOnlyPlugin = server.create('csi-plugin', { controllerRequired: false });
|
|
|
|
|
|
|
|
await PluginDetail.visit({ id: nodeOnlyPlugin.id });
|
|
|
|
|
|
|
|
assert.notOk(PluginDetail.controllerAvailabilityIsPresent);
|
|
|
|
assert.ok(PluginDetail.nodeAvailabilityIsPresent);
|
|
|
|
|
|
|
|
assert.notOk(PluginDetail.controllerHealthIsPresent);
|
|
|
|
assert.notOk(PluginDetail.controllerTableIsPresent);
|
|
|
|
});
|
2020-05-20 01:01:42 +00:00
|
|
|
|
|
|
|
test('when there are more than 10 controller or node allocations, only 10 are shown', async function(assert) {
|
|
|
|
const manyAllocationsPlugin = server.create('csi-plugin', {
|
|
|
|
shallow: true,
|
|
|
|
controllerRequired: false,
|
|
|
|
nodesExpected: 15,
|
|
|
|
});
|
|
|
|
|
|
|
|
await PluginDetail.visit({ id: manyAllocationsPlugin.id });
|
|
|
|
|
|
|
|
assert.equal(PluginDetail.nodeAllocations.length, 10);
|
|
|
|
});
|
|
|
|
|
|
|
|
test('the View All links under each allocation table link to a filtered view of the plugins allocation list', async function(assert) {
|
|
|
|
const serialize = arr => window.encodeURIComponent(JSON.stringify(arr));
|
|
|
|
|
|
|
|
await PluginDetail.visit({ id: plugin.id });
|
2020-05-20 21:22:47 +00:00
|
|
|
assert.ok(
|
|
|
|
PluginDetail.goToControllerAllocationsText.includes(plugin.controllers.models.length)
|
|
|
|
);
|
2020-05-20 01:01:42 +00:00
|
|
|
await PluginDetail.goToControllerAllocations();
|
|
|
|
assert.equal(
|
|
|
|
currentURL(),
|
|
|
|
`/csi/plugins/${plugin.id}/allocations?type=${serialize(['controller'])}`
|
|
|
|
);
|
|
|
|
|
|
|
|
await PluginDetail.visit({ id: plugin.id });
|
2020-05-20 21:22:47 +00:00
|
|
|
assert.ok(PluginDetail.goToNodeAllocationsText.includes(plugin.nodes.models.length));
|
2020-05-20 01:01:42 +00:00
|
|
|
await PluginDetail.goToNodeAllocations();
|
|
|
|
assert.equal(currentURL(), `/csi/plugins/${plugin.id}/allocations?type=${serialize(['node'])}`);
|
|
|
|
});
|
2020-05-05 21:28:15 +00:00
|
|
|
});
|