github.com/hernad/nomad@v1.6.112/ui/tests/unit/components/topo-viz-test.js (about)

     1  /**
     2   * Copyright (c) HashiCorp, Inc.
     3   * SPDX-License-Identifier: MPL-2.0
     4   */
     5  
     6  import { module, test } from 'qunit';
     7  import { setupTest } from 'ember-qunit';
     8  import setupGlimmerComponentFactory from 'nomad-ui/tests/helpers/glimmer-factory';
     9  
    10  module('Unit | Component | TopoViz', function (hooks) {
    11    setupTest(hooks);
    12    setupGlimmerComponentFactory(hooks, 'topo-viz');
    13  
    14    test('the topology object properly organizes a tree of datacenters > nodes > allocations', async function (assert) {
    15      const nodes = [
    16        { datacenter: 'dc1', id: 'node0', resources: {} },
    17        { datacenter: 'dc2', id: 'node1', resources: {} },
    18        { datacenter: 'dc1', id: 'node2', resources: {} },
    19      ];
    20  
    21      const node0Allocs = [
    22        alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'group' }),
    23        alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'group' }),
    24      ];
    25      const node1Allocs = [
    26        alloc({ nodeId: 'node1', jobId: 'job0', taskGroupName: 'group' }),
    27        alloc({ nodeId: 'node1', jobId: 'job1', taskGroupName: 'group' }),
    28      ];
    29      const node2Allocs = [
    30        alloc({ nodeId: 'node2', jobId: 'job0', taskGroupName: 'group' }),
    31        alloc({ nodeId: 'node2', jobId: 'job1', taskGroupName: 'group' }),
    32      ];
    33  
    34      const allocations = [...node0Allocs, ...node1Allocs, ...node2Allocs];
    35  
    36      const topoViz = this.createComponent({ nodes, allocations });
    37  
    38      topoViz.buildTopology();
    39  
    40      assert.deepEqual(topoViz.topology.datacenters.mapBy('name'), [
    41        'dc1',
    42        'dc2',
    43      ]);
    44      assert.deepEqual(topoViz.topology.datacenters[0].nodes.mapBy('node'), [
    45        nodes[0],
    46        nodes[2],
    47      ]);
    48      assert.deepEqual(topoViz.topology.datacenters[1].nodes.mapBy('node'), [
    49        nodes[1],
    50      ]);
    51      assert.deepEqual(
    52        topoViz.topology.datacenters[0].nodes[0].allocations.mapBy('allocation'),
    53        node0Allocs
    54      );
    55      assert.deepEqual(
    56        topoViz.topology.datacenters[1].nodes[0].allocations.mapBy('allocation'),
    57        node1Allocs
    58      );
    59      assert.deepEqual(
    60        topoViz.topology.datacenters[0].nodes[1].allocations.mapBy('allocation'),
    61        node2Allocs
    62      );
    63    });
    64  
    65    test('the topology object contains an allocation index keyed by jobId+taskGroupName', async function (assert) {
    66      assert.expect(7);
    67  
    68      const allocations = [
    69        alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'one' }),
    70        alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'one' }),
    71        alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'two' }),
    72        alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'one' }),
    73        alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'two' }),
    74        alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'three' }),
    75        alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }),
    76        alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }),
    77        alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }),
    78        alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }),
    79      ];
    80  
    81      const nodes = [{ datacenter: 'dc1', id: 'node0', resources: {} }];
    82      const topoViz = this.createComponent({ nodes, allocations });
    83  
    84      topoViz.buildTopology();
    85  
    86      assert.deepEqual(
    87        Object.keys(topoViz.topology.allocationIndex).sort(),
    88        [
    89          JSON.stringify(['job0', 'one']),
    90          JSON.stringify(['job0', 'two']),
    91  
    92          JSON.stringify(['job1', 'one']),
    93          JSON.stringify(['job1', 'two']),
    94          JSON.stringify(['job1', 'three']),
    95  
    96          JSON.stringify(['job2', 'one']),
    97        ].sort()
    98      );
    99  
   100      Object.keys(topoViz.topology.allocationIndex).forEach((key) => {
   101        const [jobId, group] = JSON.parse(key);
   102        assert.deepEqual(
   103          topoViz.topology.allocationIndex[key].mapBy('allocation'),
   104          allocations.filter(
   105            (alloc) => alloc.jobId === jobId && alloc.taskGroupName === group
   106          )
   107        );
   108      });
   109    });
   110  
   111    test('isSingleColumn is true when there is only one datacenter', async function (assert) {
   112      const oneDc = [{ datacenter: 'dc1', id: 'node0', resources: {} }];
   113      const twoDc = [...oneDc, { datacenter: 'dc2', id: 'node1', resources: {} }];
   114  
   115      const topoViz1 = this.createComponent({ nodes: oneDc, allocations: [] });
   116      const topoViz2 = this.createComponent({ nodes: twoDc, allocations: [] });
   117  
   118      topoViz1.buildTopology();
   119      topoViz2.buildTopology();
   120  
   121      assert.ok(topoViz1.isSingleColumn);
   122      assert.notOk(topoViz2.isSingleColumn);
   123    });
   124  
   125    test('isSingleColumn is true when there are multiple datacenters with a high variance in node count', async function (assert) {
   126      const uniformDcs = [
   127        { datacenter: 'dc1', id: 'node0', resources: {} },
   128        { datacenter: 'dc2', id: 'node1', resources: {} },
   129      ];
   130      const skewedDcs = [
   131        { datacenter: 'dc1', id: 'node0', resources: {} },
   132        { datacenter: 'dc2', id: 'node1', resources: {} },
   133        { datacenter: 'dc2', id: 'node2', resources: {} },
   134        { datacenter: 'dc2', id: 'node3', resources: {} },
   135        { datacenter: 'dc2', id: 'node4', resources: {} },
   136      ];
   137  
   138      const twoColumnViz = this.createComponent({
   139        nodes: uniformDcs,
   140        allocations: [],
   141      });
   142      const oneColumViz = this.createComponent({
   143        nodes: skewedDcs,
   144        allocations: [],
   145      });
   146  
   147      twoColumnViz.buildTopology();
   148      oneColumViz.buildTopology();
   149  
   150      assert.notOk(twoColumnViz.isSingleColumn);
   151      assert.ok(oneColumViz.isSingleColumn);
   152    });
   153  
   154    test('datacenterIsSingleColumn is only ever false when isSingleColumn is false and the total node count is high', async function (assert) {
   155      const manyUniformNodes = Array(25)
   156        .fill(null)
   157        .map((_, index) => ({
   158          datacenter: index > 12 ? 'dc2' : 'dc1',
   159          id: `node${index}`,
   160          resources: {},
   161        }));
   162      const manySkewedNodes = Array(25)
   163        .fill(null)
   164        .map((_, index) => ({
   165          datacenter: index > 5 ? 'dc2' : 'dc1',
   166          id: `node${index}`,
   167          resources: {},
   168        }));
   169  
   170      const oneColumnViz = this.createComponent({
   171        nodes: manyUniformNodes,
   172        allocations: [],
   173      });
   174      const twoColumnViz = this.createComponent({
   175        nodes: manySkewedNodes,
   176        allocations: [],
   177      });
   178  
   179      oneColumnViz.buildTopology();
   180      twoColumnViz.buildTopology();
   181  
   182      assert.ok(oneColumnViz.datacenterIsSingleColumn);
   183      assert.notOk(oneColumnViz.isSingleColumn);
   184  
   185      assert.notOk(twoColumnViz.datacenterIsSingleColumn);
   186      assert.ok(twoColumnViz.isSingleColumn);
   187    });
   188  
   189    test('dataForAllocation correctly calculates proportion of node utilization and group key', async function (assert) {
   190      const nodes = [
   191        { datacenter: 'dc1', id: 'node0', resources: { cpu: 100, memory: 250 } },
   192      ];
   193      const allocations = [
   194        alloc({
   195          nodeId: 'node0',
   196          jobId: 'job0',
   197          taskGroupName: 'group',
   198          allocatedResources: { cpu: 50, memory: 25 },
   199        }),
   200      ];
   201  
   202      const topoViz = this.createComponent({ nodes, allocations });
   203      topoViz.buildTopology();
   204  
   205      assert.equal(
   206        topoViz.topology.datacenters[0].nodes[0].allocations[0].cpuPercent,
   207        0.5
   208      );
   209      assert.equal(
   210        topoViz.topology.datacenters[0].nodes[0].allocations[0].memoryPercent,
   211        0.1
   212      );
   213    });
   214  
   215    test('allocations that reference nonexistent nodes are ignored', async function (assert) {
   216      const nodes = [{ datacenter: 'dc1', id: 'node0', resources: {} }];
   217  
   218      const allocations = [
   219        alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'group' }),
   220        alloc({ nodeId: 'node404', jobId: 'job1', taskGroupName: 'group' }),
   221      ];
   222  
   223      const topoViz = this.createComponent({ nodes, allocations });
   224  
   225      topoViz.buildTopology();
   226  
   227      assert.deepEqual(topoViz.topology.datacenters[0].nodes.mapBy('node'), [
   228        nodes[0],
   229      ]);
   230      assert.deepEqual(
   231        topoViz.topology.datacenters[0].nodes[0].allocations.mapBy('allocation'),
   232        [allocations[0]]
   233      );
   234    });
   235  });
   236  
   237  function alloc(props) {
   238    return {
   239      ...props,
   240      allocatedResources: props.allocatedResources || {},
   241      belongsTo(type) {
   242        return {
   243          id() {
   244            return type === 'job' ? props.jobId : props.nodeId;
   245          },
   246        };
   247      },
   248    };
   249  }