github.com/hernad/nomad@v1.6.112/ui/tests/acceptance/plugin-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 PluginDetail from 'nomad-ui/tests/pages/storage/plugins/detail'; 15 import Layout from 'nomad-ui/tests/pages/layout'; 16 17 module('Acceptance | plugin detail', function (hooks) { 18 setupApplicationTest(hooks); 19 setupMirage(hooks); 20 21 let plugin; 22 23 hooks.beforeEach(function () { 24 server.create('node-pool'); 25 server.create('node'); 26 plugin = server.create('csi-plugin', { controllerRequired: true }); 27 }); 28 29 test('it passes an accessibility audit', async function (assert) { 30 await PluginDetail.visit({ id: plugin.id }); 31 await a11yAudit(assert); 32 }); 33 34 test('/csi/plugins/:id should have a breadcrumb trail linking back to Plugins and Storage', async function (assert) { 35 await PluginDetail.visit({ id: plugin.id }); 36 37 assert.equal(Layout.breadcrumbFor('csi.index').text, 'Storage'); 38 assert.equal(Layout.breadcrumbFor('csi.plugins').text, 'Plugins'); 39 assert.equal(Layout.breadcrumbFor('csi.plugins.plugin').text, plugin.id); 40 }); 41 42 test('/csi/plugins/:id should show the plugin name in the title', async function (assert) { 43 await PluginDetail.visit({ id: plugin.id }); 44 45 assert.equal(document.title, `CSI Plugin ${plugin.id} - Nomad`); 46 assert.equal(PluginDetail.title, plugin.id); 47 }); 48 49 test('/csi/plugins/:id should list additional details for the plugin below the title', async function (assert) { 50 await PluginDetail.visit({ id: plugin.id }); 51 52 assert.ok( 53 PluginDetail.controllerHealth.includes( 54 `${Math.round( 55 (plugin.controllersHealthy / plugin.controllersExpected) * 100 56 )}%` 57 ) 58 ); 59 assert.ok( 60 PluginDetail.controllerHealth.includes( 61 `${plugin.controllersHealthy}/${plugin.controllersExpected}` 62 ) 63 ); 64 assert.ok( 65 PluginDetail.nodeHealth.includes( 66 `${Math.round((plugin.nodesHealthy / plugin.nodesExpected) * 100)}%` 67 ) 68 ); 69 assert.ok( 70 PluginDetail.nodeHealth.includes( 71 `${plugin.nodesHealthy}/${plugin.nodesExpected}` 72 ) 73 ); 74 assert.ok(PluginDetail.provider.includes(plugin.provider)); 75 }); 76 77 test('/csi/plugins/:id should list all the controller plugin allocations for the plugin', async function (assert) { 78 await PluginDetail.visit({ id: plugin.id }); 79 80 assert.equal( 81 PluginDetail.controllerAllocations.length, 82 plugin.controllers.length 83 ); 84 plugin.controllers.models 85 .sortBy('updateTime') 86 .reverse() 87 .forEach((allocation, idx) => { 88 assert.equal( 89 PluginDetail.controllerAllocations.objectAt(idx).id, 90 allocation.allocID 91 ); 92 }); 93 }); 94 95 test('/csi/plugins/:id should list all the node plugin allocations for the plugin', async function (assert) { 96 await PluginDetail.visit({ id: plugin.id }); 97 98 assert.equal(PluginDetail.nodeAllocations.length, plugin.nodes.length); 99 plugin.nodes.models 100 .sortBy('updateTime') 101 .reverse() 102 .forEach((allocation, idx) => { 103 assert.equal( 104 PluginDetail.nodeAllocations.objectAt(idx).id, 105 allocation.allocID 106 ); 107 }); 108 }); 109 110 test('each allocation should have high-level details for the allocation', async function (assert) { 111 const controller = plugin.controllers.models 112 .sortBy('updateTime') 113 .reverse()[0]; 114 const allocation = server.db.allocations.find(controller.allocID); 115 const allocStats = server.db.clientAllocationStats.find(allocation.id); 116 const taskGroup = server.db.taskGroups.findBy({ 117 name: allocation.taskGroup, 118 jobId: allocation.jobId, 119 }); 120 121 const tasks = taskGroup.taskIds.map((id) => server.db.tasks.find(id)); 122 const cpuUsed = tasks.reduce((sum, task) => sum + task.resources.CPU, 0); 123 const memoryUsed = tasks.reduce( 124 (sum, task) => sum + task.resources.MemoryMB, 125 0 126 ); 127 128 await PluginDetail.visit({ id: plugin.id }); 129 130 PluginDetail.controllerAllocations.objectAt(0).as((allocationRow) => { 131 assert.equal( 132 allocationRow.shortId, 133 allocation.id.split('-')[0], 134 'Allocation short ID' 135 ); 136 assert.equal( 137 allocationRow.createTime, 138 moment(allocation.createTime / 1000000).format('MMM D') 139 ); 140 assert.equal( 141 allocationRow.createTooltip, 142 moment(allocation.createTime / 1000000).format('MMM DD HH:mm:ss ZZ') 143 ); 144 assert.equal( 145 allocationRow.modifyTime, 146 moment(allocation.modifyTime / 1000000).fromNow() 147 ); 148 assert.equal( 149 allocationRow.health, 150 controller.healthy ? 'Healthy' : 'Unhealthy' 151 ); 152 assert.equal( 153 allocationRow.client, 154 server.db.nodes.find(allocation.nodeId).id.split('-')[0], 155 'Node ID' 156 ); 157 assert.equal( 158 allocationRow.clientTooltip.substr(0, 15), 159 server.db.nodes.find(allocation.nodeId).name.substr(0, 15), 160 'Node Name' 161 ); 162 assert.equal( 163 allocationRow.job, 164 server.db.jobs.find(allocation.jobId).name, 165 'Job name' 166 ); 167 assert.ok(allocationRow.taskGroup, 'Task group name'); 168 assert.ok(allocationRow.jobVersion, 'Job Version'); 169 assert.equal( 170 allocationRow.cpu, 171 Math.floor(allocStats.resourceUsage.CpuStats.TotalTicks) / cpuUsed, 172 'CPU %' 173 ); 174 const roundedTicks = Math.floor( 175 allocStats.resourceUsage.CpuStats.TotalTicks 176 ); 177 assert.equal( 178 allocationRow.cpuTooltip, 179 `${formatHertz(roundedTicks, 'MHz')} / ${formatHertz(cpuUsed, 'MHz')}`, 180 'Detailed CPU information is in a tooltip' 181 ); 182 assert.equal( 183 allocationRow.mem, 184 allocStats.resourceUsage.MemoryStats.RSS / 1024 / 1024 / memoryUsed, 185 'Memory used' 186 ); 187 assert.equal( 188 allocationRow.memTooltip, 189 `${formatBytes( 190 allocStats.resourceUsage.MemoryStats.RSS 191 )} / ${formatBytes(memoryUsed, 'MiB')}`, 192 'Detailed memory information is in a tooltip' 193 ); 194 }); 195 }); 196 197 test('each allocation should link to the allocation detail page', async function (assert) { 198 const controller = plugin.controllers.models 199 .sortBy('updateTime') 200 .reverse()[0]; 201 202 await PluginDetail.visit({ id: plugin.id }); 203 await PluginDetail.controllerAllocations.objectAt(0).visit(); 204 205 assert.equal(currentURL(), `/allocations/${controller.allocID}`); 206 }); 207 208 test('when there are no plugin allocations, the tables present empty states', async function (assert) { 209 const emptyPlugin = server.create('csi-plugin', { 210 controllerRequired: true, 211 controllersHealthy: 0, 212 controllersExpected: 0, 213 nodesHealthy: 0, 214 nodesExpected: 0, 215 }); 216 217 await PluginDetail.visit({ id: emptyPlugin.id }); 218 219 assert.ok(PluginDetail.controllerTableIsEmpty); 220 assert.equal( 221 PluginDetail.controllerEmptyState.headline, 222 'No Controller Plugin Allocations' 223 ); 224 225 assert.ok(PluginDetail.nodeTableIsEmpty); 226 assert.equal( 227 PluginDetail.nodeEmptyState.headline, 228 'No Node Plugin Allocations' 229 ); 230 }); 231 232 test('when the plugin is node-only, the controller information is omitted', async function (assert) { 233 const nodeOnlyPlugin = server.create('csi-plugin', { 234 controllerRequired: false, 235 }); 236 237 await PluginDetail.visit({ id: nodeOnlyPlugin.id }); 238 239 assert.notOk(PluginDetail.controllerAvailabilityIsPresent); 240 assert.ok(PluginDetail.nodeAvailabilityIsPresent); 241 242 assert.notOk(PluginDetail.controllerHealthIsPresent); 243 assert.notOk(PluginDetail.controllerTableIsPresent); 244 }); 245 246 test('when there are more than 10 controller or node allocations, only 10 are shown', async function (assert) { 247 const manyAllocationsPlugin = server.create('csi-plugin', { 248 shallow: true, 249 controllerRequired: false, 250 nodesExpected: 15, 251 }); 252 253 await PluginDetail.visit({ id: manyAllocationsPlugin.id }); 254 255 assert.equal(PluginDetail.nodeAllocations.length, 10); 256 }); 257 258 test('the View All links under each allocation table link to a filtered view of the plugins allocation list', async function (assert) { 259 const serialize = (arr) => window.encodeURIComponent(JSON.stringify(arr)); 260 261 await PluginDetail.visit({ id: plugin.id }); 262 assert.ok( 263 PluginDetail.goToControllerAllocationsText.includes( 264 plugin.controllers.models.length 265 ) 266 ); 267 await PluginDetail.goToControllerAllocations(); 268 assert.equal( 269 currentURL(), 270 `/csi/plugins/${plugin.id}/allocations?type=${serialize(['controller'])}` 271 ); 272 273 await PluginDetail.visit({ id: plugin.id }); 274 assert.ok( 275 PluginDetail.goToNodeAllocationsText.includes(plugin.nodes.models.length) 276 ); 277 await PluginDetail.goToNodeAllocations(); 278 assert.equal( 279 currentURL(), 280 `/csi/plugins/${plugin.id}/allocations?type=${serialize(['node'])}` 281 ); 282 }); 283 });