github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/ui/tests/acceptance/job-deployments-test.js (about) 1 import { currentURL } from '@ember/test-helpers'; 2 import { get } from '@ember/object'; 3 import { module, test } from 'qunit'; 4 import { setupApplicationTest } from 'ember-qunit'; 5 import { setupMirage } from 'ember-cli-mirage/test-support'; 6 import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit'; 7 import moment from 'moment'; 8 import Deployments from 'nomad-ui/tests/pages/jobs/job/deployments'; 9 10 const sum = (list, key, getter = a => a) => 11 list.reduce((sum, item) => sum + getter(get(item, key)), 0); 12 13 let job; 14 let deployments; 15 let sortedDeployments; 16 17 module('Acceptance | job deployments', function(hooks) { 18 setupApplicationTest(hooks); 19 setupMirage(hooks); 20 21 hooks.beforeEach(function() { 22 server.create('node'); 23 job = server.create('job'); 24 deployments = server.schema.deployments.where({ jobId: job.id }); 25 sortedDeployments = deployments.sort((a, b) => { 26 const aVersion = server.db.jobVersions.findBy({ jobId: a.jobId, version: a.versionNumber }); 27 const bVersion = server.db.jobVersions.findBy({ jobId: b.jobId, version: b.versionNumber }); 28 if (aVersion.submitTime < bVersion.submitTime) { 29 return 1; 30 } else if (aVersion.submitTime > bVersion.submitTime) { 31 return -1; 32 } 33 return 0; 34 }); 35 }); 36 37 test('it passes an accessibility audit', async function(assert) { 38 await Deployments.visit({ id: job.id }); 39 await a11yAudit(assert); 40 }); 41 42 test('/jobs/:id/deployments should list all job deployments', async function(assert) { 43 await Deployments.visit({ id: job.id }); 44 45 assert.ok( 46 Deployments.deployments.length, 47 deployments.length, 48 'Each deployment gets a row in the timeline' 49 ); 50 assert.equal(document.title, `Job ${job.name} deployments - Nomad`); 51 }); 52 53 test('each deployment mentions the deployment shortId, status, version, and time since it was submitted', async function(assert) { 54 await Deployments.visit({ id: job.id }); 55 56 const deployment = sortedDeployments.models[0]; 57 const version = server.db.jobVersions.findBy({ 58 jobId: deployment.jobId, 59 version: deployment.versionNumber, 60 }); 61 const deploymentRow = Deployments.deployments.objectAt(0); 62 63 assert.ok(deploymentRow.text.includes(deployment.id.split('-')[0]), 'Short ID'); 64 assert.equal(deploymentRow.status, deployment.status, 'Status'); 65 assert.ok( 66 deploymentRow.statusClass.includes(classForStatus(deployment.status)), 67 'Status Class' 68 ); 69 assert.ok(deploymentRow.version.includes(deployment.versionNumber), 'Version #'); 70 assert.ok( 71 deploymentRow.submitTime.includes(moment(version.submitTime / 1000000).fromNow()), 72 'Submit time ago' 73 ); 74 }); 75 76 test('when the deployment is running and needs promotion, the deployment item says so', async function(assert) { 77 // Ensure the deployment needs deployment 78 const deployment = sortedDeployments.models[0]; 79 const taskGroupSummary = deployment.deploymentTaskGroupSummaryIds.map(id => 80 server.schema.deploymentTaskGroupSummaries.find(id) 81 )[0]; 82 83 deployment.update('status', 'running'); 84 deployment.save(); 85 86 taskGroupSummary.update({ 87 desiredCanaries: 1, 88 placedCanaries: [], 89 promoted: false, 90 }); 91 92 taskGroupSummary.save(); 93 94 await Deployments.visit({ id: job.id }); 95 96 const deploymentRow = Deployments.deployments.objectAt(0); 97 assert.ok(deploymentRow.promotionIsRequired, 'Requires Promotion badge found'); 98 }); 99 100 test('each deployment item can be opened to show details', async function(assert) { 101 await Deployments.visit({ id: job.id }); 102 103 const deploymentRow = Deployments.deployments.objectAt(0); 104 assert.notOk(deploymentRow.hasDetails, 'No deployment body'); 105 106 await deploymentRow.toggle(); 107 assert.ok(deploymentRow.hasDetails, 'Deployment body found'); 108 }); 109 110 test('when open, a deployment shows the deployment metrics', async function(assert) { 111 await Deployments.visit({ id: job.id }); 112 113 const deployment = sortedDeployments.models[0]; 114 const deploymentRow = Deployments.deployments.objectAt(0); 115 const taskGroupSummaries = deployment.deploymentTaskGroupSummaryIds.map(id => 116 server.db.deploymentTaskGroupSummaries.find(id) 117 ); 118 119 await deploymentRow.toggle(); 120 121 assert.equal( 122 deploymentRow.metricFor('canaries').text, 123 `${sum(taskGroupSummaries, 'placedCanaries', a => a.length)} / ${sum( 124 taskGroupSummaries, 125 'desiredCanaries' 126 )}`, 127 'Canaries, both places and desired, are in the metrics' 128 ); 129 130 assert.equal( 131 deploymentRow.metricFor('placed').text, 132 sum(taskGroupSummaries, 'placedAllocs'), 133 'Placed allocs aggregates across task groups' 134 ); 135 136 assert.equal( 137 deploymentRow.metricFor('desired').text, 138 sum(taskGroupSummaries, 'desiredTotal'), 139 'Desired allocs aggregates across task groups' 140 ); 141 142 assert.equal( 143 deploymentRow.metricFor('healthy').text, 144 sum(taskGroupSummaries, 'healthyAllocs'), 145 'Healthy allocs aggregates across task groups' 146 ); 147 148 assert.equal( 149 deploymentRow.metricFor('unhealthy').text, 150 sum(taskGroupSummaries, 'unhealthyAllocs'), 151 'Unhealthy allocs aggregates across task groups' 152 ); 153 154 assert.equal( 155 deploymentRow.notification, 156 deployment.statusDescription, 157 'Status description is in the metrics block' 158 ); 159 }); 160 161 test('when open, a deployment shows a list of all task groups and their respective stats', async function(assert) { 162 await Deployments.visit({ id: job.id }); 163 164 const deployment = sortedDeployments.models[0]; 165 const deploymentRow = Deployments.deployments.objectAt(0); 166 const taskGroupSummaries = deployment.deploymentTaskGroupSummaryIds.map(id => 167 server.db.deploymentTaskGroupSummaries.find(id) 168 ); 169 170 await deploymentRow.toggle(); 171 172 assert.ok(deploymentRow.hasTaskGroups, 'Task groups found'); 173 174 assert.equal( 175 deploymentRow.taskGroups.length, 176 taskGroupSummaries.length, 177 'One row per task group' 178 ); 179 180 const taskGroup = taskGroupSummaries[0]; 181 const taskGroupRow = deploymentRow.taskGroups.findOneBy('name', taskGroup.name); 182 183 assert.equal(taskGroupRow.name, taskGroup.name, 'Name'); 184 assert.equal(taskGroupRow.promotion, promotionTestForTaskGroup(taskGroup), 'Needs Promotion'); 185 assert.equal(taskGroupRow.autoRevert, taskGroup.autoRevert ? 'Yes' : 'No', 'Auto Revert'); 186 assert.equal( 187 taskGroupRow.canaries, 188 `${taskGroup.placedCanaries.length} / ${taskGroup.desiredCanaries}`, 189 'Canaries' 190 ); 191 assert.equal( 192 taskGroupRow.allocs, 193 `${taskGroup.placedAllocs} / ${taskGroup.desiredTotal}`, 194 'Allocs' 195 ); 196 assert.equal(taskGroupRow.healthy, taskGroup.healthyAllocs, 'Healthy Allocs'); 197 assert.equal(taskGroupRow.unhealthy, taskGroup.unhealthyAllocs, 'Unhealthy Allocs'); 198 assert.equal( 199 taskGroupRow.progress, 200 moment(taskGroup.requireProgressBy).format("MMM DD, 'YY HH:mm:ss ZZ"), 201 'Progress By' 202 ); 203 }); 204 205 test('when open, a deployment shows a list of all allocations for the deployment', async function(assert) { 206 await Deployments.visit({ id: job.id }); 207 208 const deployment = sortedDeployments.models[0]; 209 const deploymentRow = Deployments.deployments.objectAt(0); 210 211 // TODO: Make this less brittle. This logic is copied from the mirage config, 212 // since there is no reference to allocations on the deployment model. 213 const allocations = server.db.allocations.where({ jobId: deployment.jobId }).slice(0, 3); 214 await deploymentRow.toggle(); 215 216 assert.ok(deploymentRow.hasAllocations, 'Allocations found'); 217 assert.equal(deploymentRow.allocations.length, allocations.length, 'One row per allocation'); 218 219 const allocation = allocations[0]; 220 const allocationRow = deploymentRow.allocations.objectAt(0); 221 222 assert.equal(allocationRow.shortId, allocation.id.split('-')[0], 'Allocation is as expected'); 223 }); 224 225 test('when the job for the deployments is not found, an error message is shown, but the URL persists', async function(assert) { 226 await Deployments.visit({ id: 'not-a-real-job' }); 227 228 assert.equal( 229 server.pretender.handledRequests 230 .filter(request => !request.url.includes('policy')) 231 .findBy('status', 404).url, 232 '/v1/job/not-a-real-job', 233 'A request to the nonexistent job is made' 234 ); 235 assert.equal(currentURL(), '/jobs/not-a-real-job/deployments', 'The URL persists'); 236 assert.ok(Deployments.error.isPresent, 'Error message is shown'); 237 assert.equal(Deployments.error.title, 'Not Found', 'Error message is for 404'); 238 }); 239 240 function classForStatus(status) { 241 const classMap = { 242 running: 'is-running', 243 successful: 'is-primary', 244 paused: 'is-light', 245 failed: 'is-error', 246 cancelled: 'is-cancelled', 247 }; 248 249 return classMap[status] || 'is-dark'; 250 } 251 252 function promotionTestForTaskGroup(taskGroup) { 253 if (taskGroup.desiredCanaries > 0 && taskGroup.promoted === false) { 254 return 'Yes'; 255 } else if (taskGroup.desiredCanaries > 0) { 256 return 'No'; 257 } 258 return 'N/A'; 259 } 260 });