Fix existing tests

This commit is contained in:
Michael Lange
2018-02-15 18:55:59 -08:00
parent 60ee8714c1
commit 3c2a1f8a4a
18 changed files with 191 additions and 260 deletions

View File

@@ -44,7 +44,7 @@ export default RESTAdapter.extend({
// owner type. In the event it isn't, findHasMany should be overridden.
store
.peekAll(relationshipType)
.filter(record => record.get(`${ownerType}.id` === snapshot.id))
.filter(record => record.get(`${ownerType}.id`) === snapshot.id)
.forEach(record => {
store.unloadRecord(record);
});

View File

@@ -5,14 +5,13 @@ import Watchable from './watchable';
export default Watchable.extend({
system: service(),
// shouldReloadAll: () => true,
buildQuery() {
const namespace = this.get('system.activeNamespace.id');
if (namespace && namespace !== 'default') {
return { namespace };
}
return {};
},
findAll() {
@@ -42,7 +41,7 @@ export default Watchable.extend({
const [name, namespace] = JSON.parse(id);
let url = this._super(name, type, hash);
if (namespace && namespace !== 'default') {
url += `?${namespace}`;
url += `?namespace=${namespace}`;
}
return url;
},
@@ -57,19 +56,17 @@ export default Watchable.extend({
},
fetchRawDefinition(job) {
const [name, namespace] = JSON.parse(job.get('id'));
const namespaceQuery = namespace && namespace !== 'default' ? { namespace } : {};
const url = this.buildURL('job', name, job, 'findRecord');
return this.ajax(url, 'GET', { data: assign(this.buildQuery() || {}, namespaceQuery) });
const url = this.buildURL('job', job.get('id'), job, 'findRecord');
return this.ajax(url, 'GET', { data: this.buildQuery() });
},
forcePeriodic(job) {
if (job.get('periodic')) {
const [name, namespace] = JSON.parse(job.get('id'));
let url = `${this.buildURL('job', name, job, 'findRecord')}/periodic/force`;
const [path, params] = this.buildURL('job', job.get('id'), job, 'findRecord').split('?');
let url = `${path}/periodic/force`;
if (namespace) {
url += `?namespace=${namespace}`;
if (params) {
url += `?${params}`;
}
return this.ajax(url, 'POST');

View File

@@ -1,6 +1,5 @@
import { get, computed } from '@ember/object';
import { assign } from '@ember/polyfills';
import { copy } from '@ember/object/internals';
import { makeArray } from '@ember/array';
import { inject as service } from '@ember/service';
import queryString from 'npm:query-string';
@@ -32,10 +31,10 @@ export default ApplicationAdapter.extend({
},
findAll(store, type, sinceToken, snapshotRecordArray, additionalParams = {}) {
const params = copy(additionalParams, true);
const params = assign(this.buildQuery(), additionalParams);
const url = this.urlForFindAll(type.modelName);
if (get(snapshotRecordArray, 'adapterOptions.watch')) {
if (get(snapshotRecordArray || {}, 'adapterOptions.watch')) {
params.index = this.get('watchList').getIndexFor(url);
}
@@ -45,10 +44,10 @@ export default ApplicationAdapter.extend({
},
findRecord(store, type, id, snapshot, additionalParams = {}) {
const params = copy(additionalParams, true);
const url = this.buildURL(type.modelName, id, snapshot, 'findRecord');
let [url, params] = this.buildURL(type.modelName, id, snapshot, 'findRecord').split('?');
params = assign(queryString.parse(params) || {}, this.buildQuery(), additionalParams);
if (get(snapshot, 'adapterOptions.watch')) {
if (get(snapshot || {}, 'adapterOptions.watch')) {
params.index = this.get('watchList').getIndexFor(url);
}

View File

@@ -32,5 +32,5 @@ export default Component.extend({
this._super(...arguments);
},
watch: watchRelationship('summary').drop(),
watch: watchRelationship('summary'),
});

View File

@@ -41,9 +41,10 @@ export default ApplicationSerializer.extend({
!hash.NamespaceID || hash.NamespaceID === 'default' ? undefined : hash.NamespaceID;
const { modelName } = modelClass;
const jobURL = this.store
const [jobURL] = this.store
.adapterFor(modelName)
.buildURL(modelName, hash.ID, hash, 'findRecord');
.buildURL(modelName, hash.ID, hash, 'findRecord')
.split('?');
return assign(this._super(...arguments), {
summary: {

View File

@@ -1,3 +1,5 @@
import Ember from 'ember';
import { typeOf } from '@ember/utils';
import { get } from '@ember/object';
import RSVP from 'rsvp';
import { task } from 'ember-concurrency';
@@ -5,8 +7,10 @@ import wait from 'nomad-ui/utils/wait';
export function watchRecord(modelName) {
return task(function*(id, throttle = 2000) {
id = get(id, 'id') || id;
while (true) {
if (typeOf(id) === 'object') {
id = get(id, 'id');
}
while (!Ember.testing) {
try {
yield RSVP.all([
this.get('store').findRecord(modelName, id, {
@@ -29,7 +33,7 @@ export function watchRecord(modelName) {
export function watchRelationship(relationshipName) {
return task(function*(model, throttle = 2000) {
while (true) {
while (!Ember.testing) {
try {
yield RSVP.all([
this.get('store')
@@ -42,7 +46,7 @@ export function watchRelationship(relationshipName) {
break;
} finally {
this.get('store')
.adapterFor(model.constructor.name)
.adapterFor(model.constructor.modelName)
.cancelReloadRelationship(model, relationshipName);
}
}
@@ -51,7 +55,7 @@ export function watchRelationship(relationshipName) {
export function watchAll(modelName) {
return task(function*(throttle = 2000) {
while (true) {
while (!Ember.testing) {
try {
yield RSVP.all([
this.get('store').findAll(modelName, { reload: true, adapterOptions: { watch: true } }),

View File

@@ -15,6 +15,8 @@ export default Factory.extend({
modifyTime: () => faker.date.past(2 / 365, REF_TIME) * 1000000,
namespace: null,
clientStatus: faker.list.random(...CLIENT_STATUSES),
desiredStatus: faker.list.random(...DESIRED_STATUSES),

View File

@@ -65,7 +65,7 @@ export default Factory.extend({
const failedTaskGroupNames = [];
for (let i = 0; i < failedTaskGroupsCount; i++) {
failedTaskGroupNames.push(
...taskGroupNames.splice(faker.random.number(taskGroupNames.length), 1)
...taskGroupNames.splice(faker.random.number(taskGroupNames.length - 1), 1)
);
}

View File

@@ -5,6 +5,7 @@ export default Factory.extend({
groupNames: [],
JobID: '',
namespace: null,
withSummary: trait({
Summary: function() {

View File

@@ -25,6 +25,7 @@ export default Factory.extend({
version.activeDeployment && 'active',
{
jobId: version.jobId,
namespace: version.job.namespace,
versionNumber: version.version,
},
].compact();

View File

@@ -1,3 +1,4 @@
import { assign } from '@ember/polyfills';
import { Factory, faker, trait } from 'ember-cli-mirage';
import { provide, provider, pickOne } from '../utils';
import { DATACENTERS } from '../common';
@@ -86,16 +87,6 @@ export default Factory.extend({
noFailedPlacements: false,
afterCreate(job, server) {
const groups = server.createList('task-group', job.groupsCount, {
job,
createAllocations: job.createAllocations,
});
job.update({
taskGroupIds: groups.mapBy('id'),
task_group_ids: groups.mapBy('id'),
});
if (!job.namespaceId) {
const namespace = server.db.namespaces.length ? pickOne(server.db.namespaces).id : null;
job.update({
@@ -108,10 +99,21 @@ export default Factory.extend({
});
}
const groups = server.createList('task-group', job.groupsCount, {
job,
createAllocations: job.createAllocations,
});
job.update({
taskGroupIds: groups.mapBy('id'),
task_group_ids: groups.mapBy('id'),
});
const hasChildren = job.periodic || job.parameterized;
const jobSummary = server.create('job-summary', hasChildren ? 'withChildren' : 'withSummary', {
groupNames: groups.mapBy('name'),
job,
namespace: job.namespace,
});
job.update({
@@ -124,22 +126,39 @@ export default Factory.extend({
.map((_, index) => {
return server.create('job-version', {
job,
namespace: job.namespace,
version: index,
noActiveDeployment: job.noActiveDeployment,
activeDeployment: job.activeDeployment,
});
});
server.createList('evaluation', faker.random.number({ min: 1, max: 5 }), { job });
const knownEvaluationProperties = {
job,
namespace: job.namespace,
};
server.createList(
'evaluation',
faker.random.number({ min: 1, max: 5 }),
knownEvaluationProperties
);
if (!job.noFailedPlacements) {
server.createList('evaluation', faker.random.number(3), 'withPlacementFailures', { job });
server.createList(
'evaluation',
faker.random.number(3),
'withPlacementFailures',
knownEvaluationProperties
);
}
if (job.failedPlacements) {
server.create('evaluation', 'withPlacementFailures', {
job,
modifyIndex: 4000,
});
server.create(
'evaluation',
'withPlacementFailures',
assign(knownEvaluationProperties, {
modifyIndex: 4000,
})
);
}
if (job.periodic) {

View File

@@ -32,6 +32,7 @@ export default Factory.extend({
.forEach((_, i) => {
server.create('allocation', {
jobId: group.job.id,
namespace: group.job.namespace,
taskGroup: group.name,
name: `${group.name}.[${i}]`,
});

View File

@@ -159,6 +159,8 @@ test('is sorted based on the sortProperty and sortDescending properties', functi
`Child ${index} is ${child.get('name')}`
);
});
return wait();
});
});
});

View File

@@ -63,11 +63,15 @@ test('Clicking Force Launch launches a new periodic child job', function(assert)
return wait().then(() => {
const id = job.get('plainId');
const namespace = job.get('namespace.name') || 'default';
let expectedURL = `/v1/job/${id}/periodic/force`;
if (namespace !== 'default') {
expectedURL += `?namespace=${namespace}`;
}
assert.ok(
server.pretender.handledRequests
.filterBy('method', 'POST')
.find(req => req.url === `/v1/job/${id}/periodic/force?namespace=${namespace}`),
.find(req => req.url === expectedURL),
'POST URL was correct'
);

View File

@@ -19,7 +19,7 @@ test('should render the placement failure (basic render)', function(assert) {
'taskGroup',
createFixture(
{
coalescedFailures: failures - 1
coalescedFailures: failures - 1,
},
name
)
@@ -77,22 +77,16 @@ test('should render the placement failure (basic render)', function(assert) {
1,
'Quota exhausted message shown'
);
assert.equal(
findAll('[data-test-placement-failure-scores]').length,
1,
'Scores message shown'
);
assert.equal(findAll('[data-test-placement-failure-scores]').length, 1, 'Scores message shown');
});
test('should render correctly when a node is not evaluated', function(assert) {
this.set(
'taskGroup',
createFixture(
{
nodesEvaluated: 1,
nodesExhausted: 0
}
)
createFixture({
nodesEvaluated: 1,
nodesExhausted: 0,
})
);
this.render(commonTemplate);
@@ -112,33 +106,34 @@ test('should render correctly when a node is not evaluated', function(assert) {
function createFixture(obj = {}, name = 'Placement Failure') {
return {
name: name,
placementFailures: assign({
coalescedFailures: 10,
nodesEvaluated: 0,
nodesAvailable: {
datacenter: 0,
placementFailures: assign(
{
coalescedFailures: 10,
nodesEvaluated: 0,
nodesAvailable: {
datacenter: 0,
},
classFiltered: {
filtered: 1,
},
constraintFiltered: {
'prop = val': 1,
},
nodesExhausted: 3,
classExhausted: {
class: 3,
},
dimensionExhausted: {
iops: 3,
},
quotaExhausted: {
quota: 'dimension',
},
scores: {
name: 3,
},
},
classFiltered: {
filtered: 1,
},
constraintFiltered: {
'prop = val': 1,
},
nodesExhausted: 3,
classExhausted: {
class: 3,
},
dimensionExhausted: {
iops: 3,
},
quotaExhausted: {
quota: 'dimension',
},
scores: {
name: 3,
},
},
obj
)
obj
),
};
}

View File

@@ -3,7 +3,13 @@ import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
moduleFor('adapter:job', 'Unit | Adapter | Job', {
unit: true,
needs: ['service:token', 'service:system', 'model:namespace', 'adapter:application'],
needs: [
'service:token',
'service:system',
'model:namespace',
'adapter:application',
'service:watchList',
],
beforeEach() {
window.sessionStorage.clear();
@@ -27,8 +33,8 @@ test('The job summary is stitched into the job request', function(assert) {
assert.deepEqual(
pretender.handledRequests.mapBy('url'),
['/v1/namespaces', `/v1/job/${jobName}`, `/v1/job/${jobName}/summary`],
'The three requests made are /namespaces, /job/:id, and /job/:id/summary'
['/v1/namespaces', `/v1/job/${jobName}`],
'The two requests made are /namespaces and /job/:id'
);
});
@@ -42,18 +48,12 @@ test('When the job has a namespace other than default, it is in the URL', functi
assert.deepEqual(
pretender.handledRequests.mapBy('url'),
[
'/v1/namespaces',
`/v1/job/${jobName}?namespace=${jobNamespace}`,
`/v1/job/${jobName}/summary?namespace=${jobNamespace}`,
],
'The three requests made are /namespaces, /job/:id?namespace=:namespace, and /job/:id/summary?namespace=:namespace'
['/v1/namespaces', `/v1/job/${jobName}?namespace=${jobNamespace}`],
'The two requests made are /namespaces and /job/:id?namespace=:namespace'
);
});
test('When there is no token set in the token service, no x-nomad-token header is set', function(
assert
) {
test('When there is no token set in the token service, no x-nomad-token header is set', function(assert) {
const { pretender } = this.server;
const jobId = JSON.stringify(['job-1', 'default']);
@@ -65,9 +65,7 @@ test('When there is no token set in the token service, no x-nomad-token header i
);
});
test('When a token is set in the token service, then x-nomad-token header is set', function(
assert
) {
test('When a token is set in the token service, then x-nomad-token header is set', function(assert) {
const { pretender } = this.server;
const jobId = JSON.stringify(['job-1', 'default']);
const secret = 'here is the secret';

View File

@@ -1,11 +1,50 @@
import { getOwner } from '@ember/application';
import { run } from '@ember/runloop';
import { moduleForModel, test } from 'ember-qunit';
moduleForModel('job', 'Unit | Model | job', {
needs: ['model:task-group', 'model:task', 'model:task-group-summary'],
needs: ['model:job-summary', 'model:task-group', 'model:task', 'model:task-group-summary'],
});
test('should expose aggregate allocations derived from task groups', function(assert) {
const store = getOwner(this).lookup('service:store');
let summary;
run(() => {
summary = store.createRecord('job-summary', {
taskGroupSummaries: [
{
name: 'one',
queuedAllocs: 1,
startingAllocs: 2,
runningAllocs: 3,
completeAllocs: 4,
failedAllocs: 5,
lostAllocs: 6,
},
{
name: 'two',
queuedAllocs: 2,
startingAllocs: 4,
runningAllocs: 6,
completeAllocs: 8,
failedAllocs: 10,
lostAllocs: 12,
},
{
name: 'three',
queuedAllocs: 3,
startingAllocs: 6,
runningAllocs: 9,
completeAllocs: 12,
failedAllocs: 15,
lostAllocs: 18,
},
],
});
});
const job = this.subject({
summary,
name: 'example',
taskGroups: [
{
@@ -24,76 +63,68 @@ test('should expose aggregate allocations derived from task groups', function(as
tasks: [],
},
],
taskGroupSummaries: [
{
name: 'one',
queuedAllocs: 1,
startingAllocs: 2,
runningAllocs: 3,
completeAllocs: 4,
failedAllocs: 5,
lostAllocs: 6,
},
{
name: 'two',
queuedAllocs: 2,
startingAllocs: 4,
runningAllocs: 6,
completeAllocs: 8,
failedAllocs: 10,
lostAllocs: 12,
},
{
name: 'three',
queuedAllocs: 3,
startingAllocs: 6,
runningAllocs: 9,
completeAllocs: 12,
failedAllocs: 15,
lostAllocs: 18,
},
],
});
assert.equal(
job.get('totalAllocs'),
job.get('taskGroups').mapBy('summary.totalAllocs').reduce((sum, allocs) => sum + allocs, 0),
job
.get('taskGroups')
.mapBy('summary.totalAllocs')
.reduce((sum, allocs) => sum + allocs, 0),
'totalAllocs is the sum of all group totalAllocs'
);
assert.equal(
job.get('queuedAllocs'),
job.get('taskGroups').mapBy('summary.queuedAllocs').reduce((sum, allocs) => sum + allocs, 0),
job
.get('taskGroups')
.mapBy('summary.queuedAllocs')
.reduce((sum, allocs) => sum + allocs, 0),
'queuedAllocs is the sum of all group queuedAllocs'
);
assert.equal(
job.get('startingAllocs'),
job.get('taskGroups').mapBy('summary.startingAllocs').reduce((sum, allocs) => sum + allocs, 0),
job
.get('taskGroups')
.mapBy('summary.startingAllocs')
.reduce((sum, allocs) => sum + allocs, 0),
'startingAllocs is the sum of all group startingAllocs'
);
assert.equal(
job.get('runningAllocs'),
job.get('taskGroups').mapBy('summary.runningAllocs').reduce((sum, allocs) => sum + allocs, 0),
job
.get('taskGroups')
.mapBy('summary.runningAllocs')
.reduce((sum, allocs) => sum + allocs, 0),
'runningAllocs is the sum of all group runningAllocs'
);
assert.equal(
job.get('completeAllocs'),
job.get('taskGroups').mapBy('summary.completeAllocs').reduce((sum, allocs) => sum + allocs, 0),
job
.get('taskGroups')
.mapBy('summary.completeAllocs')
.reduce((sum, allocs) => sum + allocs, 0),
'completeAllocs is the sum of all group completeAllocs'
);
assert.equal(
job.get('failedAllocs'),
job.get('taskGroups').mapBy('summary.failedAllocs').reduce((sum, allocs) => sum + allocs, 0),
job
.get('taskGroups')
.mapBy('summary.failedAllocs')
.reduce((sum, allocs) => sum + allocs, 0),
'failedAllocs is the sum of all group failedAllocs'
);
assert.equal(
job.get('lostAllocs'),
job.get('taskGroups').mapBy('summary.lostAllocs').reduce((sum, allocs) => sum + allocs, 0),
job
.get('taskGroups')
.mapBy('summary.lostAllocs')
.reduce((sum, allocs) => sum + allocs, 0),
'lostAllocs is the sum of all group lostAllocs'
);
});

View File

@@ -11,131 +11,7 @@ moduleForSerializer('job', 'Unit | Serializer | Job', {
],
});
test('The JobSummary object is transformed from a map to a list', function(assert) {
const original = {
ID: 'example',
ParentID: '',
Name: 'example',
Type: 'service',
Priority: 50,
Periodic: false,
ParameterizedJob: false,
Stop: false,
Status: 'running',
StatusDescription: '',
JobSummary: {
JobID: 'example',
Summary: {
cache: {
Queued: 0,
Complete: 0,
Failed: 0,
Running: 1,
Starting: 0,
Lost: 0,
},
something_else: {
Queued: 0,
Complete: 0,
Failed: 0,
Running: 2,
Starting: 0,
Lost: 0,
},
},
CreateIndex: 7,
ModifyIndex: 13,
},
CreateIndex: 7,
ModifyIndex: 9,
JobModifyIndex: 7,
};
const { data } = this.subject().normalize(JobModel, original);
assert.deepEqual(data.attributes, {
name: 'example',
plainId: 'example',
type: 'service',
priority: 50,
periodic: false,
parameterized: false,
status: 'running',
statusDescription: '',
taskGroupSummaries: [
{
name: 'cache',
queuedAllocs: 0,
completeAllocs: 0,
failedAllocs: 0,
runningAllocs: 1,
startingAllocs: 0,
lostAllocs: 0,
},
{
name: 'something_else',
queuedAllocs: 0,
completeAllocs: 0,
failedAllocs: 0,
runningAllocs: 2,
startingAllocs: 0,
lostAllocs: 0,
},
],
createIndex: 7,
modifyIndex: 9,
});
});
test('The children stats are lifted out of the JobSummary object', function(assert) {
const original = {
ID: 'example',
ParentID: '',
Name: 'example',
Type: 'service',
Priority: 50,
Periodic: false,
ParameterizedJob: false,
Stop: false,
Status: 'running',
StatusDescription: '',
JobSummary: {
JobID: 'example',
Summary: {},
Children: {
Pending: 1,
Running: 2,
Dead: 3,
},
},
CreateIndex: 7,
ModifyIndex: 9,
JobModifyIndex: 7,
};
const normalized = this.subject().normalize(JobModel, original);
assert.deepEqual(normalized.data.attributes, {
name: 'example',
plainId: 'example',
type: 'service',
priority: 50,
periodic: false,
parameterized: false,
status: 'running',
statusDescription: '',
taskGroupSummaries: [],
pendingChildren: 1,
runningChildren: 2,
deadChildren: 3,
createIndex: 7,
modifyIndex: 9,
});
});
test('`default` is used as the namespace in the job ID when there is no namespace in the payload', function(
assert
) {
test('`default` is used as the namespace in the job ID when there is no namespace in the payload', function(assert) {
const original = {
ID: 'example',
Name: 'example',