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