github.com/hernad/nomad@v1.6.112/ui/tests/unit/services/stats-trackers-registry-test.js (about) 1 /** 2 * Copyright (c) HashiCorp, Inc. 3 * SPDX-License-Identifier: MPL-2.0 4 */ 5 6 import EmberObject from '@ember/object'; 7 import Service from '@ember/service'; 8 import { module, test } from 'qunit'; 9 import { setupTest } from 'ember-qunit'; 10 import { settled } from '@ember/test-helpers'; 11 import Pretender from 'pretender'; 12 import sinon from 'sinon'; 13 import fetch from 'nomad-ui/utils/fetch'; 14 import NodeStatsTracker from 'nomad-ui/utils/classes/node-stats-tracker'; 15 16 module('Unit | Service | Stats Trackers Registry', function (hooks) { 17 setupTest(hooks); 18 19 hooks.beforeEach(function () { 20 this.subject = function () { 21 return this.owner.factoryFor('service:stats-trackers-registry').create(); 22 }; 23 }); 24 25 hooks.beforeEach(function () { 26 // Inject a mock token service 27 const authorizedRequestSpy = (this.tokenAuthorizedRequestSpy = sinon.spy()); 28 const mockToken = Service.extend({ 29 authorizedRequest(url) { 30 authorizedRequestSpy(url); 31 return fetch(url); 32 }, 33 }); 34 35 this.owner.register('service:token', mockToken); 36 this.token = this.owner.lookup('service:token'); 37 this.server = new Pretender(function () { 38 this.get('/v1/client/stats', () => [ 39 200, 40 {}, 41 JSON.stringify({ 42 Timestamp: 1234567890, 43 CPUTicksConsumed: 11, 44 Memory: { 45 Used: 12, 46 }, 47 }), 48 ]); 49 }); 50 }); 51 52 hooks.afterEach(function () { 53 this.server.shutdown(); 54 }); 55 56 const makeModelMock = (modelName, defaults) => { 57 const Class = EmberObject.extend(defaults); 58 Class.prototype.constructor.modelName = modelName; 59 return Class; 60 }; 61 62 const mockNode = makeModelMock('node', { id: 'test' }); 63 64 test('Creates a tracker when one isn’t found', function (assert) { 65 const registry = this.subject(); 66 const id = 'id'; 67 68 assert.equal( 69 registry.get('registryRef').size, 70 0, 71 'Nothing in the registry yet' 72 ); 73 74 const tracker = registry.getTracker(mockNode.create({ id })); 75 assert.ok( 76 tracker instanceof NodeStatsTracker, 77 'The correct type of tracker is made' 78 ); 79 assert.equal( 80 registry.get('registryRef').size, 81 1, 82 'The tracker was added to the registry' 83 ); 84 assert.deepEqual( 85 Array.from(registry.get('registryRef').keys()), 86 [`node:${id}`], 87 'The object in the registry has the correct key' 88 ); 89 }); 90 91 test('Returns an existing tracker when one is found', function (assert) { 92 const registry = this.subject(); 93 const node = mockNode.create(); 94 95 const tracker1 = registry.getTracker(node); 96 const tracker2 = registry.getTracker(node); 97 98 assert.equal( 99 tracker1, 100 tracker2, 101 'Returns an existing tracker for the same resource' 102 ); 103 assert.equal( 104 registry.get('registryRef').size, 105 1, 106 'Only one tracker in the registry' 107 ); 108 }); 109 110 test('Registry does not depend on persistent object references', function (assert) { 111 const registry = this.subject(); 112 const id = 'some-id'; 113 114 const node1 = mockNode.create({ id }); 115 const node2 = mockNode.create({ id }); 116 117 assert.notEqual(node1, node2, 'Two different resources'); 118 assert.equal(node1.get('id'), node2.get('id'), 'With the same IDs'); 119 assert.equal( 120 node1.constructor.modelName, 121 node2.constructor.modelName, 122 'And the same className' 123 ); 124 125 assert.equal( 126 registry.getTracker(node1), 127 registry.getTracker(node2), 128 'Return the same tracker' 129 ); 130 assert.equal( 131 registry.get('registryRef').size, 132 1, 133 'Only one tracker in the registry' 134 ); 135 }); 136 137 test('Has a max size', function (assert) { 138 const registry = this.subject(); 139 const ref = registry.get('registryRef'); 140 141 // Kind of a silly assertion, but the exact limit is arbitrary. Whether it's 10 or 1000 142 // isn't important as long as there is one. 143 assert.ok(ref.limit < Infinity, `A limit (${ref.limit}) is set`); 144 }); 145 146 test('Registry re-attaches deleted resources to cached trackers', function (assert) { 147 const registry = this.subject(); 148 const id = 'some-id'; 149 150 const node1 = mockNode.create({ id }); 151 let tracker = registry.getTracker(node1); 152 153 assert.ok(tracker.get('node'), 'The tracker has a node'); 154 155 tracker.set('node', null); 156 assert.notOk(tracker.get('node'), 'The tracker does not have a node'); 157 158 tracker = registry.getTracker(node1); 159 assert.equal( 160 tracker.get('node'), 161 node1, 162 'The node was re-attached to the tracker after calling getTracker again' 163 ); 164 }); 165 166 test('Registry re-attaches destroyed resources to cached trackers', async function (assert) { 167 const registry = this.subject(); 168 const id = 'some-id'; 169 170 const node1 = mockNode.create({ id }); 171 let tracker = registry.getTracker(node1); 172 173 assert.ok(tracker.get('node'), 'The tracker has a node'); 174 175 node1.destroy(); 176 await settled(); 177 178 assert.ok(tracker.get('node').isDestroyed, 'The tracker node is destroyed'); 179 180 const node2 = mockNode.create({ id }); 181 tracker = registry.getTracker(node2); 182 assert.equal( 183 tracker.get('node'), 184 node2, 185 'Since node1 was destroyed but it matches the tracker of node2, node2 is attached to the tracker' 186 ); 187 }); 188 189 test('Removes least recently used when something needs to be removed', function (assert) { 190 const registry = this.subject(); 191 const activeNode = mockNode.create({ id: 'active' }); 192 const inactiveNode = mockNode.create({ id: 'inactive' }); 193 const limit = registry.get('registryRef').limit; 194 195 // First put in the two tracked nodes 196 registry.getTracker(activeNode); 197 registry.getTracker(inactiveNode); 198 199 for (let i = 0; i < limit; i++) { 200 // Add a new tracker to the registry 201 const newNode = mockNode.create({ id: `node-${i}` }); 202 registry.getTracker(newNode); 203 204 // But read the active node tracker to keep it fresh 205 registry.getTracker(activeNode); 206 } 207 208 const ref = registry.get('registryRef'); 209 assert.equal(ref.size, ref.limit, 'The limit was reached'); 210 211 assert.ok( 212 ref.get('node:active'), 213 'The active tracker is still in the registry despite being added first' 214 ); 215 assert.notOk( 216 ref.get('node:inactive'), 217 'The inactive tracker got pushed out due to not being accessed' 218 ); 219 }); 220 221 test('Trackers are created using the token authorizedRequest', function (assert) { 222 const registry = this.subject(); 223 const node = mockNode.create(); 224 225 const tracker = registry.getTracker(node); 226 227 tracker.get('poll').perform(); 228 assert.ok( 229 this.tokenAuthorizedRequestSpy.calledWith( 230 `/v1/client/stats?node_id=${node.get('id')}` 231 ), 232 'The token service authorizedRequest function was used' 233 ); 234 235 return settled(); 236 }); 237 });