<commit_before>var assert = require('assert'),
  sandbox = require('sandboxed-module'),
  vows = require('vows');

vows.describe('jenkins').addBatch({
  '': {
    topic: function () {
      return {};
    },
    '': function (topic) {
    }
  }
}).exportTo(module);<commit_msg>Add jenkins.js tests.<commit_after>var assert = require('assert'),
  sandbox = require('sandboxed-module'),
  url = require('url'),
  vows = require('vows');

vows.describe('jenkins').addBatch({
  'http': {
    topic: function () {
      return function (mockErr, mockResult, checks) {
        return new (sandbox.require('../lib/jenkins', {
          requires: {
            './comm': {
              http: function (path, method, url, cb) {
                assert.equal(path, checks.path);
                assert.equal(method, checks.method);
                assert.equal(url.href, checks.url);
                cb(mockErr, mockResult);
              },
              udp: function (message, host, port, cb) {
                assert.equal(message, checks.message);
                assert.equal(host, checks.host);
                assert.equal(port, checks.port);
                cb(mockErr, mockResult);
              }
            }
          }
        })).Jenkins(url.parse('http://user:pass@localhost:8080'));
      };
    },
    'when an inexisting job is built': {
      topic: function (topic) {
        topic(null, { statusCode: 404, headers: {}, data: '' },
          { path: '/job/myjob/build?token=nestor&json={"parameter":[{"name":"myparam1","value":"myvalue1"},{"name":"myparam2","value":"myvalue2"}]}', method: 'POST', url: 'http://user:pass@localhost:8080/' }).
          build('myjob', 'myparam1=myvalue1&myparam2=myvalue2', this.callback);
      },
      'then an error should occur': function (err, result) {
        assert.equal(err.message, 'Job does not exist');
        assert.isUndefined(result);
      }
    },
    'when an authentication is required for build but not supplied': {
      topic: function (topic) {
        topic(null, { statusCode: 401, headers: {}, data: '' },
          { path: '/job/myjob/build?token=nestor&json={"parameter":[{"name":"myparam1","value":"myvalue1"},{"name":"myparam2","value":"myvalue2"}]}', method: 'POST', url: 'http://user:pass@localhost:8080/' }).
          build('myjob', 'myparam1=myvalue1&myparam2=myvalue2', this.callback);
      },
      'then an error should occur': function (err, result) {
        assert.equal(err.message, 'Username and password are required');
        assert.isUndefined(result);
      }
    },
    'when a build url is forbidden': {
      topic: function (topic) {
        topic(null, { statusCode: 403, headers: {}, data: '' },
          { path: '/job/myjob/build?token=nestor&json={"parameter":[{"name":"myparam1","value":"myvalue1"},{"name":"myparam2","value":"myvalue2"}]}', method: 'POST', url: 'http://user:pass@localhost:8080/' }).
          build('myjob', 'myparam1=myvalue1&myparam2=myvalue2', this.callback);
      },
      'then an error should occur': function (err, result) {
        assert.equal(err.message, 'Username and password are required');
        assert.isUndefined(result);
      }
    },
    'when an existing job is built': {
      topic: function (topic) {
        topic(null, { statusCode: 200, headers: {}, data: '' },
          { path: '/job/myjob/build?token=nestor&json={"parameter":[{"name":"myparam1","value":"myvalue1"},{"name":"myparam2","value":"myvalue2"}]}', method: 'POST', url: 'http://user:pass@localhost:8080/' }).
          build('myjob', 'myparam1=myvalue1&myparam2=myvalue2', this.callback);
      },
      'then result status should be ok': function (err, result) {
        assert.equal(result.status, 'ok');
        assert.isNull(err);
      }
    },
    'when jenkins has jobs': {
      topic: function (topic) {
        var data = '{"assignedLabels":[{}],"mode":"NORMAL","nodeDescription":"the master Nestor node","nodeName":"","numExecutors":2,"description":null,' +
          '"jobs":[{"name":"red-rackham","url":"http://localhost:8080/job/red-rackham/","color":"red"},' +
          '{"name":"golden-claw","url":"http://localhost:8080/job/golden-claw/","color":"blue"}],' +
          '"overallLoad":{},"primaryView":{"name":"All","url":"http://localhost:8080/"},"slaveAgentPort":0,"useCrumbs":false,"useSecurity":false,"views":[{"name":"All","url":"http://localhost:8080/"}]}';
        topic(null, { statusCode: 200, headers: {}, data: data },
          { path: '/api/json', method: 'GET', url: 'http://user:pass@localhost:8080/' }).
          dashboard(this.callback);
      },
      'then dashboard result should contain job details': function (err, result) {
        assert.equal(result.length, 2);
        assert.equal(result[0].status, 'FAIL');
        assert.equal(result[0].name, 'red-rackham');
        assert.equal(result[1].status, 'OK');
        assert.equal(result[1].name, 'golden-claw');
        assert.isNull(err);
      }
    },
    'when jenkins has no job': {
      topic: function (topic) {
        var data = '{"assignedLabels":[{}],"mode":"NORMAL","nodeDescription":"the master Nestor node","nodeName":"","numExecutors":2,"description":null,' +
          '"jobs":[],' +
          '"overallLoad":{},"primaryView":{"name":"All","url":"http://localhost:8080/"},"slaveAgentPort":0,"useCrumbs":false,"useSecurity":false,"views":[{"name":"All","url":"http://localhost:8080/"}]}';
        topic(null, { statusCode: 200, headers: {}, data: data },
          { path: '/api/json', method: 'GET', url: 'http://user:pass@localhost:8080/' }).
          dashboard(this.callback);
      },
      'then dashboard result should be empty': function (err, result) {
        assert.isEmpty(result);
        assert.isNull(err);
      }
    },
    'when there is a discoverable jenkins instance': {
      topic: function (topic) {
        topic(null, { 'server-id': '6ff95df5e7e248d51186e6d96085f42a', 'slave-port': '50802', url: 'http://localhost:8080/', version: '1.414' },
          { message: 'Long live Jenkins', host: 'localhost', port: 33848 }).
          discover('localhost', this.callback);
      },
      'then discover result should contain instance details': function (err, result) {
        assert.equal(result['server-id'], '6ff95df5e7e248d51186e6d96085f42a');
        assert.equal(result['slave-port'], '50802');
        assert.equal(result.url, 'http://localhost:8080/');
        assert.equal(result.version, '1.414');
        assert.isNull(err);
      }
    },
    'when there is an error while trying to discover jenkins instance': {
      topic: function (topic) {
        topic(new Error('some error'), null,
          { message: 'Long live Jenkins', host: 'localhost', port: 33848 }).
          discover('localhost', this.callback);
      },
      'then an error should be passed via callback': function (err, result) {
        assert.equal(err.message, 'some error');
        assert.isUndefined(result);
      }
    },
    'when jenkins has in-progress executors': {
      topic: function (topic) {
        var data = '{"busyExecutors":2,"computer":[{"actions":[],"displayName":"master",' +
          '"executors":[{"currentExecutable":{"number":39,"url":"http://localhost:8080/job/red-rackham/39/"},"currentWorkUnit":{},"idle":false,"likelyStuck":false,"number":0,"progress":31},{"currentExecutable":{"number":12,"url":"http://localhost:8080/job/golden-claw/12/"},' +
          '"currentWorkUnit":{},"idle":false,"likelyStuck":false,"number":1,"progress":28}],"icon":"computer.png","idle":false,"jnlpAgent":false,"launchSupported":true,"loadStatistics":{"busyExecutors":{},"queueLength":{},"totalExecutors":{}},"manualLaunchAllowed":true,' +
          '"monitorData":{"hudson.node_monitors.SwapSpaceMonitor":{"availablePhysicalMemory":169869312,"availableSwapSpace":124780544,"totalPhysicalMemory":2146435072,"totalSwapSpace":133726208},"hudson.node_monitors.ArchitectureMonitor":"Mac OS X (x86_64)",' +
          '"hudson.node_monitors.TemporarySpaceMonitor":{"size":98938888192},"hudson.node_monitors.ResponseTimeMonitor":{"average":145},"hudson.node_monitors.DiskSpaceMonitor":{"size":98938888192},"hudson.node_monitors.ClockMonitor":{"diff":0}},"numExecutors":2,' +
          '"offline":false,"offlineCause":null,"oneOffExecutors":[],"temporarilyOffline":false}],"displayName":"nodes","totalExecutors":2}';
        topic(null, { statusCode: 200, headers: {}, data: data },
          { path: '/computer/api/json?depth=1', method: 'GET', url: 'http://user:pass@localhost:8080/' }).
          executor(this.callback);
      },
      'then executor result should contain job progress details': function (err, result) {
        assert.equal(result.master.length, 2);
        assert.isFalse(result.master[0].idle);
        assert.equal(result.master[0].progress, '31');
        assert.equal(result.master[0].name, 'red-rackham');
        assert.isFalse(result.master[1].idle);
        assert.equal(result.master[1].progress, '28');
        assert.equal(result.master[1].name, 'golden-claw');
        assert.isNull(err);
      }
    },
    'when jenkins has idle executors': {
      topic: function (topic) {
        var data = '{"busyExecutors":0,"computer":[{"actions":[],"displayName":"master",' +
          '"executors":[{"currentExecutable":null,"currentWorkUnit":null,"idle":true,"likelyStuck":false,"number":0,"progress":-1},{"currentExecutable":null,"currentWorkUnit":null,"idle":true,"likelyStuck":false,"number":1,"progress":-1}],' +
          '"icon":"computer.png","idle":true,"jnlpAgent":false,"launchSupported":true,"loadStatistics":{"busyExecutors":{},"queueLength":{},"totalExecutors":{}},"manualLaunchAllowed":true,' +
          '"monitorData":{"hudson.node_monitors.SwapSpaceMonitor":{"availablePhysicalMemory":83886080,"availableSwapSpace":239075328,"totalPhysicalMemory":2146435072,"totalSwapSpace":268435456},"hudson.node_monitors.ArchitectureMonitor":"Mac OS X (x86_64)",' +
          '"hudson.node_monitors.TemporarySpaceMonitor":{"size":98799357952},"hudson.node_monitors.ResponseTimeMonitor":{"average":72},"hudson.node_monitors.DiskSpaceMonitor":{"size":98799357952},"hudson.node_monitors.ClockMonitor":{"diff":0}},"numExecutors":2,' +
          '"offline":false,"offlineCause":null,"oneOffExecutors":[],"temporarilyOffline":false}],"displayName":"nodes","totalExecutors":2}';
        topic(null, { statusCode: 200, headers: {}, data: data },
          { path: '/computer/api/json?depth=1', method: 'GET', url: 'http://user:pass@localhost:8080/' }).
          executor(this.callback);
      },
      'then executor result should contain idle status': function (err, result) {
        assert.equal(result.master.length, 2);
        assert.isTrue(result.master[0].idle);
        assert.isTrue(result.master[1].idle);
        assert.isNull(err);
      }
    },
    'when job exists': {
      topic: function (topic) {
        var data = '{"actions":[{"parameterDefinitions":[{"defaultParameterValue":{"value":"default-blah"},"description":"","name":"BLAH","type":"StringParameterDefinition"},{"defaultParameterValue":{"value":"Cutts"},"description":"","name":"BLUH","type":"StringParameterDefinition"}]},{}],' +
          '"description":"","displayName":"blah-sleep","name":"blah-sleep","url":"http://localhost:8080/job/blah-sleep/","buildable":true,"builds":[{"number":42,"url":"http://localhost:8080/job/blah-sleep/42/"},{"number":41,"url":"http://localhost:8080/job/blah-sleep/41/"}],' +
          '"color":"blue","firstBuild":{"number":1,"url":"http://localhost:8080/job/blah-sleep/1/"},"healthReport":[{"description":"Build stability: No recent builds failed.","iconUrl":"health-80plus.png","score":100}],"inQueue":false,"keepDependencies":false,' +
          '"lastBuild":{"number":42,"url":"http://localhost:8080/job/blah-sleep/42/"},"lastCompletedBuild":{"number":42,"url":"http://localhost:8080/job/blah-sleep/42/"},"lastFailedBuild":null,"lastStableBuild":{"number":42,"url":"http://localhost:8080/job/blah-sleep/42/"},' +
          '"lastSuccessfulBuild":{"number":42,"url":"http://localhost:8080/job/blah-sleep/42/"},"lastUnstableBuild":null,"lastUnsuccessfulBuild":{"number":18,"url":"http://localhost:8080/job/blah-sleep/18/"},"nextBuildNumber":43,' +
          '"property":[{},{"parameterDefinitions":[{"defaultParameterValue":{"name":"BLAH","value":"default-blah"},"description":"","name":"BLAH","type":"StringParameterDefinition"},{"defaultParameterValue":{"name":"BLUH","value":"Cutts"},' +
          '"description":"","name":"BLUH","type":"StringParameterDefinition"}]},{}],"queueItem":null,"concurrentBuild":false,"downstreamProjects":[],"scm":{},"upstreamProjects":[]}';
        topic(null, { statusCode: 200, headers: {}, data: data },
          { path: '/job/dummyjob/api/json', method: 'GET', url: 'http://user:pass@localhost:8080/' }).
          job('dummyjob', this.callback);
      },
      'then job result should contain details': function (err, result) {
        assert.equal(result.status, 'OK');
        assert.equal(result.reports.length, 1);
        assert.equal(result.reports[0], 'Build stability: No recent builds failed.');
        assert.isNull(err);
      }
    },
    'when job does not exist': {
      topic: function (topic) {
        topic(null, { statusCode: 404, headers: {}, data: '' },
          { path: '/job/dummyjob/api/json', method: 'GET', url: 'http://user:pass@localhost:8080/' }).
          job('dummyjob', this.callback);
      },
      'then an error should be passed via callback': function (err, result) {
        assert.equal(err.message, 'Job does not exist');
        assert.isUndefined(result);
      }
    },
    'when there are jobs in the queue': {
      topic: function (topic) {
        var data = '{"items":[' +
          '{"actions":[{"parameters":[{"name":"blah","value":"default-blah"},{"name":"bluh","value":"default-bluh"}]},{"causes":[{"shortDescription":"Started by user anonymous","userName":"anonymous"}]}],"blocked":true,"buildable":false,"params":"\\n(StringParameterValue) blah=\'default-blah\'\\n(StringParameterValue) bluh=\'default-bluh\'","stuck":false,"task":{"name":"blah-sleep1","url":"http://localhost:8080/job/blah-sleep1/","color":"blue_anime"},"why":"Build #20 is already in progress (ETA:40 sec)","buildableStartMilliseconds":1310478638367},' +
          '{"actions":[{"parameters":[{"name":"blah","value":"default-blah"},{"name":"bluh","value":"default-bluh"}]},{"causes":[{"shortDescription":"Started by user anonymous","userName":"anonymous"}]}],"blocked":true,"buildable":false,"params":"\\n(StringParameterValue) blah=\'default-blah\'\\n(StringParameterValue) bluh=\'default-bluh\'","stuck":false,"task":{"name":"blah-sleep2","url":"http://localhost:8080/job/blah-sleep2/","color":"blue_anime"},"why":"Build #20 is already in progress (ETA:40 sec)","buildableStartMilliseconds":1310478638368}' +
          ']}';
        topic(null, { statusCode: 200, headers: {}, data: data },
          { path: '/queue/api/json', method: 'GET', url: 'http://user:pass@localhost:8080/' }).
          queue(this.callback);
      },
      'then queue result should contain details': function (err, result) {
        assert.equal(result.length, 2);
        assert.equal(result[0], 'blah-sleep1');
        assert.equal(result[1], 'blah-sleep2');
        assert.isNull(err);
      }
    },
    'when queue is empty': {
      topic: function (topic) {
        topic(null, { statusCode: 200, headers: {}, data: '{"items":[]}' },
          { path: '/queue/api/json', method: 'GET', url: 'http://user:pass@localhost:8080/' }).
          queue(this.callback);
      },
      'then queue result should be empty': function (err, result) {
        assert.isEmpty(result);
        assert.isNull(err);
      }
    },
    'when unexpected status code is returned': {
      topic: function (topic) {
        topic(null, { statusCode: 400, headers: {}, data: '{"items":[]}' },
          { path: '/queue/api/json', method: 'GET', url: 'http://user:pass@localhost:8080/' }).
          queue(this.callback);
      },
      'then error should be passed via callback': function (err, result) {
        assert.equal(err.message, 'Unexpected status code 400');
        assert.isUndefined(result);
      }
    },
    'when x-jenkins header exists': {
      topic: function (topic) {
        topic(null, { statusCode: 200, headers: { 'x-jenkins': '0.88' }, data: '' },
          { path: '/', method: 'HEAD', url: 'http://user:pass@localhost:8080/' }).
          version(this.callback);
      },
      'then version should contain jenkins version value': function (err, result) {
        assert.equal(result, '0.88');
        assert.isNull(err);
      }
    },
    'when x-jenkins header does not exist': {
      topic: function (topic) {
        topic(null, { statusCode: 200, headers: {}, data: '' },
          { path: '/', method: 'HEAD', url: 'http://user:pass@localhost:8080/' }).
          version(this.callback);
      },
      'then version should pass error via callback': function (err, result) {
        assert.equal(err.message, 'Not a Jenkins server');
        assert.isUndefined(result);
      }
    },
    'when version responds with an unexpected code': {
      topic: function (topic) {
        topic(null, { statusCode: 500, headers: {}, data: '' },
          { path: '/', method: 'HEAD', url: 'http://user:pass@localhost:8080/' }).
          version(this.callback);
      },
      'then version should pass error via callback': function (err, result) {
        assert.equal(err.message, 'Unexpected status code 500');
        assert.isUndefined(result);
      }
    }
  }
}).exportTo(module);<|endoftext|>


<commit_before>// Copyright 2014 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

"use strict";

var cli = require("cli");
var crypto = require("./crypto-util");
var child_process = require('child_process');
var fs = require('fs');
var http = require('http');
var https = require('https');
var inquirer = require("inquirer");
var request = require('request');
var url = require('url');
var util = require("./acme-util");

var questions = {
  email: [{
    type: "input",
    name: "email",
    message: "Please enter your email address (for recovery purposes)",
    validate: function(value) {
      var pass = value.match(/[\w.+-]+@[\w.-]+/i);
      if (pass) {
        return true;
      } else {
        return "Please enter a valid email address";
      }
    }
  }],

  terms: [{
    type: "confirm",
    name: "terms",
    message: "Do you agree to these terms?",
    default: true,
  }],

  domain: [{
    type: "input",
    name: "domain",
    message: "Please enter the domain name for the certificate",
    validate: function(value) {
      var pass = value.match(/[\w.-]+/i);
      if (pass) {
        return true;
      } else {
        return "Please enter a valid domain name";
      }
    }
  }],

  readyToValidate: [{
    type: "input",
    name: "noop",
    message: "Press enter to when you're ready to proceed",
  }],

  files: [{
    type: "input",
    name: "keyFile",
    message: "Name for key file",
    default: "key.pem"
  },{
    type: "input",
    name: "certFile",
    message: "Name for certificate file",
    default: "cert.der"
  }],
};

var state = {
  keyPair: null,

  //newRegistrationURL: "https://www.letsencrypt-demo.org/acme/new-reg",
  newRegistrationURL: "http://localhost:4000/acme/new-reg",
  registrationURL: "",

  termsRequired: false,
  termsAgreed: false,
  termsURL: null,

  domain: null,

  newAuthorizationURL: "",
  authorizationURL: "",
  responseURL: "",
  path: "",
  retryDelay: 1000,

  newCertificateURL: "",
  certificateURL: "",
  certFile: "",
  keyFile: ""
};

function parseLink(link) {
  try {
    // NB: Takes last among links with the same "rel" value
    var links = link.split(',').map(function(link) {
      var parts = link.trim().split(";");
      var url = parts.shift().replace(/[<>]/g, "");
      var info = parts.reduce(function(acc, p) {
        var m = p.trim().match(/(.+) *= *"(.+)"/);
        if (m) acc[m[1]] = m[2];
        return acc
      }, {});
      info["url"] = url;
      return info;
    }).reduce(function(acc, link) {
      if ("rel" in link) {
        acc[link["rel"]] = link["url"]
      }
      return acc;
    }, {});
    return links;
  } catch (e) {
    return null;
  }
}

/*

The asynchronous nature of node.js libraries makes the control flow a
little hard to follow here, but it pretty much goes straight down the
page, with detours through the `inquirer` and `http` libraries.

main
  |
register
  |
getTerms
  | \
  |  getAgreement
  |  |
  |  sendAgreement
  | /
getDomain
  |
getChallenges
  |
getReadyToValidate
  |
sendResponse
  |
ensureValidation
  |
getCertificate
  |
downloadCertificate
  |
saveFiles

*/

function main() {
  inquirer.prompt(questions.files, makeKeyPair);
}

function makeKeyPair(answers) {
  state.certFile = answers.certFile;
  state.keyFile = answers.keyFile;
  console.log("Generating key pair...");
  child_process.exec("openssl req -newkey rsa:2048 -keyout " + state.keyFile + " -days 3650 -subj /CN=example.com -nodes -x509 -out temp-cert.pem", function (error, stdout, stderr) {
    if (error) {
      console.log(error);
      process.exit(1);
    }
    state.keyPair = crypto.importPemPrivateKey(fs.readFileSync(state.keyFile));

    console.log();
    inquirer.prompt(questions.email, register)
  });
}

function register(answers) {
  var email = answers.email;

  // Register public key
  state.registration = {
    contact: [ "mailto:" + email ]
  }
  var registerMessage = JSON.stringify(state.registration);
  var jws = crypto.generateSignature(state.keyPair, new Buffer(registerMessage));
  var payload = JSON.stringify(jws);

  var req = request.post(state.newRegistrationURL, {}, getTerms);
  req.write(payload)
  req.end();
}

function getTerms(err, resp) {
  if (err || Math.floor(resp.statusCode / 100) != 2) {
    // Non-2XX response
    console.log("Registration request failed:" + err);
    return;
  }

  var links = parseLink(resp.headers["link"]);
  if (!links || !("next" in links)) {
    console.log("The server did not provide information to proceed");
    return
  }

  state.registrationURL = resp.headers["location"];
  state.newAuthorizationURL = links["next"];
  state.termsRequired = ("terms-of-service" in links);

  if (state.termsRequired) {
    state.termsURL = links["terms-of-service"];
    console.log(state.termsURL);
    request.get(state.termsURL, getAgreement)
  } else {
    inquirer.prompt(questions.domain, getChallenges);
  }
}

function getAgreement(err, resp, body) {
  if (err) {
    console.log("getAgreement error:", err);
    process.exit(1);
  }
  // TODO: Check content-type
  console.log("The CA requires your agreement to terms.");
  console.log();
  console.log(body);
  console.log();

  inquirer.prompt(questions.terms, sendAgreement);
}

function sendAgreement(answers) {
  state.termsAgreed = answers.terms;

  if (state.termsRequired && !state.termsAgreed) {
    console.log("Sorry, can't proceed if you don't agree.");
    process.exit(1);
  }

  state.registration.agreement = state.termsURL;
  var registerMessage = JSON.stringify(state.registration);
  var jws = crypto.generateSignature(state.keyPair, new Buffer(registerMessage));
  var payload = JSON.stringify(jws);

  console.log("Posting agreement to: " + state.registrationURL)
  var req = request(state.registrationURL, {}, function(err, resp, body) {
    if (err) {
      console.log("Couldn't POST agreement back to server, aborting.");
      console.log("error: " + err);
      console.log(body);
      process.exit(1);
    }

    inquirer.prompt(questions.domain, getChallenges);
  });
  req.write(payload)
  req.end();
}

function getChallenges(answers) {
  state.domain = answers.domain;

  // Register public key
  var authzMessage = JSON.stringify({
    identifier: {
      type: "dns",
      value: state.domain
    }
  });
  var jws = crypto.generateSignature(state.keyPair, new Buffer(authzMessage));
  var payload = JSON.stringify(jws);

  var req = request.post(state.newAuthorizationURL, {}, getReadyToValidate);
  req.write(payload)
  req.end();
}

function getReadyToValidate(err, resp, body) {
  if (err || Math.floor(resp.statusCode / 100) != 2) {
    // Non-2XX response
    console.log("Authorization request failed with code " + resp.statusCode)
    return;
  }

  var links = parseLink(resp.headers["link"]);
  if (!links || !("next" in links)) {
    console.log("The server did not provide information to proceed");
    return
  }

  state.authorizationURL = resp.headers["location"];
  state.newCertificateURL = links["next"];

  var authz = JSON.parse(body);

  var simpleHttps = authz.challenges.filter(function(x) { return x.type == "simpleHttps"; });
  if (simpleHttps.length == 0) {
    console.log("The server didn't offer any challenges we can handle.");
    return;
  }

  var challenge = simpleHttps[0];
  var path = crypto.randomString(8) + ".txt";
  var challengePath = ".well-known/acme-challenge/" + path;
  fs.writeFileSync(challengePath, challenge.token);
  state.responseURL = challenge["uri"];
  state.path = path;

  // For local, test-mode validation
  function httpResponder(request, response) {
    console.log("Got request for", request.url);
    var host = request.headers["host"];
    if ((host === state.domain || /localhost/.test(state.newRegistrationURL)) &&
        request.method === "GET" &&
        request.url == "/" + challengePath) {
      response.writeHead(200, {"Content-Type": "text/plain"});
      response.end(challenge.token);
    } else {
      console.log("Got invalid request for", request.method, host, request.url);
      response.writeHead(404, {"Content-Type": "text/plain"});
      response.end("");
    }
  };
  if (/localhost/.test(state.newRegistrationURL)) {
    var httpServer = http.createServer(httpResponder)
    httpServer.listen(5001)
  } else {
    var httpServer = https.createServer({
      cert: fs.readFileSync("temp-cert.pem"),
      key: fs.readFileSync(state.keyFile)
    })
    httpServer.listen(443)
  }

  inquirer.prompt(questions.readyToValidate, sendResponse);
}

function sendResponse() {
  var responseMessage = JSON.stringify({
    path: state.path
  });
  var jws = crypto.generateSignature(state.keyPair, new Buffer(responseMessage));
  var payload = JSON.stringify(jws);

  cli.spinner("Validating domain");

  var options = url.parse(state.responseURL);
  options.method = "POST";
  var req = request.post(state.responseURL, {}, ensureValidation);
  req.write(payload)
  req.end();
}

function ensureValidation(err, resp, body) {
  if (Math.floor(resp.statusCode / 100) != 2) {
    // Non-2XX response
    console.log("Authorization status request failed with code " + resp.statusCode)
    return;
  }

  var authz = JSON.parse(body);

  if (authz.status == "pending") {
    setTimeout(function() {
      request.get(state.authorizationURL, {}, ensureValidation);
    }, state.retryDelay);
  } else if (authz.status == "valid") {
    cli.spinner("Validating domain ... done", true);
    console.log();
    getCertificate();
  } else if (authz.status == "invalid") {
    console.log("The CA was unable to validate the file you provisioned:"  + body);
    return;
  } else {
    console.log("The CA returned an authorization in an unexpected state");
    console.log(JSON.stringify(authz, null, "  "));
    return;
  }
}

function getCertificate() {
  var csr = crypto.generateCSR(state.keyPair, state.domain);

  var certificateMessage = JSON.stringify({
    csr: csr,
    authorizations: [ state.authorizationURL ]
  });
  var jws = crypto.generateSignature(state.keyPair, new Buffer(certificateMessage));
  var payload = JSON.stringify(jws);

  cli.spinner("Requesting certificate");

  var req = request.post({
    url: state.newCertificateURL,
    encoding: null // Return body as buffer.
  }, downloadCertificate);
  req.write(payload)
  req.end();
}

function downloadCertificate(err, resp, body) {
  if (err || Math.floor(resp.statusCode / 100) != 2) {
    // Non-2XX response
    console.log("Certificate request failed with code " + resp.statusCode);
    console.log(body.toString());
    return;
  }

  cli.spinner("Requesting certificate ... done", true);
  console.log();

  state.certificate = body;
  console.log()
  var certURL = resp.headers['location'];
  request.get({
    url: certURL,
    encoding: null // Return body as buffer.
  }, function(err, res, body) {
    if (body.toString() !== state.certificate.toString()) {
      console.log("ERROR! Cert at", certURL, "did not match returned cert.");
    } else {
      console.log("Successfully verified cert at", certURL);
      saveFiles()
    }
  });
}

function saveFiles(answers) {
  fs.writeFileSync(state.certFile, state.certificate);

  console.log("Done!")
  console.log("To try it out:");
  console.log("openssl s_server -accept 8080 -www -certform der -key "+
              state.keyFile +" -cert "+ state.certFile);

  // XXX: Explicitly exit, since something's tenacious here
  process.exit(0);
}

// BEGIN
main();

<commit_msg>Fix incorrect request.post invocation.<commit_after>// Copyright 2014 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

"use strict";

var cli = require("cli");
var crypto = require("./crypto-util");
var child_process = require('child_process');
var fs = require('fs');
var http = require('http');
var https = require('https');
var inquirer = require("inquirer");
var request = require('request');
var url = require('url');
var util = require("./acme-util");

var questions = {
  email: [{
    type: "input",
    name: "email",
    message: "Please enter your email address (for recovery purposes)",
    validate: function(value) {
      var pass = value.match(/[\w.+-]+@[\w.-]+/i);
      if (pass) {
        return true;
      } else {
        return "Please enter a valid email address";
      }
    }
  }],

  terms: [{
    type: "confirm",
    name: "terms",
    message: "Do you agree to these terms?",
    default: true,
  }],

  domain: [{
    type: "input",
    name: "domain",
    message: "Please enter the domain name for the certificate",
    validate: function(value) {
      var pass = value.match(/[\w.-]+/i);
      if (pass) {
        return true;
      } else {
        return "Please enter a valid domain name";
      }
    }
  }],

  readyToValidate: [{
    type: "input",
    name: "noop",
    message: "Press enter to when you're ready to proceed",
  }],

  files: [{
    type: "input",
    name: "keyFile",
    message: "Name for key file",
    default: "key.pem"
  },{
    type: "input",
    name: "certFile",
    message: "Name for certificate file",
    default: "cert.der"
  }],
};

var state = {
  keyPair: null,

  //newRegistrationURL: "https://www.letsencrypt-demo.org/acme/new-reg",
  newRegistrationURL: "http://localhost:4000/acme/new-reg",
  registrationURL: "",

  termsRequired: false,
  termsAgreed: false,
  termsURL: null,

  domain: null,

  newAuthorizationURL: "",
  authorizationURL: "",
  responseURL: "",
  path: "",
  retryDelay: 1000,

  newCertificateURL: "",
  certificateURL: "",
  certFile: "",
  keyFile: ""
};

function parseLink(link) {
  try {
    // NB: Takes last among links with the same "rel" value
    var links = link.split(',').map(function(link) {
      var parts = link.trim().split(";");
      var url = parts.shift().replace(/[<>]/g, "");
      var info = parts.reduce(function(acc, p) {
        var m = p.trim().match(/(.+) *= *"(.+)"/);
        if (m) acc[m[1]] = m[2];
        return acc
      }, {});
      info["url"] = url;
      return info;
    }).reduce(function(acc, link) {
      if ("rel" in link) {
        acc[link["rel"]] = link["url"]
      }
      return acc;
    }, {});
    return links;
  } catch (e) {
    return null;
  }
}

/*

The asynchronous nature of node.js libraries makes the control flow a
little hard to follow here, but it pretty much goes straight down the
page, with detours through the `inquirer` and `http` libraries.

main
  |
register
  |
getTerms
  | \
  |  getAgreement
  |  |
  |  sendAgreement
  | /
getDomain
  |
getChallenges
  |
getReadyToValidate
  |
sendResponse
  |
ensureValidation
  |
getCertificate
  |
downloadCertificate
  |
saveFiles

*/

function main() {
  inquirer.prompt(questions.files, makeKeyPair);
}

function makeKeyPair(answers) {
  state.certFile = answers.certFile;
  state.keyFile = answers.keyFile;
  console.log("Generating key pair...");
  child_process.exec("openssl req -newkey rsa:2048 -keyout " + state.keyFile + " -days 3650 -subj /CN=example.com -nodes -x509 -out temp-cert.pem", function (error, stdout, stderr) {
    if (error) {
      console.log(error);
      process.exit(1);
    }
    state.keyPair = crypto.importPemPrivateKey(fs.readFileSync(state.keyFile));

    console.log();
    inquirer.prompt(questions.email, register)
  });
}

function register(answers) {
  var email = answers.email;

  // Register public key
  state.registration = {
    contact: [ "mailto:" + email ]
  }
  var registerMessage = JSON.stringify(state.registration);
  var jws = crypto.generateSignature(state.keyPair, new Buffer(registerMessage));
  var payload = JSON.stringify(jws);

  var req = request.post(state.newRegistrationURL, {}, getTerms);
  req.write(payload)
  req.end();
}

function getTerms(err, resp) {
  if (err || Math.floor(resp.statusCode / 100) != 2) {
    // Non-2XX response
    console.log("Registration request failed:" + err);
    return;
  }

  var links = parseLink(resp.headers["link"]);
  if (!links || !("next" in links)) {
    console.log("The server did not provide information to proceed");
    return
  }

  state.registrationURL = resp.headers["location"];
  state.newAuthorizationURL = links["next"];
  state.termsRequired = ("terms-of-service" in links);

  if (state.termsRequired) {
    state.termsURL = links["terms-of-service"];
    console.log(state.termsURL);
    request.get(state.termsURL, getAgreement)
  } else {
    inquirer.prompt(questions.domain, getChallenges);
  }
}

function getAgreement(err, resp, body) {
  if (err) {
    console.log("getAgreement error:", err);
    process.exit(1);
  }
  // TODO: Check content-type
  console.log("The CA requires your agreement to terms.");
  console.log();
  console.log(body);
  console.log();

  inquirer.prompt(questions.terms, sendAgreement);
}

function sendAgreement(answers) {
  state.termsAgreed = answers.terms;

  if (state.termsRequired && !state.termsAgreed) {
    console.log("Sorry, can't proceed if you don't agree.");
    process.exit(1);
  }

  state.registration.agreement = state.termsURL;
  var registerMessage = JSON.stringify(state.registration);
  var jws = crypto.generateSignature(state.keyPair, new Buffer(registerMessage));
  var payload = JSON.stringify(jws);

  console.log("Posting agreement to: " + state.registrationURL)
  var req = request.post(state.registrationURL, function(err, resp, body) {
    if (err) {
      console.log("Couldn't POST agreement back to server, aborting.");
      console.log("error: " + err);
      console.log(body);
      process.exit(1);
    }

    inquirer.prompt(questions.domain, getChallenges);
  });
  req.write(payload)
  req.end();
}

function getChallenges(answers) {
  state.domain = answers.domain;

  // Register public key
  var authzMessage = JSON.stringify({
    identifier: {
      type: "dns",
      value: state.domain
    }
  });
  var jws = crypto.generateSignature(state.keyPair, new Buffer(authzMessage));
  var payload = JSON.stringify(jws);

  var req = request.post(state.newAuthorizationURL, {}, getReadyToValidate);
  req.write(payload)
  req.end();
}

function getReadyToValidate(err, resp, body) {
  if (err || Math.floor(resp.statusCode / 100) != 2) {
    // Non-2XX response
    console.log("Authorization request failed with code " + resp.statusCode)
    return;
  }

  var links = parseLink(resp.headers["link"]);
  if (!links || !("next" in links)) {
    console.log("The server did not provide information to proceed");
    return
  }

  state.authorizationURL = resp.headers["location"];
  state.newCertificateURL = links["next"];

  var authz = JSON.parse(body);

  var simpleHttps = authz.challenges.filter(function(x) { return x.type == "simpleHttps"; });
  if (simpleHttps.length == 0) {
    console.log("The server didn't offer any challenges we can handle.");
    return;
  }

  var challenge = simpleHttps[0];
  var path = crypto.randomString(8) + ".txt";
  var challengePath = ".well-known/acme-challenge/" + path;
  fs.writeFileSync(challengePath, challenge.token);
  state.responseURL = challenge["uri"];
  state.path = path;

  // For local, test-mode validation
  function httpResponder(req, response) {
    console.log("Got request for", req.url);
    var host = req.headers["host"];
    if ((host === state.domain || /localhost/.test(state.newRegistrationURL)) &&
        req.method === "GET" &&
        req.url == "/" + challengePath) {
      response.writeHead(200, {"Content-Type": "text/plain"});
      response.end(challenge.token);
    } else {
      console.log("Got invalid request for", req.method, host, req.url);
      response.writeHead(404, {"Content-Type": "text/plain"});
      response.end("");
    }
  };
  if (/localhost/.test(state.newRegistrationURL)) {
    var httpServer = http.createServer(httpResponder)
    httpServer.listen(5001)
  } else {
    var httpServer = https.createServer({
      cert: fs.readFileSync("temp-cert.pem"),
      key: fs.readFileSync(state.keyFile)
    }, httpResponder)
    httpServer.listen(443)
  }

  inquirer.prompt(questions.readyToValidate, sendResponse);
}

function sendResponse() {
  var responseMessage = JSON.stringify({
    path: state.path
  });
  var jws = crypto.generateSignature(state.keyPair, new Buffer(responseMessage));
  var payload = JSON.stringify(jws);

  cli.spinner("Validating domain");

  var options = url.parse(state.responseURL);
  options.method = "POST";
  var req = request.post(state.responseURL, {}, ensureValidation);
  req.write(payload)
  req.end();
}

function ensureValidation(err, resp, body) {
  if (Math.floor(resp.statusCode / 100) != 2) {
    // Non-2XX response
    console.log("Authorization status request failed with code " + resp.statusCode)
    return;
  }

  var authz = JSON.parse(body);

  if (authz.status == "pending") {
    setTimeout(function() {
      request.get(state.authorizationURL, {}, ensureValidation);
    }, state.retryDelay);
  } else if (authz.status == "valid") {
    cli.spinner("Validating domain ... done", true);
    console.log();
    getCertificate();
  } else if (authz.status == "invalid") {
    console.log("The CA was unable to validate the file you provisioned:"  + body);
    return;
  } else {
    console.log("The CA returned an authorization in an unexpected state");
    console.log(JSON.stringify(authz, null, "  "));
    return;
  }
}

function getCertificate() {
  var csr = crypto.generateCSR(state.keyPair, state.domain);

  var certificateMessage = JSON.stringify({
    csr: csr,
    authorizations: [ state.authorizationURL ]
  });
  var jws = crypto.generateSignature(state.keyPair, new Buffer(certificateMessage));
  var payload = JSON.stringify(jws);

  cli.spinner("Requesting certificate");

  var req = request.post({
    url: state.newCertificateURL,
    encoding: null // Return body as buffer.
  }, downloadCertificate);
  req.write(payload)
  req.end();
}

function downloadCertificate(err, resp, body) {
  if (err || Math.floor(resp.statusCode / 100) != 2) {
    // Non-2XX response
    console.log("Certificate request failed with code " + resp.statusCode);
    console.log(body.toString());
    return;
  }

  cli.spinner("Requesting certificate ... done", true);
  console.log();

  state.certificate = body;
  console.log()
  var certURL = resp.headers['location'];
  request.get({
    url: certURL,
    encoding: null // Return body as buffer.
  }, function(err, res, body) {
    if (err) {
      console.log("Error: Failed to fetch certificate from", certURL, ":", err);
      process.exit(1);
    }
    if (res.statusCode !== 200) {
      console.log("Error: Failed to fetch certificate from", certURL, ":", res.statusCode, res.body.toString());
  fs.writeFileSync(state.certFile, state.certificate);
      process.exit(1);
    }
    if (body.toString() !== state.certificate.toString()) {
      console.log("Error: cert at", certURL, "did not match returned cert.");
    } else {
      console.log("Successfully verified cert at", certURL);
      saveFiles()
    }
  });
}

function saveFiles(answers) {
  fs.writeFileSync(state.certFile, state.certificate);

  console.log("Done!")
  console.log("To try it out:");
  console.log("openssl s_server -accept 8080 -www -certform der -key "+
              state.keyFile +" -cert "+ state.certFile);

  // XXX: Explicitly exit, since something's tenacious here
  process.exit(0);
}

// BEGIN
main();

<|endoftext|>


<commit_before>var chai = require('chai'),
    sinon = require('sinon'),
    sinonChai = require('sinon-chai'),
    expect = chai.expect,
    rewire = require('rewire'),
    fakeDocker = require('../../lib/docker'),
    cli = rewire('../../lib/cli');

chai.use(sinonChai);

var endpointHTTP = 'http://127.0.0.1:2376',
    certPath = '/test',
    repository = 'test';

var pingSuccess = { ping: function() {} },
    pingFailure = { ping: function(callback) {
        callback(new Error());
    }};

describe('CLI', function() {
    beforeEach(function() {
        fakeExit = sinon.stub();
        fakeDocker.getClient = sinon.stub().returns(pingSuccess);
        fakeConsole = { error: sinon.stub() };
        revertDocker = cli.__set__('docker', fakeDocker);
        revertConsole = cli.__set__('console', fakeConsole);
    });

    afterEach(function() {
        revertDocker();
        revertConsole();
    });

    context('when called with default arguments', function() {
        beforeEach(function() {
            cli.argv(['node', 'server', '-e', endpointHTTP]);
        });

        expectNewDockerClientWith(endpointHTTP, '/home/.docker', 'grounds');
    });

    context('when called with custom arguments', function() {
        beforeEach(function() {
            cli.argv(['node', 'server', '-e', endpointHTTP,
                                        '--certs='+certPath,
                                        '--repository='+repository]);
        });
        // This test must use an http endpoint, otherwise it will search
        // for ssl certificates and fail.
        expectNewDockerClientWith(endpointHTTP, certPath, repository);
    });

    function expectNewDockerClientWith(endpoint, certs, repo) {
        var title = 'creates a docker client with endpoint: '+
                endpoint+', cert path: '+certs+', repository: '+repo;

        it(title, function() {
            expect(fakeDocker.getClient).to.have.been
                .calledWith(endpoint, certs, repo);
        });
    }

    context('when called with wrong docker endpoint', function() {
        beforeEach(function() {
            var invalidEndpoint = endpointHTTP.replace('http', 'ftp');

            cli.argv(['node', 'server', '-e', invalidEndpoint], fakeExit);
        });
        expectToLogError(fakeDocker.invalidDockerEndpoint);
        expectProgramToFail();
    });

    context('when called with invalid docker repository', function() {
        beforeEach(function() {
            cli.argv(['node', 'server', '-e', endpointHTTP,
                                        '--repository=/'], fakeExit);
        });
        expectToLogError(fakeDocker.invalidDockerRepository);
        expectProgramToFail();
    });

    context('when called with https docker api', function() {
        var endpointHTTPS = endpointHTTP.replace('http', 'https');

        context('with wrong certs path', function() {
            beforeEach(function() {
                cli.argv(['node', 'server', '-e', endpointHTTPS,
                                            '--certs=azerty'], fakeExit);
            });
            expectToLogError(fakeDocker.invalidDockerCertsPath);
            expectProgramToFail();
        });

        context('without ssl certificates', function() {
            beforeEach(function() {
                cli.argv(['node', 'server', '-e', endpointHTTPS,
                                            '--certs=./test'], fakeExit);
            });
            expectToLogError(fakeDocker.missingKeyCertificate);
            expectProgramToFail();
        });
    });

    context('when docker API is not responding', function() {
        beforeEach(function() {
            fakeDocker.getClient = sinon.stub().returns(pingFailure);
            revertDocker = cli.__set__('docker', fakeDocker);

            cli.argv(['node', 'server', '-e', endpointHTTP], fakeExit);
        });
        expectToLogError(fakeDocker.dockerAPINotResponding);
        expectProgramToFail();
    });

    context('when called with wrong port number', function() {
    });

    function expectProgramToFail() {
        it('exit the program with status code 1', function() {
            expect(fakeExit).to.have.been.calledWith(1);
        });
    }

    function expectToLogError(error) {
        it('logs error: '+error.message, function() {
            expect(fakeConsole.error).to.have.been.calledWith(error.message);
        });
    }
});
<commit_msg>rename docker error not responding<commit_after>var chai = require('chai'),
    sinon = require('sinon'),
    sinonChai = require('sinon-chai'),
    expect = chai.expect,
    rewire = require('rewire'),
    fakeDocker = require('../../lib/docker'),
    cli = rewire('../../lib/cli');

chai.use(sinonChai);

var endpointHTTP = 'http://127.0.0.1:2376',
    certPath = '/test',
    repository = 'test';

var pingSuccess = { ping: function() {} },
    pingFailure = { ping: function(callback) {
        callback(new Error());
    }};

describe('CLI', function() {
    beforeEach(function() {
        fakeExit = sinon.stub();
        fakeDocker.getClient = sinon.stub().returns(pingSuccess);
        fakeConsole = { error: sinon.stub() };
        revertDocker = cli.__set__('docker', fakeDocker);
        revertConsole = cli.__set__('console', fakeConsole);
    });

    afterEach(function() {
        revertDocker();
        revertConsole();
    });

    context('when called with default arguments', function() {
        beforeEach(function() {
            cli.argv(['node', 'server', '-e', endpointHTTP]);
        });

        expectNewDockerClientWith(endpointHTTP, '/home/.docker', 'grounds');
    });

    context('when called with custom arguments', function() {
        beforeEach(function() {
            cli.argv(['node', 'server', '-e', endpointHTTP,
                                        '--certs='+certPath,
                                        '--repository='+repository]);
        });
        // This test must use an http endpoint, otherwise it will search
        // for ssl certificates and fail.
        expectNewDockerClientWith(endpointHTTP, certPath, repository);
    });

    function expectNewDockerClientWith(endpoint, certs, repo) {
        var title = 'creates a docker client with endpoint: '+
                endpoint+', cert path: '+certs+', repository: '+repo;

        it(title, function() {
            expect(fakeDocker.getClient).to.have.been
                .calledWith(endpoint, certs, repo);
        });
    }

    context('when called with wrong docker endpoint', function() {
        beforeEach(function() {
            var invalidEndpoint = endpointHTTP.replace('http', 'ftp');

            cli.argv(['node', 'server', '-e', invalidEndpoint], fakeExit);
        });
        expectToLogError(fakeDocker.invalidDockerEndpoint);
        expectProgramToFail();
    });

    context('when called with invalid docker repository', function() {
        beforeEach(function() {
            cli.argv(['node', 'server', '-e', endpointHTTP,
                                        '--repository=/'], fakeExit);
        });
        expectToLogError(fakeDocker.invalidDockerRepository);
        expectProgramToFail();
    });

    context('when called with https docker api', function() {
        var endpointHTTPS = endpointHTTP.replace('http', 'https');

        context('with wrong certs path', function() {
            beforeEach(function() {
                cli.argv(['node', 'server', '-e', endpointHTTPS,
                                            '--certs=azerty'], fakeExit);
            });
            expectToLogError(fakeDocker.invalidDockerCertsPath);
            expectProgramToFail();
        });

        context('without ssl certificates', function() {
            beforeEach(function() {
                cli.argv(['node', 'server', '-e', endpointHTTPS,
                                            '--certs=./test'], fakeExit);
            });
            expectToLogError(fakeDocker.missingKeyCertificate);
            expectProgramToFail();
        });
    });

    context('when docker API is not responding', function() {
        beforeEach(function() {
            fakeDocker.getClient = sinon.stub().returns(pingFailure);
            revertDocker = cli.__set__('docker', fakeDocker);

            cli.argv(['node', 'server', '-e', endpointHTTP], fakeExit);
        });
        expectToLogError(fakeDocker.notResponding);
        expectProgramToFail();
    });

    context('when called with wrong port number', function() {
    });

    function expectProgramToFail() {
        it('exit the program with status code 1', function() {
            expect(fakeExit).to.have.been.calledWith(1);
        });
    }

    function expectToLogError(error) {
        it('logs error: '+error.message, function() {
            expect(fakeConsole.error).to.have.been.calledWith(error.message);
        });
    }
});
<|endoftext|>


<commit_before>'use strict';
/*jshint asi: true*/

var test = require('tap').test
  , createMap = require('../../lib/map')

test('\nsequences in insert mode', function (t) {

  t.test('\n# given I mapped [jk] to [escape]', function (t) {
    var map = createMap()

    map.insert('jk', 'escape')
    t.equal(map.mappings.insert.sequences.j.k, 'escape', 'it is added to the mappings under insert.sequences.j.k')

    t.equal(map.matchInsert('k', ['j']), 'escape', 'matching insert [kj] returns escape')
    t.equal(map.matchInsert('k', ['l']), null, 'matching insert [kl] returns null')
    t.equal(map.matchInsert('j', ['k']), null, 'matching insert [kj] returns null')
    t.equal(map.matchInsert('j', []), true, 'matching insert [j] returns true to indicate possible future match')

    t.end()
  })

  t.test('\n# given I mapped [mug] to [escape]', function (t) {
    var map = createMap()

    map.insert('mug', 'escape')
    t.equal(map.mappings.insert.sequences.m.u.g, 'escape', 'it is added to the mappings under insert.sequences.m.u.g')

    t.equal(map.matchInsert('g', ['m', 'u']), 'escape', 'matching insert [mug] returns escape')
    t.equal(map.matchInsert('u', ['m']), true, 'matching insert [mu] returns true to indicate possible future match')

    t.end()
  })
})

/*map.insert('ctrl-i', 'escape')
map.normal('shift-n', 'ctrl-p')*/

<commit_msg>fixing map tests<commit_after>'use strict';
/*jshint asi: true*/

var test = require('tap').test
  , createMap = require('../../lib/map')

test('\nsequences in insert mode', function (t) {

  t.test('\n# given I mapped [jk] to [escape]', function (t) {
    var map = createMap()

    map.insert('jk', 'escape')
    t.equal(map.mappings.insert.sequences.j.k, 'escape', 'it is added to the mappings under insert.sequences.j.k')

    t.equal(map.matchInsert(['j', 'k']), 'escape', 'matching insert [kj] returns escape')
    t.equal(map.matchInsert(['l', 'k']), null, 'matching insert [kl] returns null')
    t.equal(map.matchInsert(['k', 'j']), null, 'matching insert [kj] returns null')
    t.equal(map.matchInsert(['j']), true, 'matching insert [j] returns true to indicate possible future match')

    t.end()
  })

  t.test('\n# given I mapped [mug] to [escape]', function (t) {
    var map = createMap()

    map.insert('mug', 'escape')
    t.equal(map.mappings.insert.sequences.m.u.g, 'escape', 'it is added to the mappings under insert.sequences.m.u.g')

    t.equal(map.matchInsert(['m', 'u', 'g']), 'escape', 'matching insert [mug] returns escape')
    t.equal(map.matchInsert(['m', 'u']), true, 'matching insert [mu] returns true to indicate possible future match')

    t.end()
  })
})

/*map.insert('ctrl-i', 'escape')
map.normal('shift-n', 'ctrl-p')*/

<|endoftext|>


<commit_before>require("./token.js");

const fs = require("fs");
var mongoose = require("mongoose");
var chai = require("chai");
chai.use(require("chai-as-promised"));

var engine = require("../src/dslEngine");
var TokenAlreadyInsertException = require(
    "../src/utils/tokenAlreadyInsertException"
);
var NoTokenConnectedException = require(
    "../src/utils/noTokenConnectedException"
);
var NoNameException = require("../src/utils/noNameException");
var NoLabelException = require("../src/utils/noLabelException");
var WrongTypeException = require("../src/utils/wrongTypeException");

describe("LoadDSL", () => {
    var token;

    before(() => {
        token = engine.generateToken(mongoose.createConnection(
            `mongodb://${process.env.npm_package_config_CONNECTION}/prova`
        ));

        engine.pushToken(token);
    });

    after(() => {
	engine.ejectSafelyToken();
    });

    describe("#loadCollectionWithoutLabel", () => {
	it("should throw the NoLabelException", (done) => {
	    var dsl = `collection() {}`;

	    engine.loadDSL(dsl)
	    .catch((err) => {
		chai.expect(err).to.be.instanceof(NoLabelException);
		done();
	    });
	});
    });

    describe("#loadSingleCollectionEmpty", () => {
        it("should load the collection without errors", (done) => {
            var dsl = `collection (
		name: "pluto",
		label: "1",
		weight: 0
	    ) {}`;

            engine.loadDSL(dsl)
	    .then(() => {
		return done();
	    }).catch((err) => {
		return done(errs);
	    });
	});
    });

    describe("#loadCollectionWithIndex", () => {
	it("should load the collection without errors", (done) => {
	    var dsl = `collection (
		name: "pippo",
		label: "2"
	    ) {
		index (
		    query: {},
		) {}
	    }`;

	    engine.loadDSL(dsl)
	    .then(() => {
		return done();
	    }).catch((err) => {
		return done(err);
	    });
	});
    });

    // TODO
    describe("#loadCollectionWitShow", () => {
	it("should load the collection without errors", (done) => {
	    var dsl = `collection (
		name: "pippo",
		label: "3"
	    ) {
		show () {}
	    }`;

	    engine.loadDSL(dsl)
	    .then(() => {
		return done();
	    }).catch((err) => {
		return done(err);
	    });
	});
    });

    describe("#loadCollectionWithIndexColumn", () => {
	it("should load the collection without errors", (done) => {
	    var dsl = `collection (
		name: "pippo",
		label: "4"
	    ) {
		index() {
		    column (
			name: "Ecco"
		    )
		}
	    }`;

	    engine.loadDSL(dsl)
	    .then(() => {
		return done();
	    }).catch((err) => {
		return done(err);
	    });
	});
    });

    describe("#loadCollectionWithRow", () => {
	it("shoud load the collection without errors", (done) => {
	    var dsl = `collection (
		name: "pippo",
		label: "5"
	    ) {
		show () {
		    row (
			name: "gianni"
		    )
		}
	    }`;

	    engine.loadDSL(dsl)
	    .then(() => {
		return done();
	    }).catch((err) => {
		return done(err);
	    });
	});
    });
});
<commit_msg>Add some test<commit_after>require("./token.js");

const fs = require("fs");
var mongoose = require("mongoose");
var chai = require("chai");
chai.use(require("chai-as-promised"));

var engine = require("../src/dslEngine");
var TokenAlreadyInsertException = require(
    "../src/utils/tokenAlreadyInsertException"
);
var NoTokenConnectedException = require(
    "../src/utils/noTokenConnectedException"
);
var NoNameException = require("../src/utils/noNameException");
var NoLabelException = require("../src/utils/noLabelException");
var WrongTypeException = require("../src/utils/wrongTypeException");

describe("LoadDSL", () => {
    var token;

    before(() => {
        token = engine.generateToken(mongoose.createConnection(
            `mongodb://${process.env.npm_package_config_CONNECTION}/prova`
        ));

        engine.pushToken(token);
    });

    after(() => {
	engine.ejectSafelyToken();
    });

    describe("#loadCollectionWithoutLabel", () => {
	it("should throw the NoLabelException", (done) => {
	    var dsl = `collection() {}`;

	    engine.loadDSL(dsl)
	    .catch((err) => {
		chai.expect(err).to.be.instanceof(NoLabelException);
		done();
	    });
	});
    });

    describe("#loadSingleCollectionEmpty", () => {
        it("should load the collection without errors", (done) => {
            var dsl = `collection (
		name: "pluto",
		label: "1",
		weight: 0
	    ) {}`;

            engine.loadDSL(dsl)
	    .then(() => {
		return done();
	    }).catch((err) => {
		return done(errs);
	    });
	});
    });

    describe("#loadCollectionWithIndex", () => {
	it("should load the collection without errors", (done) => {
	    var dsl = `collection (
		name: "pippo",
		label: "2"
	    ) {
		index (
		    query: {},
		) {}
	    }`;

	    engine.loadDSL(dsl)
	    .then(() => {
		return done();
	    }).catch((err) => {
		return done(err);
	    });
	});
    });

    // TODO
    describe("#loadCollectionWitShow", () => {
	it("should load the collection without errors", (done) => {
	    var dsl = `collection (
		name: "pippo",
		label: "3"
	    ) {
		show () {}
	    }`;

	    engine.loadDSL(dsl)
	    .then(() => {
		return done();
	    }).catch((err) => {
		return done(err);
	    });
	});
    });

    describe("#loadCollectionWithIndexColumn", () => {
	it("should load the collection without errors", (done) => {
	    var dsl = `collection (
		name: "pippo",
		label: "4"
	    ) {
		index() {
		    column (
			name: "Ecco"
		    )
		}
	    }`;

	    engine.loadDSL(dsl)
	    .then(() => {
		return done();
	    }).catch((err) => {
		return done(err);
	    });
	});
    });

    describe("#loadCollectionWithRow", () => {
	it("should load the collection without errors", (done) => {
	    var dsl = `collection (
		name: "pippo",
		label: "5"
	    ) {
		show () {
		    row (
			name: "gianni"
		    )
		}
	    }`;

	    engine.loadDSL(dsl)
	    .then(() => {
		return done();
	    }).catch((err) => {
		return done(err);
	    });
	});
    });

    describe("#loadDocumentWithRow", () => {
	it("should load the model without errors", (done) => {
	    var dsl = `document(
		name: "pippo",
		label: "6"
	    ) {
		row(
		    name: "p"
		)
		row(
		    name: "s"
		)
	    }`;

	    engine.loadDSL(dsl)
	    .then(() => {
		return done();
	    }).catch((err) => {
		return done(err);
	    });
	});
    });

    describe("#loadWrongDocument", () => {
	it("should throw an exception", (done) => {
	    var dsl = `document(
		name: "tu"
	    ) {}`;

	    engine.loadDSL(dsl)
		.catch((err) => {
		    return done();
		});
	});
    });
});
<|endoftext|>


<commit_before>(function () { /* Begin of privacy scope */
    "use strict";
    /*global gpfSourcesPath*/

    var
        _eventsHandler,
        _tests = {},
        _names = [],
        _sources,
        _sourcesIdx,

        _testDetailsCompare = function () {
            var
                details = ["1:\t("],
                type = typeof arguments[0];
            details.push(type, ")\t");
            if ("undefined" === type) {
                details.push("undefined");
            } else {
                details.push(gpf.json.stringify(arguments[0]));
            }
            details.push("\r\n2:\t(");
            type = typeof arguments[1];
            details.push(type, ")\t");
            if ("undefined" === type) {
                details.push("undefined");
            } else {
                details.push(gpf.json.stringify(arguments[1]));
            }
            details.push("\r\nare");
            if ("equal" === this._type || "like" === this._type) {
                details.push(" not");
            }
            if ("equal" === this._type || "notEqual" === this._type) {
                details.push(" equal");
            } else {
                details.push(" alike");
            }
            return details.join("");
        },

        _testDetails = {
            exception: function () {
                return ["message: ", arguments[0].message].join("");
            },
            equal: _testDetailsCompare,
            notEqual: _testDetailsCompare,
            like: _testDetailsCompare,
            notLike: _testDetailsCompare,
            assert: function () {
                var
                    details = [],
                    idx = 1,
                    obj,
                    objIsTest,
                    testProto = TestReport.prototype,
                    property,
                    value;
                while (idx + 1 < arguments.length) {
                    obj = arguments[idx++];
                    if (null === obj) {
                        continue;
                    }
                    objIsTest = obj.constructor === TestReport;
                    for (property in obj) {
                        if (obj.hasOwnProperty(property)) {
                            if (objIsTest && testProto.hasOwnProperty(property)
                                || property.charAt(0) === "_") {
                                continue;
                            }
                            value = obj[property];
                            if (undefined === value) {
                                value = "<undefined>";
                            } else if (null === value) {
                                value = "<null>";
                            } else if ("function" === typeof value) {
                                value = "<function>";
                            }
                            details.push("\t", property, "= ", value.toString(),
                                "\n");
                        }
                    }
                }
                return details.join("");
            }
        };

    function output(type, text, eventsHandler) {
        if (!eventsHandler) {
            eventsHandler = _eventsHandler;
        }
        gpf.events.fire(type, {message: text}, eventsHandler);
    }

    function error(text, eventsHandler) {
        output("error", text, eventsHandler);
    }

    function warning(text, eventsHandler) {
        output("warning", text, eventsHandler);
    }

    function info(text, eventsHandler) {
        output("info", text, eventsHandler);
    }

    function log(text, eventsHandler) {
        output("log", text, eventsHandler);
    }

    function TestItem(type, parameters) {
        this._type = type;
        this._parameters = parameters;
    }

    gpf.extend(TestItem.prototype, {

        output: function (eventsHandler) {
            var
                message = this._parameters[this._parameters.length - 1],
                details,
                succeeded;
            if ("title" === this._type) {
                info(message, eventsHandler);
            } else {
                succeeded = this.isSuccess();
                if (!succeeded) {
                    message = [ message, ": ", this._type, " error" ];
                    details = this.getDetails();
                    if (details) {
                        message.push("\n", details);
                    }
                    message = message.join("");
                }
                if (this.isSuccess()) {
                    log(message, eventsHandler);
                } else {
                    error(message, eventsHandler);
                }
            }
        },

        getDetails: function() {
            return _testDetails[this._type].apply(this, this._parameters);
        },

        isSuccess: function() {
            if ("title" === this._type || "log" === this._type) {
                return true;
            } else if ("equal" === this._type) {
                return this._parameters[0] === this._parameters[1];
            } else if ("notEqual" === this._type) {
                return this._parameters[0] !== this._parameters[1];
            } else if ("like" === this._type) {
                return gpf.like(this._parameters[0], this._parameters[1]);
            } else if ("notLike" === this._type) {
                return !gpf.like(this._parameters[0], this._parameters[1]);
            } else if ("assert" === this._type) {
                return this._parameters[0];
            }
            return false; // Unknown test
        }

    });

    function TestReport() {
        this._items = [];
        this._errors = 0;
    }

    gpf.each("title,log,assert,equal,notEqual,like,notLike,exception"
        .split(","),
        function(/*idx, name*/){
            var name = arguments[1];
            TestReport.prototype[name] = function() {
                var item = new TestItem(name, arguments);
                this._items.push(item);
                if (!item.isSuccess()) {
                    ++this._errors;
                }
            };
        });

    gpf.extend(TestReport.prototype, {

        _items: [],
        _errors: 0,
        _sync: true,
        _waitDt: null,
        _done: false,
        _callback: null,
        _lastParams: null,

        output: function (eventsHandler) {
            var idx;
            for (idx = 0; idx < this._items.length; ++idx) {
                this._items[idx].output(eventsHandler);
            }
        },

        getTestCount: function () {
            var
                idx,
                item,
                result = 0;
            for (idx = 0; idx < this._items.length; ++idx) {
                item = this._items[idx];
                // Inelegant but works
                if (undefined !== _testDetails[item._type]) {
                    ++result;
                }
            }
            return result;
        },

        wait: function (timeout) {
            this._sync = false;
            this._waitDt = new Date();
            gpf.defer(this._waitedTooLong, timeout || TestReport.WAIT_TIMEOUT,
                this);
        },

        _waitedTooLong: function () {
            if (!this._done) {
                this.assert(false, "Waited too long");
                this.done();
            }
        },

        synchronous: function (callback, lastParams) {
            if (this._done) {
                return true;
            }
            if (!this._sync) {
                this._callback = callback;
                this._lastParams = lastParams;
            }
            return this._sync;
        },

        done: function () {
            var timeSpent = (new Date()) - this._waitDt;
            if (!this._done) {
                this._done = true;
                this.log("Waiting time: " + timeSpent + "ms");
                if (this._callback) {
                    this._callback.apply(null, [this].concat(this._lastParams));
                }
            }
        }

    });
    TestReport.WAIT_TIMEOUT = 100;

    function includeFailed(e) {
        var source = _sources[_sourcesIdx - 1];
        if (e) {
            warning(e.message);
        } else {
            warning("Missing " + source);
        }
    }

    function includeFailedAsync() {
        includeFailed();
        loadTestSources();
    }

    function wscriptInclude(src) {
        var srcFile;
        if (!wscriptInclude.fso) {
            wscriptInclude.fso =
                new ActiveXObject("Scripting.FileSystemObject");
        }
        if (wscriptInclude.fso.FileExists(src)) {
            srcFile = wscriptInclude.fso.OpenTextFile(src);
            try {
                /*jslint evil:true*/
                eval(srcFile.ReadAll());
                /*jslint evil:false*/
            } catch (e) {
                includeFailed(e);
            }
            srcFile.Close();
        } else {
            includeFailed();
        }
        return false;
    }

    function nodejsInclude(src) {
        try {
            // require is relative to the current file (i.e. manager)
            require("../" + src);
        } catch (e) {
            includeFailed(e);
        }
        return false;
    }

    function include(src) {
        log("Reading '" + src + "'");
        if ("wscript" === gpf.host()) {
            return wscriptInclude(src);
        } else if ("nodejs" === gpf.host()) {
            return nodejsInclude(src);
        } else { // browser
            gpf.http.include(src, {
                load: loadTestSources,
                error: includeFailedAsync
            });
            return true; // Asynchronous
        }
    }

    function asyncTestDone(report, callback, context) {
        var args;
        if (0 === report._items.length) {
            report.assert(false, "Empty report");
        }
        if (callback) {
            args = [report];
            if (context) {
                args.push(context);
            }
            callback.apply(null, args);
        }
    }

    function executeTest(name, callback, context) {
        var
            testFunction,
            report = new TestReport();
        if (name instanceof Function) {
            testFunction = name;
        } else {
            testFunction = _tests[name];
        }
        if (context) {
            context._start = new Date();
        }
        try {
            testFunction(report);
        } catch (e) {
            report.exception(e, "Unexpected exception");
        }
        if (report.synchronous(asyncTestDone, [callback, context])) {
            asyncTestDone(report, callback, context);
        }
    }

    function executeNextTest(context) {
        var name;
        if (context.namesIdx < _names.length) {
            name = _names[context.namesIdx];
            ++context.total;
            executeTest(name, executeAfterTest, context);
        } else {
            info("Number of tested conditions: " + context.testCount,
                _eventsHandler);
            if (0 === context.errors) {
                info("All tests succeeded (" + context.total + ")",
                    _eventsHandler);
            } else {
                error("Some tests failed (" + context.errors + "/"
                    + context.total + ")", _eventsHandler);
            }
        }
    }

    function executeAfterTest(report, context) {
        var
            name = _names[context.namesIdx],
            timespent;
        if (context && context._start) {
            timespent = (new Date()) - context._start;
        }
        context.testCount += report.getTestCount();
        ++context.namesIdx;
        if (0 === report._errors) {
            gpf.events.fire("success", {
                name: name,
                timespent: timespent
            }, _eventsHandler);
        } else {
            gpf.events.fire("failure", {
                name: name,
                timespent: timespent
            }, _eventsHandler);
            ++context.errors;
        }
        // Success or failure may lead to the inner call of gpf.testReport
        if (gpf.testReport._inProgress) {
            gpf.testReport._context = context;
        } else {
            executeNextTest(context);
        }
    }

    function executeTests() {
        var
            context = {
                namesIdx: 0,
                errors: 0,
                total: 0,
                testCount: 0
            };
        info("Test count: " + _names.length);
        executeNextTest(context);
    }

    function loadTestSources() {
        var src;
        while (_sourcesIdx < _sources.length) {
            src = "test/" + _sources[_sourcesIdx++] + ".js";
            if ("undefined" !== typeof gpfSourcesPath) {
                src = gpfSourcesPath + src;
            }
            if (include(src)) {
                return; // Asynchronous, must wait
            }
        }
        executeTests();
    }

    /**
     *
     * @param {Object} sourceTests
     * @return {undefined}
     *
     * @internal
     */
    gpf.declareTests = function (sourceTests) {
        var
            source = _sources[_sourcesIdx - 1],
            component,
            functions,
            idx,
            name;
        for (component in sourceTests) {
            if (sourceTests.hasOwnProperty(component)) {
                functions = sourceTests[component];
                for (idx = 0; idx < functions.length; ++idx) {
                    name = source + "/" + component + "/" + idx;
                    _tests[name] = functions[idx];
                    _names.push(name);
                }
            }
        }
    };

    /**
     * Handles the tests
     *
     * @param {Object/function} eventsHandler
     * @return {Number} Number of errors
     *
     * @eventParam {string} message The message
     *
     * @event error The manager outputs an error message
     * @event warning The manager outputs a warning message
     * @event info The manager outputs an information message
     * @event log The manager outputs a log message
     *
     * @eventParam {string} name The test name
     * @event success The test succeeded
     * @event failure The test failed
     */
    gpf.runTests = function (eventsHandler) {
        // Check that sources are pre-loaded first
        _eventsHandler = eventsHandler;
        if (!gpf.sources) {
            error("Missing gpf.sources");
            return 1;
        }
        _sources = gpf.sources().split(",");
        _sourcesIdx = 0;
        loadTestSources();
        return 0;
    };

    /**
     * Explain a failing test by generating a report
     *
     * @param {String} name
     * @param {Object/function} eventsHandler
     * @return {undefined}
     *
     * @eventParam {string} message The message
     *
     * @event error The manager outputs an error message
     * @event warning The manager outputs a warning message
     * @event info The manager outputs an information message
     * @event log The manager outputs a log message
     */
    gpf.testReport = function (name, eventsHandler) {
        var context;
        gpf.testReport._inProgress = true;
        executeTest(name, function (report){
            report.output(eventsHandler);
            gpf.testReport._inProgress = false;
            if (gpf.testReport._context) {
                context = gpf.testReport._context;
                gpf.testReport._context = null;
                executeNextTest(context);
            }
        });
    };

    /**
     * Used to know if a call is in progress
     *
     * @type {boolean}
     * @private
     */
    gpf.testReport._inProgress = false;

    /**
     * Call executeNext(_context) if set on completion
     *
     * @type {Object}
     * @private
     */
    gpf.testReport._context = null;

}()); /* End of privacy scope */
<commit_msg>Provided a way to wait infinitely (for DEBUGGING)<commit_after>(function () { /* Begin of privacy scope */
    "use strict";
    /*global gpfSourcesPath*/

    var
        _eventsHandler,
        _tests = {},
        _names = [],
        _sources,
        _sourcesIdx,

        _testDetailsCompare = function () {
            var
                details = ["1:\t("],
                type = typeof arguments[0];
            details.push(type, ")\t");
            if ("undefined" === type) {
                details.push("undefined");
            } else {
                details.push(gpf.json.stringify(arguments[0]));
            }
            details.push("\r\n2:\t(");
            type = typeof arguments[1];
            details.push(type, ")\t");
            if ("undefined" === type) {
                details.push("undefined");
            } else {
                details.push(gpf.json.stringify(arguments[1]));
            }
            details.push("\r\nare");
            if ("equal" === this._type || "like" === this._type) {
                details.push(" not");
            }
            if ("equal" === this._type || "notEqual" === this._type) {
                details.push(" equal");
            } else {
                details.push(" alike");
            }
            return details.join("");
        },

        _testDetails = {
            exception: function () {
                return ["message: ", arguments[0].message].join("");
            },
            equal: _testDetailsCompare,
            notEqual: _testDetailsCompare,
            like: _testDetailsCompare,
            notLike: _testDetailsCompare,
            assert: function () {
                var
                    details = [],
                    idx = 1,
                    obj,
                    objIsTest,
                    testProto = TestReport.prototype,
                    property,
                    value;
                while (idx + 1 < arguments.length) {
                    obj = arguments[idx++];
                    if (null === obj) {
                        continue;
                    }
                    objIsTest = obj.constructor === TestReport;
                    for (property in obj) {
                        if (obj.hasOwnProperty(property)) {
                            if (objIsTest && testProto.hasOwnProperty(property)
                                || property.charAt(0) === "_") {
                                continue;
                            }
                            value = obj[property];
                            if (undefined === value) {
                                value = "<undefined>";
                            } else if (null === value) {
                                value = "<null>";
                            } else if ("function" === typeof value) {
                                value = "<function>";
                            }
                            details.push("\t", property, "= ", value.toString(),
                                "\n");
                        }
                    }
                }
                return details.join("");
            }
        };

    function output(type, text, eventsHandler) {
        if (!eventsHandler) {
            eventsHandler = _eventsHandler;
        }
        gpf.events.fire(type, {message: text}, eventsHandler);
    }

    function error(text, eventsHandler) {
        output("error", text, eventsHandler);
    }

    function warning(text, eventsHandler) {
        output("warning", text, eventsHandler);
    }

    function info(text, eventsHandler) {
        output("info", text, eventsHandler);
    }

    function log(text, eventsHandler) {
        output("log", text, eventsHandler);
    }

    function TestItem(type, parameters) {
        this._type = type;
        this._parameters = parameters;
    }

    gpf.extend(TestItem.prototype, {

        output: function (eventsHandler) {
            var
                message = this._parameters[this._parameters.length - 1],
                details,
                succeeded;
            if ("title" === this._type) {
                info(message, eventsHandler);
            } else {
                succeeded = this.isSuccess();
                if (!succeeded) {
                    message = [ message, ": ", this._type, " error" ];
                    details = this.getDetails();
                    if (details) {
                        message.push("\n", details);
                    }
                    message = message.join("");
                }
                if (this.isSuccess()) {
                    log(message, eventsHandler);
                } else {
                    error(message, eventsHandler);
                }
            }
        },

        getDetails: function() {
            return _testDetails[this._type].apply(this, this._parameters);
        },

        isSuccess: function() {
            if ("title" === this._type || "log" === this._type) {
                return true;
            } else if ("equal" === this._type) {
                return this._parameters[0] === this._parameters[1];
            } else if ("notEqual" === this._type) {
                return this._parameters[0] !== this._parameters[1];
            } else if ("like" === this._type) {
                return gpf.like(this._parameters[0], this._parameters[1]);
            } else if ("notLike" === this._type) {
                return !gpf.like(this._parameters[0], this._parameters[1]);
            } else if ("assert" === this._type) {
                return this._parameters[0];
            }
            return false; // Unknown test
        }

    });

    function TestReport() {
        this._items = [];
        this._errors = 0;
    }

    gpf.each("title,log,assert,equal,notEqual,like,notLike,exception"
        .split(","),
        function(/*idx, name*/){
            var name = arguments[1];
            TestReport.prototype[name] = function() {
                var item = new TestItem(name, arguments);
                this._items.push(item);
                if (!item.isSuccess()) {
                    ++this._errors;
                }
            };
        });

    gpf.extend(TestReport.prototype, {

        _items: [],
        _errors: 0,
        _sync: true,
        _waitDt: null,
        _done: false,
        _callback: null,
        _lastParams: null,

        output: function (eventsHandler) {
            var idx;
            for (idx = 0; idx < this._items.length; ++idx) {
                this._items[idx].output(eventsHandler);
            }
        },

        getTestCount: function () {
            var
                idx,
                item,
                result = 0;
            for (idx = 0; idx < this._items.length; ++idx) {
                item = this._items[idx];
                // Inelegant but works
                if (undefined !== _testDetails[item._type]) {
                    ++result;
                }
            }
            return result;
        },

        wait: function (timeout) {
            this._sync = false;
            this._waitDt = new Date();
            if (-1 !== timeout) {
                gpf.defer(this._waitedTooLong,
                    timeout || TestReport.WAIT_TIMEOUT, this);
            }
        },

        _waitedTooLong: function () {
            if (!this._done) {
                this.assert(false, "Waited too long");
                this.done();
            }
        },

        synchronous: function (callback, lastParams) {
            if (this._done) {
                return true;
            }
            if (!this._sync) {
                this._callback = callback;
                this._lastParams = lastParams;
            }
            return this._sync;
        },

        done: function () {
            var timeSpent = (new Date()) - this._waitDt;
            if (!this._done) {
                this._done = true;
                this.log("Waiting time: " + timeSpent + "ms");
                if (this._callback) {
                    this._callback.apply(null, [this].concat(this._lastParams));
                }
            }
        }

    });
    TestReport.WAIT_TIMEOUT = 100;

    function includeFailed(e) {
        var source = _sources[_sourcesIdx - 1];
        if (e) {
            warning(e.message);
        } else {
            warning("Missing " + source);
        }
    }

    function includeFailedAsync() {
        includeFailed();
        loadTestSources();
    }

    function wscriptInclude(src) {
        var srcFile;
        if (!wscriptInclude.fso) {
            wscriptInclude.fso =
                new ActiveXObject("Scripting.FileSystemObject");
        }
        if (wscriptInclude.fso.FileExists(src)) {
            srcFile = wscriptInclude.fso.OpenTextFile(src);
            try {
                /*jslint evil:true*/
                eval(srcFile.ReadAll());
                /*jslint evil:false*/
            } catch (e) {
                includeFailed(e);
            }
            srcFile.Close();
        } else {
            includeFailed();
        }
        return false;
    }

    function nodejsInclude(src) {
        try {
            // require is relative to the current file (i.e. manager)
            require("../" + src);
        } catch (e) {
            includeFailed(e);
        }
        return false;
    }

    function include(src) {
        log("Reading '" + src + "'");
        if ("wscript" === gpf.host()) {
            return wscriptInclude(src);
        } else if ("nodejs" === gpf.host()) {
            return nodejsInclude(src);
        } else { // browser
            gpf.http.include(src, {
                load: loadTestSources,
                error: includeFailedAsync
            });
            return true; // Asynchronous
        }
    }

    function asyncTestDone(report, callback, context) {
        var args;
        if (0 === report._items.length) {
            report.assert(false, "Empty report");
        }
        if (callback) {
            args = [report];
            if (context) {
                args.push(context);
            }
            callback.apply(null, args);
        }
    }

    function executeTest(name, callback, context) {
        var
            testFunction,
            report = new TestReport();
        if (name instanceof Function) {
            testFunction = name;
        } else {
            testFunction = _tests[name];
        }
        if (context) {
            context._start = new Date();
        }
        try {
            testFunction(report);
        } catch (e) {
            report.exception(e, "Unexpected exception");
        }
        if (report.synchronous(asyncTestDone, [callback, context])) {
            asyncTestDone(report, callback, context);
        }
    }

    function executeNextTest(context) {
        var name;
        if (context.namesIdx < _names.length) {
            name = _names[context.namesIdx];
            ++context.total;
            executeTest(name, executeAfterTest, context);
        } else {
            info("Number of tested conditions: " + context.testCount,
                _eventsHandler);
            if (0 === context.errors) {
                info("All tests succeeded (" + context.total + ")",
                    _eventsHandler);
            } else {
                error("Some tests failed (" + context.errors + "/"
                    + context.total + ")", _eventsHandler);
            }
        }
    }

    function executeAfterTest(report, context) {
        var
            name = _names[context.namesIdx],
            timespent;
        if (context && context._start) {
            timespent = (new Date()) - context._start;
        }
        context.testCount += report.getTestCount();
        ++context.namesIdx;
        if (0 === report._errors) {
            gpf.events.fire("success", {
                name: name,
                timespent: timespent
            }, _eventsHandler);
        } else {
            gpf.events.fire("failure", {
                name: name,
                timespent: timespent
            }, _eventsHandler);
            ++context.errors;
        }
        // Success or failure may lead to the inner call of gpf.testReport
        if (gpf.testReport._inProgress) {
            gpf.testReport._context = context;
        } else {
            executeNextTest(context);
        }
    }

    function executeTests() {
        var
            context = {
                namesIdx: 0,
                errors: 0,
                total: 0,
                testCount: 0
            };
        info("Test count: " + _names.length);
        executeNextTest(context);
    }

    function loadTestSources() {
        var src;
        while (_sourcesIdx < _sources.length) {
            src = "test/" + _sources[_sourcesIdx++] + ".js";
            if ("undefined" !== typeof gpfSourcesPath) {
                src = gpfSourcesPath + src;
            }
            if (include(src)) {
                return; // Asynchronous, must wait
            }
        }
        executeTests();
    }

    /**
     *
     * @param {Object} sourceTests
     * @return {undefined}
     *
     * @internal
     */
    gpf.declareTests = function (sourceTests) {
        var
            source = _sources[_sourcesIdx - 1],
            component,
            functions,
            idx,
            name;
        for (component in sourceTests) {
            if (sourceTests.hasOwnProperty(component)) {
                functions = sourceTests[component];
                for (idx = 0; idx < functions.length; ++idx) {
                    name = source + "/" + component + "/" + idx;
                    _tests[name] = functions[idx];
                    _names.push(name);
                }
            }
        }
    };

    /**
     * Handles the tests
     *
     * @param {Object/function} eventsHandler
     * @return {Number} Number of errors
     *
     * @eventParam {string} message The message
     *
     * @event error The manager outputs an error message
     * @event warning The manager outputs a warning message
     * @event info The manager outputs an information message
     * @event log The manager outputs a log message
     *
     * @eventParam {string} name The test name
     * @event success The test succeeded
     * @event failure The test failed
     */
    gpf.runTests = function (eventsHandler) {
        // Check that sources are pre-loaded first
        _eventsHandler = eventsHandler;
        if (!gpf.sources) {
            error("Missing gpf.sources");
            return 1;
        }
        _sources = gpf.sources().split(",");
        _sourcesIdx = 0;
        loadTestSources();
        return 0;
    };

    /**
     * Explain a failing test by generating a report
     *
     * @param {String} name
     * @param {Object/function} eventsHandler
     * @return {undefined}
     *
     * @eventParam {string} message The message
     *
     * @event error The manager outputs an error message
     * @event warning The manager outputs a warning message
     * @event info The manager outputs an information message
     * @event log The manager outputs a log message
     */
    gpf.testReport = function (name, eventsHandler) {
        var context;
        gpf.testReport._inProgress = true;
        executeTest(name, function (report){
            report.output(eventsHandler);
            gpf.testReport._inProgress = false;
            if (gpf.testReport._context) {
                context = gpf.testReport._context;
                gpf.testReport._context = null;
                executeNextTest(context);
            }
        });
    };

    /**
     * Used to know if a call is in progress
     *
     * @type {boolean}
     * @private
     */
    gpf.testReport._inProgress = false;

    /**
     * Call executeNext(_context) if set on completion
     *
     * @type {Object}
     * @private
     */
    gpf.testReport._context = null;

}()); /* End of privacy scope */
<|endoftext|>


<commit_before>var p = require('path'),
  pkg = require(p.join(__dirname,'..','..','package.json')),
  fs = require('fs'),
  exec = require('child_process').exec,
  events = require('events'),
  evt = new events.EventEmitter(),
  semver = require('semver'),
  installer = require('../lib/installer');
require('colors');

evt.on('updatemods',function(coreupdated){
  if (coreupdated){
    delete require.cache[p.join(__dirname,'..','..','package.json')];
    pkg = require(p.join(__dirname,'..','..','package.json'));
    console.log(('NGN updated to version '+pkg.version).green.bold);
  }

  console.log('Checking for updates...');

  // Loop through modules and update accordingly.
  for (var m in pkg.ngn.modules){
    installer.update(m);
  }
  if (fs.existsSync(p.join(__dirname,'../../../ngn-dev'))){
    installer.update('ngn-dev');
  }
});

// Check for core update
exec('npm show ngn version --silent',function(e,stdout,stderr){
  if (e) throw e;
  if (semver.lt(pkg.version,stdout)){
    console.log('Updating NGN Core...'.yellow.bold);
    installer.update('ngn',function(){
      evt.emit('updatemods',true);
    });
  } else {
    console.log(('The latest version of NGN, v'+pkg.version+', is already installed.').green.bold);
    evt.emit('updatemods',false);
  }
});<commit_msg>Updated feedback<commit_after>var p = require('path'),
  pkg = require(p.join(__dirname,'..','..','package.json')),
  fs = require('fs'),
  exec = require('child_process').exec,
  events = require('events'),
  evt = new events.EventEmitter(),
  semver = require('semver'),
  installer = require('../lib/installer');
require('colors');

evt.on('updatemods',function(core){
  if (core.updated){
    delete require.cache[p.join(__dirname,'..','..','package.json')];
    pkg = require(p.join(__dirname,'..','..','package.json'));
    console.log(('NGN updated to version '+pkg.version).green.bold);
  }

  console.log('Checking for updates...');

  // Loop through modules and update accordingly.
  for (var m in pkg.ngn.modules){
    installer.update(m,function(updated){
      if (updated){
        console.log((m+' updated successfully.').green.bold);
      }
    });
  }
  if (fs.existsSync(p.join(__dirname,'../../../ngn-dev'))){
    installer.update('ngn-dev');
  }
});

// Check for core update
exec('npm show ngn version --silent',function(e,stdout,stderr){
  if (e) throw e;
  if (semver.lt(pkg.version,stdout)){
    console.log('Updating NGN Core...'.yellow.bold);
    installer.update('ngn',function(){
      evt.emit('updatemods',{updated: true});
    });
  } else {
    console.log(('The latest version of NGN, v'+pkg.version+', is already installed.').green.bold);
    evt.emit('updatemods',{updated: false});
  }
});<|endoftext|>


<commit_before>#!/usr/bin/env node

var program = require('commander');
var pkg = require('../package');
var lockdown = require('../');
var path = require('path');
var fs = require('fs');
var cwd = process.cwd();
var file = path.join(cwd, '.eslintrc');

program
    .version(pkg.version)
    .option('-d', '--debug', 'Don\'t overwrite the `.eslintrc` file')
    .parse(process.argv);

function run(config, cb) {
    lockdown(cwd, config, function(err, res) {
        if (err) return cb(err);
        return cb(null, config);
    });
}

function read(cb) {
    fs.access(file, fs.R_OK, function(err) {
        if (err) return cb(err);
        fs.readFile(file, function(err, file) {
            if (err) return cb(err);
            var config = {};
            try {
                config = JSON.parse(file);
            } catch (e) {
                return cb(e);
            }
            return cb(null, config);
        });
    });
}

function write(config, cb) {
    console.log('file', file);
    fs.writeFile(file, JSON.stringify(config, null, 4), cb);
}

// all together now
read(function(err, config) {
    if (err || !config) config = {};
    run(config, function(err, config) {
        if (err) return console.error(err);
        console.log(program);

        if (program.debug) return console.log(config);
        write(config, function(err) {
            if (err) return console.error(err);
        });
    });
});
<commit_msg>use the same output for debug as the file writer<commit_after>#!/usr/bin/env node

var program = require('commander');
var pkg = require('../package');
var lockdown = require('../');
var path = require('path');
var fs = require('fs');
var cwd = process.cwd();
var file = path.join(cwd, '.eslintrc');

program
    .version(pkg.version)
    .option('--debug', '-D', 'Don\'t overwrite the `.eslintrc` file')
    .parse(process.argv);

function run(config, cb) {
    lockdown(cwd, config, function(err, config) {
        if (err) return cb(err);
        return cb(null, config);
    });
}

function read(cb) {
    fs.access(file, fs.R_OK, function(err) {
        if (err) return cb(err);
        fs.readFile(file, function(err, file) {
            if (err) return cb(err);
            var config = {};
            try {
                config = JSON.parse(file);
            } catch (e) {
                return cb(e);
            }
            return cb(null, config);
        });
    });
}

function write(body, cb) {
    fs.writeFile(file, body, cb);
}

// all together now
read(function(err, config) {
    if (err || !config) config = {};
    run(config, function(err, config) {
        if (err) return console.error(err);
        var pretty = JSON.stringify(config, null, 4);
        if (program.debug) return console.log(pretty);
        write(pretty, function(err) {
            if (err) return console.error(err);
        });
    });
});
<|endoftext|>


<commit_before>
var mnode = require('..');

exports['Send Message to Connected Node'] = function (test) {
    test.expect(4);

    var nmsg = 0;

    var node = mnode.createNode();
    var node2 = mnode.createNode();

    var result = { sum: 0, total: 3 };

    node.registerService('service1', new Service(test, 1, result, done));
    node2.registerService('service2', new Service(test, 2, result, done));

    node.listen(3000);
    node2.connect(3000, function (client) {
        node.process({ service: 'service2', message: 2 });
        node2.process({ service: 'service1', message: 1 });
    });

    function done() {
        test.done();
        node.stop();
        node2.stop();
    }
};

function Service(test, message, result, done) {
    this.process = function (msg) {
        test.ok(msg);
        test.equal(msg, message);
        result.sum += msg;

        if (result.sum == result.total)
            done();
    };
}
<commit_msg>Refactor network test, to send message at server and client connection<commit_after>
var mnode = require('..');

exports['Send Message to Connected Node'] = function (test) {
    test.expect(4);

    var nmsg = 0;

    var node = mnode.createNode();
    var node2 = mnode.createNode();

    var result = { sum: 0, total: 3 };

    node.registerService('service1', new Service(test, 1, result, done));
    node2.registerService('service2', new Service(test, 2, result, done));

    node.listen(3000, function () {
        node.process({ service: 'service2', message: 2 });
    });

    node2.connect(3000, function () {
        node2.process({ service: 'service1', message: 1 });
    });

    function done() {
        test.done();
        node.stop();
        node2.stop();
    }
};

function Service(test, message, result, done) {
    this.process = function (msg) {
        test.ok(msg);
        test.equal(msg, message);
        result.sum += msg;

        if (result.sum == result.total)
            done();
    };
}
<|endoftext|>