github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/ui/tests/unit/utils/node-stats-tracker-test.js (about)

     1  import EmberObject from '@ember/object';
     2  import { assign } from '@ember/polyfills';
     3  import { module, test } from 'qunit';
     4  import sinon from 'sinon';
     5  import Pretender from 'pretender';
     6  import NodeStatsTracker, { stats } from 'nomad-ui/utils/classes/node-stats-tracker';
     7  import fetch from 'nomad-ui/utils/fetch';
     8  import statsTrackerFrameMissingBehavior from './behaviors/stats-tracker-frame-missing';
     9  
    10  import { settled } from '@ember/test-helpers';
    11  
    12  module('Unit | Util | NodeStatsTracker', function() {
    13    const refDate = Date.now() * 1000000;
    14    const makeDate = ts => new Date(ts / 1000000);
    15  
    16    const MockNode = overrides =>
    17      assign(
    18        {
    19          id: 'some-identifier',
    20          resources: {
    21            cpu: 2000,
    22            memory: 4096,
    23          },
    24        },
    25        overrides
    26      );
    27  
    28    const mockFrame = step => ({
    29      CPUTicksConsumed: step + 1000,
    30      Memory: {
    31        Used: (step + 2048) * 1024 * 1024,
    32      },
    33      Timestamp: refDate + step,
    34    });
    35  
    36    test('the NodeStatsTracker constructor expects a fetch definition and a node', async function(assert) {
    37      const tracker = NodeStatsTracker.create();
    38      assert.throws(
    39        () => {
    40          tracker.fetch();
    41        },
    42        /StatsTrackers need a fetch method/,
    43        'Polling does not work without a fetch method provided'
    44      );
    45    });
    46  
    47    test('the url property is computed based off the node id', async function(assert) {
    48      const node = MockNode();
    49      const tracker = NodeStatsTracker.create({ fetch, node });
    50  
    51      assert.equal(
    52        tracker.get('url'),
    53        `/v1/client/stats?node_id=${node.id}`,
    54        'Url is derived from the node id'
    55      );
    56    });
    57  
    58    test('reservedCPU and reservedMemory properties come from the node', async function(assert) {
    59      const node = MockNode();
    60      const tracker = NodeStatsTracker.create({ fetch, node });
    61  
    62      assert.equal(tracker.get('reservedCPU'), node.resources.cpu, 'reservedCPU comes from the node');
    63      assert.equal(
    64        tracker.get('reservedMemory'),
    65        node.resources.memory,
    66        'reservedMemory comes from the node'
    67      );
    68    });
    69  
    70    test('poll results in requesting the url and calling append with the resulting JSON', async function(assert) {
    71      const node = MockNode();
    72      const tracker = NodeStatsTracker.create({ fetch, node, append: sinon.spy() });
    73      const mockFrame = {
    74        Some: {
    75          data: ['goes', 'here'],
    76          twelve: 12,
    77        },
    78      };
    79  
    80      const server = new Pretender(function() {
    81        this.get('/v1/client/stats', () => [200, {}, JSON.stringify(mockFrame)]);
    82      });
    83  
    84      tracker.get('poll').perform();
    85  
    86      assert.equal(server.handledRequests.length, 1, 'Only one request was made');
    87      assert.equal(
    88        server.handledRequests[0].url,
    89        `/v1/client/stats?node_id=${node.id}`,
    90        'The correct URL was requested'
    91      );
    92  
    93      await settled();
    94      assert.ok(
    95        tracker.append.calledWith(mockFrame),
    96        'The JSON response was passed into append as a POJO'
    97      );
    98  
    99      server.shutdown();
   100    });
   101  
   102    test('append appropriately maps a data frame to the tracked stats for cpu and memory for the node', async function(assert) {
   103      const node = MockNode();
   104      const tracker = NodeStatsTracker.create({ fetch, node });
   105  
   106      assert.deepEqual(tracker.get('cpu'), [], 'No tracked cpu yet');
   107      assert.deepEqual(tracker.get('memory'), [], 'No tracked memory yet');
   108  
   109      tracker.append(mockFrame(1));
   110  
   111      assert.deepEqual(
   112        tracker.get('cpu'),
   113        [{ timestamp: makeDate(refDate + 1), used: 1001, percent: 1001 / 2000 }],
   114        'One frame of cpu'
   115      );
   116  
   117      assert.deepEqual(
   118        tracker.get('memory'),
   119        [{ timestamp: makeDate(refDate + 1), used: 2049 * 1024 * 1024, percent: 2049 / 4096 }],
   120        'One frame of memory'
   121      );
   122  
   123      tracker.append(mockFrame(2));
   124  
   125      assert.deepEqual(
   126        tracker.get('cpu'),
   127        [
   128          { timestamp: makeDate(refDate + 1), used: 1001, percent: 1001 / 2000 },
   129          { timestamp: makeDate(refDate + 2), used: 1002, percent: 1002 / 2000 },
   130        ],
   131        'Two frames of cpu'
   132      );
   133  
   134      assert.deepEqual(
   135        tracker.get('memory'),
   136        [
   137          { timestamp: makeDate(refDate + 1), used: 2049 * 1024 * 1024, percent: 2049 / 4096 },
   138          { timestamp: makeDate(refDate + 2), used: 2050 * 1024 * 1024, percent: 2050 / 4096 },
   139        ],
   140        'Two frames of memory'
   141      );
   142    });
   143  
   144    test('each stat list has maxLength equal to bufferSize', async function(assert) {
   145      const node = MockNode();
   146      const bufferSize = 10;
   147      const tracker = NodeStatsTracker.create({ fetch, node, bufferSize });
   148  
   149      for (let i = 1; i <= 20; i++) {
   150        tracker.append(mockFrame(i));
   151      }
   152  
   153      assert.equal(
   154        tracker.get('cpu.length'),
   155        bufferSize,
   156        `20 calls to append, only ${bufferSize} frames in the stats array`
   157      );
   158      assert.equal(
   159        tracker.get('memory.length'),
   160        bufferSize,
   161        `20 calls to append, only ${bufferSize} frames in the stats array`
   162      );
   163  
   164      assert.equal(
   165        +tracker.get('cpu')[0].timestamp,
   166        +makeDate(refDate + 11),
   167        'Old frames are removed in favor of newer ones'
   168      );
   169      assert.equal(
   170        +tracker.get('memory')[0].timestamp,
   171        +makeDate(refDate + 11),
   172        'Old frames are removed in favor of newer ones'
   173      );
   174    });
   175  
   176    test('the stats computed property macro constructs a NodeStatsTracker based on a nodeProp and a fetch definition', async function(assert) {
   177      const node = MockNode();
   178      const fetchSpy = sinon.spy();
   179  
   180      const SomeClass = EmberObject.extend({
   181        stats: stats('theNode', function() {
   182          return () => fetchSpy(this);
   183        }),
   184      });
   185      const someObject = SomeClass.create({
   186        theNode: node,
   187      });
   188  
   189      assert.equal(
   190        someObject.get('stats.url'),
   191        `/v1/client/stats?node_id=${node.id}`,
   192        'stats computed property macro creates a NodeStatsTracker'
   193      );
   194  
   195      someObject.get('stats').fetch();
   196  
   197      assert.ok(
   198        fetchSpy.calledWith(someObject),
   199        'the fetch factory passed into the macro gets called to assign a bound version of fetch to the NodeStatsTracker instance'
   200      );
   201    });
   202  
   203    test('changing the value of the nodeProp constructs a new NodeStatsTracker', async function(assert) {
   204      const node1 = MockNode();
   205      const node2 = MockNode();
   206      const SomeClass = EmberObject.extend({
   207        stats: stats('theNode', () => fetch),
   208      });
   209  
   210      const someObject = SomeClass.create({
   211        theNode: node1,
   212      });
   213  
   214      const stats1 = someObject.get('stats');
   215  
   216      someObject.set('theNode', node2);
   217      const stats2 = someObject.get('stats');
   218  
   219      assert.notOk(
   220        stats1 === stats2,
   221        'Changing the value of the node results in creating a new NodeStatsTracker instance'
   222      );
   223    });
   224  
   225    statsTrackerFrameMissingBehavior({
   226      resourceName: 'node',
   227      ResourceConstructor: MockNode,
   228      TrackerConstructor: NodeStatsTracker,
   229      mockFrame,
   230      compileResources(frame) {
   231        const timestamp = makeDate(frame.Timestamp);
   232        return [
   233          {
   234            timestamp,
   235            used: frame.CPUTicksConsumed,
   236            percent: frame.CPUTicksConsumed / 2000,
   237          },
   238          {
   239            timestamp,
   240            used: frame.Memory.Used,
   241            percent: frame.Memory.Used / 1024 / 1024 / 4096,
   242          },
   243        ];
   244      },
   245    });
   246  });