<commit_before>import Nightmare from 'nightmare'
import { expect } from 'chai'
import jwtDecode from 'jwt-decode'
import uuid from 'uuid'
import { BASE_URL } from './test_config'

describe('Refreshing token caused by token expiration', () => {
  describe('with a valid refresh token', () => {
    it('succeeds and subsequent refresh and retry also succeed', async () => {
      const todo = uuid.v4()
      const { accessToken, refreshToken } = await Nightmare()
        .goto(`${BASE_URL}/todo-pubsub`)
        .evaluate(() => {
          localStorage.setItem(
            'devHeaders',
            JSON.stringify({
              'X-ACCESS-TOKEN-EXPIRES-IN': '-1000',
              'X-REFRESH-TOKEN-EXPIRES-IN': '5000'
            })
          )
        })
        .insert('input[name=username]', 'alice')
        .insert('input[name=password]', 'alicepass')
        .click('#signin-button')
        .evaluate(() => {
          localStorage.removeItem('devHeaders')
        })
        .wait('input[name=add-todo]', todo)
        .insert('input[name=add-todo]', todo)
        .click('#add-todo-button')
        .wait(1000)
        .evaluate(() => ({
          accessToken: localStorage.getItem('accessToken'),
          refreshToken: localStorage.getItem('refreshToken')
        }))
        .end()
      const at = jwtDecode(accessToken)
      const rt = jwtDecode(refreshToken)
      expect(at.exp - at.iat).to.equal(60 * 60 * 2)
      expect(rt.exp - rt.iat).to.equal(5)
    })
  })

  describe('with an expired refresh token', () => {
    it('fails and subsequent refresh also fails', async () => {
      const todo = uuid.v4()
      const { path } = await Nightmare()
        .goto(`${BASE_URL}/todo-pubsub`)
        .evaluate(() => {
          localStorage.setItem(
            'devHeaders',
            JSON.stringify({
              'X-ACCESS-TOKEN-EXPIRES-IN': '-1000',
              'X-REFRESH-TOKEN-EXPIRES-IN': '-1000'
            })
          )
        })
        .insert('input[name=username]', 'alice')
        .insert('input[name=password]', 'alicepass')
        .click('#signin-button')
        .evaluate(() => {
          localStorage.removeItem('devHeaders')
        })
        .wait('input[name=add-todo]', todo)
        .insert('input[name=add-todo]', todo)
        .click('#add-todo-button')
        .wait(1000)
        .evaluate(() => ({
          path: document.location.pathname
        }))
        .end()
      expect(path).to.equal('/signin') // redirected
    })
  })
})
<commit_msg>Fix test descriptions<commit_after>import Nightmare from 'nightmare'
import { expect } from 'chai'
import jwtDecode from 'jwt-decode'
import uuid from 'uuid'
import { BASE_URL } from './test_config'

describe('Refreshing token caused by token expiration', () => {
  describe('with a valid refresh token', () => {
    it('succeeds and retrying previous request also succeeds', async () => {
      const todo = uuid.v4()
      const { accessToken, refreshToken } = await Nightmare()
        .goto(`${BASE_URL}/todo-pubsub`)
        .evaluate(() => {
          localStorage.setItem(
            'devHeaders',
            JSON.stringify({
              'X-ACCESS-TOKEN-EXPIRES-IN': '-1000',
              'X-REFRESH-TOKEN-EXPIRES-IN': '5000'
            })
          )
        })
        .insert('input[name=username]', 'alice')
        .insert('input[name=password]', 'alicepass')
        .click('#signin-button')
        .evaluate(() => {
          localStorage.removeItem('devHeaders')
        })
        .wait('input[name=add-todo]', todo)
        .insert('input[name=add-todo]', todo)
        .click('#add-todo-button')
        .wait(1000)
        .evaluate(() => ({
          accessToken: localStorage.getItem('accessToken'),
          refreshToken: localStorage.getItem('refreshToken')
        }))
        .end()
      const at = jwtDecode(accessToken)
      const rt = jwtDecode(refreshToken)
      expect(at.exp - at.iat).to.equal(60 * 60 * 2)
      expect(rt.exp - rt.iat).to.equal(5)
    })
  })

  describe('with an expired refresh token', () => {
    it('fails and the user will be signed out', async () => {
      const todo = uuid.v4()
      const { path } = await Nightmare()
        .goto(`${BASE_URL}/todo-pubsub`)
        .evaluate(() => {
          localStorage.setItem(
            'devHeaders',
            JSON.stringify({
              'X-ACCESS-TOKEN-EXPIRES-IN': '-1000',
              'X-REFRESH-TOKEN-EXPIRES-IN': '-1000'
            })
          )
        })
        .insert('input[name=username]', 'alice')
        .insert('input[name=password]', 'alicepass')
        .click('#signin-button')
        .evaluate(() => {
          localStorage.removeItem('devHeaders')
        })
        .wait('input[name=add-todo]', todo)
        .insert('input[name=add-todo]', todo)
        .click('#add-todo-button')
        .wait(1000)
        .evaluate(() => ({
          path: document.location.pathname
        }))
        .end()
      expect(path).to.equal('/signin') // redirected
    })
  })
})
<|endoftext|>


<commit_before>/* jshint node: true */
/* global document, location, Primus: false */
'use strict';

var reTrailingSlash = /\/$/;

/**
  ### loadPrimus(signalhost, callback)

  This is a convenience function that is patched into the signaller to assist
  with loading the `primus.js` client library from an `rtc-switchboard`
  signaling server.

**/
module.exports = function(signalhost, callback) {
  var anchor = document.createElement('a');
  var script;
  var baseUrl;
  var scriptSrc;

  // if the signalhost is a function, we are in single arg calling mode
  if (typeof signalhost == 'function') {
    callback = signalhost;
    signalhost = location.origin;
  }

  // initialise the anchor with the signalhost
  anchor.href = signalhost;

  // read the base path
  baseUrl = signalhost.replace(reTrailingSlash, '');
  scriptSrc = baseUrl + '/rtc.io/primus.js';

  // look for the script first
  script = document.querySelector('script[src="' + scriptSrc + '"]');

  // if we found, the script trigger the callback immediately
  if (script && typeof Primus != 'undefined') {
    return callback(null, Primus);
  }
  // otherwise, if the script exists but Primus is not loaded,
  // then wait for the load
  else if (script) {
    script.addEventListener('load', function() {
      callback(null, Primus);
    });

    return;
  }

  // otherwise create the script and load primus
  script = document.createElement('script');
  script.src = scriptSrc;

  script.onerror = callback;
  script.addEventListener('load', function() {
    // if we have a signalhost that is not basepathed at /
    // then tweak the primus prototype
    if (anchor.pathname !== '/') {
      Primus.prototype.pathname = anchor.pathname.replace(reTrailingSlash, '') +
        Primus.prototype.pathname;
    }

    callback(null, Primus);
  });

  document.body.appendChild(script);
};
<commit_msg>Allow modifications to the primus path - ref #18<commit_after>/* jshint node: true */
/* global document, location, Primus: false */
'use strict';

var reTrailingSlash = /\/$/;
var formatter = require('formatter');
var primusUrl = formatter('{{ signalhost }}{{ primusPath }}');

/**
  ### loadPrimus(signalhost, opts?, callback)

  This is a convenience function that is patched into the signaller to assist
  with loading the `primus.js` client library from an `rtc-switchboard`
  signaling server.

**/
module.exports = function(signalhost, opts, callback) {
  var anchor = document.createElement('a');
  var script;
  var scriptSrc;

  // if the signalhost is a function, we are in single arg calling mode
  if (typeof signalhost == 'function') {
    callback = signalhost;
    signalhost = location.origin;
  }

  if (typeof opts == 'function') {
    callback = opts;
    opts = {};
  }

  // initialise the anchor with the signalhost
  anchor.href = signalhost;

  // initialise the script location
  scriptSrc = primusUrl({
    signalhost: signalhost.replace(reTrailingSlash, ''),
    primusPath: (opts || {}).primusPath || '/rtc.io/primus.js'
  });

  // look for the script first
  script = document.querySelector('script[src="' + scriptSrc + '"]');

  // if we found, the script trigger the callback immediately
  if (script && typeof Primus != 'undefined') {
    return callback(null, Primus);
  }
  // otherwise, if the script exists but Primus is not loaded,
  // then wait for the load
  else if (script) {
    script.addEventListener('load', function() {
      callback(null, Primus);
    });

    return;
  }

  // otherwise create the script and load primus
  script = document.createElement('script');
  script.src = scriptSrc;

  script.onerror = callback;
  script.addEventListener('load', function() {
    // if we have a signalhost that is not basepathed at /
    // then tweak the primus prototype
    if (anchor.pathname !== '/') {
      Primus.prototype.pathname = anchor.pathname.replace(reTrailingSlash, '') +
        Primus.prototype.pathname;
    }

    callback(null, Primus);
  });

  document.body.appendChild(script);
};
<|endoftext|>


<commit_before>// Probe all details about a CouchDB.
//

var lib = require('./lib')
  , LOG = lib.getLogger('audit_couchdb')
  , util = require('util')
  , events = require('events')
  , request = require('request')
  ;

function Couch(url) {
  var self = this;
  events.EventEmitter.call(self);

  self.log = lib.getLogger(url || 'audit_couchdb.Couch');
  self.url = url || null;
  self.proxy = null;
  self.only_dbs = null;
  self.pending = {};

  //

  self.on('couchdb', function(hello) {
    self.log.debug("Scanning databases: " + self.url);
    var all_dbs = lib.join(self.url, '/_all_dbs');
    self.request({uri:all_dbs}, function(er, resp, body) {
      if(er) throw er;
      if(resp.statusCode !== 200 || !Array.isArray(body))
        throw new Error("Bad _all_dbs from " + all_dbs + ": " + JSON.stringify(body));

      self.log.debug("Databases in " + self.url + ": " + JSON.stringify(body));
      var dbs_to_check = body.filter(function(db_name) {
        return (!self.only_dbs) || (self.only_dbs.indexOf(db_name) === -1);
      })
      self.log.debug("Databases to check in " + self.url + ": " + JSON.stringify(dbs_to_check));

      if(dbs_to_check.length === 0)
        self.emit('end');

      dbs_to_check.forEach(function(db_name) {
        self.emit('db_name', db_name);
      })
    })
  })

  self.on('db_name', function(db_name) {
    var db_url  = lib.join(self.url, encodeURIComponent(db_name))
      , sec_url = lib.join(db_url, '/_security')
      ;

    self.pending[db_url] = {};

    var data = {info:null, security:null};
    function got(key, val) {
      self.log.debug("Received " + key + " for " + db_url);
      data[key] = val;

      if(data.info && data.security) {
        self.log.debug("Received metadata and security about db: " + db_url);

        if(data.info.resp.statusCode     === 401 && data.info.body.error     === 'unauthorized'
        && data.security.resp.statusCode === 401 && data.security.body.error === 'unauthorized') {
          self.log.debug("No read permission: " + db_url);
          self.emit('database_unauthorized', db_url)
        } else if (data.info.resp.statusCode !== 200 || data.security.resp.statusCode !== 200
                   || typeof data.info.body !== 'object' || typeof data.security.body !== 'object') {
          throw new Error("Unknown db responses: " + JSON.stringify({db:db_info.data, security:security.data}));
        } else {
          self.emit('database_ok', db_url, data.info.body, data.security.body);
        }

        self.emit('database', db_url, data.info, data.security);
      }
    }

    self.log.debug("Fetching db metadata: " + db_url);
    self.request({uri:db_url}, function(er, resp, body) {
      if(er) throw er;
      //if(resp.statusCode !== 200 || typeof body !== 'object' || false)
      //  throw new Error("Bad db response from " + db_url + ": " + JSON.stringify(body));
      got('info', {resp:resp, body:body});
    })

    self.log.debug("Fetching db security data: " + sec_url);
    self.request({uri:sec_url}, function(er, resp, body) {
      if(er) throw er;
      //if(resp.statusCode !== 200 || typeof body !== 'object')
      //  throw new Error("Bad db response from " + db_url + ": " + JSON.stringify(body));
      got('security', {resp:resp, body:body});
    })
  })

  self.on('database_unauthorized', function(db_url) {
    self.finish_db(db_url);
  })

  self.on('database_ok', function(db_url, db_info, security) {
    self.log.debug("Successful db fetch: " + db_url);
    var view = [ '/_all_docs?include_docs=false'
               , 'startkey=' + encodeURIComponent(JSON.stringify("_design/"))
               , 'endkey='   + encodeURIComponent(JSON.stringify("_design0"))
               ].join("&");
    view = lib.join(db_url, view);

    self.log.debug("Scanning for design documents: " + db_url);
    self.request({uri:view}, function(er, resp, body) {
      if(er) throw er;
      if(resp.statusCode !== 200 || !("rows" in body))
        throw new Error("Bad ddoc response from " + view + ": " + JSON.stringify(body));

      self.log.debug("Design documents in " + db_url + ": " + body.rows.map(function(x) { return x.id }).join(', '));

      if(body.rows.length === 0)
        self.finish_db(db_url);

      body.rows.forEach(function(row) {
        self.emit('ddoc_id', db_url, row.id);
      })
    })
  })

  self.on('ddoc_id', function(db_url, doc_id) {
    var ddoc_url = lib.join(db_url, doc_id)
      , info_url = lib.join(ddoc_url, '/_info');

    self.pending[db_url][doc_id] = 1;

    var data = {ddoc: null, info: null};
    function got(type, value) {
      self.log.debug("Received " + type + " for " + ddoc_url);
      data[type] = value;

      if(data.ddoc && data.info) {
        self.log.debug("Received both contents and info about ddoc: " + ddoc_url);
        self.emit('ddoc', db_url, data.ddoc, data.info);
      }
    }

    self.log.debug("Fetching ddoc info: " + info_url);
    self.request({uri:info_url}, function(er, resp, body) {
      if(er) throw er;
      if(resp.statusCode !== 200 || typeof body !== 'object')
        throw new Error("Bad info response for " + info_url + ": " + JSON.stringify(body));
      got('info', body);
    })

    self.log.debug("Fetching ddoc contents: " + ddoc_url);
    self.request({uri:ddoc_url}, function(er, resp, body) {
      if(er) throw er;
      if(resp.statusCode !== 200 || typeof body !== 'object')
        throw new Error("Bad ddoc response for " + ddoc_url + ": " + JSON.stringify(body));
      got('ddoc', body);
    })
  })

  self.on('ddoc', function(db_url, ddoc, info) {
    self.finish_ddoc(db_url, ddoc._id);
  })

} // Couch
util.inherits(Couch, events.EventEmitter);

Couch.prototype.finish_ddoc = function(db_url, id) {
  var self = this;

  delete self.pending[db_url][id];
  if(Object.keys(self.pending[db_url]).length === 0) {
    self.log.debug("All design docs complete: " + db_url);
    self.finish_db(db_url);
  }
}

Couch.prototype.finish_db = function(db_url) {
  var self = this;

  delete self.pending[db_url];
  if(Object.keys(self.pending).length === 0) {
    self.log.debug("All databases complete");
    self.emit('end');
  }
}

Couch.prototype.request = function request_wrapper(opts, callback) {
  var self = this;

  function json_body(er, resp, body) {
    if(!er) {
      try      { body = JSON.parse(body) }
      catch(e) { er = e }
    }

    return callback && callback.apply(this, [er, resp, body]);
  }

  opts.proxy  = opts.proxy  || self.proxy;
  opts.client = opts.client || self.client;
  opts.followRedirect = false;

  opts.headers = opts.headers || {};
  opts.headers.accept = opts.headers.accept || 'application/json';
  //opts.headers.Connection = opts.headers.Connection || 'keep-alive';

  if(opts.method && opts.method !== "GET" && opts.method !== "HEAD")
    opts.headers['content-type'] = 'application/json';

  return request.apply(this, [opts, json_body]);
}

Couch.prototype.start = function() {
  var self = this;

  if(!self.url)
    throw new Error("url required");

  self.log.debug("Pinging: " + self.url);
  self.request({uri:self.url}, function(er, resp, body) {
    if(er) throw er;
    if(resp.statusCode !== 200 || body.couchdb !== "Welcome")
      throw new Error("Bad welcome from " + self.url + ": " + JSON.stringify(body));

    self.client = resp.client;
    self.client = null; // Reusing the client isn't working right now.

    self.log.debug("Good welcome received: " + self.url);
    self.emit('couchdb', body);
  })
}

// TODO emit vulnerability

module.exports = { "Couch": Couch
                 };
<commit_msg>Variable name fix<commit_after>// Probe all details about a CouchDB.
//

var lib = require('./lib')
  , LOG = lib.getLogger('audit_couchdb')
  , util = require('util')
  , events = require('events')
  , request = require('request')
  ;

function Couch(url) {
  var self = this;
  events.EventEmitter.call(self);

  self.log = lib.getLogger(url || 'audit_couchdb.Couch');
  self.url = url || null;
  self.proxy = null;
  self.only_dbs = null;
  self.pending = {};

  //

  self.on('couchdb', function(hello) {
    self.log.debug("Scanning databases: " + self.url);
    var all_dbs = lib.join(self.url, '/_all_dbs');
    self.request({uri:all_dbs}, function(er, resp, body) {
      if(er) throw er;
      if(resp.statusCode !== 200 || !Array.isArray(body))
        throw new Error("Bad _all_dbs from " + all_dbs + ": " + JSON.stringify(body));

      self.log.debug("Databases in " + self.url + ": " + JSON.stringify(body));
      var dbs_to_check = body.filter(function(db_name) {
        return (!self.only_dbs) || (self.only_dbs.indexOf(db_name) === -1);
      })
      self.log.debug("Databases to check in " + self.url + ": " + JSON.stringify(dbs_to_check));

      if(dbs_to_check.length === 0)
        self.emit('end');

      dbs_to_check.forEach(function(db_name) {
        self.emit('db_name', db_name);
      })
    })
  })

  self.on('db_name', function(db_name) {
    var db_url  = lib.join(self.url, encodeURIComponent(db_name))
      , sec_url = lib.join(db_url, '/_security')
      ;

    self.pending[db_url] = {};

    var data = {info:null, security:null};
    function got(key, val) {
      self.log.debug("Received " + key + " for " + db_url);
      data[key] = val;

      if(data.info && data.security) {
        self.log.debug("Received metadata and security about db: " + db_url);

        if(data.info.resp.statusCode     === 401 && data.info.body.error     === 'unauthorized'
        && data.security.resp.statusCode === 401 && data.security.body.error === 'unauthorized') {
          self.log.debug("No read permission: " + db_url);
          self.emit('database_unauthorized', db_url)
        } else if (data.info.resp.statusCode !== 200 || data.security.resp.statusCode !== 200
                   || typeof data.info.body !== 'object' || typeof data.security.body !== 'object') {
          throw new Error("Unknown db responses: " + JSON.stringify({db:db_info.data, security:security.data}));
        } else {
          self.emit('database_ok', db_url, data.info.body, data.security.body);
        }

        self.emit('database', db_url, data.info, data.security);
      }
    }

    self.log.debug("Fetching db metadata: " + db_url);
    self.request({uri:db_url}, function(er, resp, body) {
      if(er) throw er;
      //if(resp.statusCode !== 200 || typeof body !== 'object' || false)
      //  throw new Error("Bad db response from " + db_url + ": " + JSON.stringify(body));
      got('info', {resp:resp, body:body});
    })

    self.log.debug("Fetching db security data: " + sec_url);
    self.request({uri:sec_url}, function(er, resp, body) {
      if(er) throw er;
      //if(resp.statusCode !== 200 || typeof body !== 'object')
      //  throw new Error("Bad db response from " + db_url + ": " + JSON.stringify(body));
      got('security', {resp:resp, body:body});
    })
  })

  self.on('database_unauthorized', function(db_url) {
    self.finish_db(db_url);
  })

  self.on('database_ok', function(db_url, db_info, security) {
    self.log.debug("Successful db fetch: " + db_url);
    var view = [ '/_all_docs?include_docs=false'
               , 'startkey=' + encodeURIComponent(JSON.stringify("_design/"))
               , 'endkey='   + encodeURIComponent(JSON.stringify("_design0"))
               ].join("&");
    view = lib.join(db_url, view);

    self.log.debug("Scanning for design documents: " + db_url);
    self.request({uri:view}, function(er, resp, body) {
      if(er) throw er;
      if(resp.statusCode !== 200 || !("rows" in body))
        throw new Error("Bad ddoc response from " + view + ": " + JSON.stringify(body));

      self.log.debug("Design documents in " + db_url + ": " + body.rows.map(function(x) { return x.id }).join(', '));

      if(body.rows.length === 0)
        self.finish_db(db_url);

      body.rows.forEach(function(row) {
        self.emit('ddoc_id', db_url, row.id);
      })
    })
  })

  self.on('ddoc_id', function(db_url, doc_id) {
    var ddoc_url = lib.join(db_url, doc_id)
      , info_url = lib.join(ddoc_url, '/_info');

    self.pending[db_url][doc_id] = 1;

    var data = {ddoc: null, info: null};
    function got(type, value) {
      self.log.debug("Received " + type + " for " + ddoc_url);
      data[type] = value;

      if(data.ddoc && data.info) {
        self.log.debug("Received both contents and info about ddoc: " + ddoc_url);
        self.emit('ddoc', db_url, data.ddoc, data.info);
      }
    }

    self.log.debug("Fetching ddoc info: " + info_url);
    self.request({uri:info_url}, function(er, resp, body) {
      if(er) throw er;
      if(resp.statusCode !== 200 || typeof body !== 'object')
        throw new Error("Bad info response for " + info_url + ": " + JSON.stringify(body));
      got('info', body);
    })

    self.log.debug("Fetching ddoc contents: " + ddoc_url);
    self.request({uri:ddoc_url}, function(er, resp, body) {
      if(er) throw er;
      if(resp.statusCode !== 200 || typeof body !== 'object')
        throw new Error("Bad ddoc response for " + ddoc_url + ": " + JSON.stringify(body));
      got('ddoc', body);
    })
  })

  self.on('ddoc', function(db_url, ddoc, info) {
    self.finish_ddoc(db_url, ddoc._id);
  })

} // Couch
util.inherits(Couch, events.EventEmitter);

Couch.prototype.finish_ddoc = function(db_url, id) {
  var self = this;

  delete self.pending[db_url][id];
  if(Object.keys(self.pending[db_url]).length === 0) {
    self.log.debug("All design docs complete: " + db_url);
    self.finish_db(db_url);
  }
}

Couch.prototype.finish_db = function(db_url) {
  var self = this;

  delete self.pending[db_url];
  if(Object.keys(self.pending).length === 0) {
    self.log.debug("All databases complete");
    self.emit('end');
  }
}

Couch.prototype.request = function request_wrapper(opts, callback) {
  var self = this;

  function json_body(er, resp, body) {
    if(!er) {
      try      { body = JSON.parse(body) }
      catch(e) { er = e }
    }

    return callback && callback.apply(this, [er, resp, body]);
  }

  opts.proxy  = opts.proxy  || self.proxy;
  opts.client = opts.client || self.client;
  opts.followRedirect = false;

  opts.headers = opts.headers || {};
  opts.headers.accept = opts.headers.accept || 'application/json';
  //opts.headers.Connection = opts.headers.Connection || 'keep-alive';

  if(opts.method && opts.method !== "GET" && opts.method !== "HEAD")
    opts.headers['content-type'] = 'application/json';

  return request.apply(self, [opts, json_body]);
}

Couch.prototype.start = function() {
  var self = this;

  if(!self.url)
    throw new Error("url required");

  self.log.debug("Pinging: " + self.url);
  self.request({uri:self.url}, function(er, resp, body) {
    if(er) throw er;
    if(resp.statusCode !== 200 || body.couchdb !== "Welcome")
      throw new Error("Bad welcome from " + self.url + ": " + JSON.stringify(body));

    self.client = resp.client;
    self.client = null; // Reusing the client isn't working right now.

    self.log.debug("Good welcome received: " + self.url);
    self.emit('couchdb', body);
  })
}

// TODO emit vulnerability

module.exports = { "Couch": Couch
                 };
<|endoftext|>


<commit_before>var buster = require("buster");
var busterEventedLogger = require("buster-evented-logger")
var assert = buster.assert;
var refute = buster.refute;
var busterCli = require("../lib/buster-cli");
var cliHelper = require("../lib/test-helper");

buster.testCase("buster-cli", {
    setUp: function () {
        this.cli = Object.create(busterCli);
    },

    "should have logger": function () {
        assert(busterEventedLogger.isPrototypeOf(this.cli.logger));
    },

    "should run without callback": function () {
        cliHelper.mockLogger(this);
        this.cli.run(["--help"]);
        assert(true);
    },

    "generic help output": {
        setUp: function () {
            cliHelper.mockLogger(this);
        },

        "should include mission statement": function (done) {
            var self = this;
            var statement = "A small CLI that only lives in the test suite.";
            this.cli.missionStatement = statement;
            this.cli.run(["--help"], function () {
                assert.match(self.stdout, statement);
                done();
            });
        },

        "should include description": function (done) {
            var self = this;
            var desc = "How about that.";
            this.cli.description = desc;
            this.cli.run(["--help"], function () {
                assert.match(self.stdout, desc);
                done();
            });
        },

        "should list help output for all options, including --help": function (done) {
            var self = this;
            var portOpt = this.cli.opt("-p", "--port", "Help text is here.");

            this.cli.run(["--help"], function () {
                assert.match(self.stdout, "-h/--help: Shows this message.");
                assert.match(self.stderr, "-h/--help: Help text is here.");
                done();
            });
        }
    },

    "option": {
        setUp: function () {
            this.port = this.cli.opt("-p", "--port", "Help text is here.", {});
        },

        "should be addressable by short key": function (done) {
            var self = this;
            this.cli.run(["-p"], function () {
                assert(self.port.isSet);
                done();
            });
        },

        "should be addressable by long key": function (done) {
            var self = this;
            this.cli.run(["--port"], function () {
                assert(self.port.isSet);
                done();
            });
        }
    },

    "test calls 'loadOptions' once if present": function (done) {
        var self = this;
        cliHelper.mockLogger(this);
        this.cli.loadOptions = this.spy();

        this.cli.run(["--help"], function () {
            self.cli.run(["--help"], function () {
                assert(self.cli.loadOptions.calledOnce);
                done();
            });
        });
    },

    "help topics": {
        setUp: function () {
            cliHelper.mockLogger(this);
            this.cli.helpTopics = {
                "topic": "This is the text for the topic.",
                "other": "Another topic"
            }
        },

        "should be listed with the description of --help in the --help output": function (done) {
            var self = this;
            this.cli.run(["--help"], function () {
                assert.match(self.stdout, "See also --help [topic,other].");
                done();
            });
        },

        "should print topic help with --help sometopic": function (done) {
            var self = this;
            this.cli.run(["--help", "topic"], function () {
                assert.equals(self.stdout, "This is the text for the topic.\n");
                done();
            });
        },

        "should print error message with --help noneexistingtopic": function (done) {
            var self = this;
            this.cli.run(["--help", "doesnotexist"], function () {
                assert.equals(self.stderr, "No such help topic 'doesnotexist'. "
                              + "Try without a specific help topic, or one of: topic,other.\n");
                done();
            });
        },

        "should print topic unwrapped when just one topic": function (done) {
            var self = this;

            this.cli.helpTopics = {
                "topic": "This is the text for the topic."
            }

            this.cli.run(["--help"], function () {
                assert.match(self.stdout, "See also --help topic.");
                done();
            });
        },

        "should not print any topic information when empty topics object is specified": function (done) {
            this.cli.helpTopics = {};
            var self = this;
            this.cli.run(["--help"], function () {
                refute.match(self.stdout, "See also --help [].");
                done();
            });
        }
    },

    "option restricted to list of values": {
        setUp: function () {
            cliHelper.mockLogger(this);
            this.aaaOpt = this.cli.opt("-a", "--aaa", "Aaaaa!", {
                values: ["foo", "bar", "baz"]
            });
        },

        "should list available options in help output": function (done) {
            var self = this;
            this.cli.run(["--help"], function () {
                assert.match(self.stdout, "One of foo, bar, baz.");
                done();
            });
        },

        "should get value set when value passed to it": function (done) {
            var self = this;
            this.cli.run(["-a", "bar"], function () {
                assert.equals(self.aaaOpt.value(), "bar");
                done();
            });
        },

        "should error when getting a value not in the list": function (done) {
            var self = this;
            this.cli.run(["-a", "lolcat"], function () {
                // The actual error message comes from buster-args.
                // TODO: Find a better way to test the error msg here.
                refute.equals(self.stderr, "");
                done();
            });
        }
    },

    "option with default value": {
        setUp: function () {
            cliHelper.mockLogger(this);
            this.aaaOpt = this.cli.opt("-f", "--ffff", "Fffffuuu", {
                defaultValue: "DRM"
            });
        },

        "should print default value in help text": function (done) {
            var self = this;
            this.cli.run(["--help"], function () {
                assert.match(self.stdout, "Default is DRM.");
                done();
            });
        },

        "should have default value": function (done) {
            var self = this;
            this.cli.run([], function () {
                assert.equals(self.aaaOpt.value(), "DRM");
                done();
            });
        },

        "should provide overridden value": function (done) {
            var self = this;
            this.cli.run(["-f", "gaming consoles"], function () {
                assert.equals(self.aaaOpt.value(), "gaming consoles");
                done();
            });
        },

        "// should fail with no value": function (done) {
            // Not failing. Probably a flaw in buster-args.
            var self = this;
            this.cli.run(["-f"], function () {
                refute.equals(self.stderr, "");
                done();
            });
        }
    },

    "option with value": {
        setUp: function () {
            cliHelper.mockLogger(this);
            this.someOpt = this.cli.opt("-s", "--ss", "A creeper.", {
                hasValue: true
            });
        },

        "should get value assigned": function (done) {
            var self = this;
            this.cli.run(["-s", "ssssssBOOOOOM!"], function () {
                assert.equals(self.someOpt.value(), "ssssssBOOOOOM!");
                done();
            });
        }
    },

    "option with validator": {
        setUp: function () {
            cliHelper.mockLogger(this);
            this.anOpt = this.cli.opt("-c", "--character", "The character.", {
                validators: {"required": "Here's a custom error msg."}
            });
        },

        "should perform validation": function (done) {
            var self = this;
            this.cli.run([], function () {
                assert.match(self.stderr, "Here's a custom error msg.");
                done();
            });
        }
    },

    "operand": {
        setUp: function () {
            cliHelper.mockLogger(this);
            this.fooOpd = this.cli.opd("Foo", "Does a foo.");
        },

        "should be listed in --help output": function (done) {
            var self = this;
            this.cli.run(["--help"], function () {
                assert.match(self.stdout, /Foo +: Does a foo/);
                done();
            });
        },

        "should get value assigned": function (done) {
            var self = this;
            this.cli.run(["some value"], function () {
                assert.equals(self.fooOpd.value(), "some value");
                done();
            });
        }
    },

    "should call onRun when there are no errors": function (done) {
        this.cli.onRun = function () {
            assert(true);
            done();
        };

        this.cli.run([], function () {});
    },

    "should not call onRun when there are errors": function (done) {
        var self = this;
        cliHelper.mockLogger(this);
        this.cli.onRun = this.spy();
        var someOpt = this.cli.opt("-a", "--aa", "Aaaaa");
        someOpt.addValidator(function () { return "An error."; });
        this.cli.run(["-a"], function () {
            refute(self.cli.onRun.called);
            done();
        });
    },

    "panicing": {
        setUp: function () {
            cliHelper.mockLogger(this);
            this.stub(process, "exit");
        },

        "should logg to stderr": function (done) {
            var self = this;

            this.cli.onRun = function () {
                this.err("Uh-oh! Trouble!");
            };

            this.cli.run([], function () {
                assert.equals(self.stdout, "");
                assert.match(self.stderr, "Uh-oh! Trouble!");
                done();
            });
        }
    }
});
<commit_msg>Fix spell failure test failure<commit_after>var buster = require("buster");
var busterEventedLogger = require("buster-evented-logger")
var assert = buster.assert;
var refute = buster.refute;
var busterCli = require("../lib/buster-cli");
var cliHelper = require("../lib/test-helper");

buster.testCase("buster-cli", {
    setUp: function () {
        this.cli = Object.create(busterCli);
    },

    "should have logger": function () {
        assert(busterEventedLogger.isPrototypeOf(this.cli.logger));
    },

    "should run without callback": function () {
        cliHelper.mockLogger(this);
        this.cli.run(["--help"]);
        assert(true);
    },

    "generic help output": {
        setUp: function () {
            cliHelper.mockLogger(this);
        },

        "should include mission statement": function (done) {
            var self = this;
            var statement = "A small CLI that only lives in the test suite.";
            this.cli.missionStatement = statement;
            this.cli.run(["--help"], function () {
                assert.match(self.stdout, statement);
                done();
            });
        },

        "should include description": function (done) {
            var self = this;
            var desc = "How about that.";
            this.cli.description = desc;
            this.cli.run(["--help"], function () {
                assert.match(self.stdout, desc);
                done();
            });
        },

        "should list help output for all options, including --help": function (done) {
            var self = this;
            var portOpt = this.cli.opt("-p", "--port", "Help text is here.");

            this.cli.run(["--help"], function () {
                assert.match(self.stdout, "-h/--help: Show this message.");
                assert.match(self.stdout, "-p/--port: Help text is here.");
                done();
            });
        }
    },

    "option": {
        setUp: function () {
            this.port = this.cli.opt("-p", "--port", "Help text is here.", {});
        },

        "should be addressable by short key": function (done) {
            var self = this;
            this.cli.run(["-p"], function () {
                assert(self.port.isSet);
                done();
            });
        },

        "should be addressable by long key": function (done) {
            var self = this;
            this.cli.run(["--port"], function () {
                assert(self.port.isSet);
                done();
            });
        }
    },

    "test calls 'loadOptions' once if present": function (done) {
        var self = this;
        cliHelper.mockLogger(this);
        this.cli.loadOptions = this.spy();

        this.cli.run(["--help"], function () {
            self.cli.run(["--help"], function () {
                assert(self.cli.loadOptions.calledOnce);
                done();
            });
        });
    },

    "help topics": {
        setUp: function () {
            cliHelper.mockLogger(this);
            this.cli.helpTopics = {
                "topic": "This is the text for the topic.",
                "other": "Another topic"
            }
        },

        "should be listed with the description of --help in the --help output": function (done) {
            var self = this;
            this.cli.run(["--help"], function () {
                assert.match(self.stdout, "See also --help [topic,other].");
                done();
            });
        },

        "should print topic help with --help sometopic": function (done) {
            var self = this;
            this.cli.run(["--help", "topic"], function () {
                assert.equals(self.stdout, "This is the text for the topic.\n");
                done();
            });
        },

        "should print error message with --help noneexistingtopic": function (done) {
            var self = this;
            this.cli.run(["--help", "doesnotexist"], function () {
                assert.equals(self.stderr, "No such help topic 'doesnotexist'. "
                              + "Try without a specific help topic, or one of: topic,other.\n");
                done();
            });
        },

        "should print topic unwrapped when just one topic": function (done) {
            var self = this;

            this.cli.helpTopics = {
                "topic": "This is the text for the topic."
            }

            this.cli.run(["--help"], function () {
                assert.match(self.stdout, "See also --help topic.");
                done();
            });
        },

        "should not print any topic information when empty topics object is specified": function (done) {
            this.cli.helpTopics = {};
            var self = this;
            this.cli.run(["--help"], function () {
                refute.match(self.stdout, "See also --help [].");
                done();
            });
        }
    },

    "option restricted to list of values": {
        setUp: function () {
            cliHelper.mockLogger(this);
            this.aaaOpt = this.cli.opt("-a", "--aaa", "Aaaaa!", {
                values: ["foo", "bar", "baz"]
            });
        },

        "should list available options in help output": function (done) {
            var self = this;
            this.cli.run(["--help"], function () {
                assert.match(self.stdout, "One of foo, bar, baz.");
                done();
            });
        },

        "should get value set when value passed to it": function (done) {
            var self = this;
            this.cli.run(["-a", "bar"], function () {
                assert.equals(self.aaaOpt.value(), "bar");
                done();
            });
        },

        "should error when getting a value not in the list": function (done) {
            var self = this;
            this.cli.run(["-a", "lolcat"], function () {
                // The actual error message comes from buster-args.
                // TODO: Find a better way to test the error msg here.
                refute.equals(self.stderr, "");
                done();
            });
        }
    },

    "option with default value": {
        setUp: function () {
            cliHelper.mockLogger(this);
            this.aaaOpt = this.cli.opt("-f", "--ffff", "Fffffuuu", {
                defaultValue: "DRM"
            });
        },

        "should print default value in help text": function (done) {
            var self = this;
            this.cli.run(["--help"], function () {
                assert.match(self.stdout, "Default is DRM.");
                done();
            });
        },

        "should have default value": function (done) {
            var self = this;
            this.cli.run([], function () {
                assert.equals(self.aaaOpt.value(), "DRM");
                done();
            });
        },

        "should provide overridden value": function (done) {
            var self = this;
            this.cli.run(["-f", "gaming consoles"], function () {
                assert.equals(self.aaaOpt.value(), "gaming consoles");
                done();
            });
        },

        "// should fail with no value": function (done) {
            // Not failing. Probably a flaw in buster-args.
            var self = this;
            this.cli.run(["-f"], function () {
                refute.equals(self.stderr, "");
                done();
            });
        }
    },

    "option with value": {
        setUp: function () {
            cliHelper.mockLogger(this);
            this.someOpt = this.cli.opt("-s", "--ss", "A creeper.", {
                hasValue: true
            });
        },

        "should get value assigned": function (done) {
            var self = this;
            this.cli.run(["-s", "ssssssBOOOOOM!"], function () {
                assert.equals(self.someOpt.value(), "ssssssBOOOOOM!");
                done();
            });
        }
    },

    "option with validator": {
        setUp: function () {
            cliHelper.mockLogger(this);
            this.anOpt = this.cli.opt("-c", "--character", "The character.", {
                validators: {"required": "Here's a custom error msg."}
            });
        },

        "should perform validation": function (done) {
            var self = this;
            this.cli.run([], function () {
                assert.match(self.stderr, "Here's a custom error msg.");
                done();
            });
        }
    },

    "operand": {
        setUp: function () {
            cliHelper.mockLogger(this);
            this.fooOpd = this.cli.opd("Foo", "Does a foo.");
        },

        "should be listed in --help output": function (done) {
            var self = this;
            this.cli.run(["--help"], function () {
                assert.match(self.stdout, /Foo +: Does a foo/);
                done();
            });
        },

        "should get value assigned": function (done) {
            var self = this;
            this.cli.run(["some value"], function () {
                assert.equals(self.fooOpd.value(), "some value");
                done();
            });
        }
    },

    "should call onRun when there are no errors": function (done) {
        this.cli.onRun = function () {
            assert(true);
            done();
        };

        this.cli.run([], function () {});
    },

    "should not call onRun when there are errors": function (done) {
        var self = this;
        cliHelper.mockLogger(this);
        this.cli.onRun = this.spy();
        var someOpt = this.cli.opt("-a", "--aa", "Aaaaa");
        someOpt.addValidator(function () { return "An error."; });
        this.cli.run(["-a"], function () {
            refute(self.cli.onRun.called);
            done();
        });
    },

    "panicing": {
        setUp: function () {
            cliHelper.mockLogger(this);
            this.stub(process, "exit");
        },

        "should logg to stderr": function (done) {
            var self = this;

            this.cli.onRun = function () {
                this.err("Uh-oh! Trouble!");
            };

            this.cli.run([], function () {
                assert.equals(self.stdout, "");
                assert.match(self.stderr, "Uh-oh! Trouble!");
                done();
            });
        }
    }
});
<|endoftext|>


<commit_before>var Future = require('fibers/future');
var files = require('./files.js');
var path = require('path');
var fs = require('fs');
var unipackage = require('./unipackage.js');
var fiberHelpers = require('./fiber-helpers.js');
var Fiber = require('fibers');
var request = require('request');
var _ = require('underscore');

// a bit of a hack
var _meteor;
var getMeteor = function (context) {
  if (! _meteor) {
    _meteor = unipackage.load({
      library: context.library,
      packages: [ 'livedata', 'mongo-livedata' ],
      release: context.releaseVersion
    }).meteor.Meteor;
  }

  return _meteor;
};

var _galaxy;
var getGalaxy = function (context) {
  if (! _galaxy) {
    var Meteor = getMeteor(context);
    if (!context.galaxy) {
      process.stderr.write("Could not find a deploy endpoint. " +
                           "You can set the GALAXY environment variable, " +
                           "or configure your site's DNS to resolve to " +
                           "your Galaxy's proxy.\n");
      process.exit(1);
    }

    _galaxy = Meteor.connect(context.galaxy.url);
  }

  return _galaxy;
};

var exitWithError = function (error, messages) {
  messages = messages || {};
  var msg = messages[error.error];
  if (msg)
    process.stderr.write(msg + "\n");
  else if (error.message)
    process.stderr.write("Denied: " + error.message + "\n");

  process.exit(1);
};

// XXX copied from galaxy/tool/galaxy.js
var prettyCall = function (galaxy, name, args, messages) {
  try {
    var ret = galaxy.apply(name, args);
  } catch (e) {
    exitWithError(e, messages);
  }
  return ret;
};

var prettySub = function (galaxy, name, args, messages) {
  var onError = function (e) {
    exitWithError(e, messages);
  };

  try {
    var ret = galaxy._subscribeAndWait(name, args, {onLateError: onError});
  } catch (e) {
    onError(e);
  }
  return ret;
};

exports.discoverGalaxy = function (app) {
  app = app + ":" + (process.env.DISCOVERY_PORT || 443);
  var url = "https://" + app + "/_GALAXY_";
  var fut = new Future();

  if (process.env.GALAXY)
    return process.env.GALAXY;

  // At some point we may want to send a version in the request so that galaxy
  // can respond differently to different versions of meteor.
  request({
    url: url,
    json: true,
    followRedirect: false
  }, function (err, resp, body) {
    if (! err &&
        resp.statusCode === 200 &&
        body &&
        _.has(body, "galaxyDiscoveryVersion") &&
        _.has(body, "galaxyUrl") &&
        (body.galaxyDiscoveryVersion === "galaxy-discovery-pre0")) {
      fut.return(body.galaxyUrl);
    } else {
      fut.return(null);
    }
  });
  return fut.wait();
};

exports.deleteApp = function (context) {
  throw new Error("Not implemented");
};

// options:
// - context
// - app
// - appDir
// - settings
// - bundleOptions
// - starball
// XXX refactor this to separate the "maybe bundle" part from "actually deploy"
//     so we can be careful to not rely on any of the app dir context when
//     in --star mode.
exports.deploy = function (options) {
  var galaxy = getGalaxy(options.context);
  var Meteor = getMeteor(options.context);

  var tmpdir = files.mkdtemp('deploy');
  var buildDir = path.join(tmpdir, 'build');
  var topLevelDirName = path.basename(options.appDir);
  var bundlePath = path.join(buildDir, topLevelDirName);
  var bundler = require('./bundler.js');
  var starball;
  if (!options.starball) {
    process.stdout.write('Deploying ' + options.app + '. Bundling...\n');
    var bundleResult = bundler.bundle(options.appDir, bundlePath,
                                      options.bundleOptions);
    if (bundleResult.errors) {
      process.stdout.write("\n\nErrors prevented deploying:\n");
      process.stdout.write(bundleResult.errors.formatMessages());
      process.exit(1);
    }

    // S3 (which is what's likely on the other end our upload) requires
    // a content-length header for HTTP PUT uploads. That means that we
    // have to actually tgz up the bundle before we can start the upload
    // rather than streaming it. S3 has an alternate API for doing
    // chunked uploads, but (a) it has a minimum chunk size of 5 MB, so
    // it doesn't help us much (many/most stars will be smaller than
    // that), and (b) it's nonstandard, so we'd have to bake in S3's
    // specific scheme. Doesn't seem worthwhile for now, so just tar to
    // a temporary directory. If stars get radically bigger then it
    // might be worthwhile to tar to memory and spill to S3 every 5MB.
    starball = path.join(tmpdir, topLevelDirName + ".tar.gz");
    files.createTarball(bundlePath, starball);
  } else {
    starball = options.starball;
  }
  process.stdout.write('Uploading...\n');

  var created = true;
  var appConfig = {
      METEOR_SETTINGS: options.settings
  };
  try {
    galaxy.call('createApp', options.app, appConfig);
  } catch (e) {
    if (e instanceof Meteor.Error && e.error === 'already-exists') {
      // Cool, it already exists. No problem. Just set the settings if they were
      // passed. We explicitly check for undefined because we want to allow you
      // to unset settings by passing an empty file.
      if (options.settings !== undefined) {
        try {
          galaxy.call('updateAppConfiguration', options.app, appConfig);
        } catch (e) {
          exitWithError(e);
        }
      }
      created = false;
    } else {
      exitWithError(e);
    }
  }

  // Get the upload information from Galaxy. It's a surprise if this
  // fails (we already know the app exists.)
  var info = prettyCall(galaxy, 'beginUploadStar', [options.app]);

  // Upload
  // XXX copied from galaxy/tool/galaxy.js
  var fileSize = fs.statSync(starball).size;
  var fileStream = fs.createReadStream(starball);
  var future = new Future;
  var req = request.put({
    url: info.put,
    headers: { 'content-length': fileSize,
               'content-type': 'application/octet-stream' },
    strictSSL: true
  }, function (error, response, body) {
    if (error || ((response.statusCode !== 200)
                  && (response.statusCode !== 201))) {
      if (error && error.message)
        process.stderr.write("Upload failed: " + error.message + "\n");
      else
        process.stderr.write("Upload failed" +
                             (response.statusCode ?
                              " (" + response.statusCode + ")\n" : "\n"));
      process.exit(1);
    }
    future.return();
  });

  fileStream.pipe(req);
  future.wait();

  var result = prettyCall(galaxy, 'completeUploadStar', [info.id], {
    'no-such-upload': 'Upload request expired. Try again.'
  });

  if (created)
    process.stderr.write(options.app + ": created app\n");

  process.stderr.write(options.app + ": " +
                       "pushed revision " + result.serial + "\n");
  // Close the connection to Galaxy (otherwise Node will continue running).
  galaxy.close();
};

// options:
// - context
// - app
// - streaming (BOOL)
exports.logs = function (options) {
  var logReaderURL;
  if (options.context.galaxy.adminBaseUrl) {
    logReaderURL = options.context.galaxy.adminBaseUrl + "log-reader";
  } else {
    var galaxy = getGalaxy(options.context);
    logReaderURL = prettyCall(galaxy, "getLogReaderURL", [], {
      'no-log-reader': "Can't find log reader service"
    });
    galaxy.close();
  }

  var lastLogId = null;
  var logReader = getMeteor(options.context).connect(logReaderURL);
  var Log = unipackage.load({
    library: options.context.library,
    packages: [ 'logging' ],
    release: options.context.releaseVersion
  }).logging.Log;

  var ok = logReader.registerStore('logs', {
    update: function (msg) {
      // Ignore all messages but 'changed'
      if (msg.msg !== 'changed')
        return;
      var obj = msg.fields.obj;
      lastLogId = msg.fields.id;
      obj = Log.parse(obj);
      obj && console.log(Log.format(obj, {color: true}));
    }
  });

  if (!ok)
    throw new Error("Can't listen to messages on the logs collection");

  var logsSubscription = null;
  // In case of reconnect recover the state so user sees only new logs
  logReader.onReconnect = function () {
    logsSubscription && logsSubscription.stop();
    var opts = { streaming: options.streaming };
    if (lastLogId)
      opts.resumeAfterId = lastLogId;
    logsSubscription = logReader.subscribe("logsForApp", options.app, opts);
  };
  logsSubscription = prettySub(logReader, "logsForApp",
                               [options.app, {streaming: options.streaming}],
                               {"no-such-app": "No such app: " + options.app});

  // if streaming is needed there is no point in closing connection
  if (!options.streaming) {
    // Close connections to Galaxy and log-reader
    // (otherwise Node will continue running).
    logReader.close();
  }
};

// options:
// - context
// - app
exports.temporaryMongoUrl = function (options) {
  var galaxy = getGalaxy(options.context);
  var url = galaxy.call('getTemporaryMongoUrl', options.app);
  galaxy.close();
  return url;
};
<commit_msg>Followup adding comment to d5a1295 fix.<commit_after>var Future = require('fibers/future');
var files = require('./files.js');
var path = require('path');
var fs = require('fs');
var unipackage = require('./unipackage.js');
var fiberHelpers = require('./fiber-helpers.js');
var Fiber = require('fibers');
var request = require('request');
var _ = require('underscore');

// a bit of a hack
var _meteor;
var getMeteor = function (context) {
  if (! _meteor) {
    _meteor = unipackage.load({
      library: context.library,
      packages: [ 'livedata', 'mongo-livedata' ],
      release: context.releaseVersion
    }).meteor.Meteor;
  }

  return _meteor;
};

var _galaxy;
var getGalaxy = function (context) {
  if (! _galaxy) {
    var Meteor = getMeteor(context);
    if (!context.galaxy) {
      process.stderr.write("Could not find a deploy endpoint. " +
                           "You can set the GALAXY environment variable, " +
                           "or configure your site's DNS to resolve to " +
                           "your Galaxy's proxy.\n");
      process.exit(1);
    }

    _galaxy = Meteor.connect(context.galaxy.url);
  }

  return _galaxy;
};

var exitWithError = function (error, messages) {
  messages = messages || {};
  var msg = messages[error.error];
  if (msg)
    process.stderr.write(msg + "\n");
  else if (error.message)
    process.stderr.write("Denied: " + error.message + "\n");

  process.exit(1);
};

// XXX copied from galaxy/tool/galaxy.js
var prettyCall = function (galaxy, name, args, messages) {
  try {
    var ret = galaxy.apply(name, args);
  } catch (e) {
    exitWithError(e, messages);
  }
  return ret;
};

var prettySub = function (galaxy, name, args, messages) {
  var onError = function (e) {
    exitWithError(e, messages);
  };

  try {
    var ret = galaxy._subscribeAndWait(name, args, {onLateError: onError});
  } catch (e) {
    onError(e);
  }
  return ret;
};

exports.discoverGalaxy = function (app) {
  app = app + ":" + (process.env.DISCOVERY_PORT || 443);
  var url = "https://" + app + "/_GALAXY_";
  var fut = new Future();

  if (process.env.GALAXY)
    return process.env.GALAXY;

  // At some point we may want to send a version in the request so that galaxy
  // can respond differently to different versions of meteor.
  request({
    url: url,
    json: true,
    // We don't want to be confused by, eg, a non-Galaxy-hosted site which
    // redirects to a Galaxy-hosted site.
    followRedirect: false
  }, function (err, resp, body) {
    if (! err &&
        resp.statusCode === 200 &&
        body &&
        _.has(body, "galaxyDiscoveryVersion") &&
        _.has(body, "galaxyUrl") &&
        (body.galaxyDiscoveryVersion === "galaxy-discovery-pre0")) {
      fut.return(body.galaxyUrl);
    } else {
      fut.return(null);
    }
  });
  return fut.wait();
};

exports.deleteApp = function (context) {
  throw new Error("Not implemented");
};

// options:
// - context
// - app
// - appDir
// - settings
// - bundleOptions
// - starball
// XXX refactor this to separate the "maybe bundle" part from "actually deploy"
//     so we can be careful to not rely on any of the app dir context when
//     in --star mode.
exports.deploy = function (options) {
  var galaxy = getGalaxy(options.context);
  var Meteor = getMeteor(options.context);

  var tmpdir = files.mkdtemp('deploy');
  var buildDir = path.join(tmpdir, 'build');
  var topLevelDirName = path.basename(options.appDir);
  var bundlePath = path.join(buildDir, topLevelDirName);
  var bundler = require('./bundler.js');
  var starball;
  if (!options.starball) {
    process.stdout.write('Deploying ' + options.app + '. Bundling...\n');
    var bundleResult = bundler.bundle(options.appDir, bundlePath,
                                      options.bundleOptions);
    if (bundleResult.errors) {
      process.stdout.write("\n\nErrors prevented deploying:\n");
      process.stdout.write(bundleResult.errors.formatMessages());
      process.exit(1);
    }

    // S3 (which is what's likely on the other end our upload) requires
    // a content-length header for HTTP PUT uploads. That means that we
    // have to actually tgz up the bundle before we can start the upload
    // rather than streaming it. S3 has an alternate API for doing
    // chunked uploads, but (a) it has a minimum chunk size of 5 MB, so
    // it doesn't help us much (many/most stars will be smaller than
    // that), and (b) it's nonstandard, so we'd have to bake in S3's
    // specific scheme. Doesn't seem worthwhile for now, so just tar to
    // a temporary directory. If stars get radically bigger then it
    // might be worthwhile to tar to memory and spill to S3 every 5MB.
    starball = path.join(tmpdir, topLevelDirName + ".tar.gz");
    files.createTarball(bundlePath, starball);
  } else {
    starball = options.starball;
  }
  process.stdout.write('Uploading...\n');

  var created = true;
  var appConfig = {
      METEOR_SETTINGS: options.settings
  };
  try {
    galaxy.call('createApp', options.app, appConfig);
  } catch (e) {
    if (e instanceof Meteor.Error && e.error === 'already-exists') {
      // Cool, it already exists. No problem. Just set the settings if they were
      // passed. We explicitly check for undefined because we want to allow you
      // to unset settings by passing an empty file.
      if (options.settings !== undefined) {
        try {
          galaxy.call('updateAppConfiguration', options.app, appConfig);
        } catch (e) {
          exitWithError(e);
        }
      }
      created = false;
    } else {
      exitWithError(e);
    }
  }

  // Get the upload information from Galaxy. It's a surprise if this
  // fails (we already know the app exists.)
  var info = prettyCall(galaxy, 'beginUploadStar', [options.app]);

  // Upload
  // XXX copied from galaxy/tool/galaxy.js
  var fileSize = fs.statSync(starball).size;
  var fileStream = fs.createReadStream(starball);
  var future = new Future;
  var req = request.put({
    url: info.put,
    headers: { 'content-length': fileSize,
               'content-type': 'application/octet-stream' },
    strictSSL: true
  }, function (error, response, body) {
    if (error || ((response.statusCode !== 200)
                  && (response.statusCode !== 201))) {
      if (error && error.message)
        process.stderr.write("Upload failed: " + error.message + "\n");
      else
        process.stderr.write("Upload failed" +
                             (response.statusCode ?
                              " (" + response.statusCode + ")\n" : "\n"));
      process.exit(1);
    }
    future.return();
  });

  fileStream.pipe(req);
  future.wait();

  var result = prettyCall(galaxy, 'completeUploadStar', [info.id], {
    'no-such-upload': 'Upload request expired. Try again.'
  });

  if (created)
    process.stderr.write(options.app + ": created app\n");

  process.stderr.write(options.app + ": " +
                       "pushed revision " + result.serial + "\n");
  // Close the connection to Galaxy (otherwise Node will continue running).
  galaxy.close();
};

// options:
// - context
// - app
// - streaming (BOOL)
exports.logs = function (options) {
  var logReaderURL;
  if (options.context.galaxy.adminBaseUrl) {
    logReaderURL = options.context.galaxy.adminBaseUrl + "log-reader";
  } else {
    var galaxy = getGalaxy(options.context);
    logReaderURL = prettyCall(galaxy, "getLogReaderURL", [], {
      'no-log-reader': "Can't find log reader service"
    });
    galaxy.close();
  }

  var lastLogId = null;
  var logReader = getMeteor(options.context).connect(logReaderURL);
  var Log = unipackage.load({
    library: options.context.library,
    packages: [ 'logging' ],
    release: options.context.releaseVersion
  }).logging.Log;

  var ok = logReader.registerStore('logs', {
    update: function (msg) {
      // Ignore all messages but 'changed'
      if (msg.msg !== 'changed')
        return;
      var obj = msg.fields.obj;
      lastLogId = msg.fields.id;
      obj = Log.parse(obj);
      obj && console.log(Log.format(obj, {color: true}));
    }
  });

  if (!ok)
    throw new Error("Can't listen to messages on the logs collection");

  var logsSubscription = null;
  // In case of reconnect recover the state so user sees only new logs
  logReader.onReconnect = function () {
    logsSubscription && logsSubscription.stop();
    var opts = { streaming: options.streaming };
    if (lastLogId)
      opts.resumeAfterId = lastLogId;
    logsSubscription = logReader.subscribe("logsForApp", options.app, opts);
  };
  logsSubscription = prettySub(logReader, "logsForApp",
                               [options.app, {streaming: options.streaming}],
                               {"no-such-app": "No such app: " + options.app});

  // if streaming is needed there is no point in closing connection
  if (!options.streaming) {
    // Close connections to Galaxy and log-reader
    // (otherwise Node will continue running).
    logReader.close();
  }
};

// options:
// - context
// - app
exports.temporaryMongoUrl = function (options) {
  var galaxy = getGalaxy(options.context);
  var url = galaxy.call('getTemporaryMongoUrl', options.app);
  galaxy.close();
  return url;
};
<|endoftext|>


<commit_before>/**
 * XadillaX created at 2015-03-11 13:53:05
 *
 * Copyright (c) 2015 Huaban.com, all rights
 * reserved
 */
var should = require("should");
var async = require("async");
var illyria = require("illyria");
var illyria2 = require("../");

describe("client protocol", function() {
    var SERVER_PORT = 2399;
    var server, client;

    function init(callback) {
        async.waterfall([
            function(callback) {
                if(server) {
                    server.close(callback);
                } else callback();
            }
        ], function() {
            server = illyria.createServer({
                port: SERVER_PORT
            });
            callback();
        });
    }

    function startup(callback) {
        server.listen(function() {
            client = illyria2.createClient("127.0.0.1", SERVER_PORT, {
                runTimeout: 1000,
                retryInterval: 1000,
                reconnect: true
            });

            client.connect(function() {
                callback();
            });
        });
    }

    describe("match old server", function() {
        before(function(done) {
            init(function() {
                server.expose({
                    name: "test",
                    methods: {
                        echo: function(req, resp) {
                            req.params().should.be.eql("illyria");
                            resp.send("illyria");
                        },

                        json: function(req, resp) {
                            req.param("json").should.be.eql(true);
                            req.params().should.be.eql({ json: true });
                            resp.send(req.params());
                        },

                        error: function(req, resp) {
                            resp.json({ err: "test error" });
                        },

                        cut: function(req/**, resp*/) {
                            req.socket.end();
                            req.socket.destroy();
                        },

                        eecho: function(req, resp) {
                            resp.send(req.params());
                        }
                    }
                });

                startup(done);
            });
        });

        it("should received \"echo\"", function(done) {
            client.send("test", "echo", "illyria", function(err, data) {
                if(err) err.should.be.empty;
                data.should.be.eql("illyria");
                done();
            });
        });

        it("should received { json: true }", function(done) {
            client.send("test", "json", { json: true }, function(err, data) {
                if(err) err.should.be.empy;
                data.should.be.eql({ json: true });
                done();
            });
        });

        it("should received error", function(done) {
            client.send("test", "error", {}, function(err) {
                (err instanceof Error).should.be.eql(true);
                err.message.should.be.eql("test error");
                done();
            });
        });

        it("should echo the whole body", function(done) {
            var _data = "";
            for(var i = 0; i < 1000000; i++) {
                _data += "a";
            }

            client.send("test", "eecho", _data, function(err, data) {
                if(err) err.should.be.empy;
                data.should.be.eql(_data);
                done();
            });
        });

        it("should reconnect and ok", function(done) {
            client.send("test", "cut", {}, function(err) {
                err.message.indexOf("Timeout").should.not.be.eql(-1);

                setTimeout(function() {
                    client.send("test", "json", { json: true }, function(err, data) {
                        if(err) err.should.be.empy;
                        data.should.be.eql({ json: true });
                        done();
                    });
                }, 500);
            });
        });

        it("should occur error and reconnect", function(done) {
            var errors = [ "manually error", "ISocket: sending on a bad socket." ];
            client.on("error", function(err) {
                if(errors[0] === undefined) return;
                err.message.should.be.eql(errors[0]);
                errors.shift();
            });

            client.socket.socket.emit("error", new Error("manually error"));

            client.send("test", "echo", "illyria", function() {});

            setTimeout(function() {
                client.send("test", "echo", "illyria", function(err, data) {
                    if(err) err.should.be.empy;
                    data.should.be.eql("illyria");
                    done();
                });
            }, 1500);
        });

        it("should reconnect after several tries", function(done) {
            this.timeout(100000);

            // var errors = [ "manually error", "ISocket: sending on a bad socket." ];
            server.close();
            server = null;
            client.socket.socket.end();

            setTimeout(function() {
                init(function() {
                    server.expose({
                        name: "test",
                        methods: {
                            echo: function(req, resp) {
                                req.params().should.be.eql("illyria");
                                resp.send("illyria");
                            }
                        }
                    });

                    server.listen(function() {
                        client.send("test", "echo", "illyria", function(err) {
                            err.message.should.be.eql("Timeout when send and wait for response after 1000ms.");
                        });

                        setTimeout(function() {
                            client.send("test", "echo", "illyria", function(err, echo) {
                                should(err).be.empty;
                                echo.should.be.eql("illyria");
                                done();
                            });
                        }, 2000);
                    });
                });
            }, 7000);

            // setTimeout(function() {
            //     client.send("test", "echo", "illyria", function(err, data) {
            //         if(err) err.should.be.empy;
            //         data.should.be.eql("illyria");
            //         done();
            //     });
            // }, 1500);
        });
    });
});

<commit_msg>edit test case<commit_after>/**
 * XadillaX created at 2015-03-11 13:53:05
 *
 * Copyright (c) 2015 Huaban.com, all rights
 * reserved
 */
require("should");
var async = require("async");
var illyria = require("illyria");
var illyria2 = require("../");

describe("client protocol", function() {
    var SERVER_PORT = 2399;
    var server, client;

    function init(callback) {
        async.waterfall([
            function(callback) {
                if(server) {
                    server.close(callback);
                } else callback();
            }
        ], function() {
            server = illyria.createServer({
                port: SERVER_PORT
            });
            callback();
        });
    }

    function startup(callback) {
        server.listen(function() {
            client = illyria2.createClient("127.0.0.1", SERVER_PORT, {
                runTimeout: 1000,
                retryInterval: 1000,
                reconnect: true
            });

            client.connect(function() {
                callback();
            });
        });
    }

    describe("match old server", function() {
        before(function(done) {
            init(function() {
                server.expose({
                    name: "test",
                    methods: {
                        echo: function(req, resp) {
                            req.params().should.be.eql("illyria");
                            resp.send("illyria");
                        },

                        json: function(req, resp) {
                            req.param("json").should.be.eql(true);
                            req.params().should.be.eql({ json: true });
                            resp.send(req.params());
                        },

                        error: function(req, resp) {
                            resp.json({ err: "test error" });
                        },

                        cut: function(req/**, resp*/) {
                            req.socket.end();
                            req.socket.destroy();
                        },

                        eecho: function(req, resp) {
                            resp.send(req.params());
                        }
                    }
                });

                startup(done);
            });
        });

        it("should received \"echo\"", function(done) {
            client.send("test", "echo", "illyria", function(err, data) {
                if(err) err.should.be.empty;
                data.should.be.eql("illyria");
                done();
            });
        });

        it("should received { json: true }", function(done) {
            client.send("test", "json", { json: true }, function(err, data) {
                if(err) err.should.be.empy;
                data.should.be.eql({ json: true });
                done();
            });
        });

        it("should received error", function(done) {
            client.send("test", "error", {}, function(err) {
                (err instanceof Error).should.be.eql(true);
                err.message.should.be.eql("test error");
                done();
            });
        });

        it("should echo the whole body", function(done) {
            var _data = "";
            for(var i = 0; i < 1000000; i++) {
                _data += "a";
            }

            client.send("test", "eecho", _data, function(err, data) {
                if(err) err.should.be.empy;
                data.should.be.eql(_data);
                done();
            });
        });

        it("should reconnect and ok", function(done) {
            client.send("test", "cut", {}, function(err) {
                err.message.indexOf("Timeout").should.not.be.eql(-1);

                setTimeout(function() {
                    client.send("test", "json", { json: true }, function(err, data) {
                        if(err) err.should.be.empy;
                        data.should.be.eql({ json: true });
                        done();
                    });
                }, 500);
            });
        });

        it("should occur error and reconnect", function(done) {
            var errors = [ "manually error", "ISocket: sending on a bad socket." ];
            client.on("error", function(err) {
                if(errors[0] === undefined) return;
                err.message.should.be.eql(errors[0]);
                errors.shift();
            });

            client.socket.socket.emit("error", new Error("manually error"));

            client.send("test", "echo", "illyria", function() {});

            setTimeout(function() {
                client.send("test", "echo", "illyria", function(err, data) {
                    if(err) err.should.be.empy;
                    data.should.be.eql("illyria");
                    done();
                });
            }, 1500);
        });

        it("should reconnect after several tries", function(done) {
            this.timeout(100000);

            // var errors = [ "manually error", "ISocket: sending on a bad socket." ];
            server.close();
            server = null;
            client.socket.socket.end();

            setTimeout(function() {
                init(function() {
                    server.expose({
                        name: "test",
                        methods: {
                            echo: function(req, resp) {
                                req.params().should.be.eql("illyria");
                                resp.send("illyria");
                            }
                        }
                    });

                    var send = function() {
                        client.send("test", "echo", "illyria", function(err, echo) {
                            if(err) {
                                err.message.should.be.eql("Timeout when send and wait for response after 1000ms.");
                                return setTimeout(send, 1000);
                            }

                            echo.should.be.eql("illyria");
                            done();
                        });
                    };

                    server.listen(function() {
                        setTimeout(send, 1000);
                    });
                });
            }, 7000);

            // setTimeout(function() {
            //     client.send("test", "echo", "illyria", function(err, data) {
            //         if(err) err.should.be.empy;
            //         data.should.be.eql("illyria");
            //         done();
            //     });
            // }, 1500);
        });
    });
});

<|endoftext|>


<commit_before>(function (root, factory) {
    'use strict';

    if (typeof define === 'function' && define.amd) {
        define(['marionette', 'underscore'], factory);
    }
    else {
        root.Marionette.Tooltip = factory(root.Marionette, root._);
    }
}(this, function (Marionette, _) {
    'use strict';

    var ContainerView = Marionette.LayoutView.extend({
        template: _.template('

'),
        className: 'tooltip',

        regions: {
            region: '.js-tooltip'
        },

        events: {},

        initialize: function () {
            var prevents = this.prevents;

            if (_.isString(prevents)) {
                this.events[prevents] = 'preventPropagation';
            }
            else if (_.isArray(prevents)) {
                _.each(prevents, function (event) {
                    this.events[event] = 'preventPropagation';
                }, this);
            }
        },

        preventPropagation: function (event) {
            event.stopPropagation();
        }
    });

    return Marionette.Behavior.extend({
        defaults: {
            direction: 'top'
        },

        initialize: function () {
            var Container = ContainerView.extend({
                className: this.options.className || 'tooltip',
                prevents: this.options.prevents
            });

            this.tooltip = new Container;
        },

        onShow: function () {
            var View = _.result(this.view, 'getTooltip');

            if (View === void 0) {
                throw new Marionette.Error({
                    name: 'TooltipException',
                    message: 'tooltip is not defined'
                });
            }

            this.tooltip.render();
            this.tooltip.region.show(new View);

            this.tooltip.$el.addClass('direction-' + this.options.direction);
            this.el.insertBefore(this.tooltip.el, null);
        },

        onDestroy: function () {
            this.tooltip.destroy();
        },

        setPosition: function () {
            var target;

            if (this.view.ui && this.view.ui.tooltipTarget !== void 0) {
                target = this.view.ui.tooltipTarget;
            }
            else {
                target = this.$el;
            }

            // tooltip target
            var parentPosition = target.position();
            var parentWidth = target.outerWidth();
            var parentHeight = target.outerHeight();

            // tooltip element
            var height = this.tooltip.$el.outerHeight();
            var width = this.tooltip.$el.outerWidth();

            // tooltip default offset
            var top = parentPosition.top - height / 2 + parentHeight / 2;
            var left = parentPosition.left - width / 2 + parentWidth / 2;

            // ajust tooltip position based on tooltip direction
            switch (this.options.direction) {
                case 'top': {
                    top = parentPosition.top - height;
                    break;
                }
                case 'bottom': {
                    top = parentHeight + parentPosition.top;
                    break;
                }
                case 'left': {
                    left = parentPosition.left - width;
                    break;
                }
                case 'right': {
                    left = parentPosition.left + parentWidth;
                    break;
                }
            }

            this.tooltip.$el.css({
                top: top,
                left: left
            });
        },

        onShowTooltip: function () {
            this.setPosition();
            this.tooltip.$el.stop(true).fadeIn(200);
        },

        onHideTooltip: function () {
            this.tooltip.$el.stop(true).fadeOut(200);
        },

        onToggleTooltip: function () {
            if (this.tooltip.$el.css('display') === 'none') {
                this.onShowTooltip();
            }
            else {
                this.onHideTooltip();
            }
        }
    });
}));
<commit_msg>Update marionette.tooltip.js<commit_after>(function (root, factory) {
    'use strict';

    if (typeof define === 'function' && define.amd) {
        define(['marionette', 'underscore'], factory);
    }
    else {
        root.Marionette.Tooltip = factory(root.Marionette, root._);
    }
}(this, function (Marionette, _) {
    'use strict';

    var ContainerView = Marionette.LayoutView.extend({
        template: _.template('

'),
        className: 'tooltip',

        regions: {
            region: '.js-tooltip'
        },

        events: {},

        initialize: function () {
            var prevents = this.prevents;

            if (_.isString(prevents)) {
                this.events[prevents] = 'preventPropagation';
            }
            else if (_.isArray(prevents)) {
                _.each(prevents, function (event) {
                    this.events[event] = 'preventPropagation';
                }, this);
            }
        },

        preventPropagation: function (event) {
            event.stopPropagation();
        }
    });

    return Marionette.Behavior.extend({
        defaults: {
            direction: 'top'
        },

        initialize: function () {
            var Container = ContainerView.extend({
                className: this.options.className || 'tooltip',
                prevents: this.options.prevents
            });

            this.tooltip = new Container;
        },

        onShow: function () {
            var View = _.result(this.view, 'getTooltip');

            if (View === void 0) {
                throw new Marionette.Error({
                    name: 'TooltipException',
                    message: 'tooltip is not defined'
                });
            }

            this.tooltip.render();
            this.tooltip.region.show(new View);

            this.tooltip.$el.addClass('direction-' + this.options.direction);

            if (this.view.ui && this.view.ui.tooltipTarget !== void 0) {
                this.view.ui.tooltipTarget.after(this.tooltip.el);
            }
            else {
                this.el.insertBefore(this.tooltip.el, null);
            }
        },

        onDestroy: function () {
            this.tooltip.destroy();
        },

        setPosition: function () {
            var target;

            if (this.view.ui && this.view.ui.tooltipTarget !== void 0) {
                target = this.view.ui.tooltipTarget;
            }
            else {
                target = this.$el;
            }

            // tooltip target
            var parentPosition = target.position();
            var parentWidth = target.outerWidth();
            var parentHeight = target.outerHeight();

            // tooltip element
            var height = this.tooltip.$el.outerHeight();
            var width = this.tooltip.$el.outerWidth();

            // tooltip default offset
            var top = parentPosition.top - height / 2 + parentHeight / 2;
            var left = parentPosition.left - width / 2 + parentWidth / 2;

            // ajust tooltip position based on tooltip direction
            switch (this.options.direction) {
                case 'top': {
                    top = parentPosition.top - height;
                    break;
                }
                case 'bottom': {
                    top = parentHeight + parentPosition.top;
                    break;
                }
                case 'left': {
                    left = parentPosition.left - width;
                    break;
                }
                case 'right': {
                    left = parentPosition.left + parentWidth;
                    break;
                }
            }

            this.tooltip.$el.css({
                top: top,
                left: left
            });
        },

        onShowTooltip: function () {
            this.setPosition();
            this.tooltip.$el.stop(true).fadeIn(200);
        },

        onHideTooltip: function () {
            this.tooltip.$el.stop(true).fadeOut(200);
        },

        onToggleTooltip: function () {
            if (this.tooltip.$el.css('display') === 'none') {
                this.onShowTooltip();
            }
            else {
                this.onHideTooltip();
            }
        }
    });
}));
<|endoftext|>