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