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