import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import setupGlimmerComponentFactory from 'nomad-ui/tests/helpers/glimmer-factory'; module('Unit | Component | TopoViz', function(hooks) { setupTest(hooks); setupGlimmerComponentFactory(hooks, 'topo-viz'); test('the topology object properly organizes a tree of datacenters > nodes > allocations', async function(assert) { const nodes = [ { datacenter: 'dc1', id: 'node0', resources: {} }, { datacenter: 'dc2', id: 'node1', resources: {} }, { datacenter: 'dc1', id: 'node2', resources: {} }, ]; const node0Allocs = [ alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'group' }), alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'group' }), ]; const node1Allocs = [ alloc({ nodeId: 'node1', jobId: 'job0', taskGroupName: 'group' }), alloc({ nodeId: 'node1', jobId: 'job1', taskGroupName: 'group' }), ]; const node2Allocs = [ alloc({ nodeId: 'node2', jobId: 'job0', taskGroupName: 'group' }), alloc({ nodeId: 'node2', jobId: 'job1', taskGroupName: 'group' }), ]; const allocations = [...node0Allocs, ...node1Allocs, ...node2Allocs]; const topoViz = this.createComponent({ nodes, allocations }); topoViz.buildTopology(); assert.deepEqual(topoViz.topology.datacenters.mapBy('name'), ['dc1', 'dc2']); assert.deepEqual(topoViz.topology.datacenters[0].nodes.mapBy('node'), [nodes[0], nodes[2]]); assert.deepEqual(topoViz.topology.datacenters[1].nodes.mapBy('node'), [nodes[1]]); assert.deepEqual( topoViz.topology.datacenters[0].nodes[0].allocations.mapBy('allocation'), node0Allocs ); assert.deepEqual( topoViz.topology.datacenters[1].nodes[0].allocations.mapBy('allocation'), node1Allocs ); assert.deepEqual( topoViz.topology.datacenters[0].nodes[1].allocations.mapBy('allocation'), node2Allocs ); }); test('the topology object contains an allocation index keyed by jobId+taskGroupName', async function(assert) { const allocations = [ alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'one' }), alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'one' }), alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'two' }), alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'one' }), alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'two' }), alloc({ nodeId: 'node0', jobId: 'job1', taskGroupName: 'three' }), alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }), alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }), alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }), alloc({ nodeId: 'node0', jobId: 'job2', taskGroupName: 'one' }), ]; const nodes = [{ datacenter: 'dc1', id: 'node0', resources: {} }]; const topoViz = this.createComponent({ nodes, allocations }); topoViz.buildTopology(); assert.deepEqual( Object.keys(topoViz.topology.allocationIndex).sort(), [ JSON.stringify(['job0', 'one']), JSON.stringify(['job0', 'two']), JSON.stringify(['job1', 'one']), JSON.stringify(['job1', 'two']), JSON.stringify(['job1', 'three']), JSON.stringify(['job2', 'one']), ].sort() ); Object.keys(topoViz.topology.allocationIndex).forEach(key => { const [jobId, group] = JSON.parse(key); assert.deepEqual( topoViz.topology.allocationIndex[key].mapBy('allocation'), allocations.filter(alloc => alloc.jobId === jobId && alloc.taskGroupName === group) ); }); }); test('isSingleColumn is true when there is only one datacenter', async function(assert) { const oneDc = [{ datacenter: 'dc1', id: 'node0', resources: {} }]; const twoDc = [...oneDc, { datacenter: 'dc2', id: 'node1', resources: {} }]; const topoViz1 = this.createComponent({ nodes: oneDc, allocations: [] }); const topoViz2 = this.createComponent({ nodes: twoDc, allocations: [] }); topoViz1.buildTopology(); topoViz2.buildTopology(); assert.ok(topoViz1.isSingleColumn); assert.notOk(topoViz2.isSingleColumn); }); test('isSingleColumn is true when there are multiple datacenters with a high variance in node count', async function(assert) { const uniformDcs = [ { datacenter: 'dc1', id: 'node0', resources: {} }, { datacenter: 'dc2', id: 'node1', resources: {} }, ]; const skewedDcs = [ { datacenter: 'dc1', id: 'node0', resources: {} }, { datacenter: 'dc2', id: 'node1', resources: {} }, { datacenter: 'dc2', id: 'node2', resources: {} }, { datacenter: 'dc2', id: 'node3', resources: {} }, { datacenter: 'dc2', id: 'node4', resources: {} }, ]; const twoColumnViz = this.createComponent({ nodes: uniformDcs, allocations: [] }); const oneColumViz = this.createComponent({ nodes: skewedDcs, allocations: [] }); twoColumnViz.buildTopology(); oneColumViz.buildTopology(); assert.notOk(twoColumnViz.isSingleColumn); assert.ok(oneColumViz.isSingleColumn); }); test('datacenterIsSingleColumn is only ever false when isSingleColumn is false and the total node count is high', async function(assert) { const manyUniformNodes = Array(25) .fill(null) .map((_, index) => ({ datacenter: index > 12 ? 'dc2' : 'dc1', id: `node${index}`, resources: {}, })); const manySkewedNodes = Array(25) .fill(null) .map((_, index) => ({ datacenter: index > 5 ? 'dc2' : 'dc1', id: `node${index}`, resources: {}, })); const oneColumnViz = this.createComponent({ nodes: manyUniformNodes, allocations: [] }); const twoColumnViz = this.createComponent({ nodes: manySkewedNodes, allocations: [] }); oneColumnViz.buildTopology(); twoColumnViz.buildTopology(); assert.ok(oneColumnViz.datacenterIsSingleColumn); assert.notOk(oneColumnViz.isSingleColumn); assert.notOk(twoColumnViz.datacenterIsSingleColumn); assert.ok(twoColumnViz.isSingleColumn); }); test('dataForAllocation correctly calculates proportion of node utilization and group key', async function(assert) { const nodes = [{ datacenter: 'dc1', id: 'node0', resources: { cpu: 100, memory: 250 } }]; const allocations = [ alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'group', allocatedResources: { cpu: 50, memory: 25 }, }), ]; const topoViz = this.createComponent({ nodes, allocations }); topoViz.buildTopology(); assert.equal(topoViz.topology.datacenters[0].nodes[0].allocations[0].cpuPercent, 0.5); assert.equal(topoViz.topology.datacenters[0].nodes[0].allocations[0].memoryPercent, 0.1); }); test('allocations that reference nonexistent nodes are ignored', async function(assert) { const nodes = [{ datacenter: 'dc1', id: 'node0', resources: {} }]; const allocations = [ alloc({ nodeId: 'node0', jobId: 'job0', taskGroupName: 'group' }), alloc({ nodeId: 'node404', jobId: 'job1', taskGroupName: 'group' }), ]; const topoViz = this.createComponent({ nodes, allocations }); topoViz.buildTopology(); assert.deepEqual(topoViz.topology.datacenters[0].nodes.mapBy('node'), [nodes[0]]); assert.deepEqual(topoViz.topology.datacenters[0].nodes[0].allocations.mapBy('allocation'), [ allocations[0], ]); }); }); function alloc(props) { return { ...props, allocatedResources: props.allocatedResources || {}, belongsTo(type) { return { id() { return type === 'job' ? props.jobId : props.nodeId; }, }; }, }; }