github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/ui/tests/acceptance/task-fs-test.js (about) 1 import { currentURL, visit } from '@ember/test-helpers'; 2 import { Promise } from 'rsvp'; 3 import { module, test } from 'qunit'; 4 import { setupApplicationTest } from 'ember-qunit'; 5 import moment from 'moment'; 6 7 import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; 8 import Response from 'ember-cli-mirage/response'; 9 10 import { formatBytes } from 'nomad-ui/helpers/format-bytes'; 11 import { filesForPath } from 'nomad-ui/mirage/config'; 12 13 import FS from 'nomad-ui/tests/pages/allocations/task/fs'; 14 15 let allocation; 16 let task; 17 let files; 18 19 const fileSort = (prop, files) => { 20 let dir = []; 21 let file = []; 22 files.forEach(f => { 23 if (f.isDir) { 24 dir.push(f); 25 } else { 26 file.push(f); 27 } 28 }); 29 30 return dir.sortBy(prop).concat(file.sortBy(prop)); 31 }; 32 33 module('Acceptance | task fs', function(hooks) { 34 setupApplicationTest(hooks); 35 setupMirage(hooks); 36 37 hooks.beforeEach(async function() { 38 server.create('agent'); 39 server.create('node', 'forceIPv4'); 40 const job = server.create('job', { createAllocations: false }); 41 42 allocation = server.create('allocation', { jobId: job.id, clientStatus: 'running' }); 43 task = server.schema.taskStates.where({ allocationId: allocation.id }).models[0]; 44 task.name = 'task-name'; 45 task.save(); 46 47 // Reset files 48 files = []; 49 50 // Nested files 51 files.push(server.create('allocFile', { isDir: true, name: 'directory' })); 52 files.push(server.create('allocFile', { isDir: true, name: 'another', parent: files[0] })); 53 files.push( 54 server.create('allocFile', 'file', { 55 name: 'something.txt', 56 fileType: 'txt', 57 parent: files[1], 58 }) 59 ); 60 61 files.push(server.create('allocFile', { isDir: true, name: 'empty-directory' })); 62 files.push(server.create('allocFile', 'file', { fileType: 'txt' })); 63 files.push(server.create('allocFile', 'file', { fileType: 'txt' })); 64 }); 65 66 test('visiting /allocations/:allocation_id/:task_name/fs', async function(assert) { 67 await FS.visit({ id: allocation.id, name: task.name }); 68 assert.equal(currentURL(), `/allocations/${allocation.id}/${task.name}/fs`, 'No redirect'); 69 }); 70 71 test('when the task is not running, an empty state is shown', async function(assert) { 72 // The API 500s on stat when not running 73 this.server.get('/client/fs/stat/:allocation_id', () => { 74 return new Response(500, {}, 'no such file or directory'); 75 }); 76 77 task.update({ 78 finishedAt: new Date(), 79 }); 80 81 await FS.visit({ id: allocation.id, name: task.name }); 82 assert.ok(FS.hasEmptyState, 'Non-running task has no files'); 83 assert.ok( 84 FS.emptyState.headline.includes('Task is not Running'), 85 'Empty state explains the condition' 86 ); 87 }); 88 89 test('visiting /allocations/:allocation_id/:task_name/fs/:path', async function(assert) { 90 const paths = ['some-file.log', 'a/deep/path/to/a/file.log', '/', 'Unicode™®']; 91 92 const testPath = async filePath => { 93 let pathWithLeadingSlash = filePath; 94 95 if (!pathWithLeadingSlash.startsWith('/')) { 96 pathWithLeadingSlash = `/${filePath}`; 97 } 98 99 await FS.visitPath({ id: allocation.id, name: task.name, path: filePath }); 100 assert.equal( 101 currentURL(), 102 `/allocations/${allocation.id}/${task.name}/fs/${encodeURIComponent(filePath)}`, 103 'No redirect' 104 ); 105 assert.equal( 106 document.title, 107 `${pathWithLeadingSlash} - Task ${task.name} filesystem - Nomad` 108 ); 109 assert.equal(FS.breadcrumbsText, `${task.name} ${filePath.replace(/\//g, ' ')}`.trim()); 110 }; 111 112 await paths.reduce(async (prev, filePath) => { 113 await prev; 114 return testPath(filePath); 115 }, Promise.resolve()); 116 }); 117 118 test('navigating allocation filesystem', async function(assert) { 119 await FS.visitPath({ id: allocation.id, name: task.name, path: '/' }); 120 121 const sortedFiles = fileSort('name', filesForPath(this.server.schema.allocFiles, '').models); 122 123 assert.ok(FS.fileViewer.isHidden); 124 125 assert.equal(FS.directoryEntries.length, 4); 126 127 assert.equal(FS.breadcrumbsText, task.name); 128 129 assert.equal(FS.breadcrumbs.length, 1); 130 assert.ok(FS.breadcrumbs[0].isActive); 131 assert.equal(FS.breadcrumbs[0].text, 'task-name'); 132 133 FS.directoryEntries[0].as(directory => { 134 const fileRecord = sortedFiles[0]; 135 assert.equal(directory.name, fileRecord.name, 'directories should come first'); 136 assert.ok(directory.isDirectory); 137 assert.equal(directory.size, '', 'directory sizes are hidden'); 138 assert.equal(directory.lastModified, moment(fileRecord.modTime).fromNow()); 139 assert.notOk(directory.path.includes('//'), 'paths shouldn’t have redundant separators'); 140 }); 141 142 FS.directoryEntries[2].as(file => { 143 const fileRecord = sortedFiles[2]; 144 assert.equal(file.name, fileRecord.name); 145 assert.ok(file.isFile); 146 assert.equal(file.size, formatBytes([fileRecord.size])); 147 assert.equal(file.lastModified, moment(fileRecord.modTime).fromNow()); 148 }); 149 150 await FS.directoryEntries[0].visit(); 151 152 assert.equal(FS.directoryEntries.length, 1); 153 154 assert.equal(FS.breadcrumbs.length, 2); 155 assert.equal(FS.breadcrumbsText, `${task.name} ${files[0].name}`); 156 157 assert.notOk(FS.breadcrumbs[0].isActive); 158 159 assert.equal(FS.breadcrumbs[1].text, files[0].name); 160 assert.ok(FS.breadcrumbs[1].isActive); 161 162 await FS.directoryEntries[0].visit(); 163 164 assert.equal(FS.directoryEntries.length, 1); 165 assert.notOk( 166 FS.directoryEntries[0].path.includes('//'), 167 'paths shouldn’t have redundant separators' 168 ); 169 170 assert.equal(FS.breadcrumbs.length, 3); 171 assert.equal(FS.breadcrumbsText, `${task.name} ${files[0].name} ${files[1].name}`); 172 assert.equal(FS.breadcrumbs[2].text, files[1].name); 173 174 assert.notOk( 175 FS.breadcrumbs[0].path.includes('//'), 176 'paths shouldn’t have redundant separators' 177 ); 178 assert.notOk( 179 FS.breadcrumbs[1].path.includes('//'), 180 'paths shouldn’t have redundant separators' 181 ); 182 183 await FS.breadcrumbs[1].visit(); 184 assert.equal(FS.breadcrumbsText, `${task.name} ${files[0].name}`); 185 assert.equal(FS.breadcrumbs.length, 2); 186 }); 187 188 test('sorting allocation filesystem directory', async function(assert) { 189 this.server.get('/client/fs/ls/:allocation_id', () => { 190 return [ 191 { 192 Name: 'aaa-big-old-file', 193 IsDir: false, 194 Size: 19190000, 195 ModTime: moment() 196 .subtract(1, 'year') 197 .format(), 198 }, 199 { 200 Name: 'mmm-small-mid-file', 201 IsDir: false, 202 Size: 1919, 203 ModTime: moment() 204 .subtract(6, 'month') 205 .format(), 206 }, 207 { 208 Name: 'zzz-med-new-file', 209 IsDir: false, 210 Size: 191900, 211 ModTime: moment().format(), 212 }, 213 { 214 Name: 'aaa-big-old-directory', 215 IsDir: true, 216 Size: 19190000, 217 ModTime: moment() 218 .subtract(1, 'year') 219 .format(), 220 }, 221 { 222 Name: 'mmm-small-mid-directory', 223 IsDir: true, 224 Size: 1919, 225 ModTime: moment() 226 .subtract(6, 'month') 227 .format(), 228 }, 229 { 230 Name: 'zzz-med-new-directory', 231 IsDir: true, 232 Size: 191900, 233 ModTime: moment().format(), 234 }, 235 ]; 236 }); 237 238 await FS.visitPath({ id: allocation.id, name: task.name, path: '/' }); 239 240 assert.deepEqual(FS.directoryEntryNames(), [ 241 'aaa-big-old-directory', 242 'mmm-small-mid-directory', 243 'zzz-med-new-directory', 244 'aaa-big-old-file', 245 'mmm-small-mid-file', 246 'zzz-med-new-file', 247 ]); 248 249 await FS.sortBy('Name'); 250 251 assert.deepEqual(FS.directoryEntryNames(), [ 252 'zzz-med-new-file', 253 'mmm-small-mid-file', 254 'aaa-big-old-file', 255 'zzz-med-new-directory', 256 'mmm-small-mid-directory', 257 'aaa-big-old-directory', 258 ]); 259 260 await FS.sortBy('ModTime'); 261 262 assert.deepEqual(FS.directoryEntryNames(), [ 263 'zzz-med-new-file', 264 'mmm-small-mid-file', 265 'aaa-big-old-file', 266 'zzz-med-new-directory', 267 'mmm-small-mid-directory', 268 'aaa-big-old-directory', 269 ]); 270 271 await FS.sortBy('ModTime'); 272 273 assert.deepEqual(FS.directoryEntryNames(), [ 274 'aaa-big-old-directory', 275 'mmm-small-mid-directory', 276 'zzz-med-new-directory', 277 'aaa-big-old-file', 278 'mmm-small-mid-file', 279 'zzz-med-new-file', 280 ]); 281 282 await FS.sortBy('Size'); 283 284 assert.deepEqual( 285 FS.directoryEntryNames(), 286 [ 287 'aaa-big-old-file', 288 'zzz-med-new-file', 289 'mmm-small-mid-file', 290 'zzz-med-new-directory', 291 'mmm-small-mid-directory', 292 'aaa-big-old-directory', 293 ], 294 'expected files to be sorted by descending size and directories to be sorted by descending name' 295 ); 296 297 await FS.sortBy('Size'); 298 299 assert.deepEqual( 300 FS.directoryEntryNames(), 301 [ 302 'aaa-big-old-directory', 303 'mmm-small-mid-directory', 304 'zzz-med-new-directory', 305 'mmm-small-mid-file', 306 'zzz-med-new-file', 307 'aaa-big-old-file', 308 ], 309 'expected directories to be sorted by name and files to be sorted by ascending size' 310 ); 311 }); 312 313 test('viewing a file', async function(assert) { 314 const node = server.db.nodes.find(allocation.nodeId); 315 316 server.get(`http://${node.httpAddr}/v1/client/fs/readat/:allocation_id`, function() { 317 return new Response(500); 318 }); 319 320 await FS.visitPath({ id: allocation.id, name: task.name, path: '/' }); 321 322 const sortedFiles = fileSort('name', filesForPath(this.server.schema.allocFiles, '').models); 323 const fileRecord = sortedFiles.find(f => !f.isDir); 324 const fileIndex = sortedFiles.indexOf(fileRecord); 325 326 await FS.directoryEntries[fileIndex].visit(); 327 328 assert.equal(FS.breadcrumbsText, `${task.name} ${fileRecord.name}`); 329 330 assert.ok(FS.fileViewer.isPresent); 331 332 const requests = this.server.pretender.handledRequests; 333 const secondAttempt = requests.pop(); 334 const firstAttempt = requests.pop(); 335 336 assert.equal( 337 firstAttempt.url.split('?')[0], 338 `//${node.httpAddr}/v1/client/fs/readat/${allocation.id}`, 339 'Client is hit first' 340 ); 341 assert.equal(firstAttempt.status, 500, 'Client request fails'); 342 assert.equal( 343 secondAttempt.url.split('?')[0], 344 `/v1/client/fs/readat/${allocation.id}`, 345 'Server is hit second' 346 ); 347 }); 348 349 test('viewing an empty directory', async function(assert) { 350 await FS.visitPath({ id: allocation.id, name: task.name, path: '/empty-directory' }); 351 352 assert.ok(FS.isEmptyDirectory); 353 }); 354 355 test('viewing paths that produce stat API errors', async function(assert) { 356 this.server.get('/client/fs/stat/:allocation_id', () => { 357 return new Response(500, {}, 'no such file or directory'); 358 }); 359 360 await FS.visitPath({ id: allocation.id, name: task.name, path: '/what-is-this' }); 361 assert.equal(FS.error.title, 'Not Found', '500 is interpreted as 404'); 362 363 await visit('/'); 364 365 this.server.get('/client/fs/stat/:allocation_id', () => { 366 return new Response(999); 367 }); 368 369 await FS.visitPath({ id: allocation.id, name: task.name, path: '/what-is-this' }); 370 assert.equal(FS.error.title, 'Error', 'other statuses are passed through'); 371 }); 372 373 test('viewing paths that produce ls API errors', async function(assert) { 374 this.server.get('/client/fs/ls/:allocation_id', () => { 375 return new Response(500, {}, 'no such file or directory'); 376 }); 377 378 await FS.visitPath({ id: allocation.id, name: task.name, path: files[0].name }); 379 assert.equal(FS.error.title, 'Not Found', '500 is interpreted as 404'); 380 381 await visit('/'); 382 383 this.server.get('/client/fs/ls/:allocation_id', () => { 384 return new Response(999); 385 }); 386 387 await FS.visitPath({ id: allocation.id, name: task.name, path: files[0].name }); 388 assert.equal(FS.error.title, 'Error', 'other statuses are passed through'); 389 }); 390 });