-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3c09777
commit 912d3df
Showing
1 changed file
with
222 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
import EmberObject from '@ember/object'; | ||
import { assign } from '@ember/polyfills'; | ||
import wait from 'ember-test-helpers/wait'; | ||
import { module, test } from 'ember-qunit'; | ||
import sinon from 'sinon'; | ||
import Pretender from 'pretender'; | ||
import NodeStatsTracker, { stats } from 'nomad-ui/utils/classes/node-stats-tracker'; | ||
import fetch from 'nomad-ui/utils/fetch'; | ||
|
||
module('Unit | Util | NodeStatsTracker'); | ||
|
||
const refDate = Date.now(); | ||
|
||
const MockNode = overrides => | ||
assign( | ||
{ | ||
id: 'some-identifier', | ||
resources: { | ||
cpu: 2000, | ||
memory: 4096, | ||
}, | ||
}, | ||
overrides | ||
); | ||
|
||
const mockFrame = step => ({ | ||
CPUTicksConsumed: step + 1000, | ||
Memory: { | ||
Used: (step + 2048) * 1024 * 1024, | ||
}, | ||
Timestamp: refDate + step, | ||
}); | ||
|
||
test('the NodeStatsTracker constructor expects a fetch definition and a node', function(assert) { | ||
const tracker = NodeStatsTracker.create(); | ||
assert.throws( | ||
() => { | ||
tracker.poll(); | ||
}, | ||
/StatsTrackers need a fetch method/, | ||
'Polling does not work without a fetch method provided' | ||
); | ||
}); | ||
|
||
test('the url property is computed based off the node id', function(assert) { | ||
const node = MockNode(); | ||
const tracker = NodeStatsTracker.create({ fetch, node }); | ||
|
||
assert.equal( | ||
tracker.get('url'), | ||
`/v1/client/stats?node_id=${node.id}`, | ||
'Url is derived from the node id' | ||
); | ||
}); | ||
|
||
test('reservedCPU and reservedMemory properties come from the node', function(assert) { | ||
const node = MockNode(); | ||
const tracker = NodeStatsTracker.create({ fetch, node }); | ||
|
||
assert.equal(tracker.get('reservedCPU'), node.resources.cpu, 'reservedCPU comes from the node'); | ||
assert.equal( | ||
tracker.get('reservedMemory'), | ||
node.resources.memory, | ||
'reservedMemory comes from the node' | ||
); | ||
}); | ||
|
||
test('poll results in requesting the url and calling append with the resulting JSON', function(assert) { | ||
const node = MockNode(); | ||
const tracker = NodeStatsTracker.create({ fetch, node, append: sinon.spy() }); | ||
const mockFrame = { | ||
Some: { | ||
data: ['goes', 'here'], | ||
twelve: 12, | ||
}, | ||
}; | ||
|
||
const server = new Pretender(function() { | ||
this.get('/v1/client/stats', () => [200, {}, JSON.stringify(mockFrame)]); | ||
}); | ||
|
||
tracker.poll(); | ||
|
||
assert.equal(server.handledRequests.length, 1, 'Only one request was made'); | ||
assert.equal( | ||
server.handledRequests[0].url, | ||
`/v1/client/stats?node_id=${node.id}`, | ||
'The correct URL was requested' | ||
); | ||
|
||
return wait().then(() => { | ||
assert.ok( | ||
tracker.append.calledWith(mockFrame), | ||
'The JSON response was passed into append as a POJO' | ||
); | ||
|
||
server.shutdown(); | ||
}); | ||
}); | ||
|
||
test('append appropriately maps a data frame to the tracked stats for cpu and memory for the node', function(assert) { | ||
const node = MockNode(); | ||
const tracker = NodeStatsTracker.create({ fetch, node }); | ||
|
||
assert.deepEqual(tracker.get('cpu'), [], 'No tracked cpu yet'); | ||
assert.deepEqual(tracker.get('memory'), [], 'No tracked memory yet'); | ||
|
||
tracker.append(mockFrame(1)); | ||
|
||
assert.deepEqual( | ||
tracker.get('cpu'), | ||
[{ timestamp: refDate + 1, used: 1001, percent: 1001 / 2000 }], | ||
'One frame of cpu' | ||
); | ||
|
||
assert.deepEqual( | ||
tracker.get('memory'), | ||
[{ timestamp: refDate + 1, used: 2049 * 1024 * 1024, percent: 2049 / 4096 }], | ||
'One frame of memory' | ||
); | ||
|
||
tracker.append(mockFrame(2)); | ||
|
||
assert.deepEqual( | ||
tracker.get('cpu'), | ||
[ | ||
{ timestamp: refDate + 1, used: 1001, percent: 1001 / 2000 }, | ||
{ timestamp: refDate + 2, used: 1002, percent: 1002 / 2000 }, | ||
], | ||
'Two frames of cpu' | ||
); | ||
|
||
assert.deepEqual( | ||
tracker.get('memory'), | ||
[ | ||
{ timestamp: refDate + 1, used: 2049 * 1024 * 1024, percent: 2049 / 4096 }, | ||
{ timestamp: refDate + 2, used: 2050 * 1024 * 1024, percent: 2050 / 4096 }, | ||
], | ||
'Two frames of memory' | ||
); | ||
}); | ||
|
||
test('each stat list has maxLength equal to bufferSize', function(assert) { | ||
const node = MockNode(); | ||
const bufferSize = 10; | ||
const tracker = NodeStatsTracker.create({ fetch, node, bufferSize }); | ||
|
||
for (let i = 1; i <= 20; i++) { | ||
tracker.append(mockFrame(i)); | ||
} | ||
|
||
assert.equal( | ||
tracker.get('cpu.length'), | ||
bufferSize, | ||
`20 calls to append, only ${bufferSize} frames in the stats array` | ||
); | ||
assert.equal( | ||
tracker.get('memory.length'), | ||
bufferSize, | ||
`20 calls to append, only ${bufferSize} frames in the stats array` | ||
); | ||
|
||
assert.equal( | ||
tracker.get('cpu')[0].timestamp, | ||
refDate + 11, | ||
'Old frames are removed in favor of newer ones' | ||
); | ||
assert.equal( | ||
tracker.get('memory')[0].timestamp, | ||
refDate + 11, | ||
'Old frames are removed in favor of newer ones' | ||
); | ||
}); | ||
|
||
test('the stats computed property macro constructs a NodeStatsTracker based on a nodeProp and a fetch definition', function(assert) { | ||
const node = MockNode(); | ||
const fetchSpy = sinon.spy(); | ||
|
||
const SomeClass = EmberObject.extend({ | ||
stats: stats('theNode', function() { | ||
return () => fetchSpy(this); | ||
}), | ||
}); | ||
const someObject = SomeClass.create({ | ||
theNode: node, | ||
}); | ||
|
||
assert.equal( | ||
someObject.get('stats.url'), | ||
`/v1/client/stats?node_id=${node.id}`, | ||
'stats computed property macro creates a NodeStatsTracker' | ||
); | ||
|
||
someObject.get('stats').fetch(); | ||
|
||
assert.ok( | ||
fetchSpy.calledWith(someObject), | ||
'the fetch factory passed into the macro gets called to assign a bound version of fetch to the NodeStatsTracker instance' | ||
); | ||
}); | ||
|
||
test('changing the value of the nodeProp constructs a new NodeStatsTracker', function(assert) { | ||
const node1 = MockNode(); | ||
const node2 = MockNode(); | ||
const SomeClass = EmberObject.extend({ | ||
stats: stats('theNode', () => fetch), | ||
}); | ||
|
||
const someObject = SomeClass.create({ | ||
theNode: node1, | ||
}); | ||
|
||
const stats1 = someObject.get('stats'); | ||
|
||
someObject.set('theNode', node2); | ||
const stats2 = someObject.get('stats'); | ||
|
||
assert.notOk( | ||
stats1 === stats2, | ||
'Changing the value of the node results in creating a new NodeStatsTracker instance' | ||
); | ||
}); |