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  });