Name
global.AvailabilityOutageProcessorTester
Description
No description available
Script
var AvailabilityOutageProcessorTester = Class.create();
AvailabilityOutageProcessorTester.prototype = {
initialize: function() {
this.LOGS = true;
this.DESCRIPTION = true;
this.VERSION = 'v2';
this.OUTAGE_DATA = [
// Same type outage cases. Merge/Ignore (for planned outages)
{
label: '10',
description: 'Single outage',
workingData: [
{begin: 10, end: 11, type: 'outage'},
],
expectedData: [
{begin: 10, end: 11, type: 'outage'},
],
},
{
label: '11',
description: 'Single planned outage',
workingData: [
{begin: 10, end: 11, type: 'planned'},
],
expectedData: [],
},
{
label: '20',
description: 'Overlapping (same start/end) planned outage',
workingData: [
{begin: 10, end: 11, type: 'planned'},
{begin: 10, end: 11, type: 'planned'},
],
expectedData: [],
},
{
label: '30',
description: 'Overlapping (end/begin) outages. Merge into 1 outage',
workingData: [
{begin: 10, end: 11, type: 'outage'},
{begin: 11, end: 12, type: 'outage'},
],
expectedData: [
{begin: 10, end: 12, type: 'outage'},
],
},
{
label: '40',
description: 'Overlapping. Merge into 1 outage',
workingData: [
{begin: 10, end: 13, type: 'outage'},
{begin: 11, end: 12, type: 'outage'},
],
expectedData: [
{begin: 10, end: 13, type: 'outage'},
],
},
// Build longest planned outage tests
{
label: '50',
description: 'Overlapping planned outage',
workingData: [
{begin: 10, end: 13, type: 'planned'},
{begin: 11, end: 12, type: 'planned'},
],
expectedData: [],
},
{
label: '51',
description: 'Overlapping planned outage II', // Tests build longest planned outage
workingData: [
{begin: 10, end: 11, type: 'planned'},
{begin: 11, end: 14, type: 'planned'},
{begin: 13, end: 15, type: 'planned'},
],
expectedData: [],
},
{
label: '52',
description: 'Overlapping planned outage III', // Tests build longest planned outage
workingData: [
{begin: 10, end: 11, type: 'planned'},
{begin: 11, end: 14, type: 'planned'}, // Test begin and end (upper boundary) is same
{begin: 14, end: 15, type: 'planned'},
],
expectedData: [],
},
{
label: '53',
description: 'Overlapping planned outage IV', // Tests build longest planned outage
workingData: [
{begin: 10, end: 13, type: 'planned'}, // Test begin and end (upper boundary) is same
{begin: 11, end: 14, type: 'planned'}, // Test begin and end (upper boundary) is same
{begin: 14, end: 15, type: 'planned'},
],
expectedData: [],
},
{
label: '54',
description: 'Overlapping planned outage V', // Tests build longest planned outage
workingData: [
{begin: 10, end: 11, type: 'planned'},
{begin: 12, end: 14, type: 'planned'}, // Test outside range
],
expectedData: [],
},
{
label: '60',
description: 'Planned outage splits outage',
workingData: [
{begin: 5, end: 10, type: 'outage'},
{begin: 6, end: 7, type: 'planned'},
],
expectedData: [
{begin: 5, end: 6, type: 'outage'},
{begin: 7, end: 10, type: 'outage'},
],
},
{
label: '70',
description: 'Overlapping outages (diff type). Begin is same date',
workingData: [
{begin: 5, end: 10, type: 'outage'},
{begin: 5, end: 7, type: 'planned'},
],
expectedData: [
{begin: 7, end: 10, type: 'outage'},
],
},
// Different type outage cases.
{
label: '100',
description: 'Same begin/end for outage and planned',
workingData: [
{begin: 10, end: 14, type: 'outage'},
{begin: 10, end: 14, type: 'planned'},
],
expectedData: [],
},
{
label: '101',
description: 'Same begin/end for outage and planned, order reversed',
workingData: [
{begin: 10, end: 14, type: 'planned'},
{begin: 10, end: 14, type: 'outage'},
],
expectedData: [],
},
{
label: '110',
description: 'Trim end. Overlapping outage by 1 hr',
workingData: [
{begin: 10, end: 14, type: 'outage'},
{begin: 11, end: 14, type: 'planned'},
],
expectedData: [
{begin: 10, end: 11, type: 'outage'},
],
},
{
label: '120 - #5',
description: 'Trim begin. Overlapping outage by 2 hrs',
workingData: [
{begin: 10, end: 14, type: 'outage'},
{begin: 10, end: 12, type: 'planned'},
],
expectedData: [
{begin: 12, end: 14, type: 'outage'},
],
},
{
label: '121 - #5 inv',
description: 'Trim begin. Overlapping outage by 2 hrs. Reverse order',
workingData: [
{begin: 10, end: 12, type: 'planned'},
{begin: 10, end: 14, type: 'outage'},
],
expectedData: [
{begin: 12, end: 14, type: 'outage'},
],
},
{
label: '130 - #6',
description: 'Split outage into 2 (Planned outage in the middle)',
workingData: [
{begin: 10, end: 14, type: 'outage'},
{begin: 11, end: 13, type: 'planned'},
],
expectedData: [
{begin: 10, end: 11, type: 'outage'},
{begin: 13, end: 14, type: 'outage'},
],
},
{
label: '140 - #7',
description: 'Overlapping outages. Merge outages',
workingData: [
{begin: 10, end: 12, type: 'outage'},
{begin: 11, end: 14, type: 'outage'},
],
expectedData: [
{begin: 10, end: 14, type: 'outage'},
],
},
{
label: '150 - #8',
description: 'Non overlapping outages',
workingData: [
{begin: 10, end: 11, type: 'outage'},
{begin: 12, end: 14, type: 'outage'},
],
expectedData: [
{begin: 10, end: 11, type: 'outage'},
{begin: 12, end: 14, type: 'outage'},
],
},
{
label: '160 - #9',
description: 'Split 2nd outage',
workingData: [
{begin: 10, end: 12, type: 'outage'},
{begin: 11, end: 14, type: 'outage'},
{begin: 12, end: 13, type: 'planned'},
],
expectedData: [
{begin: 10, end: 12, type: 'outage'},
{begin: 13, end: 14, type: 'outage'},
],
},
{
label: '170 - #10',
description: 'Overlapping cases. No outages should be created',
workingData: [
{begin: 10, end: 12, type: 'outage'},
{begin: 10, end: 12, type: 'planned'},
{begin: 11, end: 14, type: 'outage'},
{begin: 12, end: 14, type: 'planned'},
],
expectedData: [],
},
{
label: '171 - #10 diff order',
description: 'Merge planned outages. Resulting planned outage covers all outages',
workingData: [
{begin: 10, end: 12, type: 'planned'},
{begin: 10, end: 12, type: 'outage'},
{begin: 11, end: 14, type: 'outage'},
{begin: 12, end: 14, type: 'planned'},
],
expectedData: [],
},
{
label: '180 - #11',
description: 'Dedup outage with exact same duration',
workingData: [
{begin: 10, end: 12, type: 'outage'},
{begin: 10, end: 12, type: 'outage'},
],
expectedData: [
{begin: 10, end: 12, type: 'outage'},
],
},
{
label: '190 - #14',
description: 'Merge all outages with first outage',
workingData: [
{begin: 9, end: 14, type: 'outage'},
{begin: 10, end: 13, type: 'outage'},
{begin: 11, end: 12, type: 'outage'},
],
expectedData: [
{begin: 9, end: 14, type: 'outage'},
],
},
{
label: '200 - #15',
description: 'Split first outage and merge upper outage with 2nd outage',
workingData: [
{begin: 9, end: 13, type: 'outage'},
{begin: 10, end: 11, type: 'planned'},
{begin: 12, end: 14, type: 'outage'},
],
expectedData: [
{begin: 9, end: 10, type: 'outage'},
{begin: 11, end: 14, type: 'outage'},
],
},
{
label: '210 - #16',
description: 'Split and merge outages',
workingData: [
{begin: 10, end: 17, type: 'outage'},
{begin: 11, end: 15, type: 'planned'},
{begin: 12, end: 13, type: 'outage'},
{begin: 14, end: 16, type: 'outage'},
],
expectedData: [
{begin: 10, end: 11, type: 'outage'},
{begin: 15, end: 17, type: 'outage'},
],
},
{
label: '220 - #17',
description: 'Planned outage covers all outages except last (trims it)',
workingData: [
{begin: 10, end: 18, type: 'planned'},
{begin: 11, end: 12, type: 'outage'},
{begin: 13, end: 15, type: 'outage'},
{begin: 14, end: 16, type: 'planned'},
{begin: 17, end: 19, type: 'outage'},
],
expectedData: [
{begin: 18, end: 19, type: 'outage'},
],
},
{
label: '230 - #18',
description: 'Planned outage covers all outages except last (trims it). Different set',
workingData: [
{begin: 10, end: 18, type: 'outage'},
{begin: 10, end: 18, type: 'planned'},
{begin: 11, end: 12, type: 'planned'},
{begin: 13, end: 14, type: 'outage'},
{begin: 15, end: 17, type: 'planned'},
{begin: 16, end: 19, type: 'outage'},
],
expectedData: [
{begin: 18, end: 19, type: 'outage'},
],
},
{
label: '230 - #18 diff order',
description: 'Planned outage covers all outages except last (trims it). Different set',
workingData: [
{begin: 10, end: 18, type: 'planned'},
{begin: 10, end: 18, type: 'outage'},
{begin: 11, end: 12, type: 'planned'},
{begin: 13, end: 14, type: 'outage'},
{begin: 15, end: 17, type: 'planned'},
{begin: 16, end: 19, type: 'outage'},
],
expectedData: [
{begin: 18, end: 19, type: 'outage'},
],
},
{
label: '240 - #19',
description: 'Planned outage covers all outages except last (trims it). Different set',
workingData: [
{begin: 10, end: 19, type: 'planned'},
{begin: 11, end: 21, type: 'outage'},
{begin: 12, end: 13, type: 'planned'},
{begin: 14, end: 15, type: 'outage'},
{begin: 16, end: 19, type: 'planned'},
{begin: 17, end: 18, type: 'outage'},
],
expectedData: [
{begin: 19, end: 21, type: 'outage'},
],
},
{
label: '250',
description: 'Merge planned outage. Ignore outage. Trim outage',
workingData: [
{begin: 10, end: 11, type: 'planned'},
{begin: 11, end: 17, type: 'planned'},
{begin: 14, end: 15, type: 'outage'}, // Planned covers whole outage
{begin: 14, end: 18, type: 'outage'}, // Planned should trim
],
expectedData: [
{begin: 17, end: 18, type: 'outage'},
],
},
{
label: '260',
description: 'Nested scenario I',
workingData: [
{begin: 5, end: 19, type: 'outage'},
{begin: 5, end: 7, type: 'outage'},
{begin: 8, end: 16, type: 'planned'},
{begin: 9, end: 17, type: 'outage'},
{begin: 10, end: 11, type: 'planned'},
{begin: 12, end: 14, type: 'planned'},
{begin: 13, end: 15, type: 'outage'},
{begin: 18, end: 20, type: 'planned'},
],
expectedData: [
{begin: 5, end: 8, type: 'outage'},
{begin: 16, end: 18, type: 'outage'},
],
},
{
label: '270',
description: 'Nested scenario II',
workingData: [
{begin: 1, end: 10, type: 'outage'},
{begin: 2, end: 3, type: 'planned'},
{begin: 4, end: 5, type: 'planned'},
{begin: 6, end: 13, type: 'outage'},
{begin: 7, end: 8, type: 'planned'},
{begin: 9, end: 11, type: 'planned'},
{begin: 12, end: 15, type: 'planned'},
{begin: 14, end: 16, type: 'outage'},
],
expectedData: [
{begin: 1, end: 2, type: 'outage'},
{begin: 3, end: 4, type: 'outage'},
{begin: 5, end: 7, type: 'outage'},
{begin: 8, end: 9, type: 'outage'},
{begin: 11, end: 12, type: 'outage'},
{begin: 15, end: 16, type: 'outage'},
],
},
];
},
/**
* Wrapper around gs.info
* @param {String} message
*/
_gslog: function(message) {
if (this.LOGS)
gs.info(message);
},
/**
* Main method that runs all scenarios or a single scenario
* @param {String|Number} scenario Either "all" or the number of scenario to run
* @param {String} version Whether the tester should run with "v1" or "v2" engine. Default is v2
*/
processScenarios: function(scenario, version) {
// Check params are valid
if (typeof scenario != 'number' && scenario != 'all') {
this._gslog('@Invalid type of scenario');
return;
}
this.VERSION = version || 'v2';
var availabilityOutageProcessor;
var passedCount = 0;
var failedCount = 0;
var failedLabels = [];
var scenariosToRun = this._getScenarioData(scenario); // Is always an array
var totalScenarios = scenariosToRun.length;
// Use correct version
if (this._isV1())
availabilityOutageProcessor = new global.AvailabilityCalculator(); // Av. V1 wrapper class.
else
availabilityOutageProcessor = new global.AvailabilityOutageProcessor(); // Av. V2 dedicated class.
for (var i = 0; i < totalScenarios; i++) {
var processedOutages = [];
var scenarioWorkingData = scenariosToRun[i].workingData;
if (this._isV1()) {
var localScenarioOutages = scenarioWorkingData;
// The way V1 works is that it process outage one by one, comparing a single outage to the already built array of outages
for (var j = 0; j < localScenarioOutages.length; j++) {
var currentOutage = localScenarioOutages[j];
availabilityOutageProcessor._processOutage(processedOutages, currentOutage.type, currentOutage.start, currentOutage.end);
}
processedOutages = this._v1PluckOutPlannedOutages(processedOutages, scenario);
} else
processedOutages = availabilityOutageProcessor.processOutages(scenarioWorkingData, 0, 100);
var scenarioResult = this._assertScenario(i, processedOutages, scenariosToRun[i]);
if (scenarioResult === true) {
passedCount++;
} else {
failedCount++;
failedLabels.push(scenarioResult);
}
}
this._gslog('************* RUN SUMMARY *************');
this._gslog('%%%%% TOTAL SCENARIOS: '+totalScenarios);
this._gslog('%%%%% TOTAL PASSED ✅: '+passedCount);
if (failedCount > 0) {
this._gslog('%%%%% TOTAL FAILED ❌: '+failedCount);
this._gslog('%%%%% FAILED LABELS:');
for (var i = 0; i < failedLabels.length; i++) {
this._gslog('❌ ' + failedLabels[i]);
}
}
},
/**
* Returns whether the class property is set to v1 or not
* @returns {Boolean}
*/
_isV1: function() {
return this.VERSION === 'v1';
},
/**
* Gets the data for a particular run
* @param {Array<Object>} scenario
* @returns {Array<Object>}
*/
_getScenarioData: function(scenario) {
var scenarioData;
if (scenario === 'all')
scenarioData = this.OUTAGE_DATA;
else
scenarioData = [this.OUTAGE_DATA[scenario]];
if (this._isV1())
scenarioData = this._v1RetrofitScenario(scenarioData);
return scenarioData;
},
/**
* Modifies begin/start in outages
* @param {Array<Object>} scenarioData
* @returns {Array<Object>}
*/
_v1RetrofitScenario: function(scenarioData) {
return scenarioData.map(function(scenario) {
scenario.workingData = scenario.workingData.map(function(outage) {
return {start: outage.begin, end: outage.end, type: outage.type};
});
scenario.expectedData = scenario.expectedData.map(function(outage) {
return {start: outage.begin, end: outage.end, type: outage.type};
});
return scenario;
});
},
/**
* Makes sure that there are no planned outages left in the resulting array
* @param {Array<Object>} outagesArray
* @returns {Array<Object>}
*/
_v1PluckOutPlannedOutages: function(outagesArray) {
return outagesArray.filter(function(outage) {
return outage.type === 'outage';
});
},
/**
* Asserts scenario (processedOutages) against assertData
* @param {Number} scenario
* @param {Array<Object>} processedOutages
* @param {Array<Object>} assertData
* @returns
*/
_assertScenario: function(scenario, processedOutages, assertData) {
this._gslog('🎉🎉🎉🎉🎉🎉🎉🎉🎉 SCENARIO #'+(assertData.label)+' 🎉🎉🎉🎉🎉🎉🎉🎉🎉');
this._gslog('📕 Description: '+(assertData.description));
var expectedData = assertData.expectedData;
// Print both arrays
this._gslog('[DS] expected:' + JSON.stringify(expectedData));
this._gslog('[DS] actual :' + JSON.stringify(processedOutages));
var assertErrors = 0;
if (processedOutages.length === expectedData.length) {
this._gslog('✅ Length checks out');
} else {
this._gslog('❌❌ Length doesnt check out. Cannot assert');
return assertData.label;
}
for (var i = 0; i < processedOutages.length; i++) {
this._gslog('%%%%% SCENARIO DATA #' + (i + 1));
var currentProcessedOutage = processedOutages[i];
var currentExpectedOutage = expectedData[i];
var field = this._isV1() ? 'start' : 'begin';
var fieldLabel = ((field).charAt(0).toUpperCase()) + field.slice(1);
if (currentProcessedOutage[field] == currentExpectedOutage[field])
this._gslog('✅ '+fieldLabel+' checks out');
else {
this._gslog('❌❌ '+fieldLabel+' doesnt check out');
assertErrors++;
}
// Assert end
if (currentProcessedOutage.end == currentExpectedOutage.end)
this._gslog('✅ End checks out');
else {
this._gslog('❌❌ End doesnt check out');
assertErrors++;
}
// Assert type
if (currentProcessedOutage.type == currentExpectedOutage.type)
this._gslog('✅ Type checks out');
else {
this._gslog('❌❌ Type doesnt check out');
assertErrors++;
}
}
if (assertErrors === 0)
return true;
else
return assertData.label;
},
type: 'AvailabilityOutageProcessorTester'
};
Sys ID
3fcd678153c5211054f0ddeeff7b12d8