2018-09-19 22:19:06 +00:00
|
|
|
|
import EmberObject from '@ember/object';
|
|
|
|
|
import Service from '@ember/service';
|
2019-03-13 00:04:16 +00:00
|
|
|
|
import { module, test } from 'qunit';
|
|
|
|
|
import { setupTest } from 'ember-qunit';
|
|
|
|
|
import { settled } from '@ember/test-helpers';
|
2018-09-19 22:19:06 +00:00
|
|
|
|
import Pretender from 'pretender';
|
|
|
|
|
import sinon from 'sinon';
|
|
|
|
|
import fetch from 'nomad-ui/utils/fetch';
|
|
|
|
|
import NodeStatsTracker from 'nomad-ui/utils/classes/node-stats-tracker';
|
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
module('Unit | Service | Stats Trackers Registry', function(hooks) {
|
|
|
|
|
setupTest(hooks);
|
|
|
|
|
|
|
|
|
|
hooks.beforeEach(function() {
|
|
|
|
|
this.subject = function() {
|
2019-03-13 00:40:39 +00:00
|
|
|
|
return this.owner.factoryFor('service:stats-trackers-registry').create();
|
2019-03-13 00:04:16 +00:00
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
hooks.beforeEach(function() {
|
2018-09-19 22:19:06 +00:00
|
|
|
|
// Inject a mock token service
|
|
|
|
|
const authorizedRequestSpy = (this.tokenAuthorizedRequestSpy = sinon.spy());
|
|
|
|
|
const mockToken = Service.extend({
|
|
|
|
|
authorizedRequest(url) {
|
|
|
|
|
authorizedRequestSpy(url);
|
|
|
|
|
return fetch(url);
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
|
this.token = this.owner.lookup('service:token');
|
2018-09-19 22:19:06 +00:00
|
|
|
|
this.server = new Pretender(function() {
|
|
|
|
|
this.get('/v1/client/stats', () => [
|
|
|
|
|
200,
|
|
|
|
|
{},
|
|
|
|
|
JSON.stringify({
|
|
|
|
|
Timestamp: 1234567890,
|
|
|
|
|
CPUTicksConsumed: 11,
|
|
|
|
|
Memory: {
|
|
|
|
|
Used: 12,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
]);
|
|
|
|
|
});
|
2019-03-13 00:04:16 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
hooks.afterEach(function() {
|
2018-09-19 22:19:06 +00:00
|
|
|
|
this.server.shutdown();
|
2019-03-13 00:04:16 +00:00
|
|
|
|
});
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
const makeModelMock = (modelName, defaults) => {
|
|
|
|
|
const Class = EmberObject.extend(defaults);
|
|
|
|
|
Class.prototype.constructor.modelName = modelName;
|
|
|
|
|
return Class;
|
|
|
|
|
};
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
const mockNode = makeModelMock('node', { id: 'test' });
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
test('Creates a tracker when one isn’t found', function(assert) {
|
|
|
|
|
const registry = this.subject();
|
|
|
|
|
const id = 'id';
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
assert.equal(registry.get('registryRef').size, 0, 'Nothing in the registry yet');
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
const tracker = registry.getTracker(mockNode.create({ id }));
|
|
|
|
|
assert.ok(tracker instanceof NodeStatsTracker, 'The correct type of tracker is made');
|
|
|
|
|
assert.equal(registry.get('registryRef').size, 1, 'The tracker was added to the registry');
|
|
|
|
|
assert.deepEqual(
|
|
|
|
|
Array.from(registry.get('registryRef').keys()),
|
|
|
|
|
[`node:${id}`],
|
|
|
|
|
'The object in the registry has the correct key'
|
|
|
|
|
);
|
|
|
|
|
});
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
test('Returns an existing tracker when one is found', function(assert) {
|
|
|
|
|
const registry = this.subject();
|
|
|
|
|
const node = mockNode.create();
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
const tracker1 = registry.getTracker(node);
|
|
|
|
|
const tracker2 = registry.getTracker(node);
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
assert.equal(tracker1, tracker2, 'Returns an existing tracker for the same resource');
|
|
|
|
|
assert.equal(registry.get('registryRef').size, 1, 'Only one tracker in the registry');
|
|
|
|
|
});
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
test('Registry does not depend on persistent object references', function(assert) {
|
|
|
|
|
const registry = this.subject();
|
|
|
|
|
const id = 'some-id';
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
const node1 = mockNode.create({ id });
|
|
|
|
|
const node2 = mockNode.create({ id });
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
assert.notEqual(node1, node2, 'Two different resources');
|
|
|
|
|
assert.equal(node1.get('id'), node2.get('id'), 'With the same IDs');
|
2019-03-13 00:40:39 +00:00
|
|
|
|
assert.equal(
|
|
|
|
|
node1.constructor.modelName,
|
|
|
|
|
node2.constructor.modelName,
|
|
|
|
|
'And the same className'
|
|
|
|
|
);
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
assert.equal(registry.getTracker(node1), registry.getTracker(node2), 'Return the same tracker');
|
|
|
|
|
assert.equal(registry.get('registryRef').size, 1, 'Only one tracker in the registry');
|
|
|
|
|
});
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
test('Has a max size', function(assert) {
|
|
|
|
|
const registry = this.subject();
|
|
|
|
|
const ref = registry.get('registryRef');
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
// Kind of a silly assertion, but the exact limit is arbitrary. Whether it's 10 or 1000
|
|
|
|
|
// isn't important as long as there is one.
|
|
|
|
|
assert.ok(ref.limit < Infinity, `A limit (${ref.limit}) is set`);
|
|
|
|
|
});
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
test('Registry re-attaches deleted resources to cached trackers', function(assert) {
|
|
|
|
|
const registry = this.subject();
|
|
|
|
|
const id = 'some-id';
|
2019-03-29 23:09:14 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
const node1 = mockNode.create({ id });
|
|
|
|
|
let tracker = registry.getTracker(node1);
|
2019-03-29 23:09:14 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
assert.ok(tracker.get('node'), 'The tracker has a node');
|
2019-03-29 23:09:14 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
tracker.set('node', null);
|
|
|
|
|
assert.notOk(tracker.get('node'), 'The tracker does not have a node');
|
2019-03-29 23:09:14 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
tracker = registry.getTracker(node1);
|
|
|
|
|
assert.equal(
|
|
|
|
|
tracker.get('node'),
|
|
|
|
|
node1,
|
|
|
|
|
'The node was re-attached to the tracker after calling getTracker again'
|
|
|
|
|
);
|
|
|
|
|
});
|
2019-03-29 23:09:14 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
test('Registry re-attaches destroyed resources to cached trackers', async function(assert) {
|
|
|
|
|
const registry = this.subject();
|
|
|
|
|
const id = 'some-id';
|
2019-03-29 23:09:14 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
const node1 = mockNode.create({ id });
|
|
|
|
|
let tracker = registry.getTracker(node1);
|
2019-03-29 23:09:14 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
assert.ok(tracker.get('node'), 'The tracker has a node');
|
2019-03-29 23:09:14 +00:00
|
|
|
|
|
|
|
|
|
node1.destroy();
|
2019-03-13 00:04:16 +00:00
|
|
|
|
await settled();
|
2019-03-29 23:09:14 +00:00
|
|
|
|
|
|
|
|
|
assert.ok(tracker.get('node').isDestroyed, 'The tracker node is destroyed');
|
|
|
|
|
|
|
|
|
|
const node2 = mockNode.create({ id });
|
|
|
|
|
tracker = registry.getTracker(node2);
|
|
|
|
|
assert.equal(
|
|
|
|
|
tracker.get('node'),
|
|
|
|
|
node2,
|
|
|
|
|
'Since node1 was destroyed but it matches the tracker of node2, node2 is attached to the tracker'
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
test('Removes least recently used when something needs to be removed', function(assert) {
|
|
|
|
|
const registry = this.subject();
|
|
|
|
|
const activeNode = mockNode.create({ id: 'active' });
|
|
|
|
|
const inactiveNode = mockNode.create({ id: 'inactive' });
|
|
|
|
|
const limit = registry.get('registryRef').limit;
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
// First put in the two tracked nodes
|
|
|
|
|
registry.getTracker(activeNode);
|
|
|
|
|
registry.getTracker(inactiveNode);
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
for (let i = 0; i < limit; i++) {
|
|
|
|
|
// Add a new tracker to the registry
|
|
|
|
|
const newNode = mockNode.create({ id: `node-${i}` });
|
|
|
|
|
registry.getTracker(newNode);
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
// But read the active node tracker to keep it fresh
|
|
|
|
|
registry.getTracker(activeNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ref = registry.get('registryRef');
|
|
|
|
|
assert.equal(ref.size, ref.limit, 'The limit was reached');
|
|
|
|
|
|
|
|
|
|
assert.ok(
|
|
|
|
|
ref.get('node:active'),
|
|
|
|
|
'The active tracker is still in the registry despite being added first'
|
|
|
|
|
);
|
|
|
|
|
assert.notOk(
|
|
|
|
|
ref.get('node:inactive'),
|
|
|
|
|
'The inactive tracker got pushed out due to not being accessed'
|
|
|
|
|
);
|
|
|
|
|
});
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
test('Trackers are created using the token authorizedRequest', function(assert) {
|
|
|
|
|
const registry = this.subject();
|
|
|
|
|
const node = mockNode.create();
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
const tracker = registry.getTracker(node);
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
tracker.get('poll').perform();
|
|
|
|
|
assert.ok(
|
|
|
|
|
this.tokenAuthorizedRequestSpy.calledWith(`/v1/client/stats?node_id=${node.get('id')}`),
|
|
|
|
|
'The token service authorizedRequest function was used'
|
|
|
|
|
);
|
2018-09-19 22:19:06 +00:00
|
|
|
|
|
2019-03-13 00:04:16 +00:00
|
|
|
|
return settled();
|
|
|
|
|
});
|
2018-09-19 22:19:06 +00:00
|
|
|
|
});
|