2021-12-28 19:30:38 +00:00
/* eslint-disable qunit/require-expect */
/* eslint-disable qunit/no-conditional-assertions */
2020-10-29 12:46:42 +00:00
import { module , test } from 'qunit' ;
import { setupApplicationTest } from 'ember-qunit' ;
2020-11-04 18:22:24 +00:00
import { currentURL , visit } from '@ember/test-helpers' ;
2020-10-29 12:46:42 +00:00
import { setupMirage } from 'ember-cli-mirage/test-support' ;
import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit' ;
import Response from 'ember-cli-mirage/response' ;
import moment from 'moment' ;
2021-11-04 22:48:40 +00:00
import { formatBytes , formatHertz , replaceMinus } from 'nomad-ui/utils/units' ;
2020-10-29 12:46:42 +00:00
import Optimize from 'nomad-ui/tests/pages/optimize' ;
2020-12-10 17:51:22 +00:00
import Layout from 'nomad-ui/tests/pages/layout' ;
2020-10-29 12:46:42 +00:00
import JobsList from 'nomad-ui/tests/pages/jobs/list' ;
2022-03-08 17:28:36 +00:00
import collapseWhitespace from '../helpers/collapse-whitespace' ;
2020-10-29 12:46:42 +00:00
let managementToken , clientToken ;
function getLatestRecommendationSubmitTimeForJob ( job ) {
const tasks = job . taskGroups . models
. mapBy ( 'tasks.models' )
. reduce ( ( tasks , taskModels ) => tasks . concat ( taskModels ) , [ ] ) ;
const recommendations = tasks . reduce (
2021-12-28 16:08:12 +00:00
( recommendations , task ) =>
recommendations . concat ( task . recommendations . models ) ,
2020-10-29 12:46:42 +00:00
[ ]
) ;
return Math . max ( ... recommendations . mapBy ( 'submitTime' ) ) ;
}
2021-12-28 14:45:20 +00:00
module ( 'Acceptance | optimize' , function ( hooks ) {
2020-10-29 12:46:42 +00:00
setupApplicationTest ( hooks ) ;
setupMirage ( hooks ) ;
2021-12-28 14:45:20 +00:00
hooks . beforeEach ( async function ( ) {
2020-11-06 14:21:38 +00:00
server . create ( 'feature' , { name : 'Dynamic Application Sizing' } ) ;
2020-10-29 12:46:42 +00:00
server . create ( 'node' ) ;
server . createList ( 'namespace' , 2 ) ;
const jobs = server . createList ( 'job' , 2 , {
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 2 ,
namespaceId : server . db . namespaces [ 1 ] . id ,
} ) ;
jobs . sort ( ( jobA , jobB ) => {
return (
getLatestRecommendationSubmitTimeForJob ( jobB ) -
getLatestRecommendationSubmitTimeForJob ( jobA )
) ;
} ) ;
[ this . job1 , this . job2 ] = jobs ;
managementToken = server . create ( 'token' ) ;
clientToken = server . create ( 'token' ) ;
window . localStorage . clear ( ) ;
window . localStorage . nomadTokenSecret = managementToken . secretId ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'it passes an accessibility audit' , async function ( assert ) {
2020-10-29 12:46:42 +00:00
await Optimize . visit ( ) ;
await a11yAudit ( assert ) ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'lets recommendations be toggled, reports the choices to the recommendations API, and displays task group recommendations serially' , async function ( assert ) {
2020-10-29 12:46:42 +00:00
const currentTaskGroup = this . job1 . taskGroups . models [ 0 ] ;
const nextTaskGroup = this . job2 . taskGroups . models [ 0 ] ;
2021-02-16 14:43:18 +00:00
const currentTaskGroupHasCPURecommendation = currentTaskGroup . tasks . models
. mapBy ( 'recommendations.models' )
. flat ( )
2021-12-28 14:45:20 +00:00
. find ( ( r ) => r . resource === 'CPU' ) ;
2021-02-16 14:43:18 +00:00
2022-07-11 22:06:18 +00:00
const currentTaskGroupHasMemoryRecommendation =
currentTaskGroup . tasks . models
. mapBy ( 'recommendations.models' )
. flat ( )
. find ( ( r ) => r . resource === 'MemoryMB' ) ;
2021-02-16 14:43:18 +00:00
// If no CPU recommendation, will not be able to accept recommendation with all memory recommendations turned off
if ( ! currentTaskGroupHasCPURecommendation ) {
const currentTaskGroupTask = currentTaskGroup . tasks . models [ 0 ] ;
this . server . create ( 'recommendation' , {
task : currentTaskGroupTask ,
resource : 'CPU' ,
} ) ;
}
2022-07-11 22:06:18 +00:00
if ( ! currentTaskGroupHasMemoryRecommendation ) {
const currentTaskGroupTask = currentTaskGroup . tasks . models [ 0 ] ;
this . server . create ( 'recommendation' , {
task : currentTaskGroupTask ,
resource : 'MemoryMB' ,
} ) ;
}
2021-02-16 14:43:18 +00:00
await Optimize . visit ( ) ;
2020-12-10 17:51:22 +00:00
assert . equal ( Layout . breadcrumbFor ( 'optimize' ) . text , 'Recommendations' ) ;
2020-10-29 12:46:42 +00:00
assert . equal (
Optimize . recommendationSummaries [ 0 ] . slug ,
` ${ this . job1 . name } / ${ currentTaskGroup . name } `
) ;
2020-11-04 18:22:24 +00:00
assert . equal (
2020-12-10 17:51:22 +00:00
Layout . breadcrumbFor ( 'optimize.summary' ) . text ,
2020-11-04 18:22:24 +00:00
` ${ this . job1 . name } / ${ currentTaskGroup . name } `
) ;
2021-12-28 16:08:12 +00:00
assert . equal (
Optimize . recommendationSummaries [ 0 ] . namespace ,
this . job1 . namespace
) ;
2020-10-29 12:46:42 +00:00
assert . equal (
Optimize . recommendationSummaries [ 1 ] . slug ,
` ${ this . job2 . name } / ${ nextTaskGroup . name } `
) ;
const currentRecommendations = currentTaskGroup . tasks . models . reduce (
2021-12-28 16:08:12 +00:00
( recommendations , task ) =>
recommendations . concat ( task . recommendations . models ) ,
2020-10-29 12:46:42 +00:00
[ ]
) ;
2021-12-28 16:08:12 +00:00
const latestSubmitTime = Math . max (
... currentRecommendations . mapBy ( 'submitTime' )
) ;
2020-10-29 12:46:42 +00:00
2021-12-28 14:45:20 +00:00
Optimize . recommendationSummaries [ 0 ] . as ( ( summary ) => {
2020-10-29 12:46:42 +00:00
assert . equal (
summary . date ,
2021-12-28 16:08:12 +00:00
moment ( new Date ( latestSubmitTime / 1000000 ) ) . format (
'MMM DD HH:mm:ss ZZ'
)
2020-10-29 12:46:42 +00:00
) ;
const currentTaskGroupAllocations = server . schema . allocations . where ( {
jobId : currentTaskGroup . job . name ,
taskGroup : currentTaskGroup . name ,
} ) ;
assert . equal ( summary . allocationCount , currentTaskGroupAllocations . length ) ;
const { currCpu , currMem } = currentTaskGroup . tasks . models . reduce (
( currentResources , task ) => {
currentResources . currCpu += task . resources . CPU ;
currentResources . currMem += task . resources . MemoryMB ;
return currentResources ;
} ,
{ currCpu : 0 , currMem : 0 }
) ;
const { recCpu , recMem } = currentRecommendations . reduce (
( recommendedResources , recommendation ) => {
if ( recommendation . resource === 'CPU' ) {
recommendedResources . recCpu += recommendation . value ;
} else {
recommendedResources . recMem += recommendation . value ;
}
return recommendedResources ;
} ,
{ recCpu : 0 , recMem : 0 }
) ;
const cpuDiff = recCpu > 0 ? recCpu - currCpu : 0 ;
const memDiff = recMem > 0 ? recMem - currMem : 0 ;
const cpuSign = cpuDiff > 0 ? '+' : '' ;
const memSign = memDiff > 0 ? '+' : '' ;
const cpuDiffPercent = Math . round ( ( 100 * cpuDiff ) / currCpu ) ;
const memDiffPercent = Math . round ( ( 100 * memDiff ) / currMem ) ;
assert . equal (
2021-11-04 22:48:40 +00:00
replaceMinus ( summary . cpu ) ,
2021-12-28 16:08:12 +00:00
cpuDiff
? ` ${ cpuSign } ${ formatHertz (
cpuDiff ,
'MHz'
) } $ { cpuSign } $ { cpuDiffPercent } % `
: ''
2020-10-29 12:46:42 +00:00
) ;
assert . equal (
2021-11-04 22:48:40 +00:00
replaceMinus ( summary . memory ) ,
2021-12-28 16:08:12 +00:00
memDiff
? ` ${ memSign } ${ formattedMemDiff (
memDiff
) } $ { memSign } $ { memDiffPercent } % `
: ''
2020-10-29 12:46:42 +00:00
) ;
assert . equal (
2021-11-04 22:48:40 +00:00
replaceMinus ( summary . aggregateCpu ) ,
2021-03-29 23:16:48 +00:00
cpuDiff
2021-12-28 16:08:12 +00:00
? ` ${ cpuSign } ${ formatHertz (
cpuDiff * currentTaskGroupAllocations . length ,
'MHz'
) } `
2021-03-29 23:16:48 +00:00
: ''
2020-10-29 12:46:42 +00:00
) ;
assert . equal (
2021-11-04 22:48:40 +00:00
replaceMinus ( summary . aggregateMemory ) ,
2021-12-28 16:08:12 +00:00
memDiff
? ` ${ memSign } ${ formattedMemDiff (
memDiff * currentTaskGroupAllocations . length
) } `
: ''
2020-10-29 12:46:42 +00:00
) ;
} ) ;
assert . ok ( Optimize . recommendationSummaries [ 0 ] . isActive ) ;
assert . notOk ( Optimize . recommendationSummaries [ 1 ] . isActive ) ;
assert . equal ( Optimize . card . slug . jobName , this . job1 . name ) ;
assert . equal ( Optimize . card . slug . groupName , currentTaskGroup . name ) ;
const summaryMemoryBefore = Optimize . recommendationSummaries [ 0 ] . memory ;
let toggledAnything = true ;
// Toggle off all memory
if ( Optimize . card . togglesTable . toggleAllMemory . isPresent ) {
await Optimize . card . togglesTable . toggleAllMemory . toggle ( ) ;
assert . notOk ( Optimize . card . togglesTable . tasks [ 0 ] . memory . isActive ) ;
assert . notOk ( Optimize . card . togglesTable . tasks [ 1 ] . memory . isActive ) ;
} else if ( ! Optimize . card . togglesTable . tasks [ 0 ] . cpu . isDisabled ) {
await Optimize . card . togglesTable . tasks [ 0 ] . memory . toggle ( ) ;
} else {
toggledAnything = false ;
}
assert . equal (
Optimize . recommendationSummaries [ 0 ] . memory ,
summaryMemoryBefore ,
'toggling recommendations doesn’ t affect the summary table diffs'
) ;
const currentTaskIds = currentTaskGroup . tasks . models . mapBy ( 'id' ) ;
2021-12-28 14:45:20 +00:00
const taskIdFilter = ( task ) => currentTaskIds . includes ( task . taskId ) ;
2020-10-29 12:46:42 +00:00
const cpuRecommendationIds = server . schema . recommendations
. where ( { resource : 'CPU' } )
. models . filter ( taskIdFilter )
. mapBy ( 'id' ) ;
const memoryRecommendationIds = server . schema . recommendations
. where ( { resource : 'MemoryMB' } )
. models . filter ( taskIdFilter )
. mapBy ( 'id' ) ;
2021-12-28 16:08:12 +00:00
const appliedIds = toggledAnything
? cpuRecommendationIds
: memoryRecommendationIds ;
2020-10-29 12:46:42 +00:00
const dismissedIds = toggledAnything ? memoryRecommendationIds : [ ] ;
await Optimize . card . acceptButton . click ( ) ;
2021-12-28 16:08:12 +00:00
const request = server . pretender . handledRequests
. filterBy ( 'method' , 'POST' )
. pop ( ) ;
2020-10-29 12:46:42 +00:00
const { Apply , Dismiss } = JSON . parse ( request . requestBody ) ;
assert . equal ( request . url , '/v1/recommendations/apply' ) ;
assert . deepEqual ( Apply , appliedIds ) ;
assert . deepEqual ( Dismiss , dismissedIds ) ;
assert . equal ( Optimize . card . slug . jobName , this . job2 . name ) ;
assert . equal ( Optimize . card . slug . groupName , nextTaskGroup . name ) ;
assert . ok ( Optimize . recommendationSummaries [ 1 ] . isActive ) ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'can navigate between summaries via the table' , async function ( assert ) {
2020-10-29 12:46:42 +00:00
server . createList ( 'job' , 10 , {
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 2 ,
namespaceId : server . db . namespaces [ 1 ] . id ,
} ) ;
await Optimize . visit ( ) ;
await Optimize . recommendationSummaries [ 1 ] . click ( ) ;
assert . equal (
` ${ Optimize . card . slug . jobName } / ${ Optimize . card . slug . groupName } ` ,
Optimize . recommendationSummaries [ 1 ] . slug
) ;
assert . ok ( Optimize . recommendationSummaries [ 1 ] . isActive ) ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'can visit a summary directly via URL' , async function ( assert ) {
2020-11-04 18:22:24 +00:00
server . createList ( 'job' , 10 , {
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 2 ,
namespaceId : server . db . namespaces [ 1 ] . id ,
} ) ;
await Optimize . visit ( ) ;
const lastSummary =
2021-12-28 16:08:12 +00:00
Optimize . recommendationSummaries [
Optimize . recommendationSummaries . length - 1
] ;
2020-11-04 18:22:24 +00:00
const collapsedSlug = lastSummary . slug . replace ( ' / ' , '/' ) ;
// preferable to use page object’ s visitable but it encodes the slash
2021-12-28 16:08:12 +00:00
await visit (
` /optimize/ ${ collapsedSlug } ?namespace= ${ lastSummary . namespace } `
) ;
2020-11-04 18:22:24 +00:00
assert . equal (
` ${ Optimize . card . slug . jobName } / ${ Optimize . card . slug . groupName } ` ,
lastSummary . slug
) ;
assert . ok ( lastSummary . isActive ) ;
2021-12-28 16:08:12 +00:00
assert . equal (
currentURL ( ) ,
` /optimize/ ${ collapsedSlug } ?namespace= ${ lastSummary . namespace } `
) ;
2020-11-04 18:22:24 +00:00
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'when a summary is not found, an error message is shown, but the URL persists' , async function ( assert ) {
2020-11-04 18:22:24 +00:00
await visit ( '/optimize/nonexistent/summary?namespace=anamespace' ) ;
2021-12-28 16:08:12 +00:00
assert . equal (
currentURL ( ) ,
'/optimize/nonexistent/summary?namespace=anamespace'
) ;
2020-11-04 18:22:24 +00:00
assert . ok ( Optimize . applicationError . isPresent ) ;
assert . equal ( Optimize . applicationError . title , 'Not Found' ) ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'cannot return to already-processed summaries' , async function ( assert ) {
2020-10-29 12:46:42 +00:00
await Optimize . visit ( ) ;
await Optimize . card . acceptButton . click ( ) ;
assert . ok ( Optimize . recommendationSummaries [ 0 ] . isDisabled ) ;
await Optimize . recommendationSummaries [ 0 ] . click ( ) ;
assert . ok ( Optimize . recommendationSummaries [ 1 ] . isActive ) ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'can dismiss a set of recommendations' , async function ( assert ) {
2020-10-29 12:46:42 +00:00
await Optimize . visit ( ) ;
const currentTaskGroup = this . job1 . taskGroups . models [ 0 ] ;
const currentTaskIds = currentTaskGroup . tasks . models . mapBy ( 'id' ) ;
2021-12-28 14:45:20 +00:00
const taskIdFilter = ( task ) => currentTaskIds . includes ( task . taskId ) ;
2020-10-29 12:46:42 +00:00
const idsBeforeDismissal = server . schema . recommendations
. all ( )
. models . filter ( taskIdFilter )
. mapBy ( 'id' ) ;
await Optimize . card . dismissButton . click ( ) ;
2021-12-28 16:08:12 +00:00
const request = server . pretender . handledRequests
. filterBy ( 'method' , 'POST' )
. pop ( ) ;
2020-10-29 12:46:42 +00:00
const { Apply , Dismiss } = JSON . parse ( request . requestBody ) ;
assert . equal ( request . url , '/v1/recommendations/apply' ) ;
assert . deepEqual ( Apply , [ ] ) ;
assert . deepEqual ( Dismiss , idsBeforeDismissal ) ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'it displays an error encountered trying to save and proceeds to the next summary when the error is dismissed' , async function ( assert ) {
server . post ( '/recommendations/apply' , function ( ) {
2020-10-29 12:46:42 +00:00
return new Response ( 500 , { } , null ) ;
} ) ;
await Optimize . visit ( ) ;
await Optimize . card . acceptButton . click ( ) ;
assert . ok ( Optimize . error . isPresent ) ;
assert . equal ( Optimize . error . headline , 'Recommendation error' ) ;
assert . equal (
Optimize . error . errors ,
'Error: Ember Data Request POST /v1/recommendations/apply returned a 500 Payload (application/json)'
) ;
await Optimize . error . dismiss ( ) ;
assert . equal ( Optimize . card . slug . jobName , this . job2 . name ) ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'it displays an empty message when there are no recommendations' , async function ( assert ) {
2020-10-29 12:46:42 +00:00
server . db . recommendations . remove ( ) ;
await Optimize . visit ( ) ;
assert . ok ( Optimize . empty . isPresent ) ;
assert . equal ( Optimize . empty . headline , 'No Recommendations' ) ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'it displays an empty message after all recommendations have been processed' , async function ( assert ) {
2020-10-29 12:46:42 +00:00
await Optimize . visit ( ) ;
await Optimize . card . acceptButton . click ( ) ;
await Optimize . card . acceptButton . click ( ) ;
assert . ok ( Optimize . empty . isPresent ) ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'it redirects to jobs and hides the gutter link when the token lacks permissions' , async function ( assert ) {
2020-10-29 12:46:42 +00:00
window . localStorage . nomadTokenSecret = clientToken . secretId ;
await Optimize . visit ( ) ;
2021-10-21 14:24:07 +00:00
assert . equal ( currentURL ( ) , '/jobs?namespace=*' ) ;
2020-12-10 17:51:22 +00:00
assert . ok ( Layout . gutter . optimize . isHidden ) ;
2020-10-29 12:46:42 +00:00
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'it reloads partially-loaded jobs' , async function ( assert ) {
2020-10-29 12:46:42 +00:00
await JobsList . visit ( ) ;
await Optimize . visit ( ) ;
assert . equal ( Optimize . recommendationSummaries . length , 2 ) ;
} ) ;
} ) ;
2021-12-28 14:45:20 +00:00
module ( 'Acceptance | optimize search and facets' , function ( hooks ) {
2020-11-06 21:53:58 +00:00
setupApplicationTest ( hooks ) ;
setupMirage ( hooks ) ;
2021-12-28 14:45:20 +00:00
hooks . beforeEach ( async function ( ) {
2020-11-12 00:43:47 +00:00
server . create ( 'feature' , { name : 'Dynamic Application Sizing' } ) ;
2020-11-06 21:53:58 +00:00
server . create ( 'node' ) ;
server . createList ( 'namespace' , 2 ) ;
managementToken = server . create ( 'token' ) ;
window . localStorage . clear ( ) ;
window . localStorage . nomadTokenSecret = managementToken . secretId ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'search field narrows summary table results, changes the active summary if it no longer matches, and displays a no matches message when there are none' , async function ( assert ) {
2020-11-10 19:37:58 +00:00
server . create ( 'job' , {
2020-11-09 17:47:54 +00:00
name : 'zzzzzz' ,
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 6 ,
} ) ;
// Ensure this job’ s recommendations are sorted to the top of the table
const futureSubmitTime = ( Date . now ( ) + 10000 ) * 1000000 ;
server . db . recommendations . update ( { submitTime : futureSubmitTime } ) ;
2020-11-10 19:37:58 +00:00
server . create ( 'job' , {
2020-11-09 15:28:40 +00:00
name : 'oooooo' ,
createRecommendations : true ,
groupsCount : 2 ,
groupTaskCount : 4 ,
} ) ;
2020-11-10 19:37:58 +00:00
server . create ( 'job' , {
2020-11-09 15:28:40 +00:00
name : 'pppppp' ,
createRecommendations : true ,
groupsCount : 2 ,
groupTaskCount : 4 ,
} ) ;
await Optimize . visit ( ) ;
2020-11-09 17:47:54 +00:00
assert . equal ( Optimize . card . slug . jobName , 'zzzzzz' ) ;
2020-11-12 00:43:47 +00:00
assert . equal (
2022-03-08 17:28:36 +00:00
collapseWhitespace ( Optimize . search . placeholder ) ,
2020-11-12 00:43:47 +00:00
` Search ${ Optimize . recommendationSummaries . length } recommendations... `
) ;
2020-11-09 15:32:49 +00:00
2020-11-09 15:28:40 +00:00
await Optimize . search . fillIn ( 'ooo' ) ;
assert . equal ( Optimize . recommendationSummaries . length , 2 ) ;
assert . ok ( Optimize . recommendationSummaries [ 0 ] . slug . startsWith ( 'oooooo' ) ) ;
2020-11-09 15:41:18 +00:00
2020-11-09 17:47:54 +00:00
assert . equal ( Optimize . card . slug . jobName , 'oooooo' ) ;
assert . ok ( currentURL ( ) . includes ( 'oooooo' ) ) ;
2020-11-09 15:41:18 +00:00
await Optimize . search . fillIn ( 'qqq' ) ;
assert . notOk ( Optimize . card . isPresent ) ;
assert . ok ( Optimize . empty . isPresent ) ;
assert . equal ( Optimize . empty . headline , 'No Matches' ) ;
2020-11-10 15:38:46 +00:00
assert . equal ( currentURL ( ) , '/optimize?search=qqq' ) ;
2020-11-30 14:18:44 +00:00
await Optimize . search . fillIn ( '' ) ;
assert . equal ( Optimize . card . slug . jobName , 'zzzzzz' ) ;
assert . ok ( Optimize . recommendationSummaries [ 0 ] . isActive ) ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'the namespaces toggle doesn’ t show when there aren’ t namespaces' , async function ( assert ) {
2020-11-30 14:18:44 +00:00
server . db . namespaces . remove ( ) ;
server . create ( 'job' , {
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 4 ,
} ) ;
await Optimize . visit ( ) ;
2021-04-29 20:00:59 +00:00
assert . ok ( Optimize . facets . namespace . isHidden ) ;
2020-11-09 15:28:40 +00:00
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'processing a summary moves to the next one in the sorted list' , async function ( assert ) {
2020-11-10 19:37:58 +00:00
server . create ( 'job' , {
2020-11-09 21:00:58 +00:00
name : 'ooo111' ,
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 4 ,
} ) ;
2020-11-10 19:37:58 +00:00
server . create ( 'job' , {
2020-11-09 21:00:58 +00:00
name : 'pppppp' ,
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 4 ,
} ) ;
2020-11-10 19:37:58 +00:00
server . create ( 'job' , {
2020-11-09 21:00:58 +00:00
name : 'ooo222' ,
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 4 ,
} ) ;
// Directly set the sorting of the above jobs’ s summaries in the table
const futureSubmitTime = ( Date . now ( ) + 10000 ) * 1000000 ;
const nowSubmitTime = Date . now ( ) * 1000000 ;
const pastSubmitTime = ( Date . now ( ) - 10000 ) * 1000000 ;
const jobNameToRecommendationSubmitTime = {
ooo111 : futureSubmitTime ,
pppppp : nowSubmitTime ,
ooo222 : pastSubmitTime ,
} ;
2021-12-28 14:45:20 +00:00
server . schema . recommendations . all ( ) . models . forEach ( ( recommendation ) => {
2020-11-09 21:00:58 +00:00
const parentJob = recommendation . task . taskGroup . job ;
2021-12-28 16:08:12 +00:00
const submitTimeForJob =
jobNameToRecommendationSubmitTime [ parentJob . name ] ;
2020-11-09 21:00:58 +00:00
recommendation . submitTime = submitTimeForJob ;
recommendation . save ( ) ;
} ) ;
await Optimize . visit ( ) ;
await Optimize . search . fillIn ( 'ooo' ) ;
await Optimize . card . acceptButton . click ( ) ;
assert . equal ( Optimize . card . slug . jobName , 'ooo222' ) ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( 'the optimize page has appropriate faceted search options' , async function ( assert ) {
2020-11-06 21:53:58 +00:00
server . createList ( 'job' , 4 , {
status : 'running' ,
createRecommendations : true ,
childrenCount : 0 ,
} ) ;
await Optimize . visit ( ) ;
2021-04-29 20:00:59 +00:00
assert . ok ( Optimize . facets . namespace . isPresent , 'Namespace facet found' ) ;
2020-11-06 21:53:58 +00:00
assert . ok ( Optimize . facets . type . isPresent , 'Type facet found' ) ;
assert . ok ( Optimize . facets . status . isPresent , 'Status facet found' ) ;
assert . ok ( Optimize . facets . datacenter . isPresent , 'Datacenter facet found' ) ;
assert . ok ( Optimize . facets . prefix . isPresent , 'Prefix facet found' ) ;
} ) ;
2021-04-29 20:00:59 +00:00
testSingleSelectFacet ( 'Namespace' , {
facet : Optimize . facets . namespace ,
paramName : 'namespace' ,
expectedOptions : [ 'All (*)' , 'default' , 'namespace-1' ] ,
optionToSelect : 'namespace-1' ,
async beforeEach ( ) {
2021-12-28 16:08:12 +00:00
server . createList ( 'job' , 2 , {
namespaceId : 'default' ,
createRecommendations : true ,
} ) ;
server . createList ( 'job' , 2 , {
namespaceId : 'namespace-1' ,
createRecommendations : true ,
} ) ;
2021-04-29 20:00:59 +00:00
await Optimize . visit ( ) ;
} ,
filter ( taskGroup , selection ) {
return taskGroup . job . namespaceId === selection ;
} ,
} ) ;
2020-11-06 21:53:58 +00:00
testFacet ( 'Type' , {
facet : Optimize . facets . type ,
paramName : 'type' ,
expectedOptions : [ 'Service' , 'System' ] ,
async beforeEach ( ) {
server . createList ( 'job' , 2 , {
type : 'service' ,
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 2 ,
} ) ;
server . createList ( 'job' , 2 , {
type : 'system' ,
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 2 ,
} ) ;
await Optimize . visit ( ) ;
} ,
filter ( taskGroup , selection ) {
let displayType = taskGroup . job . type ;
return selection . includes ( displayType ) ;
} ,
} ) ;
testFacet ( 'Status' , {
facet : Optimize . facets . status ,
paramName : 'status' ,
expectedOptions : [ 'Pending' , 'Running' , 'Dead' ] ,
async beforeEach ( ) {
server . createList ( 'job' , 2 , {
status : 'pending' ,
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 2 ,
childrenCount : 0 ,
} ) ;
server . createList ( 'job' , 2 , {
status : 'running' ,
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 2 ,
childrenCount : 0 ,
} ) ;
2020-11-12 00:43:47 +00:00
server . createList ( 'job' , 2 , {
status : 'dead' ,
createRecommendations : true ,
childrenCount : 0 ,
} ) ;
2020-11-06 21:53:58 +00:00
await Optimize . visit ( ) ;
} ,
filter : ( taskGroup , selection ) => selection . includes ( taskGroup . job . status ) ,
} ) ;
testFacet ( 'Datacenter' , {
facet : Optimize . facets . datacenter ,
paramName : 'dc' ,
expectedOptions ( jobs ) {
const allDatacenters = new Set (
jobs . mapBy ( 'datacenters' ) . reduce ( ( acc , val ) => acc . concat ( val ) , [ ] )
) ;
return Array . from ( allDatacenters ) . sort ( ) ;
} ,
async beforeEach ( ) {
server . create ( 'job' , {
datacenters : [ 'pdx' , 'lax' ] ,
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 2 ,
childrenCount : 0 ,
} ) ;
server . create ( 'job' , {
datacenters : [ 'pdx' , 'ord' ] ,
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 2 ,
childrenCount : 0 ,
} ) ;
server . create ( 'job' , {
datacenters : [ 'lax' , 'jfk' ] ,
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 2 ,
childrenCount : 0 ,
} ) ;
server . create ( 'job' , {
datacenters : [ 'jfk' , 'dfw' ] ,
createRecommendations : true ,
groupsCount : 1 ,
groupTaskCount : 2 ,
childrenCount : 0 ,
} ) ;
2021-12-28 16:08:12 +00:00
server . create ( 'job' , {
datacenters : [ 'pdx' ] ,
createRecommendations : true ,
childrenCount : 0 ,
} ) ;
2020-11-06 21:53:58 +00:00
await Optimize . visit ( ) ;
} ,
2021-12-28 14:45:20 +00:00
filter : ( taskGroup , selection ) =>
taskGroup . job . datacenters . find ( ( dc ) => selection . includes ( dc ) ) ,
2020-11-06 21:53:58 +00:00
} ) ;
testFacet ( 'Prefix' , {
facet : Optimize . facets . prefix ,
paramName : 'prefix' ,
expectedOptions : [ 'hashi (3)' , 'nmd (2)' , 'pre (2)' ] ,
async beforeEach ( ) {
[
'pre-one' ,
'hashi_one' ,
'nmd.one' ,
'one-alone' ,
'pre_two' ,
'hashi.two' ,
'hashi-three' ,
'nmd_two' ,
'noprefix' ,
2021-12-28 14:45:20 +00:00
] . forEach ( ( name ) => {
2020-11-06 21:53:58 +00:00
server . create ( 'job' , {
name ,
createRecommendations : true ,
createAllocations : true ,
groupsCount : 1 ,
groupTaskCount : 2 ,
childrenCount : 0 ,
} ) ;
} ) ;
await Optimize . visit ( ) ;
} ,
2020-11-12 00:43:47 +00:00
filter : ( taskGroup , selection ) =>
2021-12-28 14:45:20 +00:00
selection . find ( ( prefix ) => taskGroup . job . name . startsWith ( prefix ) ) ,
2020-11-06 21:53:58 +00:00
} ) ;
2021-04-29 20:00:59 +00:00
async function facetOptions ( assert , beforeEach , facet , expectedOptions ) {
await beforeEach ( ) ;
await facet . toggle ( ) ;
let expectation ;
if ( typeof expectedOptions === 'function' ) {
expectation = expectedOptions ( server . db . jobs ) ;
} else {
expectation = expectedOptions ;
}
assert . deepEqual (
2021-12-28 14:45:20 +00:00
facet . options . map ( ( option ) => option . label . trim ( ) ) ,
2021-04-29 20:00:59 +00:00
expectation ,
'Options for facet are as expected'
) ;
}
function testSingleSelectFacet (
label ,
{ facet , paramName , beforeEach , filter , expectedOptions , optionToSelect }
) {
2021-12-28 14:45:20 +00:00
test ( ` the ${ label } facet has the correct options ` , async function ( assert ) {
2021-04-29 20:00:59 +00:00
await facetOptions . call ( this , assert , beforeEach , facet , expectedOptions ) ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( ` the ${ label } facet filters the jobs list by ${ label } ` , async function ( assert ) {
2020-11-06 21:53:58 +00:00
await beforeEach ( ) ;
await facet . toggle ( ) ;
2021-04-29 20:00:59 +00:00
const option = facet . options . findOneBy ( 'label' , optionToSelect ) ;
const selection = option . key ;
await option . select ( ) ;
2021-12-28 16:08:12 +00:00
const sortedRecommendations = server . db . recommendations
. sortBy ( 'submitTime' )
. reverse ( ) ;
2021-04-29 20:00:59 +00:00
const recommendationTaskGroups = server . schema . tasks
. find ( sortedRecommendations . mapBy ( 'taskId' ) . uniq ( ) )
. models . mapBy ( 'taskGroup' )
. uniqBy ( 'id' )
2021-12-28 14:45:20 +00:00
. filter ( ( group ) => filter ( group , selection ) ) ;
2021-04-29 20:00:59 +00:00
Optimize . recommendationSummaries . forEach ( ( summary , index ) => {
const group = recommendationTaskGroups [ index ] ;
assert . equal ( summary . slug , ` ${ group . job . name } / ${ group . name } ` ) ;
} ) ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( ` selecting an option in the ${ label } facet updates the ${ paramName } query param ` , async function ( assert ) {
2021-04-29 20:00:59 +00:00
await beforeEach ( ) ;
await facet . toggle ( ) ;
const option = facet . options . objectAt ( 1 ) ;
const selection = option . key ;
await option . select ( ) ;
assert . ok (
currentURL ( ) . includes ( ` ${ paramName } = ${ selection } ` ) ,
'URL has the correct query param key and value'
2020-11-06 21:53:58 +00:00
) ;
} ) ;
2021-04-29 20:00:59 +00:00
}
2021-12-28 16:08:12 +00:00
function testFacet (
label ,
{ facet , paramName , beforeEach , filter , expectedOptions }
) {
2021-12-28 14:45:20 +00:00
test ( ` the ${ label } facet has the correct options ` , async function ( assert ) {
2021-04-29 20:00:59 +00:00
await facetOptions . call ( this , assert , beforeEach , facet , expectedOptions ) ;
} ) ;
2020-11-06 21:53:58 +00:00
2021-12-28 14:45:20 +00:00
test ( ` the ${ label } facet filters the recommendation summaries by ${ label } ` , async function ( assert ) {
2020-11-06 21:53:58 +00:00
let option ;
await beforeEach ( ) ;
await facet . toggle ( ) ;
option = facet . options . objectAt ( 0 ) ;
await option . toggle ( ) ;
const selection = [ option . key ] ;
2021-12-28 16:08:12 +00:00
const sortedRecommendations = server . db . recommendations
. sortBy ( 'submitTime' )
. reverse ( ) ;
2020-11-06 21:53:58 +00:00
2020-11-12 00:43:47 +00:00
const recommendationTaskGroups = server . schema . tasks
. find ( sortedRecommendations . mapBy ( 'taskId' ) . uniq ( ) )
. models . mapBy ( 'taskGroup' )
. uniqBy ( 'id' )
2021-12-28 14:45:20 +00:00
. filter ( ( group ) => filter ( group , selection ) ) ;
2020-11-06 21:53:58 +00:00
Optimize . recommendationSummaries . forEach ( ( summary , index ) => {
const group = recommendationTaskGroups [ index ] ;
assert . equal ( summary . slug , ` ${ group . job . name } / ${ group . name } ` ) ;
} ) ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( ` selecting multiple options in the ${ label } facet results in a broader search ` , async function ( assert ) {
2020-11-06 21:53:58 +00:00
const selection = [ ] ;
await beforeEach ( ) ;
await facet . toggle ( ) ;
const option1 = facet . options . objectAt ( 0 ) ;
const option2 = facet . options . objectAt ( 1 ) ;
await option1 . toggle ( ) ;
selection . push ( option1 . key ) ;
await option2 . toggle ( ) ;
selection . push ( option2 . key ) ;
2021-12-28 16:08:12 +00:00
const sortedRecommendations = server . db . recommendations
. sortBy ( 'submitTime' )
. reverse ( ) ;
2020-11-06 21:53:58 +00:00
2020-11-12 00:43:47 +00:00
const recommendationTaskGroups = server . schema . tasks
. find ( sortedRecommendations . mapBy ( 'taskId' ) . uniq ( ) )
. models . mapBy ( 'taskGroup' )
. uniqBy ( 'id' )
2021-12-28 14:45:20 +00:00
. filter ( ( group ) => filter ( group , selection ) ) ;
2020-11-06 21:53:58 +00:00
Optimize . recommendationSummaries . forEach ( ( summary , index ) => {
const group = recommendationTaskGroups [ index ] ;
assert . equal ( summary . slug , ` ${ group . job . name } / ${ group . name } ` ) ;
} ) ;
} ) ;
2021-12-28 14:45:20 +00:00
test ( ` selecting options in the ${ label } facet updates the ${ paramName } query param ` , async function ( assert ) {
2020-11-06 21:53:58 +00:00
const selection = [ ] ;
await beforeEach ( ) ;
await facet . toggle ( ) ;
const option1 = facet . options . objectAt ( 0 ) ;
const option2 = facet . options . objectAt ( 1 ) ;
await option1 . toggle ( ) ;
selection . push ( option1 . key ) ;
await option2 . toggle ( ) ;
selection . push ( option2 . key ) ;
2021-12-28 16:08:12 +00:00
assert . ok (
currentURL ( ) . includes ( encodeURIComponent ( JSON . stringify ( selection ) ) )
) ;
2020-11-06 21:53:58 +00:00
} ) ;
}
} ) ;
2020-10-29 12:46:42 +00:00
function formattedMemDiff ( memDiff ) {
const absMemDiff = Math . abs ( memDiff ) ;
const negativeSign = memDiff < 0 ? '-' : '' ;
2021-03-29 23:16:48 +00:00
return negativeSign + formatBytes ( absMemDiff , 'MiB' ) ;
2020-10-29 12:46:42 +00:00
}