<commit_before>"use strict";

// global vars
var base_layer_dict = null;
var map = null;

// the entire catalog
var catalog = null;

// list of layers which automatically become overlay rasters instead of regular rasters
var overlay_list = ['WINDVEC', 'WINDVEC1000FT', 'WINDVEC4000FT', 'WINDVEC6000FT', 'SMOKE1000FT', 'SMOKE4000FT', 'SMOKE6000FT', 'FIRE_AREA', 'SMOKE_INT', 'FGRNHFX', 'FLINEINT'];

// Variables containing input data
var rasters = null;
var domains = null;
var sorted_timestamps = null;
var raster_base = null;
var raster_dict = {};  // rasters that can't be overlaid on other rasters
var overlay_dict = {}; // rasters that can be overlaid on top of each other and on top of raster_dict rasters

// Display context
var current_domain = null;
var layer_ctrl = null;
var current_display = {}; // dictionary of layer name -> layer of currently displayed data
var current_timestamp = null; // currently displayed timestamp
var preloaded = {}; // dictionary containing information on what frames have been preloaded for which rasters/layers
var displayed_colorbar = null; // name of layer currently displaying its colorbar (maybe display multiple cbs?)

// Variables storing animation/playback context
var playing = false;
var current_frame = 0;

function initialize_fdds() {

//  initialize base layers & build map
base_layer_dict = {
/*
	'MapQuest': L.tileLayer('http://{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png', {
													attribution: 'Data and imagery by MapQuest',
													subdomains: ['otile1', 'otile2', 'otile3', 'otile4']}),
*/
	'MapQuest' : MQ.mapLayer(),
/*	'MQ Satellite': L.tileLayer('http://{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.png', {
															attribution: 'Data and imagery by MapQuest',
															subdomains: ['otile1', 'otile2', 'otile3', 'otile4']}),*/
	'OSM': L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
										 attribution: '© OpenStreetMap contributors'})
};

// construct map with the base layers
map = L.map('map-fd', {
	center: [37.34, -121.89],
	zoom: 7,
	layers: [base_layer_dict['OSM']],
  zoomControl: false,
  minZoom: 3
});

// add scale & zoom controls to the map
L.control.scale({ position: 'bottomright' }).addTo(map);

map.on('overlayadd', function (e) { handle_overlayadd(e.name, e.layer) });

map.on('overlayremove', function(e) {
	delete current_display[e.name];

	if(displayed_colorbar == e.name) {
    $('#raster-colorbar').attr('src', '');
    $('#raster-colorbar').hide();
		displayed_colorbar = null;
	}
});
}

function handle_overlayadd(name, layer) {
  // register in currently displayed layers and bring to front if it's an overlay
  console.log('name ' + name + ' layer ' + layer);
  current_display[name] = layer;
  if(overlay_list.indexOf(name) >= 0) {
    layer.bringToFront();
  } else {
    layer.bringToBack();
  }

  // if the overlay being added now has a colorbar and there is none displayed, show it
  // if(displayed_colorbar == null) {
    var rasters_now = rasters[current_domain][current_timestamp];
    if('colorbar' in rasters_now[name]) {
        var cb_url = raster_base + rasters_now[name].colorbar;
        $('#raster-colorbar').attr('src', cb_url).show();
        displayed_colorbar = name;
    }
  // }

  // preload all displayed variables for eight frames
  preload_variables(8);
}

function setup_for_domain(dom_id) {
  // set the current domain
  current_domain = dom_id;

  // remove any existing layers from map
  var displayed_layers = Object.keys(current_display);
  for(var layer_name in current_display) {
    map.removeLayer(current_display[layer_name]);
  }
  preloaded = {};
  current_display = {};

	// retrieve all times (we assume the first domain is selected)
	sorted_timestamps = Object.keys(rasters[current_domain]).sort();

	// setup for time first frame
	current_frame = 0;
	current_timestamp = sorted_timestamps[0];
	if(playing) toggle_play();

	// populate jquery time slider
	$('#time-slider').slider({
		min: 0,
		value: 0,
		max: sorted_timestamps.length - 1,
		change: function(event, ui) {
			setup_for_time(ui.value);
		},
		slide: function(event, ui) {}
	});

	$('#time-slider').mousedown(function(e) {
		if(playing) toggle_play();
		e.stopPropagation();
	});

	if(sorted_timestamps.length < 2) {
		$('#time-slider').hide();
		$('#play-control-button').hide();
	} else {
		$('#time-slider').show();
		$('#play-control-button').show();
	}

  // zoom into raster region
  var first_rasters = rasters[dom_id][sorted_timestamps[0]];
  var vars = Object.keys(first_rasters);
  var cs = first_rasters[vars[0]].coords;
  map.fitBounds([ [cs[0][1], cs[0][0]], [cs[2][1], cs[2][0]] ]);

  // build the layer groups
  raster_dict = {};
  overlay_dict = {};
  $.each(first_rasters, function(r) {
    var raster_info = first_rasters[r];
    var cs = raster_info.coords;
    var layer = L.imageOverlay(raster_base + raster_info.raster,
                                [[cs[0][1], cs[0][0]], [cs[2][1], cs[2][0]]],
                                {
                                  attribution: 'SJSU WIRC',
                                  opacity: 0.5
                                });
    if(overlay_list.indexOf(r) >= 0) {
        overlay_dict[r] = layer;
    } else {
        raster_dict[r] = layer;
    }
  });

  // remove any existing layer control
  if (layer_ctrl != null) {
    layer_ctrl.removeFrom(map);
  }

  // add a new layer control to the map
  layer_ctrl = L.control.groupedLayers(base_layer_dict, {
    'Rasters': raster_dict,
    'Overlays': overlay_dict
  }, {
    collapsed: false
  }).addTo(map);

  $.each(first_rasters, function(r) {
  	if(displayed_layers.indexOf(r) >= 0) {
      var layer = null;
      if(r in raster_dict) {
        layer = raster_dict[r];
      } else {
        layer = overlay_dict[r];
      }
			map.addLayer(layer);
      handle_overlayadd(r, layer);
		}
  });
  layer_ctrl._update();

  setup_for_time(0);
}

// this function should assume that the correct layers are already displayed
function setup_for_time(frame_ndx) {
  current_frame = frame_ndx;
  var timestamp = sorted_timestamps[frame_ndx];
  current_timestamp = timestamp;
  var rasters_now = rasters[current_domain][timestamp];

  // set current time
  $('#time-valid').text(timestamp).show();

  preload_variables(8);

  // modify the URL each displayed cluster is pointing to
  // so that the current timestamp is reflected
  for (var layer_name in current_display) {
    var layer = current_display[layer_name];
    if(layer != null) {
      var raster_info = rasters_now[layer_name];
      var cs = raster_info.coords;
      layer.setUrl(raster_base + raster_info.raster,
                  [ [cs[0][1], cs[0][0]], [cs[2][1], cs[2][0]] ],
                  { attribution: 'SJSU WIRC', opacity: 0.5 });
    }
  }
}

function handle_catalog_click(path) {
  // close selection dialog
  $('.catalog-menu').hide();

 // show job description
  var catPath = path.substring(0,path.lastIndexOf("/") + 1) + "catalog.json";

  //REVERT THIS BEFORE COMMITTING
  $.getJSON(catPath.replaceAll(":", "_"), function(data) {
  // $.getJSON(catPath, function(data) {
	catalog = data;
    $.each(data, function(cat_name) {
      var cat_entry = data[cat_name];
      var desc = cat_entry.description + ' Experimental forecast ONLY';
      $("#displayTest").show();
      $("#displayTest2").show();
      $("#displayTest").html(desc);
    });
  });

  // REVERT THIS
  $.getJSON(path.replaceAll(":", "_"), function(selected_simulation) {
  // $.getJSON(path, function(selected_simulation) {
    // store in global state
    rasters = selected_simulation;
    // REVERT THIS
    raster_base = "https://demo.openwfm.org/ch/" + path.substring(0, path.lastIndexOf('/') + 1);
    // raster_base = path.substring(0, path.lastIndexOf('/') + 1);

    // retrieve all domains
    domains = Object.keys(rasters);
    current_domain = domains[0];

    // update the domain radio buttons
    $('#domain-checkboxes').empty();
    $('#domain-checkboxes').append('
Active domain

');
    for(var dom in domains) {
      var dom_id = domains[dom];
      var checked = '';
      if(dom_id == '1') { checked = ' checked="yes"'}
      $('#domain-checkboxes').append('

<input type="radio" name="domains" id="' + dom_id + '"' + checked + ' onclick="setup_for_domain(\''+dom_id+'\');"/>' + dom_id + '

');
    }
    $('#domain-selector').show();

    setup_for_domain(current_domain);
  });
}

// Section containing animation/playback code
function frame_ready(frame_ndx) {
  // for all layers currently displayed
  for(var key in current_display) {
    // if the current frame is not preloaded yet
    if(!(frame_ndx in preloaded[key])) {
      //console.log('Frame ' + frame_ndx + ' not ready for var ' + key);
      return false;
    }
    // check if the raster has a colorbar
    var cb_key = key + '_cb';
    if(cb_key in preloaded) {
      // it does, is it preloaded?
      if (!(frame_ndx in preloaded[cb_key])) {
        //console.log('Frame ' + frame_ndx + ' (colorbar) not ready for var ' + key);
        return false;
      }
    }
  }
  //console.log('Frame ' + frame_ndx + ' is ready for display.');
  return true;
}

function open_catalog() {
  $('#select-dialog').dialog('option', 'width', 600);
  $('#select-dialog').dialog('option', 'height', 400);
  $('#select-dialog').dialog('option', 'z-index', 1400);
  $('#select-dialog').dialog('open');
  $('.ui-dialog-titlebar-close').blur();
  $('#catalog-list-1').focus();
}

function schedule_next_frame() {
  if(current_frame == sorted_timestamps.length-1){
    window.setTimeout(next_frame, 1000);
  } else {
    window.setTimeout(next_frame, 330);
  }
}

function next_frame() {
  if (playing) {
    current_frame = (current_frame + 1) % sorted_timestamps.length;
    if(frame_ready(current_frame)) {
      $('#time-slider').slider('value', current_frame);
      schedule_next_frame();
    } else {
      // if the next frame is not ready, preload further and wait longer
      window.setTimeout(wait_for_frame, 500);
      preload_variables(8);
    }
  }
}

function wait_for_frame() {
  // don't do anything if playing has been cancelled
  if(!playing) {
    return
  }
  // wait until current frame is loaded
  if(frame_ready(current_frame)) {
    $('#time-slider').slider('value', current_frame);
    schedule_next_frame();
  } else {
    // keep waiting until all parts of frame are loaded
    window.setTimeout(wait_for_frame, 250);
  }
}

function toggle_play() {
  if (!playing) {
    $('#play-control-button > span').text('Pause');
    $('#play-control-button > i').attr('class', 'pause icon');
    playing = true;
    next_frame();
  } else {
    $('#play-control-button > span').text('Play');
    $('#play-control-button > i').attr('class', 'play icon');
    playing = false;
  }
}

/* Code handling auxiliary tasks */
function preload_variables(preload_count) {
  var rasters_dom = rasters[current_domain];
  var n_rasters = Object.keys(rasters_dom).length;
  for(var counter=0; counter < preload_count; counter++) {
    var i = (current_frame + counter) % n_rasters;
    var timestamp = sorted_timestamps[i];
    for(var var_name in current_display) {
      // it could happen that a timestamp is missing the variable
      if(var_name in rasters_dom[timestamp]) {
        // have we already preloaded this variable? If not indicate nothing is preloaded.
        if(!(var_name in preloaded)) {
          preloaded[var_name] = {};
        }

        if(!(i in preloaded[var_name])) {
          //console.log('Frame ' + i + ' not preloaded for ' + var_name + ' (current_frame = ' + current_frame + ')');
          var var_info = rasters_dom[timestamp][var_name];
					var img = new Image();
					img.onload = function (ndx, var_name, img, preloaded) { return function() { preloaded[var_name][ndx] = img; } } (i, var_name, img, preloaded);
					img.src = raster_base + var_info.raster;
        }
      }
    }
  }
}

<commit_msg>fixes formatting in partial1<commit_after>"use strict";

// global vars
var base_layer_dict = null;
var map = null;

// the entire catalog
var catalog = null;

// list of layers which automatically become overlay rasters instead of regular rasters
var overlay_list = ['WINDVEC', 'WINDVEC1000FT', 'WINDVEC4000FT', 'WINDVEC6000FT', 'SMOKE1000FT', 'SMOKE4000FT', 'SMOKE6000FT', 'FIRE_AREA', 'SMOKE_INT', 'FGRNHFX', 'FLINEINT'];

// Variables containing input data
var rasters = null;
var domains = null;
var sorted_timestamps = null;
var raster_base = null;
var raster_dict = {};  // rasters that can't be overlaid on other rasters
var overlay_dict = {}; // rasters that can be overlaid on top of each other and on top of raster_dict rasters

// Display context
var current_domain = null;
var layer_ctrl = null;
var current_display = {}; // dictionary of layer name -> layer of currently displayed data
var current_timestamp = null; // currently displayed timestamp
var preloaded = {}; // dictionary containing information on what frames have been preloaded for which rasters/layers
var displayed_colorbar = null; // name of layer currently displaying its colorbar (maybe display multiple cbs?)

// Variables storing animation/playback context
var playing = false;
var current_frame = 0;

function initialize_fdds() {

  //  initialize base layers & build map
  base_layer_dict = {
  /*
    'MapQuest': L.tileLayer('http://{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png', {
                            attribution: 'Data and imagery by MapQuest',
                            subdomains: ['otile1', 'otile2', 'otile3', 'otile4']}),
  */
    'MapQuest' : MQ.mapLayer(),
  /*	'MQ Satellite': L.tileLayer('http://{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.png', {
                                attribution: 'Data and imagery by MapQuest',
                                subdomains: ['otile1', 'otile2', 'otile3', 'otile4']}),*/
    'OSM': L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
                      attribution: '© OpenStreetMap contributors'})
  };

  // construct map with the base layers
  map = L.map('map-fd', {
    center: [37.34, -121.89],
    zoom: 7,
    layers: [base_layer_dict['OSM']],
    zoomControl: false,
    minZoom: 3
  });

  // add scale & zoom controls to the map
  L.control.scale({ position: 'bottomright' }).addTo(map);

  map.on('overlayadd', function (e) { handle_overlayadd(e.name, e.layer) });

  map.on('overlayremove', function(e) {
    delete current_display[e.name];

    if(displayed_colorbar == e.name) {
      $('#raster-colorbar').attr('src', '');
      $('#raster-colorbar').hide();
      displayed_colorbar = null;
    }
  });
}

function handle_overlayadd(name, layer) {
  // register in currently displayed layers and bring to front if it's an overlay
  console.log('name ' + name + ' layer ' + layer);
  current_display[name] = layer;
  if(overlay_list.indexOf(name) >= 0) {
    layer.bringToFront();
  } else {
    layer.bringToBack();
  }

  // if the overlay being added now has a colorbar and there is none displayed, show it
  // if(displayed_colorbar == null) {
    var rasters_now = rasters[current_domain][current_timestamp];
    if('colorbar' in rasters_now[name]) {
        var cb_url = raster_base + rasters_now[name].colorbar;
        $('#raster-colorbar').attr('src', cb_url).show();
        displayed_colorbar = name;
    }
  // }

  // preload all displayed variables for eight frames
  preload_variables(8);
}

function setup_for_domain(dom_id) {
  // set the current domain
  current_domain = dom_id;

  // remove any existing layers from map
  var displayed_layers = Object.keys(current_display);
  for(var layer_name in current_display) {
    map.removeLayer(current_display[layer_name]);
  }
  preloaded = {};
  current_display = {};

	// retrieve all times (we assume the first domain is selected)
	sorted_timestamps = Object.keys(rasters[current_domain]).sort();

	// setup for time first frame
	current_frame = 0;
	current_timestamp = sorted_timestamps[0];
	if(playing) toggle_play();

	// populate jquery time slider
	$('#time-slider').slider({
		min: 0,
		value: 0,
		max: sorted_timestamps.length - 1,
		change: function(event, ui) {
			setup_for_time(ui.value);
		},
		slide: function(event, ui) {}
	});

	$('#time-slider').mousedown(function(e) {
		if(playing) toggle_play();
		e.stopPropagation();
	});

	if(sorted_timestamps.length < 2) {
		$('#time-slider').hide();
		$('#play-control-button').hide();
	} else {
		$('#time-slider').show();
		$('#play-control-button').show();
	}

  // zoom into raster region
  var first_rasters = rasters[dom_id][sorted_timestamps[0]];
  var vars = Object.keys(first_rasters);
  var cs = first_rasters[vars[0]].coords;
  map.fitBounds([ [cs[0][1], cs[0][0]], [cs[2][1], cs[2][0]] ]);

  // build the layer groups
  raster_dict = {};
  overlay_dict = {};
  $.each(first_rasters, function(r) {
    var raster_info = first_rasters[r];
    var cs = raster_info.coords;
    var layer = L.imageOverlay(raster_base + raster_info.raster,
                                [[cs[0][1], cs[0][0]], [cs[2][1], cs[2][0]]],
                                {
                                  attribution: 'SJSU WIRC',
                                  opacity: 0.5
                                });
    if(overlay_list.indexOf(r) >= 0) {
        overlay_dict[r] = layer;
    } else {
        raster_dict[r] = layer;
    }
  });

  // remove any existing layer control
  if (layer_ctrl != null) {
    layer_ctrl.removeFrom(map);
  }

  // add a new layer control to the map
  layer_ctrl = L.control.groupedLayers(base_layer_dict, {
    'Rasters': raster_dict,
    'Overlays': overlay_dict
  }, {
    collapsed: false
  }).addTo(map);

  $.each(first_rasters, function(r) {
  	if(displayed_layers.indexOf(r) >= 0) {
      var layer = null;
      if(r in raster_dict) {
        layer = raster_dict[r];
      } else {
        layer = overlay_dict[r];
      }
			map.addLayer(layer);
      handle_overlayadd(r, layer);
		}
  });
  layer_ctrl._update();

  setup_for_time(0);
}

// this function should assume that the correct layers are already displayed
function setup_for_time(frame_ndx) {
  current_frame = frame_ndx;
  var timestamp = sorted_timestamps[frame_ndx];
  current_timestamp = timestamp;
  var rasters_now = rasters[current_domain][timestamp];

  // set current time
  $('#time-valid').text(timestamp).show();

  preload_variables(8);

  // modify the URL each displayed cluster is pointing to
  // so that the current timestamp is reflected
  for (var layer_name in current_display) {
    var layer = current_display[layer_name];
    if(layer != null) {
      var raster_info = rasters_now[layer_name];
      var cs = raster_info.coords;
      layer.setUrl(raster_base + raster_info.raster,
                  [ [cs[0][1], cs[0][0]], [cs[2][1], cs[2][0]] ],
                  { attribution: 'SJSU WIRC', opacity: 0.5 });
    }
  }
}

function handle_catalog_click(path) {
  // close selection dialog
  $('.catalog-menu').hide();

 // show job description
  var catPath = path.substring(0,path.lastIndexOf("/") + 1) + "catalog.json";

  //REVERT THIS BEFORE COMMITTING
  $.getJSON(catPath.replaceAll(":", "_"), function(data) {
  // $.getJSON(catPath, function(data) {
	catalog = data;
    $.each(data, function(cat_name) {
      var cat_entry = data[cat_name];
      var desc = cat_entry.description + ' Experimental forecast ONLY';
      $("#displayTest").show();
      $("#displayTest2").show();
      $("#displayTest").html(desc);
    });
  });

  // REVERT THIS
  $.getJSON(path.replaceAll(":", "_"), function(selected_simulation) {
  // $.getJSON(path, function(selected_simulation) {
    // store in global state
    rasters = selected_simulation;
    // REVERT THIS
    raster_base = "https://demo.openwfm.org/ch/" + path.substring(0, path.lastIndexOf('/') + 1);
    // raster_base = path.substring(0, path.lastIndexOf('/') + 1);

    // retrieve all domains
    domains = Object.keys(rasters);
    current_domain = domains[0];

    // update the domain radio buttons
    $('#domain-checkboxes').empty();
    $('#domain-checkboxes').append('
Active domain

');
    for(var dom in domains) {
      var dom_id = domains[dom];
      var checked = '';
      if(dom_id == '1') { checked = ' checked="yes"'}
      $('#domain-checkboxes').append('

<input type="radio" name="domains" id="' + dom_id + '"' + checked + ' onclick="setup_for_domain(\''+dom_id+'\');"/>' + dom_id + '

');
    }
    $('#domain-selector').show();

    setup_for_domain(current_domain);
  });
}

// Section containing animation/playback code
function frame_ready(frame_ndx) {
  // for all layers currently displayed
  for(var key in current_display) {
    // if the current frame is not preloaded yet
    if(!(frame_ndx in preloaded[key])) {
      //console.log('Frame ' + frame_ndx + ' not ready for var ' + key);
      return false;
    }
    // check if the raster has a colorbar
    var cb_key = key + '_cb';
    if(cb_key in preloaded) {
      // it does, is it preloaded?
      if (!(frame_ndx in preloaded[cb_key])) {
        //console.log('Frame ' + frame_ndx + ' (colorbar) not ready for var ' + key);
        return false;
      }
    }
  }
  //console.log('Frame ' + frame_ndx + ' is ready for display.');
  return true;
}

function open_catalog() {
  $('#select-dialog').dialog('option', 'width', 600);
  $('#select-dialog').dialog('option', 'height', 400);
  $('#select-dialog').dialog('option', 'z-index', 1400);
  $('#select-dialog').dialog('open');
  $('.ui-dialog-titlebar-close').blur();
  $('#catalog-list-1').focus();
}

function schedule_next_frame() {
  if(current_frame == sorted_timestamps.length-1){
    window.setTimeout(next_frame, 1000);
  } else {
    window.setTimeout(next_frame, 330);
  }
}

function next_frame() {
  if (playing) {
    current_frame = (current_frame + 1) % sorted_timestamps.length;
    if(frame_ready(current_frame)) {
      $('#time-slider').slider('value', current_frame);
      schedule_next_frame();
    } else {
      // if the next frame is not ready, preload further and wait longer
      window.setTimeout(wait_for_frame, 500);
      preload_variables(8);
    }
  }
}

function wait_for_frame() {
  // don't do anything if playing has been cancelled
  if(!playing) {
    return
  }
  // wait until current frame is loaded
  if(frame_ready(current_frame)) {
    $('#time-slider').slider('value', current_frame);
    schedule_next_frame();
  } else {
    // keep waiting until all parts of frame are loaded
    window.setTimeout(wait_for_frame, 250);
  }
}

function toggle_play() {
  if (!playing) {
    $('#play-control-button > span').text('Pause');
    $('#play-control-button > i').attr('class', 'pause icon');
    playing = true;
    next_frame();
  } else {
    $('#play-control-button > span').text('Play');
    $('#play-control-button > i').attr('class', 'play icon');
    playing = false;
  }
}

/* Code handling auxiliary tasks */
function preload_variables(preload_count) {
  var rasters_dom = rasters[current_domain];
  var n_rasters = Object.keys(rasters_dom).length;
  for(var counter=0; counter < preload_count; counter++) {
    var i = (current_frame + counter) % n_rasters;
    var timestamp = sorted_timestamps[i];
    for(var var_name in current_display) {
      // it could happen that a timestamp is missing the variable
      if(var_name in rasters_dom[timestamp]) {
        // have we already preloaded this variable? If not indicate nothing is preloaded.
        if(!(var_name in preloaded)) {
          preloaded[var_name] = {};
        }

        if(!(i in preloaded[var_name])) {
          //console.log('Frame ' + i + ' not preloaded for ' + var_name + ' (current_frame = ' + current_frame + ')');
          var var_info = rasters_dom[timestamp][var_name];
					var img = new Image();
					img.onload = function (ndx, var_name, img, preloaded) { return function() { preloaded[var_name][ndx] = img; } } (i, var_name, img, preloaded);
					img.src = raster_base + var_info.raster;
        }
      }
    }
  }
}

<|endoftext|>


<commit_before>'use strict';

const Brakes = require('../lib/Brakes');
const globalStats = require('../lib/globalStats');
const expect = require('chai').expect;
const EventEmitter = require('events').EventEmitter;
const sinon = require('sinon');
const TimeOutError = require('../lib/TimeOutError');
const CircuitBrokenError = require('../lib/CircuitBrokenError');

const defaultOptions = {
  bucketSpan: 1000,
  bucketNum: 60,
  name: 'defaultBrake',
  group: 'defaultBrakeGroup',
  circuitDuration: 30000,
  statInterval: 1200,
  startDelay: 5000,
  threshold: 0.5,
  timeout: 15000
};

const noop = function noop(foo, err, cb) {
  if (typeof err === 'function') {
    cb = err;
    err = null;
  }
  cb(err ? new Error(err) : null, foo);
};
const nopr = function nopr(foo, err) {
  return new Promise((resolve, reject) => {
    if (err) reject(new Error(err));
    else resolve(foo);
  });
};
const slowpr = function slowpr(foo) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(foo);
    }, 50);
  });
};
const fbpr = function fallback(foo, err) {
  return new Promise((resolve) => {
    resolve(foo || err);
  });
};
describe('Brakes Class', () => {
  it('Should be an instance of EventEmitter', () => {
    const brake = new Brakes(noop);
    expect(brake).to.be.instanceof(EventEmitter);
  });
  it('Should be instantiated with default options', () => {
    const brake = new Brakes(noop);
    // const snapshotSpy = sinon.spy(brake._stats, 'startSnapshots');
    // const statsSpy = sinon.spy(brake, '_startStatsCheck');
    // expect(snapshotSpy.calledOnce).to.equal(true);
    // expect(statsSpy.calledOnce).to.equal(true);
    // expect(brake._stats).to.be.instanceof(Stats);
    expect(brake._opts).to.deep.equal(defaultOptions);
  });
  it('Should promisify the service func', () => {
    const brake = new Brakes(noop);
    return brake._serviceCall('test').then((result) => {
      expect(result).to.equal('test');
    });
  });
  it('Should promisify and reject service func', () => {
    const brake = new Brakes(noop);
    return brake._serviceCall(null, 'err').then(null, (err) => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
    });
  });
  it('Should accept a promise', () => {
    const brake = new Brakes(nopr);
    return brake._serviceCall('test').then((result) => {
      expect(result).to.equal('test');
    });
  });
  it('Should reject a promise', () => {
    const brake = new Brakes(nopr);
    return brake._serviceCall(null, 'err').then(null, (err) => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
    });
  });
  it('Throw an error if not passed a function', () => {
    expect(() => {
      const brake = new Brakes();
      brake.test();
    }).to.throw();
  });
  it('Should be instantiated with a name', () => {
    const overrides = {
      name: 'allYourNameAreBelongToUs'
    };
    const brake = new Brakes(noop, overrides);
    expect(brake.name).to.deep.equal(overrides.name);
  });
  it('Should be instantiated with a group', () => {
    const overrides = {
      group: 'allYourGroupAreBelongToUs'
    };
    const brake = new Brakes(noop, overrides);
    expect(brake.group).to.deep.equal(overrides.group);
  });
  it('Should be instantiated with override options', () => {
    const overrides = {
      bucketSpan: 10001,
      bucketNum: 601,
      circuitDuration: 300001,
      statInterval: 1,
      name: 'PUT:/path',
      group: 'fakeGroup',
      startDelay: 50010,
      threshold: 0.3,
      timeout: 100
    };
    const brake = new Brakes(noop, overrides);
    expect(brake._opts).to.deep.equal(overrides);
  });
  it('Should Resolve a service call and trigger event', () => {
    const brake = new Brakes(nopr);
    const spy = sinon.spy(() => {});
    brake.on('success', spy);
    return brake.exec('foo').then(result => {
      expect(result).to.equal('foo');
      expect(spy.calledOnce).to.equal(true);
    });
  });
  it('Should Reject a service call and trigger event', () => {
    const brake = new Brakes(noop);
    const spy = sinon.spy(() => {});
    brake.on('failure', spy);
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
      expect(spy.calledOnce).to.equal(true);
    });
  });
  it('Should timeout a service call and trigger event', () => {
    const brake = new Brakes(slowpr, {
      timeout: 1
    });
    const spy = sinon.spy(() => {});
    brake.on('timeout', spy);
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(TimeOutError);
      expect(spy.calledOnce).to.equal(true);
    });
  });
  it('Should auto reject if circuit is broken', () => {
    const brake = new Brakes(nopr);
    brake._circuitOpen = true;
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(CircuitBrokenError);
    });
  });
  it('Should call fallback if circuit is broken', () => {
    const brake = new Brakes(nopr);
    brake.fallback(fbpr);
    brake._circuitOpen = true;
    return brake.exec('test').then(result => {
      expect(result).to.equal('test');
    });
  });
  it('Fallback should cascade fail', () => {
    const brake = new Brakes(nopr);
    brake.fallback(noop);
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
    });
  });
  it('Fallback should succeed', () => {
    const brake = new Brakes(nopr);
    brake.fallback(fbpr);
    return brake.exec(null, 'thisShouldFailFirstCall').then(result => {
      expect(result).to.equal('thisShouldFailFirstCall');
    });
  });
  it('_open should open', (done) => {
    const brake = new Brakes(nopr, {
      circuitDuration: 10
    });

    const statsCheckSpy = sinon.spy(brake, '_stopStatsCheck');
    const statsResetSpy = sinon.spy(brake._stats, 'reset');
    const openSpy = sinon.spy(brake, '_close');
    const eventSpy = sinon.spy(() => {});
    brake.on('circuitOpen', eventSpy);

    // test if check is made properly
    brake._circuitOpen = true;
    brake._open();
    expect(eventSpy.calledOnce).to.equal(false);

    // test actual opening
    brake._circuitOpen = false;
    brake._open();

    expect(brake._circuitOpen).to.equal(true);
    expect(statsCheckSpy.calledOnce).to.equal(true);
    expect(eventSpy.calledOnce).to.equal(true);

    setTimeout(() => {
      expect(statsResetSpy.calledOnce).to.equal(true);
      expect(openSpy.calledOnce).to.equal(true);
      done();
    }, 20);

  });
  it('_close should close', () => {
    const brake = new Brakes(nopr);
    brake._circuitOpened = true;
    const funcSpy = sinon.spy(brake, '_startStatsCheck');
    const eventSpy = sinon.spy(() => {});
    brake.on('circuitClosed', eventSpy);
    brake._close();
    expect(brake._circuitOpen).to.equal(false);
    expect(funcSpy.calledOnce).to.equal(true);
    expect(eventSpy.calledOnce).to.equal(true);
  });
  it('_startStatsCheck should set flag after delay', (done) => {
    const brake = new Brakes(nopr, {
      startDelay: 1
    });
    brake._stopStatsCheck();
    setTimeout(() => {
      expect(brake._checkingStatus).to.equal(true);
      done();
    }, 5);
  });
  it('_snapshotHandler should transform stats object and emit', (done) => {
    const brake = new Brakes(nopr, {
      name: 'brake1',
      group: 'brakeGroup1'
    });
    const eventSpy = sinon.spy(() => {});
    brake.on('snapshot', eventSpy);
    brake._snapshotHandler({
      foo: 'bar'
    });
    setTimeout(() => {
      expect(eventSpy.calledOnce).to.equal(true);
      const statsObj = eventSpy.firstCall.args[0];
      expect(statsObj.name).to.equal('brake1');
      expect(statsObj.group).to.equal('brakeGroup1');
      expect(statsObj.time).to.be.a('number');
      expect(statsObj.stats).to.deep.equal({
        foo: 'bar'
      });
      done();
    }, 5);
  });
  it('_stopStatsCheck should set flag', () => {
    const brake = new Brakes(nopr);
    brake._stopStatsCheck();
    expect(brake._checkingStatus).to.equal(false);
  });
  it('destroy() should remove all references', () => {
    const brake = new Brakes(nopr);

    // first test that we are handling all appropriate events
    const expectedEvents = ['success', 'timeout', 'failure', 'snapshot'];
    const actualEvents = Object.keys(brake._events);
    expect(actualEvents).to.have.members(expectedEvents);
    expect(Object.keys(brake._events).length).to.equal(expectedEvents.length);

    const deregisterStub = sinon.stub(globalStats, 'deregister');
    const removeEventStub = sinon.stub(brake, 'removeAllListeners');

    brake.destroy();
    expect(deregisterStub.calledOnce).to.equal(true);
    expect(removeEventStub.callCount).to.equal(actualEvents.length);
  });
  it('getGlobalStats should return instance of globalStats', () => {
    const brake = new Brakes(nopr);
    expect(brake.getGlobalStats()).to.equal(globalStats);
    expect(Brakes.getGlobalStats()).to.equal(globalStats);
  });
  it('_checkStats should not check when status flag is false', () => {
    const brake = new Brakes(nopr);
    const spy = sinon.spy(brake, '_close');
    brake._checkStats();
    expect(spy.calledOnce).to.equal(false);
  });
  it('_checkStats should not check when total is 0', () => {
    const brake = new Brakes(nopr);
    brake._checkingStatus = true;
    const spy = sinon.spy(brake, '_close');
    brake._checkStats({
      total: 0
    });
    expect(spy.calledOnce).to.equal(false);
  });
  it('_checkStats should not check when circuit is broken is 0', () => {
    const brake = new Brakes(nopr);
    brake._checkingStatus = true;
    brake._closed = true;
    const spy = sinon.spy(brake, '_close');
    brake._checkStats({
      total: 1
    });
    expect(spy.calledOnce).to.equal(false);
  });
  it('_checkStats should check and not close', () => {
    const brake = new Brakes(nopr);
    const spy = sinon.spy(brake, '_close');
    brake._checkStats({
      successful: 10,
      total: 10
    });
    expect(spy.calledOnce).to.equal(false);
  });
  it('_checkStats should check and not close', () => {
    const brake = new Brakes(nopr);
    brake._checkingStatus = true;
    const spy = sinon.spy(brake, '_open');
    brake._checkStats({
      successful: 1,
      total: 10
    });
    expect(spy.calledOnce).to.equal(true);
  });
});
<commit_msg>improved brakes output<commit_after>'use strict';

const Brakes = require('../lib/Brakes');
const globalStats = require('../lib/globalStats');
const expect = require('chai').expect;
const EventEmitter = require('events').EventEmitter;
const sinon = require('sinon');
const TimeOutError = require('../lib/TimeOutError');
const CircuitBrokenError = require('../lib/CircuitBrokenError');

const defaultOptions = {
  bucketSpan: 1000,
  bucketNum: 60,
  name: 'defaultBrake',
  group: 'defaultBrakeGroup',
  circuitDuration: 30000,
  statInterval: 1200,
  startDelay: 5000,
  threshold: 0.5,
  timeout: 15000
};

const noop = function noop(foo, err, cb) {
  if (typeof err === 'function') {
    cb = err;
    err = null;
  }
  cb(err ? new Error(err) : null, foo);
};
const nopr = function nopr(foo, err) {
  return new Promise((resolve, reject) => {
    if (err) reject(new Error(err));
    else resolve(foo);
  });
};
const slowpr = function slowpr(foo) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(foo);
    }, 50);
  });
};
const fbpr = function fallback(foo, err) {
  return new Promise((resolve) => {
    resolve(foo || err);
  });
};
describe('Brakes Class', () => {
  it('Should be an instance of EventEmitter', () => {
    const brake = new Brakes(noop);
    expect(brake).to.be.instanceof(EventEmitter);
  });
  it('Should be instantiated with default options', () => {
    const brake = new Brakes(noop);
    // const snapshotSpy = sinon.spy(brake._stats, 'startSnapshots');
    // const statsSpy = sinon.spy(brake, '_startStatsCheck');
    // expect(snapshotSpy.calledOnce).to.equal(true);
    // expect(statsSpy.calledOnce).to.equal(true);
    // expect(brake._stats).to.be.instanceof(Stats);
    expect(brake._opts).to.deep.equal(defaultOptions);
  });
  it('Should promisify the service func', () => {
    const brake = new Brakes(noop);
    return brake._serviceCall('test').then((result) => {
      expect(result).to.equal('test');
    });
  });
  it('Should promisify and reject service func', () => {
    const brake = new Brakes(noop);
    return brake._serviceCall(null, 'err').then(null, (err) => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
    });
  });
  it('Should accept a promise', () => {
    const brake = new Brakes(nopr);
    return brake._serviceCall('test').then((result) => {
      expect(result).to.equal('test');
    });
  });
  it('Should reject a promise', () => {
    const brake = new Brakes(nopr);
    return brake._serviceCall(null, 'err').then(null, (err) => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
    });
  });
  it('Throw an error if not passed a function', () => {
    expect(() => {
      const brake = new Brakes();
      brake.test();
    }).to.throw();
  });
  it('Should be instantiated with a name', () => {
    const overrides = {
      name: 'allYourNameAreBelongToUs'
    };
    const brake = new Brakes(noop, overrides);
    expect(brake.name).to.deep.equal(overrides.name);
  });
  it('Should be instantiated with a group', () => {
    const overrides = {
      group: 'allYourGroupAreBelongToUs'
    };
    const brake = new Brakes(noop, overrides);
    expect(brake.group).to.deep.equal(overrides.group);
  });
  it('Should be instantiated with override options', () => {
    const overrides = {
      bucketSpan: 10001,
      bucketNum: 601,
      circuitDuration: 300001,
      statInterval: 1,
      name: 'PUT:/path',
      group: 'fakeGroup',
      startDelay: 50010,
      threshold: 0.3,
      timeout: 100
    };
    const brake = new Brakes(noop, overrides);
    expect(brake._opts).to.deep.equal(overrides);
  });
  it('Should Resolve a service call and trigger event', () => {
    const brake = new Brakes(nopr);
    const spy = sinon.spy(() => {});
    brake.on('success', spy);
    return brake.exec('foo').then(result => {
      expect(result).to.equal('foo');
      expect(spy.calledOnce).to.equal(true);
    });
  });
  it('Should Reject a service call and trigger event', () => {
    const brake = new Brakes(noop);
    const spy = sinon.spy(() => {});
    brake.on('failure', spy);
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
      expect(spy.calledOnce).to.equal(true);
    });
  });
  it('Should timeout a service call and trigger event', () => {
    const brake = new Brakes(slowpr, {
      timeout: 1
    });
    const spy = sinon.spy(() => {});
    brake.on('timeout', spy);
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(TimeOutError);
      expect(spy.calledOnce).to.equal(true);
    });
  });
  it('Should auto reject if circuit is broken', () => {
    const brake = new Brakes(nopr);
    brake._circuitOpen = true;
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(CircuitBrokenError);
    });
  });
  it('Should call fallback if circuit is broken', () => {
    const brake = new Brakes(nopr);
    brake.fallback(fbpr);
    brake._circuitOpen = true;
    return brake.exec('test').then(result => {
      expect(result).to.equal('test');
    });
  });
  it('Fallback should cascade fail', () => {
    const brake = new Brakes(nopr);
    brake.fallback(noop);
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
    });
  });
  it('Fallback should succeed', () => {
    const brake = new Brakes(nopr);
    brake.fallback(fbpr);
    return brake.exec(null, 'thisShouldFailFirstCall').then(result => {
      expect(result).to.equal('thisShouldFailFirstCall');
    });
  });
  it('_open should open', (done) => {
    const brake = new Brakes(nopr, {
      circuitDuration: 10
    });

    const statsCheckSpy = sinon.spy(brake, '_stopStatsCheck');
    const statsResetSpy = sinon.spy(brake._stats, 'reset');
    const openSpy = sinon.spy(brake, '_close');
    const eventSpy = sinon.spy(() => {});
    brake.on('circuitOpen', eventSpy);

    // test if check is made properly
    brake._circuitOpen = true;
    brake._open();
    expect(eventSpy.calledOnce).to.equal(false);

    // test actual opening
    brake._circuitOpen = false;
    brake._open();

    expect(brake._circuitOpen).to.equal(true);
    expect(statsCheckSpy.calledOnce).to.equal(true);
    expect(eventSpy.calledOnce).to.equal(true);

    setTimeout(() => {
      expect(statsResetSpy.calledOnce).to.equal(true);
      expect(openSpy.calledOnce).to.equal(true);
      done();
    }, 20);

  });
  it('_close should close', () => {
    const brake = new Brakes(nopr);
    brake._circuitOpened = true;
    const funcSpy = sinon.spy(brake, '_startStatsCheck');
    const eventSpy = sinon.spy(() => {});
    brake.on('circuitClosed', eventSpy);
    brake._close();
    expect(brake._circuitOpen).to.equal(false);
    expect(funcSpy.calledOnce).to.equal(true);
    expect(eventSpy.calledOnce).to.equal(true);
  });
  it('_startStatsCheck should set flag after delay', (done) => {
    const brake = new Brakes(nopr, {
      startDelay: 1
    });
    brake._stopStatsCheck();
    setTimeout(() => {
      expect(brake._checkingStatus).to.equal(true);
      done();
    }, 5);
  });
  it('_snapshotHandler should transform stats object and emit', (done) => {
    const brake = new Brakes(nopr, {
      name: 'brake1',
      group: 'brakeGroup1'
    });
    const eventSpy = sinon.spy(() => {});
    brake.on('snapshot', eventSpy);
    brake._snapshotHandler({
      foo: 'bar'
    });
    setTimeout(() => {
      expect(eventSpy.calledOnce).to.equal(true);
      const statsObj = eventSpy.firstCall.args[0];
      expect(statsObj.name).to.equal('brake1');
      expect(statsObj.group).to.equal('brakeGroup1');
      expect(statsObj.time).to.be.a('number');
      expect(statsObj.threshold).to.equal(defaultOptions.threshold);
      expect(statsObj.circuitDuration).to.equal(defaultOptions.circuitDuration);
      expect(statsObj.stats).to.deep.equal({
        foo: 'bar'
      });
      done();
    }, 5);
  });
  it('_stopStatsCheck should set flag', () => {
    const brake = new Brakes(nopr);
    brake._stopStatsCheck();
    expect(brake._checkingStatus).to.equal(false);
  });
  it('destroy() should remove all references', () => {
    const brake = new Brakes(nopr);

    // first test that we are handling all appropriate events
    const expectedEvents = ['success', 'timeout', 'failure', 'snapshot'];
    const actualEvents = Object.keys(brake._events);
    expect(actualEvents).to.have.members(expectedEvents);
    expect(Object.keys(brake._events).length).to.equal(expectedEvents.length);

    const deregisterStub = sinon.stub(globalStats, 'deregister');
    const removeEventStub = sinon.stub(brake, 'removeAllListeners');

    brake.destroy();
    expect(deregisterStub.calledOnce).to.equal(true);
    expect(removeEventStub.callCount).to.equal(actualEvents.length);
  });
  it('getGlobalStats should return instance of globalStats', () => {
    const brake = new Brakes(nopr);
    expect(brake.getGlobalStats()).to.equal(globalStats);
    expect(Brakes.getGlobalStats()).to.equal(globalStats);
  });
  it('_checkStats should not check when status flag is false', () => {
    const brake = new Brakes(nopr);
    const spy = sinon.spy(brake, '_close');
    brake._checkStats();
    expect(spy.calledOnce).to.equal(false);
  });
  it('_checkStats should not check when total is 0', () => {
    const brake = new Brakes(nopr);
    brake._checkingStatus = true;
    const spy = sinon.spy(brake, '_close');
    brake._checkStats({
      total: 0
    });
    expect(spy.calledOnce).to.equal(false);
  });
  it('_checkStats should not check when circuit is broken is 0', () => {
    const brake = new Brakes(nopr);
    brake._checkingStatus = true;
    brake._closed = true;
    const spy = sinon.spy(brake, '_close');
    brake._checkStats({
      total: 1
    });
    expect(spy.calledOnce).to.equal(false);
  });
  it('_checkStats should check and not close', () => {
    const brake = new Brakes(nopr);
    const spy = sinon.spy(brake, '_close');
    brake._checkStats({
      successful: 10,
      total: 10
    });
    expect(spy.calledOnce).to.equal(false);
  });
  it('_checkStats should check and not close', () => {
    const brake = new Brakes(nopr);
    brake._checkingStatus = true;
    const spy = sinon.spy(brake, '_open');
    brake._checkStats({
      successful: 1,
      total: 10
    });
    expect(spy.calledOnce).to.equal(true);
  });
});
<|endoftext|>


<commit_before>'use strict';

const Brakes = require('../lib/Brakes');
const globalStats = require('../lib/globalStats');
const expect = require('chai').expect;
const EventEmitter = require('events').EventEmitter;
const sinon = require('sinon');
const TimeOutError = require('../lib/TimeOutError');
const CircuitBrokenError = require('../lib/CircuitBrokenError');

let brake;

const defaultOptions = {
  bucketSpan: 1000,
  bucketNum: 60,
  name: 'defaultBrake',
  group: 'defaultBrakeGroup',
  registerGlobal: true,
  circuitDuration: 30000,
  statInterval: 1200,
  waitThreshold: 100,
  threshold: 0.5,
  timeout: 15000,
  healthCheckInterval: 5000,
  healthCheck: undefined,
  fallback: undefined
};

const noop = function noop(foo, err, cb) {
  if (typeof err === 'function') {
    cb = err;
    err = null;
  }
  cb(err ? new Error(err) : null, foo);
};
const nopr = function nopr(foo, err) {
  return new Promise((resolve, reject) => {
    if (err) reject(new Error(err));
    else resolve(foo);
  });
};
const slowpr = function slowpr(foo) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(foo);
    }, 50);
  });
};
const fbpr = function fallback(foo, err) {
  return new Promise((resolve) => {
    resolve(foo || err);
  });
};
const hc = function healthCheck(foo, err) {
  return new Promise((resolve) => {
    resolve(foo || err);
  });
};
describe('Brakes Class', () => {
  afterEach(() => {
    if (brake instanceof Brakes) {
      brake.destroy();
      brake = undefined;
    }
  });
  it('Should be an instance of EventEmitter', () => {
    brake = new Brakes(noop);
    expect(brake).to.be.instanceof(EventEmitter);
  });
  it('Should be instantiated with default options', () => {
    brake = new Brakes(noop);
    // const snapshotSpy = sinon.spy(brake._stats, 'startSnapshots');
    // const statsSpy = sinon.spy(brake, '_startStatsCheck');
    // expect(snapshotSpy.calledOnce).to.equal(true);
    // expect(statsSpy.calledOnce).to.equal(true);
    // expect(brake._stats).to.be.instanceof(Stats);
    expect(brake._opts).to.deep.equal(defaultOptions);
  });
  it('Should promisify the service func', () => {
    brake = new Brakes(noop);
    return brake._serviceCall('test').then((result) => {
      expect(result).to.equal('test');
    });
  });
  it('Should promisify and reject service func', () => {
    brake = new Brakes(noop);
    return brake._serviceCall(null, 'err').then(null, (err) => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
    });
  });
  it('Should accept a promise', () => {
    brake = new Brakes(nopr);
    return brake._serviceCall('test').then((result) => {
      expect(result).to.equal('test');
    });
  });
  it('Should reject a promise', () => {
    brake = new Brakes(nopr);
    return brake._serviceCall(null, 'err').then(null, (err) => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
    });
  });
  it('Throw an error if not passed a function', () => {
    expect(() => {
      brake = new Brakes();
      brake.test();
    }).to.throw();
  });
  it('Should be instantiated with a name', () => {
    const overrides = {
      name: 'allYourNameAreBelongToUs'
    };
    brake = new Brakes(noop, overrides);
    expect(brake.name).to.deep.equal(overrides.name);
  });
  it('Should be instantiated with a group', () => {
    const overrides = {
      group: 'allYourGroupAreBelongToUs'
    };
    brake = new Brakes(noop, overrides);
    expect(brake.group).to.deep.equal(overrides.group);
  });
  it('Should be instantiated with override options', () => {
    const overrides = {
      bucketSpan: 10001,
      bucketNum: 601,
      circuitDuration: 300001,
      statInterval: 1,
      registerGlobal: false,
      name: 'PUT:/path',
      group: 'fakeGroup',
      waitThreshold: 1000,
      threshold: 0.3,
      timeout: 100,
      healthCheckInterval: 1000,
      healthCheck: () => Promise.resolve(),
      fallback: () => Promise.resolve()
    };
    brake = new Brakes(noop, overrides);
    expect(brake._opts).to.deep.equal(overrides);
  });
  it('Should Resolve a service call and trigger event', () => {
    brake = new Brakes(nopr);
    const spy = sinon.spy(() => {});
    brake.on('success', spy);
    return brake.exec('foo').then(result => {
      expect(result).to.equal('foo');
      expect(spy.calledOnce).to.equal(true);
    });
  });
  it('Should Reject a service call and trigger event', () => {
    brake = new Brakes(noop);
    const spy = sinon.spy(() => {});
    brake.on('failure', spy);
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
      expect(spy.calledOnce).to.equal(true);
    });
  });
  it('Should timeout a service call and trigger event', () => {
    brake = new Brakes(slowpr, {
      timeout: 1
    });
    const spy = sinon.spy(() => {});
    brake.on('timeout', spy);
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(TimeOutError);
      expect(spy.calledOnce).to.equal(true);
    });
  });
  it('Should auto reject if circuit is broken', () => {
    brake = new Brakes(nopr);
    brake._circuitOpen = true;
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(CircuitBrokenError);
    });
  });
  it('Should call fallback if circuit is broken', () => {
    brake = new Brakes(nopr);
    brake.fallback(fbpr);
    brake._circuitOpen = true;
    return brake.exec('test').then(result => {
      expect(result).to.equal('test');
    });
  });
  it('Fallback should cascade fail', () => {
    brake = new Brakes(nopr);
    brake.fallback(noop);
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
    });
  });
  it('Fallback should succeed', () => {
    brake = new Brakes(nopr);
    brake.fallback(fbpr);
    return brake.exec(null, 'thisShouldFailFirstCall').then(result => {
      expect(result).to.equal('thisShouldFailFirstCall');
    });
  });
  it('Should call healthCheck if circuit is broken', () => {
    brake = new Brakes(nopr);
    brake.healthCheck(fbpr);
    const hcSpy = sinon.spy(brake, '_healthCheck');

    brake._open();
    setTimeout(() => {
      expect(hcSpy.calledOnce).to.equal(true);
    }, 500);
  });
  it('Should close circuit if _setHealthInterval is called with successful health check', () => {
    brake = new Brakes(nopr);
    brake.healthCheck(hc);
    const hcSpy = sinon.spy(brake, '_setHealthInterval');
    const statsResetSpy = sinon.spy(brake._stats, 'reset');
    const closeSpy = sinon.spy(brake, '_close');

    brake._open();

    setTimeout(() => {
      expect(hcSpy.calledOnce).to.equal(true);
      expect(statsResetSpy.calledOnce).to.equal(true);
      expect(closeSpy.calledOnce).to.equal(true);
    }, 500);
  });
  it('Should accept health check & fallback function from options', () => {
    brake = new Brakes(nopr, {healthCheck: hc, fallback: fbpr});
    expect(brake._healthCheck).to.equal(hc);
    expect(brake._fallback).to.equal(fbpr);
  });
  it('_open should open', (done) => {
    brake = new Brakes(nopr, {
      circuitDuration: 10
    });

    const statsResetSpy = sinon.spy(brake._stats, 'reset');
    const openSpy = sinon.spy(brake, '_close');
    const eventSpy = sinon.spy(() => {});
    brake.on('circuitOpen', eventSpy);

    // test if check is made properly
    brake._circuitOpen = true;
    brake._open();
    expect(eventSpy.calledOnce).to.equal(false);

    // test actual opening
    brake._circuitOpen = false;
    brake._open();

    expect(brake._circuitOpen).to.equal(true);
    expect(eventSpy.calledOnce).to.equal(true);

    setTimeout(() => {
      expect(statsResetSpy.calledOnce).to.equal(true);
      expect(openSpy.calledOnce).to.equal(true);
      done();
    }, 20);

  });
  it('_close should close', () => {
    brake = new Brakes(nopr);
    brake._circuitOpened = true;
    const eventSpy = sinon.spy(() => {});
    brake.on('circuitClosed', eventSpy);
    brake._close();
    expect(brake._circuitOpen).to.equal(false);
    expect(eventSpy.calledOnce).to.equal(true);
  });
  it('_snapshotHandler should transform stats object and emit', (done) => {
    brake = new Brakes(nopr, {
      name: 'brake1',
      group: 'brakeGroup1',
      registerGlobal: false
    });
    const eventSpy = sinon.spy(() => {});
    brake.on('snapshot', eventSpy);
    brake._snapshotHandler({
      foo: 'bar'
    });
    setTimeout(() => {
      expect(eventSpy.calledOnce).to.equal(true);
      const statsObj = eventSpy.firstCall.args[0];
      expect(statsObj.name).to.equal('brake1');
      expect(statsObj.group).to.equal('brakeGroup1');
      expect(statsObj.time).to.be.a('number');
      expect(statsObj.threshold).to.equal(defaultOptions.threshold);
      expect(statsObj.circuitDuration).to.equal(defaultOptions.circuitDuration);
      expect(statsObj.stats).to.deep.equal({
        foo: 'bar'
      });
      done();
    }, 5);
  });
  it('destroy() should remove all references', () => {
    brake = new Brakes(nopr);

    // first test that we are handling all appropriate events
    const expectedEvents = ['success', 'timeout', 'failure', 'snapshot'];
    const actualEvents = Object.keys(brake._events);
    expect(actualEvents).to.have.members(expectedEvents);
    expect(Object.keys(brake._events).length).to.equal(expectedEvents.length);

    const deregisterStub = sinon.stub(globalStats, 'deregister');
    const removeEventStub = sinon.stub(brake, 'removeAllListeners', () => true);
    brake.destroy();
    expect(deregisterStub.calledOnce).to.equal(true);
    expect(removeEventStub.callCount).to.equal(actualEvents.length);
    brake.removeAllListeners.restore();
    globalStats.deregister.restore();
  });
  it('getGlobalStats should return instance of globalStats', () => {
    brake = new Brakes(nopr);
    expect(brake.getGlobalStats()).to.equal(globalStats);
    expect(Brakes.getGlobalStats()).to.equal(globalStats);
  });
  it('_checkStats should not check when threshold isn\'t met', () => {
    brake = new Brakes(nopr);
    const spy = sinon.spy(brake, '_close');
    brake._checkStats({
      total: 50
    });
    expect(spy.calledOnce).to.equal(false);
  });
  it('_checkStats should not check when total is 0', () => {
    brake = new Brakes(nopr);
    brake._checkingStatus = true;
    const spy = sinon.spy(brake, '_open');
    brake._checkStats({
      total: 0
    });
    expect(spy.calledOnce).to.equal(false);
  });
  it('_checkStats should not check when circuit is broken is 0', () => {
    brake = new Brakes(nopr);
    brake._checkingStatus = true;
    brake._closed = true;
    const spy = sinon.spy(brake, '_open');
    brake._checkStats({
      total: 1
    });
    expect(spy.calledOnce).to.equal(false);
  });
  it('isOpen should return whether or not circuit is open', () => {
    brake = new Brakes(nopr);
    brake._circuitOpen = true;
    expect(brake.isOpen()).to.equal(true);
    brake._circuitOpen = false;
    expect(brake.isOpen()).to.equal(false);
  });
  it('_checkStats should check and not close', () => {
    brake = new Brakes(nopr);
    const spy = sinon.spy(brake, '_open');
    brake._checkStats({
      successful: 450,
      total: 500
    });
    expect(spy.calledOnce).to.equal(false);
  });
  it('_checkStats should check and close', () => {
    brake = new Brakes(nopr);
    brake._checkingStatus = true;
    const spy = sinon.spy(brake, '_open');
    brake._checkStats({
      successful: 1,
      total: 200
    });
    expect(spy.calledOnce).to.equal(true);
  });
});
<commit_msg>improved tests coverage for health check<commit_after>'use strict';

const Brakes = require('../lib/Brakes');
const globalStats = require('../lib/globalStats');
const expect = require('chai').expect;
const EventEmitter = require('events').EventEmitter;
const sinon = require('sinon');
const TimeOutError = require('../lib/TimeOutError');
const CircuitBrokenError = require('../lib/CircuitBrokenError');

let brake;

const defaultOptions = {
  bucketSpan: 1000,
  bucketNum: 60,
  name: 'defaultBrake',
  group: 'defaultBrakeGroup',
  registerGlobal: true,
  circuitDuration: 30000,
  statInterval: 1200,
  waitThreshold: 100,
  threshold: 0.5,
  timeout: 15000,
  healthCheckInterval: 5000,
  healthCheck: undefined,
  fallback: undefined
};

const noop = function noop(foo, err, cb) {
  if (typeof err === 'function') {
    cb = err;
    err = null;
  }
  cb(err ? new Error(err) : null, foo);
};
const nopr = function nopr(foo, err) {
  return new Promise((resolve, reject) => {
    if (err) reject(new Error(err));
    else resolve(foo);
  });
};
const slowpr = function slowpr(foo) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(foo);
    }, 50);
  });
};
const fbpr = function fallback(foo, err) {
  return new Promise((resolve) => {
    resolve(foo || err);
  });
};
const hc = function healthCheck(foo, err) {
  return new Promise((resolve) => {
    resolve(foo || err);
  });
};
describe('Brakes Class', () => {
  afterEach(() => {
    if (brake instanceof Brakes) {
      brake.destroy();
      brake = undefined;
    }
  });
  it('Should be an instance of EventEmitter', () => {
    brake = new Brakes(noop);
    expect(brake).to.be.instanceof(EventEmitter);
  });
  it('Should be instantiated with default options', () => {
    brake = new Brakes(noop);
    // const snapshotSpy = sinon.spy(brake._stats, 'startSnapshots');
    // const statsSpy = sinon.spy(brake, '_startStatsCheck');
    // expect(snapshotSpy.calledOnce).to.equal(true);
    // expect(statsSpy.calledOnce).to.equal(true);
    // expect(brake._stats).to.be.instanceof(Stats);
    expect(brake._opts).to.deep.equal(defaultOptions);
  });
  it('Should promisify the service func', () => {
    brake = new Brakes(noop);
    return brake._serviceCall('test').then((result) => {
      expect(result).to.equal('test');
    });
  });
  it('Should promisify and reject service func', () => {
    brake = new Brakes(noop);
    return brake._serviceCall(null, 'err').then(null, (err) => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
    });
  });
  it('Should accept a promise', () => {
    brake = new Brakes(nopr);
    return brake._serviceCall('test').then((result) => {
      expect(result).to.equal('test');
    });
  });
  it('Should reject a promise', () => {
    brake = new Brakes(nopr);
    return brake._serviceCall(null, 'err').then(null, (err) => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
    });
  });
  it('Throw an error if not passed a function', () => {
    expect(() => {
      brake = new Brakes();
      brake.test();
    }).to.throw();
  });
  it('Should be instantiated with a name', () => {
    const overrides = {
      name: 'allYourNameAreBelongToUs'
    };
    brake = new Brakes(noop, overrides);
    expect(brake.name).to.deep.equal(overrides.name);
  });
  it('Should be instantiated with a group', () => {
    const overrides = {
      group: 'allYourGroupAreBelongToUs'
    };
    brake = new Brakes(noop, overrides);
    expect(brake.group).to.deep.equal(overrides.group);
  });
  it('Should be instantiated with override options', () => {
    const overrides = {
      bucketSpan: 10001,
      bucketNum: 601,
      circuitDuration: 300001,
      statInterval: 1,
      registerGlobal: false,
      name: 'PUT:/path',
      group: 'fakeGroup',
      waitThreshold: 1000,
      threshold: 0.3,
      timeout: 100,
      healthCheckInterval: 1000,
      healthCheck: () => Promise.resolve(),
      fallback: () => Promise.resolve()
    };
    brake = new Brakes(noop, overrides);
    expect(brake._opts).to.deep.equal(overrides);
  });
  it('Should Resolve a service call and trigger event', () => {
    brake = new Brakes(nopr);
    const spy = sinon.spy(() => {});
    brake.on('success', spy);
    return brake.exec('foo').then(result => {
      expect(result).to.equal('foo');
      expect(spy.calledOnce).to.equal(true);
    });
  });
  it('Should Reject a service call and trigger event', () => {
    brake = new Brakes(noop);
    const spy = sinon.spy(() => {});
    brake.on('failure', spy);
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
      expect(spy.calledOnce).to.equal(true);
    });
  });
  it('Should timeout a service call and trigger event', () => {
    brake = new Brakes(slowpr, {
      timeout: 1
    });
    const spy = sinon.spy(() => {});
    brake.on('timeout', spy);
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(TimeOutError);
      expect(spy.calledOnce).to.equal(true);
    });
  });
  it('Should auto reject if circuit is broken', () => {
    brake = new Brakes(nopr);
    brake._circuitOpen = true;
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(CircuitBrokenError);
    });
  });
  it('Should call fallback if circuit is broken', () => {
    brake = new Brakes(nopr);
    brake.fallback(fbpr);
    brake._circuitOpen = true;
    return brake.exec('test').then(result => {
      expect(result).to.equal('test');
    });
  });
  it('Fallback should cascade fail', () => {
    brake = new Brakes(nopr);
    brake.fallback(noop);
    return brake.exec(null, 'err').then(null, err => {
      expect(err).to.be.instanceof(Error);
      expect(err.message).to.equal('err');
    });
  });
  it('Fallback should succeed', () => {
    brake = new Brakes(nopr);
    brake.fallback(fbpr);
    return brake.exec(null, 'thisShouldFailFirstCall').then(result => {
      expect(result).to.equal('thisShouldFailFirstCall');
    });
  });
  it('Should call healthCheck if circuit is broken', () => {
    brake = new Brakes(nopr);
    brake.healthCheck(fbpr);
    const hcSpy = sinon.spy(brake, '_healthCheck');

    brake._open();
    setTimeout(() => {
      expect(hcSpy.calledOnce).to.equal(true);
    }, 500);
  });
  it('Should close circuit if _setHealthInterval is called with successful health check', (done) => {
    brake = new Brakes(nopr, {
      healthCheckInterval: 1
    });
    brake.healthCheck(hc);
    const hcSpy = sinon.spy(brake, '_setHealthInterval');
    const statsResetSpy = sinon.spy(brake._stats, 'reset');
    const closeSpy = sinon.spy(brake, '_close');

    brake._open();

    setTimeout(() => {
      expect(hcSpy.calledOnce).to.equal(true);
      expect(statsResetSpy.calledOnce).to.equal(true);
      expect(closeSpy.calledOnce).to.equal(true);
      clearInterval(brake._healthInterval);
      done();
    }, 5);
  });
  it('Should accept health check & fallback function from options', () => {
    brake = new Brakes(nopr, {
      healthCheck: hc,
      fallback: fbpr
    });
    expect(brake._healthCheck).to.equal(hc);
    expect(brake._fallback).to.equal(fbpr);
  });
  it('Should accept a health check function that is async', () => {
    brake = new Brakes(nopr, {
      healthCheck: noop
    });
    expect(brake._healthCheck('foo').then).to.not.equal(undefined);
  });
  it('_setHealthInterval should open', (done) => {
    brake = new Brakes(nopr, {
      healthCheck: hc,
      healthCheckInterval: 1
    });
    const statsResetSpy = sinon.spy(brake._stats, 'reset');
    const openSpy = sinon.spy(brake, '_close');
    // check normal behavior
    brake._circuitOpen = true;
    brake._setHealthInterval();
    setTimeout(() => {
      expect(statsResetSpy.called).to.equal(true);
      expect(openSpy.called).to.equal(true);
      brake._close();
      clearInterval(brake._healthInterval);
      done();
    }, 5);
  });
  it('_setHealthInterval should emit error', (done) => {
    brake = new Brakes(nopr, {
      healthCheck: nopr.bind(null, null, 'thisisanerror'),
      healthCheckInterval: 0
    });

    const eventSpy = sinon.spy(() => {});
    brake.on('healthCheckFailed', eventSpy);

    // check normal behavior
    brake._circuitOpen = true;
    brake._setHealthInterval();
    setTimeout(() => {
      expect(eventSpy.called).to.equal(true);
      brake._close();
      clearInterval(brake._healthInterval);
      done();
    }, 5);
  });
  it('_setHealthInterval should do nothing if interval is already set', () => {
    brake = new Brakes(nopr, {
      healthCheck: nopr.bind(null, null, 'thisisanerror'),
      healthCheckInterval: 0
    });
    brake._healthInterval = 'foo';
    brake._setHealthInterval();
    expect(brake._healthInterval).to.equal('foo');
  });
  it('_setHealthInterval should clearInterval, if circuit is opened', (done) => {
    brake = new Brakes(nopr, {
      healthCheck: hc,
      healthCheckInterval: 0
    });
    brake._circuitOpen = false;
    brake._setHealthInterval();
    setTimeout(() => {
      expect(brake._healthInterval).to.equal(undefined);
      done();
    }, 3);
  });
  it('_open should open', (done) => {
    brake = new Brakes(nopr, {
      circuitDuration: 10
    });

    const statsResetSpy = sinon.spy(brake._stats, 'reset');
    const openSpy = sinon.spy(brake, '_close');
    const eventSpy = sinon.spy(() => {});
    brake.on('circuitOpen', eventSpy);

    // test if check is made properly
    brake._circuitOpen = true;
    brake._open();
    expect(eventSpy.calledOnce).to.equal(false);

    // test actual opening
    brake._circuitOpen = false;
    brake._open();

    expect(brake._circuitOpen).to.equal(true);
    expect(eventSpy.calledOnce).to.equal(true);

    setTimeout(() => {
      expect(statsResetSpy.calledOnce).to.equal(true);
      expect(openSpy.calledOnce).to.equal(true);
      done();
    }, 20);

  });
  it('_close should close', () => {
    brake = new Brakes(nopr);
    brake._circuitOpened = true;
    const eventSpy = sinon.spy(() => {});
    brake.on('circuitClosed', eventSpy);
    brake._close();
    expect(brake._circuitOpen).to.equal(false);
    expect(eventSpy.calledOnce).to.equal(true);
  });
  it('_snapshotHandler should transform stats object and emit', (done) => {
    brake = new Brakes(nopr, {
      name: 'brake1',
      group: 'brakeGroup1',
      registerGlobal: false
    });
    const eventSpy = sinon.spy(() => {});
    brake.on('snapshot', eventSpy);
    brake._snapshotHandler({
      foo: 'bar'
    });
    setTimeout(() => {
      expect(eventSpy.calledOnce).to.equal(true);
      const statsObj = eventSpy.firstCall.args[0];
      expect(statsObj.name).to.equal('brake1');
      expect(statsObj.group).to.equal('brakeGroup1');
      expect(statsObj.time).to.be.a('number');
      expect(statsObj.threshold).to.equal(defaultOptions.threshold);
      expect(statsObj.circuitDuration).to.equal(defaultOptions.circuitDuration);
      expect(statsObj.stats).to.deep.equal({
        foo: 'bar'
      });
      done();
    }, 5);
  });
  it('destroy() should remove all references', () => {
    brake = new Brakes(nopr);

    // first test that we are handling all appropriate events
    const expectedEvents = ['success', 'timeout', 'failure', 'snapshot'];
    const actualEvents = Object.keys(brake._events);
    expect(actualEvents).to.have.members(expectedEvents);
    expect(Object.keys(brake._events).length).to.equal(expectedEvents.length);

    const deregisterStub = sinon.stub(globalStats, 'deregister');
    const removeEventStub = sinon.stub(brake, 'removeAllListeners', () => true);
    brake.destroy();
    expect(deregisterStub.calledOnce).to.equal(true);
    expect(removeEventStub.callCount).to.equal(actualEvents.length);
    brake.removeAllListeners.restore();
    globalStats.deregister.restore();
  });
  it('getGlobalStats should return instance of globalStats', () => {
    brake = new Brakes(nopr);
    expect(brake.getGlobalStats()).to.equal(globalStats);
    expect(Brakes.getGlobalStats()).to.equal(globalStats);
  });
  it('_checkStats should not check when threshold isn\'t met', () => {
    brake = new Brakes(nopr);
    const spy = sinon.spy(brake, '_close');
    brake._checkStats({
      total: 50
    });
    expect(spy.calledOnce).to.equal(false);
  });
  it('_checkStats should not check when total is 0', () => {
    brake = new Brakes(nopr);
    brake._checkingStatus = true;
    const spy = sinon.spy(brake, '_open');
    brake._checkStats({
      total: 0
    });
    expect(spy.calledOnce).to.equal(false);
  });
  it('_checkStats should not check when circuit is broken is 0', () => {
    brake = new Brakes(nopr);
    brake._checkingStatus = true;
    brake._closed = true;
    const spy = sinon.spy(brake, '_open');
    brake._checkStats({
      total: 1
    });
    expect(spy.calledOnce).to.equal(false);
  });
  it('isOpen should return whether or not circuit is open', () => {
    brake = new Brakes(nopr);
    brake._circuitOpen = true;
    expect(brake.isOpen()).to.equal(true);
    brake._circuitOpen = false;
    expect(brake.isOpen()).to.equal(false);
  });
  it('_checkStats should check and not close', () => {
    brake = new Brakes(nopr);
    const spy = sinon.spy(brake, '_open');
    brake._checkStats({
      successful: 450,
      total: 500
    });
    expect(spy.calledOnce).to.equal(false);
  });
  it('_checkStats should check and close', () => {
    brake = new Brakes(nopr);
    brake._checkingStatus = true;
    const spy = sinon.spy(brake, '_open');
    brake._checkStats({
      successful: 1,
      total: 200
    });
    expect(spy.calledOnce).to.equal(true);
  });
});
<|endoftext|>


<commit_before>var Link = require('./link');
var url = require('url');

/**
 * The Representation object is the Resource Representation. This might be
 * response to a GET request.
 *
 * It typically includes the request body + a number of relevant http headers.
 */
var Representation = function(uri, contentType, body) {

  this.uri = uri;
  this.contentType = contentType;
  this.body = body;
  this.links = [];
  this.embedded = {};

  if (typeof this.body._links !== 'undefined') {
    parseHALLinks(this);
  }
  if (typeof this.body._embedded !== 'undefined') {
    parseHALEmbedded(this);
  }
  delete this.body._links;
  delete this.body._embedded;

};

/**
 * Parse the HAL _links object and populate the 'links' property.
 */
var parseHALLinks = function(representation) {

  for(var relType in representation.body._links) {
    var links = representation.body._links[relType];
    if (!Array.isArray(links)) {
      links = [links];
    }
    for(var ii in links) {
      representation.links.push(
        new Link(
          relType,
          links[ii].href,
          links[ii].type
        )
      );
    }
  }

};

/**
 * Parse the HAL _embedded object. Right now we're just grabbing the
 * information from _embedded and turn it into links.
 */
var parseHALEmbedded = function(representation) {

  for(var relType in representation.body._embedded) {
    var embedded = representation.body._embedded[relType];
    if (!Array.isArray(embedded)) {
      embedded = [embedded];
    }
    for(var ii in embedded) {

      var uri = url.resolve(this.uri, embedded[ii]._links.self.href);

      representation.links.push(
        new Link(
          relType,
          uri
        )
      );

      this.embedded[uri] = embedded[ii];

    }
  }
};

module.exports = Representation;
<commit_msg>this -> representation<commit_after>var Link = require('./link');
var url = require('url');

/**
 * The Representation object is the Resource Representation. This might be
 * response to a GET request.
 *
 * It typically includes the request body + a number of relevant http headers.
 */
var Representation = function(uri, contentType, body) {

  this.uri = uri;
  this.contentType = contentType;
  this.body = body;
  this.links = [];
  this.embedded = {};

  if (typeof this.body._links !== 'undefined') {
    parseHALLinks(this);
  }
  if (typeof this.body._embedded !== 'undefined') {
    parseHALEmbedded(this);
  }
  delete this.body._links;
  delete this.body._embedded;

};

/**
 * Parse the HAL _links object and populate the 'links' property.
 */
var parseHALLinks = function(representation) {

  for(var relType in representation.body._links) {
    var links = representation.body._links[relType];
    if (!Array.isArray(links)) {
      links = [links];
    }
    for(var ii in links) {
      representation.links.push(
        new Link(
          relType,
          links[ii].href,
          links[ii].type
        )
      );
    }
  }

};

/**
 * Parse the HAL _embedded object. Right now we're just grabbing the
 * information from _embedded and turn it into links.
 */
var parseHALEmbedded = function(representation) {

  for(var relType in representation.body._embedded) {
    var embedded = representation.body._embedded[relType];
    if (!Array.isArray(embedded)) {
      embedded = [embedded];
    }
    for(var ii in embedded) {

      var uri = url.resolve(
        representation.uri,
        embedded[ii]._links.self.href
      );

      representation.links.push(
        new Link(
          relType,
          uri
        )
      );

      representation.embedded[uri] = embedded[ii];

    }
  }
};

module.exports = Representation;
<|endoftext|>