github.com/ferranbt/nomad@v0.9.3-0.20190607002617-85c449b7667c/ui/tests/acceptance/task-detail-test.js (about) 1 import { currentURL } from '@ember/test-helpers'; 2 import { module, test } from 'qunit'; 3 import { setupApplicationTest } from 'ember-qunit'; 4 import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; 5 import Task from 'nomad-ui/tests/pages/allocations/task/detail'; 6 import moment from 'moment'; 7 8 let allocation; 9 let task; 10 11 module('Acceptance | task detail', function(hooks) { 12 setupApplicationTest(hooks); 13 setupMirage(hooks); 14 15 hooks.beforeEach(async function() { 16 server.create('agent'); 17 server.create('node'); 18 server.create('job', { createAllocations: false }); 19 allocation = server.create('allocation', 'withTaskWithPorts', { clientStatus: 'running' }); 20 task = server.db.taskStates.where({ allocationId: allocation.id })[0]; 21 22 await Task.visit({ id: allocation.id, name: task.name }); 23 }); 24 25 test('/allocation/:id/:task_name should name the task and list high-level task information', async function(assert) { 26 assert.ok(Task.title.includes(task.name), 'Task name'); 27 assert.ok(Task.state.includes(task.state), 'Task state'); 28 29 assert.ok( 30 Task.startedAt.includes(moment(task.startedAt).format("MMM DD, 'YY HH:mm:ss ZZ")), 31 'Task started at' 32 ); 33 }); 34 35 test('breadcrumbs match jobs / job / task group / allocation / task', async function(assert) { 36 const { jobId, taskGroup } = allocation; 37 const job = server.db.jobs.find(jobId); 38 39 const shortId = allocation.id.split('-')[0]; 40 41 assert.equal(Task.breadcrumbFor('jobs.index').text, 'Jobs', 'Jobs is the first breadcrumb'); 42 assert.equal( 43 Task.breadcrumbFor('jobs.job.index').text, 44 job.name, 45 'Job is the second breadcrumb' 46 ); 47 assert.equal( 48 Task.breadcrumbFor('jobs.job.task-group').text, 49 taskGroup, 50 'Task Group is the third breadcrumb' 51 ); 52 assert.equal( 53 Task.breadcrumbFor('allocations.allocation').text, 54 shortId, 55 'Allocation short id is the fourth breadcrumb' 56 ); 57 assert.equal( 58 Task.breadcrumbFor('allocations.allocation.task').text, 59 task.name, 60 'Task name is the fifth breadcrumb' 61 ); 62 63 await Task.breadcrumbFor('jobs.index').visit(); 64 assert.equal(currentURL(), '/jobs', 'Jobs breadcrumb links correctly'); 65 66 await Task.visit({ id: allocation.id, name: task.name }); 67 await Task.breadcrumbFor('jobs.job.index').visit(); 68 assert.equal(currentURL(), `/jobs/${job.id}`, 'Job breadcrumb links correctly'); 69 70 await Task.visit({ id: allocation.id, name: task.name }); 71 await Task.breadcrumbFor('jobs.job.task-group').visit(); 72 assert.equal( 73 currentURL(), 74 `/jobs/${job.id}/${taskGroup}`, 75 'Task Group breadcrumb links correctly' 76 ); 77 78 await Task.visit({ id: allocation.id, name: task.name }); 79 await Task.breadcrumbFor('allocations.allocation').visit(); 80 assert.equal( 81 currentURL(), 82 `/allocations/${allocation.id}`, 83 'Allocations breadcrumb links correctly' 84 ); 85 }); 86 87 test('/allocation/:id/:task_name should include resource utilization graphs', async function(assert) { 88 assert.equal(Task.resourceCharts.length, 2, 'Two resource utilization graphs'); 89 assert.equal(Task.resourceCharts.objectAt(0).name, 'CPU', 'First chart is CPU'); 90 assert.equal(Task.resourceCharts.objectAt(1).name, 'Memory', 'Second chart is Memory'); 91 }); 92 93 test('the addresses table lists all reserved and dynamic ports', async function(assert) { 94 const taskResources = allocation.taskResourceIds 95 .map(id => server.db.taskResources.find(id)) 96 .find(resources => resources.name === task.name); 97 const reservedPorts = taskResources.resources.Networks[0].ReservedPorts; 98 const dynamicPorts = taskResources.resources.Networks[0].DynamicPorts; 99 const addresses = reservedPorts.concat(dynamicPorts); 100 101 assert.equal(Task.addresses.length, addresses.length, 'All addresses are listed'); 102 }); 103 104 test('each address row shows the label and value of the address', async function(assert) { 105 const taskResources = allocation.taskResourceIds 106 .map(id => server.db.taskResources.find(id)) 107 .findBy('name', task.name); 108 const networkAddress = taskResources.resources.Networks[0].IP; 109 const reservedPorts = taskResources.resources.Networks[0].ReservedPorts; 110 const dynamicPorts = taskResources.resources.Networks[0].DynamicPorts; 111 const address = reservedPorts.concat(dynamicPorts).sortBy('Label')[0]; 112 113 const addressRow = Task.addresses.objectAt(0); 114 assert.equal( 115 addressRow.isDynamic, 116 reservedPorts.includes(address) ? 'No' : 'Yes', 117 'Dynamic port is denoted as such' 118 ); 119 assert.equal(addressRow.name, address.Label, 'Label'); 120 assert.equal(addressRow.address, `${networkAddress}:${address.Value}`, 'Value'); 121 }); 122 123 test('the events table lists all recent events', async function(assert) { 124 const events = server.db.taskEvents.where({ taskStateId: task.id }); 125 126 assert.equal(Task.events.length, events.length, `Lists ${events.length} events`); 127 }); 128 129 test('each recent event should list the time, type, and description of the event', async function(assert) { 130 const event = server.db.taskEvents.where({ taskStateId: task.id })[0]; 131 const recentEvent = Task.events.objectAt(Task.events.length - 1); 132 133 assert.equal( 134 recentEvent.time, 135 moment(event.time / 1000000).format("MMM DD, 'YY HH:mm:ss ZZ"), 136 'Event timestamp' 137 ); 138 assert.equal(recentEvent.type, event.type, 'Event type'); 139 assert.equal(recentEvent.message, event.displayMessage, 'Event message'); 140 }); 141 142 test('when the allocation is not found, the application errors', async function(assert) { 143 await Task.visit({ id: 'not-a-real-allocation', name: task.name }); 144 145 assert.equal( 146 server.pretender.handledRequests.findBy('status', 404).url, 147 '/v1/allocation/not-a-real-allocation', 148 'A request to the nonexistent allocation is made' 149 ); 150 assert.equal( 151 currentURL(), 152 `/allocations/not-a-real-allocation/${task.name}`, 153 'The URL persists' 154 ); 155 assert.ok(Task.error.isPresent, 'Error message is shown'); 156 assert.equal(Task.error.title, 'Not Found', 'Error message is for 404'); 157 }); 158 159 test('when the allocation is found but the task is not, the application errors', async function(assert) { 160 await Task.visit({ id: allocation.id, name: 'not-a-real-task-name' }); 161 162 assert.ok( 163 server.pretender.handledRequests 164 .filterBy('status', 200) 165 .mapBy('url') 166 .includes(`/v1/allocation/${allocation.id}`), 167 'A request to the allocation is made successfully' 168 ); 169 assert.equal( 170 currentURL(), 171 `/allocations/${allocation.id}/not-a-real-task-name`, 172 'The URL persists' 173 ); 174 assert.ok(Task.error.isPresent, 'Error message is shown'); 175 assert.equal(Task.error.title, 'Not Found', 'Error message is for 404'); 176 }); 177 178 test('task can be restarted', async function(assert) { 179 await Task.restart.idle(); 180 await Task.restart.confirm(); 181 182 const request = server.pretender.handledRequests.findBy('method', 'PUT'); 183 assert.equal( 184 request.url, 185 `/v1/client/allocation/${allocation.id}/restart`, 186 'Restart request is made for the allocation' 187 ); 188 189 assert.deepEqual( 190 JSON.parse(request.requestBody), 191 { TaskName: task.name }, 192 'Restart request is made for the correct task' 193 ); 194 }); 195 196 test('when task restart fails, an error message is shown', async function(assert) { 197 server.pretender.put('/v1/client/allocation/:id/restart', () => [403, {}, '']); 198 199 await Task.restart.idle(); 200 await Task.restart.confirm(); 201 202 assert.ok(Task.inlineError.isShown, 'Inline error is shown'); 203 assert.ok(Task.inlineError.title.includes('Could Not Restart Task'), 'Title is descriptive'); 204 assert.ok( 205 /ACL token.+?allocation lifecycle/.test(Task.inlineError.message), 206 'Message mentions ACLs and the appropriate permission' 207 ); 208 209 await Task.inlineError.dismiss(); 210 211 assert.notOk(Task.inlineError.isShown, 'Inline error is no longer shown'); 212 }); 213 }); 214 215 module('Acceptance | task detail (no addresses)', function(hooks) { 216 setupApplicationTest(hooks); 217 setupMirage(hooks); 218 219 hooks.beforeEach(async function() { 220 server.create('agent'); 221 server.create('node'); 222 server.create('job'); 223 allocation = server.create('allocation', 'withoutTaskWithPorts', { clientStatus: 'running' }); 224 task = server.db.taskStates.where({ allocationId: allocation.id })[0]; 225 226 await Task.visit({ id: allocation.id, name: task.name }); 227 }); 228 229 test('when the task has no addresses, the addresses table is not shown', async function(assert) { 230 assert.notOk(Task.hasAddresses, 'No addresses table'); 231 }); 232 }); 233 234 module('Acceptance | task detail (different namespace)', function(hooks) { 235 setupApplicationTest(hooks); 236 setupMirage(hooks); 237 238 hooks.beforeEach(async function() { 239 server.create('agent'); 240 server.create('node'); 241 server.create('namespace'); 242 server.create('namespace', { id: 'other-namespace' }); 243 server.create('job', { createAllocations: false, namespaceId: 'other-namespace' }); 244 allocation = server.create('allocation', 'withTaskWithPorts', { clientStatus: 'running' }); 245 task = server.db.taskStates.where({ allocationId: allocation.id })[0]; 246 247 await Task.visit({ id: allocation.id, name: task.name }); 248 }); 249 250 test('breadcrumbs match jobs / job / task group / allocation / task', async function(assert) { 251 const { jobId, taskGroup } = allocation; 252 const job = server.db.jobs.find(jobId); 253 254 await Task.breadcrumbFor('jobs.index').visit(); 255 assert.equal( 256 currentURL(), 257 '/jobs?namespace=other-namespace', 258 'Jobs breadcrumb links correctly' 259 ); 260 261 await Task.visit({ id: allocation.id, name: task.name }); 262 await Task.breadcrumbFor('jobs.job.index').visit(); 263 assert.equal( 264 currentURL(), 265 `/jobs/${job.id}?namespace=other-namespace`, 266 'Job breadcrumb links correctly' 267 ); 268 269 await Task.visit({ id: allocation.id, name: task.name }); 270 await Task.breadcrumbFor('jobs.job.task-group').visit(); 271 assert.equal( 272 currentURL(), 273 `/jobs/${job.id}/${taskGroup}?namespace=other-namespace`, 274 'Task Group breadcrumb links correctly' 275 ); 276 277 await Task.visit({ id: allocation.id, name: task.name }); 278 await Task.breadcrumbFor('allocations.allocation').visit(); 279 assert.equal( 280 currentURL(), 281 `/allocations/${allocation.id}`, 282 'Allocations breadcrumb links correctly' 283 ); 284 }); 285 }); 286 287 module('Acceptance | task detail (not running)', function(hooks) { 288 setupApplicationTest(hooks); 289 setupMirage(hooks); 290 291 hooks.beforeEach(async function() { 292 server.create('agent'); 293 server.create('node'); 294 server.create('namespace'); 295 server.create('namespace', { id: 'other-namespace' }); 296 server.create('job', { createAllocations: false, namespaceId: 'other-namespace' }); 297 allocation = server.create('allocation', 'withTaskWithPorts', { clientStatus: 'complete' }); 298 task = server.db.taskStates.where({ allocationId: allocation.id })[0]; 299 300 await Task.visit({ id: allocation.id, name: task.name }); 301 }); 302 303 test('when the allocation for a task is not running, the resource utilization graphs are replaced by an empty message', async function(assert) { 304 assert.equal(Task.resourceCharts.length, 0, 'No resource charts'); 305 assert.equal(Task.resourceEmptyMessage, "Task isn't running", 'Empty message is appropriate'); 306 }); 307 });