1
0
mirror of https://github.com/verdaccio/verdaccio.git synced 2025-02-21 07:29:37 +01:00

Merge pull request #235 from verdaccio/refactor_test

Refactor functional testing
This commit is contained in:
Juan Picado @jotadeveloper 2017-07-03 20:59:39 +02:00 committed by GitHub
commit da0618c442
40 changed files with 1020 additions and 603 deletions

@ -6,4 +6,4 @@ node_js:
- '8'
sudo: false
script: npm install . && npm run test-travis
after_success: npm run coverage:codecov && npm run coverage:codacy
after_success: npm run coverage:codecov

@ -167,10 +167,14 @@ notify:
# expression
packagePattern: ^example-package-regex$
# Any flags to be used with the regular expression
packagePatternFlags: i
# since verdaccio 2.2.2 this property has been disabled read #108
# it will be re-enabled after 2.5.0
# packagePatternFlags: i
# If this endpoint requires specific headers, set them here
# as an array of key: value objects.
headers: [{'Content-type': 'application/x-www-form-urlencoded'}]
# headers supports as well a literal object
headers: {'Content-type': 'application/x-www-form-urlencoded'}
# set the URL endpoint for this call
endpoint: https://hooks.slack.com/...
# Finally, the content you will be sending in the body.

@ -4,6 +4,8 @@
/* eslint no-empty:0 */
'use strict';
const _ = require('lodash');
if (process.getuid && process.getuid() === 0) {
global.console.error('Verdaccio doesn\'t need superuser privileges. Don\'t run it under root.');
}
@ -182,13 +184,17 @@ function afterConfigLoad() {
});
// undocumented stuff for tests
if (typeof(process.send) === 'function') {
process.send({verdaccio_started: true});
if (_.isFunction(process.send)) {
process.send({
verdaccio_started: true,
});
}
}
process.on('uncaughtException', function(err) {
logger.logger.fatal( {err: err},
logger.logger.fatal( {
err: err,
},
'uncaught exception, please report this\n@{err.stack}' );
process.exit(255);
});

@ -8,7 +8,9 @@ const logger = require('./logger');
const handleNotify = function(metadata, notifyEntry) {
let regex;
if (metadata.name && notifyEntry.packagePattern) {
regex = new RegExp(notifyEntry.packagePattern, notifyEntry.packagePatternFlags || '');
// FUTURE: comment out due https://github.com/verdaccio/verdaccio/pull/108#issuecomment-312421052
// regex = new RegExp(notifyEntry.packagePattern, notifyEntry.packagePatternFlags || '');
regex = new RegExp(notifyEntry.packagePattern);
if (!regex.test(metadata.name)) {
return;
}
@ -44,26 +46,32 @@ const handleNotify = function(metadata, notifyEntry) {
options.url = notifyEntry.endpoint;
}
request(options, function(err, response, body) {
if (err) {
logger.logger.error({err: err}, ' notify error: @{err.message}' );
} else {
logger.logger.info({content: content}, 'A notification has been shipped: @{content}');
if (body) {
logger.logger.debug({body: body}, ' body: @{body}' );
return new Promise(( resolve, reject) => {
request(options, function(err, response, body) {
if (err || response.statusCode >= 400) {
const errorMessage = _.isNil(err) ? response.statusMessage : err;
logger.logger.error({err: errorMessage}, ' notify error: @{err.message}' );
reject(errorMessage);
} else {
logger.logger.info({content: content}, 'A notification has been shipped: @{content}');
if (body) {
logger.logger.debug({body: body}, ' body: @{body}' );
}
resolve(_.isNil(body) === false ? body : null);
}
}
});
});
};
const notify = function(metadata, config) {
if (config.notify) {
if (config.notify.content) {
handleNotify(metadata, config.notify);
return handleNotify(metadata, config.notify);
} else {
// multiple notifications endpoints PR #108
for (const key in config.notify) {
if (config.notify.hasOwnProperty(key)) {
handleNotify(metadata, config.notify[key]);
return handleNotify(metadata, config.notify[key]);
}
}
}

@ -44,9 +44,7 @@
"devDependencies": {
"browserify": "^13.0.0",
"browserify-handlebars": "^1.0.0",
"codacy-coverage": "^2.0.2",
"codecov": "^2.2.0",
"coveralls": "^2.13.0",
"eslint": "^3.19.0",
"eslint-config-google": "^0.7.1",
"grunt": "^1.0.1",
@ -72,11 +70,10 @@
"server"
],
"scripts": {
"test": "npm run lint && mocha ./test/functional ./test/unit --reporter=spec --full-trace",
"test": "npm run lint && mocha ./test/functional ./test/unit",
"test:coverage": "nyc mocha -R spec ./test/functional ./test/unit",
"coverage:coveralls": "nyc report --reporter=text-lcov | coveralls",
"coverage:html": "nyc report --reporter=html",
"coverage:codecov": "nyc report --reporter=lcov | codecov",
"coverage:codacy": "nyc report --reporter=lcov && cat coverage/lcov.info | codacy-coverage",
"test-travis": "npm run lint && npm run test:coverage",
"test-only": "mocha ./test/functional ./test/unit",
"lint": "eslint .",

@ -1,22 +1,32 @@
'use strict';
module.exports = function() {
describe('access control', function() {
let server = process.server;
let oldauth;
describe('package access control', function() {
const server = process.server;
const buildToken = (auth) => {
return `Basic ${(new Buffer(auth).toString('base64'))}`;
};
let oldAuth;
before(function() {
oldauth = server.authstr;
oldAuth = server.authstr;
});
after(function() {
server.authstr = oldauth;
server.authstr = oldAuth;
});
function check_access(auth, pkg, ok) {
/**
* Check whether the user is allowed to fetch packages
* @param auth {object} disable auth
* @param pkg {string} package name
* @param ok {boolean}
*/
function checkAccess(auth, pkg, ok) {
it((ok ? 'allows' : 'forbids') +' access ' + auth + ' to ' + pkg, function() {
server.authstr = auth? `Basic ${(new Buffer(auth).toString('base64'))}`: undefined;
let req = server.get_package(pkg);
server.authstr = auth ? buildToken(auth) : undefined;
let req = server.getPackage(pkg);
if (ok) {
return req.status(404).body_error(/no such package available/);
} else {
@ -25,10 +35,16 @@ module.exports = function() {
});
}
function check_publish(auth, pkg, ok) {
/**
* Check whether the user is allowed to publish packages
* @param auth {object} disable auth
* @param pkg {string} package name
* @param ok {boolean}
*/
function checkPublish(auth, pkg, ok) {
it(`${(ok ? 'allows' : 'forbids')} publish ${auth} to ${pkg}`, function() {
server.authstr = auth? `Basic ${(new Buffer(auth).toString('base64'))}`: undefined;
let req = server.put_package(pkg, require('./lib/package')(pkg));
server.authstr = auth ? buildToken(auth) : undefined;
const req = server.putPackage(pkg, require('./lib/package')(pkg));
if (ok) {
return req.status(404).body_error(/this package cannot be added/);
} else {
@ -36,38 +52,48 @@ module.exports = function() {
}
});
}
const badPass = 'test:badpass';
const testPass = 'test:test';
// credentials
const badCredentials = 'test:badpass';
// test user is logged by default
const validCredentials = 'test:test';
// defined on server1 configuration
const testAccessOnly = 'test-access-only';
const testPublishOnly = 'test-publish-only';
const testOnlyTest = 'test-only-test';
const testOnlyAuth = 'test-only-auth';
check_access(testPass, testAccessOnly, true);
check_access(undefined, testAccessOnly, true);
check_access(badPass, testAccessOnly, true);
check_publish(testPass, testAccessOnly, false);
check_publish(undefined, testAccessOnly, false);
check_publish(badPass, testAccessOnly, false);
check_access(testPass, testPublishOnly, false);
check_access(undefined, testPublishOnly, false);
check_access(badPass, testPublishOnly, false);
check_publish(testPass, testPublishOnly, true);
check_publish(undefined, testPublishOnly, true);
check_publish(badPass, testPublishOnly, true);
// all are allowed to access
checkAccess(validCredentials, testAccessOnly, true);
checkAccess(undefined, testAccessOnly, true);
checkAccess(badCredentials, testAccessOnly, true);
checkPublish(validCredentials, testAccessOnly, false);
checkPublish(undefined, testAccessOnly, false);
checkPublish(badCredentials, testAccessOnly, false);
check_access(testPass, testOnlyTest, true);
check_access(undefined, testOnlyTest, false);
check_access(badPass, testOnlyTest, false);
check_publish(testPass, testOnlyTest, true);
check_publish(undefined, testOnlyTest, false);
check_publish(badPass, testOnlyTest, false);
// all are allowed to publish
checkAccess(validCredentials, testPublishOnly, false);
checkAccess(undefined, testPublishOnly, false);
checkAccess(badCredentials, testPublishOnly, false);
checkPublish(validCredentials, testPublishOnly, true);
checkPublish(undefined, testPublishOnly, true);
checkPublish(badCredentials, testPublishOnly, true);
check_access(testPass, testOnlyAuth, true);
check_access(undefined, testOnlyAuth, false);
check_access(badPass, testOnlyAuth, false);
check_publish(testPass, testOnlyAuth, true);
check_publish(undefined, testOnlyAuth, false);
check_publish(badPass, testOnlyAuth, false);
// only user "test" is allowed to publish and access
checkAccess(validCredentials, testOnlyTest, true);
checkAccess(undefined, testOnlyTest, false);
checkAccess(badCredentials, testOnlyTest, false);
checkPublish(validCredentials, testOnlyTest, true);
checkPublish(undefined, testOnlyTest, false);
checkPublish(badCredentials, testOnlyTest, false);
// only authenticated users are allowed
checkAccess(validCredentials, testOnlyAuth, true);
checkAccess(undefined, testOnlyAuth, false);
checkAccess(badCredentials, testOnlyAuth, false);
checkPublish(validCredentials, testOnlyAuth, true);
checkPublish(undefined, testOnlyAuth, false);
checkPublish(badCredentials, testOnlyAuth, false);
});
};

@ -8,12 +8,12 @@ module.exports = function() {
let server = process.server;
it('add tag - 404', function() {
return server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1').status(404).body_error(/no such package/);
return server.addTag('testpkg-tag', 'tagtagtag', '0.0.1').status(404).body_error(/no such package/);
});
describe('addtag', function() {
before(function() {
return server.put_package('testpkg-tag', eval(
return server.putPackage('testpkg-tag', eval(
'(' + readfile('fixtures/publish.json5')
.toString('utf8')
.replace(/__NAME__/g, 'testpkg-tag')
@ -27,19 +27,19 @@ module.exports = function() {
});
it('add tag - bad ver', function() {
return server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1-x')
return server.addTag('testpkg-tag', 'tagtagtag', '0.0.1-x')
.status(404)
.body_error(/version doesn't exist/);
});
it('add tag - bad tag', function() {
return server.add_tag('testpkg-tag', 'tag/tag/tag', '0.0.1-x')
return server.addTag('testpkg-tag', 'tag/tag/tag', '0.0.1-x')
.status(403)
.body_error(/invalid tag/);
});
it('add tag - good', function() {
return server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1')
return server.addTag('testpkg-tag', 'tagtagtag', '0.0.1')
.status(201)
.body_ok(/tagged/);
});

@ -36,7 +36,7 @@ module.exports = function() {
let pass = 'preexisting';
before(function() {
return fs.appendFileSync(
path.join(__dirname, 'test-storage', '.htpasswd'),
path.join(__dirname, 'store/test-storage', '.htpasswd'),
'preexisting:$apr1$4YSboUa9$yVKjE7.PxIOuK3M4D7VjX.'
);
});

@ -1,120 +1,155 @@
'use strict';
require('./lib/startup');
const assert = require('assert');
const crypto = require('crypto');
function readfile(x) {
return require('fs').readFileSync(__dirname + '/' + x);
function readfile(folderPath) {
return require('fs').readFileSync(__dirname + '/' + folderPath);
}
function getPackage(name) {
return require('./lib/package')(name);
}
function createHash() {
return crypto.createHash('sha1');
}
module.exports = function() {
let server = process.server;
let server2 = process.server2;
it('trying to fetch non-existent package', function() {
return server.get_package('testpkg').status(404).body_error(/no such package/);
});
describe('basic test endpoints', function() {
describe('testpkg', function() {
before(function() {
return server.add_package('testpkg');
});
require('./basic/whoIam')(server);
require('./basic/ping')(server);
it('creating new package', function() {/* test for before() */});
describe('handling packages', function() {
it('downloading non-existent tarball', function() {
return server.get_tarball('testpkg', 'blahblah').status(404).body_error(/no such file/);
});
it('uploading incomplete tarball', function() {
return server.put_tarball_incomplete('testpkg', 'blahblah1', readfile('fixtures/binary'), 3000);
});
describe('tarball', function() {
before(function() {
return server.put_tarball('testpkg', 'blahblah', readfile('fixtures/binary'))
.status(201)
.body_ok(/.*/);
return server.addPackage('testpkg');
});
it('uploading new tarball', function() {/* test for before() */});
it('creating new package', function() {/* test for before() */});
it('downloading newly created tarball', function() {
return server.get_tarball('testpkg', 'blahblah')
.status(200)
.then(function(body) {
assert.deepEqual(body, readfile('fixtures/binary'));
});
it('downloading non-existent tarball', function() {
return server.getTarball('testpkg', 'blahblah').status(404).body_error(/no such file/);
});
it('uploading new package version (bad sha)', function() {
let pkg = require('./lib/package')('testpkg');
pkg.dist.shasum = crypto.createHash('sha1').update('fake').digest('hex');
return server.put_version('testpkg', '0.0.1', pkg)
.status(400)
.body_error(/shasum error/);
it('uploading incomplete tarball', function() {
return server.putTarballIncomplete('testpkg', 'blahblah1', readfile('fixtures/binary'), 3000);
});
describe('version', function() {
describe('publishing package', function() {
before(function() {
let pkg = require('./lib/package')('testpkg');
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex');
return server.put_version('testpkg', '0.0.1', pkg)
return server.putTarball('testpkg', 'blahblah', readfile('fixtures/binary'))
.status(201)
.body_ok(/published/);
.body_ok(/.*/);
});
it('uploading new package version', function() {/* test for before() */});
after(function() {
return server.removeTarball('testpkg').status(201);
});
it('downloading newly created package', function() {
return server.get_package('testpkg')
it('remove a tarball', function() {
/* test for before() */
});
it('uploading new tarball', function() {
/* test for after() */
});
it('remove non existing tarball', function() {
return server.removeTarball('testpkg404').status(404);
});
it('downloading newly created tarball', function() {
return server.getTarball('testpkg', 'blahblah')
.status(200)
.then(function(body) {
assert.equal(body.name, 'testpkg');
assert.equal(body.versions['0.0.1'].name, 'testpkg');
assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55551/testpkg/-/blahblah');
assert.deepEqual(body['dist-tags'], {latest: '0.0.1'});
assert.deepEqual(body, readfile('fixtures/binary'));
});
});
it('downloading package via server2', function() {
return server2.get_package('testpkg')
.status(200)
.then(function(body) {
assert.equal(body.name, 'testpkg');
assert.equal(body.versions['0.0.1'].name, 'testpkg');
assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55552/testpkg/-/blahblah');
assert.deepEqual(body['dist-tags'], {latest: '0.0.1'});
});
it('uploading new package version (bad sha)', function() {
let pkg = getPackage('testpkg');
pkg.dist.shasum = createHash().update('fake').digest('hex');
return server.putVersion('testpkg', '0.0.1', pkg)
.status(400)
.body_error(/shasum error/);
});
describe('publishing version', function() {
before(function() {
const pkg = getPackage('testpkg');
pkg.dist.shasum = createHash().update(readfile('fixtures/binary')).digest('hex');
return server.putVersion('testpkg', '0.0.1', pkg)
.status(201)
.body_ok(/published/);
});
it('uploading new package version', function() {
/* test for before() */
});
it('downloading newly created package', function() {
return server.getPackage('testpkg')
.status(200)
.then(function(body) {
assert.equal(body.name, 'testpkg');
assert.equal(body.versions['0.0.1'].name, 'testpkg');
assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55551/testpkg/-/blahblah');
assert.deepEqual(body['dist-tags'], {
latest: '0.0.1'
});
});
});
it('downloading package via server2', function() {
return server2.getPackage('testpkg')
.status(200)
.then(function(body) {
assert.equal(body.name, 'testpkg');
assert.equal(body.versions['0.0.1'].name, 'testpkg');
assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55552/testpkg/-/blahblah');
assert.deepEqual(body['dist-tags'], {
latest: '0.0.1'
});
});
});
});
});
});
});
it('uploading new package version for bad pkg', function() {
return server.put_version('testpxg', '0.0.1', require('./lib/package')('testpxg'))
.status(404)
.body_error(/no such package/);
});
describe('handle failures on endpoints', function() {
it('doubleerr test', function() {
return server.put_tarball('testfwd2', 'blahblah', readfile('fixtures/binary'))
.status(404)
.body_error(/no such/);
});
it('publishing package / bad ro uplink', function() {
return server.put_package('baduplink', require('./lib/package')('baduplink'))
.status(503)
.body_error(/one of the uplinks is down, refuse to publish/);
});
it('should fails trying to fetch non-existent package', function() {
return server.getPackage('testpkg').status(404).body_error(/no such package/);
});
it('should fails on publish a version for non existing package', function() {
return server.putVersion('testpxg', '0.0.1', getPackage('testpxg'))
.status(404)
.body_error(/no such package/);
});
it('should be a package not found', function() {
return server.putTarball('nonExistingPackage', 'blahblah', readfile('fixtures/binary'))
.status(404)
.body_error(/no such/);
});
it('should fails on publish package in a bad uplink', function() {
return server.putPackage('baduplink', getPackage('baduplink'))
.status(503)
.body_error(/one of the uplinks is down, refuse to publish/);
});
it('who am I?', function() {
return server.whoami().then(function(username) {
assert.equal(username, 'test');
});
});
};

@ -0,0 +1,16 @@
'use strict';
const assert = require('assert');
const _ = require('lodash');
module.exports = function(server) {
it('ping', function () {
return server.ping().then(function (data) {
// it's always an empty object
assert.ok(_.isObject(data));
});
});
};

@ -0,0 +1,14 @@
'use strict';
const assert = require('assert');
module.exports = function(server) {
it('who am I?', function () {
return server.whoami().then(function (username) {
assert.equal(username, 'test');
});
});
};

@ -1 +1,57 @@
{"_id":"testpkg-newnpmreg","name":"testpkg-newnpmreg","description":"","dist-tags":{"foo":"0.0.0","latest":"0.0.0"},"versions":{"0.0.0":{"name":"testpkg-newnpmreg","version":"0.0.0","description":"","main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"repository":{"type":"git","url":""},"author":"","license":"ISC","_id":"testpkg-newnpmreg@0.0.0","dist":{"shasum":"8ee7331cbc641581b1a8cecd9d38d744a8feb863","tarball":"http://localhost:1234/testpkg-newnpmreg/-/testpkg-newnpmreg-0.0.0.tgz"},"_from":".","_npmVersion":"1.3.1","_npmUser":{"name":"alex","email":"alex@kocharin.ru"},"maintainers":[{"name":"alex","email":"alex@kocharin.ru"}]}},"readme":"blah blah blah","maintainers":[{"name":"alex","email":"alex@kocharin.ru"}],"_attachments":{"testpkg-newnpmreg-0.0.0.tgz":{"content_type":"application/octet-stream","data":"H4sIAAAAAAAAA+2TsW7CMBCGM/spTh6YKHUSIJLXqkPnrixWcIMLsS3btCDEu/fs0Ba1SFVVVISUP8Odzqf/zlY+K+qlaOSt7eLo2RudnVmMsel4DBjzasKOY1JZlJDlRVkU5aSspnnG8pIVOZ6fe5FTWvsgHK7yV5/uLvARr0Q7qkUrKadB+mCXzY2Wr9q2TjZ0SF+k88poPGUj/LAyl752yoauioVWqJgpPZcb/Hmw0jV4ynfJEw9lvTAwo/fOGcdBG4h18FbW6knJ+YzCYAByowLkdD+kTlrjVTBumzy2Nq7XqIDea7eKY7FJrMPCuG6Hlaql9rHr4fGO7i/9pFcl+4X/rWhX557xA/9FVZ3gv+j5/w9F+jl8g58c0OeQyCdH3HOglETsObxTTw7McwLJClt+wzz5JD45IPEcEHjMEfg0r8M9pQfaOSDs5NLP16tXr15XqzeJD6m5AAwAAA==","length":352}}}
{
"_id": "testpkg-newnpmreg",
"name": "testpkg-newnpmreg",
"description": "",
"dist-tags": {
"foo": "0.0.0",
"latest": "0.0.0"
},
"versions": {
"0.0.0": {
"name": "testpkg-newnpmreg",
"version": "0.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": ""
},
"author": "",
"license": "ISC",
"_id": "testpkg-newnpmreg@0.0.0",
"dist": {
"shasum": "8ee7331cbc641581b1a8cecd9d38d744a8feb863",
"tarball": "http:\/\/localhost:1234\/testpkg-newnpmreg\/-\/testpkg-newnpmreg-0.0.0.tgz"
},
"_from": ".",
"_npmVersion": "1.3.1",
"_npmUser": {
"name": "alex",
"email": "alex@kocharin.ru"
},
"maintainers": [
{
"name": "alex",
"email": "alex@kocharin.ru"
}
]
}
},
"readme": "blah blah blah",
"maintainers": [
{
"name": "alex",
"email": "alex@kocharin.ru"
}
],
"_attachments": {
"testpkg-newnpmreg-0.0.0.tgz": {
"content_type": "application\/octet-stream",
"data": "H4sIAAAAAAAAA+2TsW7CMBCGM\/spTh6YKHUSIJLXqkPnrixWcIMLsS3btCDEu\/fs0Ba1SFVVVISUP8Odzqf\/zlY+K+qlaOSt7eLo2RudnVmMsel4DBjzasKOY1JZlJDlRVkU5aSspnnG8pIVOZ6fe5FTWvsgHK7yV5\/uLvARr0Q7qkUrKadB+mCXzY2Wr9q2TjZ0SF+k88poPGUj\/LAyl752yoauioVWqJgpPZcb\/Hmw0jV4ynfJEw9lvTAwo\/fOGcdBG4h18FbW6knJ+YzCYAByowLkdD+kTlrjVTBumzy2Nq7XqIDea7eKY7FJrMPCuG6Hlaql9rHr4fGO7i\/9pFcl+4X\/rWhX557xA\/9FVZ3gv+j5\/w9F+jl8g58c0OeQyCdH3HOglETsObxTTw7McwLJClt+wzz5JD45IPEcEHjMEfg0r8M9pQfaOSDs5NLP16tXr15XqzeJD6m5AAwAAA==",
"length": 352
}
}
}

@ -1 +1,42 @@
{"_id":"@test/scoped","name":"@test/scoped","description":"test... test... test...","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@test/scoped","version":"1.0.0","description":"test... test... test...","main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"author":"","license":"ISC","readme":"ERROR: No README data found!","_id":"@test/scoped@1.0.0","_shasum":"6e67b14e2c0e450b942e2bc8086b49e90f594790","_from":".","_npmVersion":"2.0.1","_nodeVersion":"0.10.25","_npmUser":{},"dist":{"shasum":"6e67b14e2c0e450b942e2bc8086b49e90f594790","tarball":"http://localhost:4873/@test/scoped/-/@test/scoped-1.0.0.tgz"}}},"readme":"ERROR: No README data found!","_attachments":{"@test/scoped-1.0.0.tgz":{"content_type":"application/octet-stream","data":"H4sIAAAAAAAAAytITM5OTE/VL4DQelnF+XkMVAYGBgZmZiYKQNrQ3NQAmQYDYwMg29DIxMTY2Mzc1NSYwcDQ2MDIDChPbYdgA6XFJYlFQKdQag7ELwpweoiAaqW8xNxUJSslh5LU4hL94uT8gtQUJR2lstSi4sz8PKCEoZ6BngFQJCW1OLkos6AEIgpSraenp4BGA9XlJmaCFGTmpaRWAJMTUASir1jJqhqsDSiZmpyRrxCj5FpUlF9kpZCXD9auUFyQmpyZlpmaEqOkoKamkFqRWaJgqFSro5RYWpKRXwTUBzQsJzM5Na8Y5GLPYGel2oEOv6EOCtDyf2Vibg617SCQ/41MzZHyv+Fo/qcnAOV+KwXU3M8FzfxWCuC8z4WU863QMzyM5gJleysFWK7nguZ5Ky4FsAqgFaTkeS5IjgfqUuKCZngrBWB+5xro4Bp2AJb/QZGhC4l/XXCs65WkV1HJDgL539QAOf8bmwHzv4khWD2V7McLRnj+l+/mgDCY307enXXYQKTN+LUmn5QRq/u+5mVOLy/szBZTXN1764bRpKAgp3t7j08XuS7itTLT4+P+P49iligvXC/2ydVmZendyg9vfLbOiOjZqOPNYHsm2OxLmOHhUglVT5n0Sql0brFjOqcM7b8qxGe+37PB4lT+95fvmOTrVK0ueU3pKqp6PPVztrrvWq5di9afssrV8mlh5JZw43q65OrW94t8SwVYDIrWaLfmcZWErmCuU+8pqe37lHy7zVN1O5vZl3NRyZYhy3LZw7VXym/VMhOZ5h3A/lZxyXJR0er9pmK/CzbPnbaq6OyR7/zbv5S8/L677Kryv/suO2f/6sn/0X+p5kC9RPmfdOP/9Qvb6vjmv1S3/SMT9e1kQ40d2783Sw7OOzyz6pLxec4tohVH/Geoy3684erJb8P+ZG7Mr51pZ2eZvr7/QpbVdU4yA8/ARuEoGAWjYBSQBQDM0BedABAAAA==","length":736}}}
{
"_id": "@test\/scoped",
"name": "@test\/scoped",
"description": "test... test... test...",
"dist-tags": {
"latest": "1.0.0"
},
"versions": {
"1.0.0": {
"name": "@test\/scoped",
"version": "1.0.0",
"description": "test... test... test...",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"readme": "ERROR: No README data found!",
"_id": "@test\/scoped@1.0.0",
"_shasum": "6e67b14e2c0e450b942e2bc8086b49e90f594790",
"_from": ".",
"_npmVersion": "2.0.1",
"_nodeVersion": "0.10.25",
"_npmUser": {
},
"dist": {
"shasum": "6e67b14e2c0e450b942e2bc8086b49e90f594790",
"tarball": "http:\/\/localhost:4873\/@test\/scoped\/-\/@test\/scoped-1.0.0.tgz"
}
}
},
"readme": "ERROR: No README data found!",
"_attachments": {
"@test\/scoped-1.0.0.tgz": {
"content_type": "application\/octet-stream",
"data": "H4sIAAAAAAAAAytITM5OTE\/VL4DQelnF+XkMVAYGBgZmZiYKQNrQ3NQAmQYDYwMg29DIxMTY2Mzc1NSYwcDQ2MDIDChPbYdgA6XFJYlFQKdQag7ELwpweoiAaqW8xNxUJSslh5LU4hL94uT8gtQUJR2lstSi4sz8PKCEoZ6BngFQJCW1OLkos6AEIgpSraenp4BGA9XlJmaCFGTmpaRWAJMTUASir1jJqhqsDSiZmpyRrxCj5FpUlF9kpZCXD9auUFyQmpyZlpmaEqOkoKamkFqRWaJgqFSro5RYWpKRXwTUBzQsJzM5Na8Y5GLPYGel2oEOv6EOCtDyf2Vibg617SCQ\/41MzZHyv+Fo\/qcnAOV+KwXU3M8FzfxWCuC8z4WU863QMzyM5gJleysFWK7nguZ5Ky4FsAqgFaTkeS5IjgfqUuKCZngrBWB+5xro4Bp2AJb\/QZGhC4l\/XXCs65WkV1HJDgL539QAOf8bmwHzv4khWD2V7McLRnj+l+\/mgDCY307enXXYQKTN+LUmn5QRq\/u+5mVOLy\/szBZTXN1764bRpKAgp3t7j08XuS7itTLT4+P+P49iligvXC\/2ydVmZendyg9vfLbOiOjZqOPNYHsm2OxLmOHhUglVT5n0Sql0brFjOqcM7b8qxGe+37PB4lT+95fvmOTrVK0ueU3pKqp6PPVztrrvWq5di9afssrV8mlh5JZw43q65OrW94t8SwVYDIrWaLfmcZWErmCuU+8pqe37lHy7zVN1O5vZl3NRyZYhy3LZw7VXym\/VMhOZ5h3A\/lZxyXJR0er9pmK\/CzbPnbaq6OyR7\/zbv5S8\/L677Kryv\/suO2f\/6sn\/0X+p5kC9RPmfdOP\/9Qvb6vjmv1S3\/SMT9e1kQ40d2783Sw7OOzyz6pLxec4tohVH\/Geoy3684erJb8P+ZG7Mr51pZ2eZvr7\/QpbVdU4yA8\/ARuEoGAWjYBSQBQDM0BedABAAAA==",
"length": 736
}
}
}

@ -1,90 +0,0 @@
'use strict';
const fs = require('fs');
const path = require('path');
const assert = require('assert');
const crypto = require('crypto');
const STORAGE = 'test-storage3';
const TARBALL = 'blahblah';
const PKG_GH131 = 'pkg-gh131';
const PKG_GH1312 = 'pkg-gh1312';
function isCached(pkgname, tarballname) {
return fs.existsSync(path.join(__dirname, STORAGE, pkgname, tarballname));
}
function readfile(x) {
return fs.readFileSync(path.join(__dirname, x));
}
module.exports = function() {
const server = process.server;
const server2 = process.server2;
const server3 = process.server3;
before(function() {
return server.add_package(PKG_GH131);
});
before(function() {
return server.put_tarball(PKG_GH131, TARBALL, readfile('fixtures/binary'))
.status(201)
.body_ok(/.*/);
});
before(function() {
const pkg = require('./lib/package')(PKG_GH131);
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex');
return server.put_version(PKG_GH131, '0.0.1', pkg)
.status(201)
.body_ok(/published/);
});
before(function() {
return server3.get_package(PKG_GH131)
.status(200);
});
before(function() {
return server3.get_tarball(PKG_GH131, TARBALL)
.status(200);
});
it('should be caching packages from uplink server1', function () {
assert.equal(isCached(PKG_GH131, TARBALL), true);
});
before(function() {
return server2.add_package(PKG_GH1312);
});
before(function() {
return server2.put_tarball(PKG_GH1312, TARBALL, readfile('fixtures/binary'))
.status(201)
.body_ok(/.*/);
});
before(function() {
const pkg = require('./lib/package')(PKG_GH1312);
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex');
return server2.put_version(PKG_GH1312, '0.0.1', pkg)
.status(201)
.body_ok(/published/);
});
before(function() {
return server3.get_package(PKG_GH1312)
.status(200);
});
before(function() {
return server3.get_tarball(PKG_GH1312, TARBALL)
.status(200);
});
it('must not be caching packages from uplink server2', function () {
assert.equal(isCached(PKG_GH1312, TARBALL), false);
});
};

@ -12,14 +12,14 @@ module.exports = function() {
let server2 = process.server2;
it('downloading non-existent tarball #1 / srv2', function() {
return server2.get_tarball('testpkg-gh29', 'blahblah')
return server2.getTarball('testpkg-gh29', 'blahblah')
.status(404)
.body_error(/no such package/);
});
describe('pkg-gh29', function() {
before(function() {
return server.put_package('testpkg-gh29', require('./lib/package')('testpkg-gh29'))
return server.putPackage('testpkg-gh29', require('./lib/package')('testpkg-gh29'))
.status(201)
.body_ok(/created new package/);
});
@ -27,14 +27,14 @@ module.exports = function() {
it('creating new package / srv1', function() {});
it('downloading non-existent tarball #2 / srv2', function() {
return server2.get_tarball('testpkg-gh29', 'blahblah')
return server2.getTarball('testpkg-gh29', 'blahblah')
.status(404)
.body_error(/no such file/);
});
describe('tarball', function() {
before(function() {
return server.put_tarball('testpkg-gh29', 'blahblah', readfile('fixtures/binary'))
return server.putTarball('testpkg-gh29', 'blahblah', readfile('fixtures/binary'))
.status(201)
.body_ok(/.*/);
});
@ -45,7 +45,7 @@ module.exports = function() {
before(function() {
let pkg = require('./lib/package')('testpkg-gh29');
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex');
return server.put_version('testpkg-gh29', '0.0.1', pkg)
return server.putVersion('testpkg-gh29', '0.0.1', pkg)
.status(201)
.body_ok(/published/);
});
@ -53,7 +53,7 @@ module.exports = function() {
it('uploading new package version / srv1', function() {});
it('downloading newly created tarball / srv2', function() {
return server2.get_tarball('testpkg-gh29', 'blahblah')
return server2.getTarball('testpkg-gh29', 'blahblah')
.status(200)
.then(function(body) {
assert.deepEqual(body, readfile('fixtures/binary'));

@ -49,12 +49,12 @@ module.exports = function() {
});
it('should not fail on bad gzip', function() {
return server.get_package('testexp_baddata')
return server.getPackage('testexp_baddata')
.status(404);
});
it('should understand gzipped data from uplink', function() {
return server.get_package('testexp_gzip')
return server.getPackage('testexp_gzip')
.status(200)
.response(function(res) {
assert.equal(res.headers['content-encoding'], undefined);

@ -2,30 +2,37 @@
require('./lib/startup');
const _ = require('lodash');
const assert = require('assert');
const exec = require('child_process').exec;
describe('Func', function() {
describe('Create registry servers', function() {
const server = process.server;
const server2 = process.server2;
const server3 = process.server3;
before(function(done) {
Promise.all([
require('./lib/startup').start('./test-storage', './config-1.yaml'),
require('./lib/startup').start('./test-storage2', './config-2.yaml'),
require('./lib/startup').start('./test-storage3', './config-3.yaml'),
require('./lib/startup').start('./store/test-storage', '/store/config-1.yaml'),
require('./lib/startup').start('./store/test-storage2', '/store/config-2.yaml'),
require('./lib/startup').start('./store/test-storage3', '/store/config-3.yaml'),
]).then(() => {
done();
});
});
});
before(function() {
return Promise.all([server, server2, server3].map(function(server) {
return server.debug().status(200).then(function(body) {
server.pid = body.pid;
return new Promise(function(resolve, reject) {
exec('lsof -p ' + Number(server.pid), function(err, result) {
if (_.isNil(err) === false) {
reject(err);
}
assert.equal(err, null);
server.fdlist = result.replace(/ +/g, ' ');
resolve();
@ -35,13 +42,19 @@ describe('Func', function() {
}));
});
before(function auth() {
return Promise.all([server, server2, server3].map(function(server, cb) {
return server.auth('test', 'test').status(201).body_ok(/'test'/);
before(function testBasicAuthentication() {
return Promise.all([server, server2, server3].map(function(server) {
// log in on server1
return server.auth('test', 'test')
.status(201)
.body_ok(/'test'/);
}));
});
it('authenticate', function() {/* test for before() */});
it('authenticate', function() {
/* test for before() */
});
require('./access')();
require('./basic')();
@ -60,20 +73,27 @@ describe('Func', function() {
require('./logout')();
require('./addtag')();
require('./plugins')();
require('./notify')();
// requires packages published to server1/server2
require('./gh131')();
require('./uplink.cache')();
after(function(done) {
const check = (server) => {
return new Promise(function(resolve, reject) {
exec('lsof -p ' + parseInt(server.pid, 10), function(err, result) {
exec(`lsof -p ${parseInt(server.pid, 10)}`, function(err, result) {
if (err) {
reject();
} else {
result = result.split('\n').filter(function(q) {
if (q.match(/TCP .*->.* \(ESTABLISHED\)/)) return false;
if (q.match(/\/libcrypt-[^\/]+\.so/)) return false;
if (q.match(/\/node_modules\/crypt3\/build\/Release/)) return false;
result = result.split('\n').filter(function(query) {
if (query.match(/TCP .*->.* \(ESTABLISHED\)/)) {
return false;
}
if (query.match(/\/libcrypt-[^\/]+\.so/)) {
return false;
}
if (query.match(/\/node_modules\/crypt3\/build\/Release/)) {
return false;
}
return true;
}).join('\n').replace(/ +/g, ' ');
assert.equal(server.fdlist, result);
@ -82,12 +102,14 @@ describe('Func', function() {
});
});
};
Promise.all([check(server), check(server2), check(server3)]).then(function() {
done();
}, (reason) => {
assert.equal(reason, null);
done();
});
done();
});
});
});

@ -1,6 +1,6 @@
module.exports = function(name, version) {
return {
name,
name: name,
version: version || '0.0.0',
dist: {
shasum: 'fake',

@ -0,0 +1,116 @@
'use strict';
const assert = require('assert');
const request = require('request');
const requestData = Symbol('smart_request_data');
const _ = require('lodash');
class PromiseAssert extends Promise {
constructor(options) {
super(options);
}
status(expected) {
const selfData = this[requestData];
return injectResponse(this, this.then(function(body) {
try {
assert.equal(selfData.response.statusCode, expected);
} catch(err) {
selfData.error.message = err.message;
throw selfData.error;
}
return body;
}));
}
body_ok(expected) {
const self_data = this[requestData];
return injectResponse(this, this.then(function(body) {
try {
if (_.isRegExp(expected)) {
assert(body.ok.match(expected), '\'' + body.ok + '\' doesn\'t match ' + expected);
} else {
assert.equal(body.ok, expected);
}
assert.equal(body.error, null);
} catch(err) {
self_data.error.message = err.message;
throw self_data.error;
}
return body;
}));
}
body_error(expected) {
const self_data = this[requestData];
return injectResponse(this, this.then(function(body) {
try {
if (_.isRegExp(expected)) {
assert(body.error.match(expected), body.error + ' doesn\'t match ' + expected);
} else {
assert.equal(body.error, expected);
}
assert.equal(body.ok, null);
} catch(err) {
self_data.error.message = err.message;
throw self_data.error;
}
return body;
}));
}
request(callback) {
callback(this[requestData].request);
return this;
}
response(cb) {
const selfData = this[requestData];
return injectResponse(this, this.then(function(body) {
cb(selfData.response);
return body;
}));
}
send(data) {
this[requestData].request.end(data);
return this;
}
}
function injectResponse(smartObject, promise) {
promise[requestData] = smartObject[requestData];
return promise;
}
function smartRequest(options) {
const smartObject = {};
smartObject[requestData] = {};
smartObject[requestData].error = Error();
Error.captureStackTrace(smartObject[requestData].error, smartRequest);
const result = new PromiseAssert(function(resolve, reject) {
smartObject[requestData].request = request(options, function(err, res, body) {
if (err) {
return reject(err);
}
// store the response on symbol
smartObject[requestData].response = res;
resolve(body);
});
});
return injectResponse(smartObject, result);
}
module.exports = smartRequest;

@ -1,169 +1,200 @@
'use strict';
const assert = require('assert');
const request = require('./smart_request');
const request = require('./request');
const _ = require('lodash');
const buildAuthHeader = (user, pass) => {
return `Basic ${(new Buffer(`${user}:${pass}`)).toString('base64')}`;
};
class Server {
constructor(url) {
this.url = url.replace(/\/$/, '');
this.userAgent = 'node/v8.1.2 linux x64';
this.authstr = buildAuthHeader('test', 'test');
}
request(options) {
assert(options.uri);
const headers = options.headers || {};
headers.accept = headers.accept || 'application/json';
headers['user-agent'] = headers['user-agent'] || this.userAgent;
headers.authorization = headers.authorization || this.authstr;
return request({
url: this.url + options.uri,
method: options.method || 'GET',
headers: headers,
encoding: options.encoding,
json: _.isNil(options.json) === false ? options.json : true,
});
}
auth(name, password) {
this.authstr = buildAuthHeader(name, password);
return this.request({
uri: `/-/user/org.couchdb.user:${encodeURIComponent(name)}/-rev/undefined`,
method: 'PUT',
json: {
name: name,
password: password,
email: 'test@example.com',
_id: `org.couchdb.user:${name}`,
type: 'user',
roles: [],
date: new Date(),
},
});
}
logout(token) {
return this.request({
uri: `/-/user/token/${encodeURIComponent(token)}`,
method: 'DELETE',
});
}
getPackage(name) {
return this.request({
uri: `/${encodeURIComponent(name)}`,
method: 'GET',
});
}
putPackage(name, data) {
if (_.isObject(data) && !Buffer.isBuffer(data)) {
data = JSON.stringify(data);
}
return this.request({
uri: `/${encodeURIComponent(name)}`,
method: 'PUT',
headers: {
'content-type': 'application/json',
},
}).send(data);
}
putVersion(name, version, data) {
if (_.isObject(data) && !Buffer.isBuffer(data)) {
data = JSON.stringify(data);
}
return this.request({
uri: `/${encodeURIComponent(name)}/${encodeURIComponent(version)}/-tag/latest`,
method: 'PUT',
headers: {
'content-type': 'application/json',
},
}).send(data);
}
getTarball(name, filename) {
return this.request({
uri: `/${encodeURIComponent(name)}/-/${encodeURIComponent(filename)}`,
method: 'GET',
encoding: null,
});
}
putTarball(name, filename, data) {
return this.request({
uri: `/${encodeURIComponent(name)}/-/${encodeURIComponent(filename)}/whatever`,
method: 'PUT',
headers: {
'content-type': 'application/octet-stream',
},
}).send(data);
}
removeTarball(name) {
return this.request({
uri: `/${encodeURIComponent(name)}/-rev/whatever`,
method: 'DELETE',
headers: {
'content-type': 'application/json; charset=utf-8',
},
});
}
addTag(name, tag, version) {
return this.request({
uri: `/${encodeURIComponent(name)}/${encodeURIComponent(tag)}`,
method: 'PUT',
headers: {
'content-type': 'application/json',
},
}).send(JSON.stringify(version));
}
putTarballIncomplete(name, filename, data, size, cb) {
let promise = this.request({
uri: `/${encodeURIComponent(name)}/-/${encodeURIComponent(filename)}/whatever`,
method: 'PUT',
headers: {
'content-type': 'application/octet-stream',
'content-length': size,
},
timeout: 1000,
});
promise.request(function(req) {
req.write(data);
setTimeout(function() {
req.req.abort();
}, 20);
});
return new Promise(function(resolve, reject) {
promise
.then(function() {
reject(Error('no error'));
})
.catch(function(err) {
if (err.code === 'ECONNRESET') {
resolve();
} else {
reject(err);
}
});
});
}
addPackage(name) {
return this.putPackage(name, require('./package')(name))
.status(201)
.body_ok('created new package');
}
whoami() {
return this.request({
uri: '/-/whoami'
}).status(200)
.then(function(body) {
return body.username;
});
}
ping() {
return this.request({
uri: '/-/ping'
}).status(200)
.then(function(body) {
return body;
});
}
debug() {
return this.request({
uri: '/-/_debug',
method: 'GET',
headers: {
'content-type': 'application/json',
},
})
}
function Server(url) {
let self = Object.create(Server.prototype);
self.url = url.replace(/\/$/, '');
self.userAgent = 'node/v0.10.8 linux x64';
self.authstr = 'Basic '+(new Buffer('test:test')).toString('base64');
return self;
}
Server.prototype.request = function(options) {
assert(options.uri);
let headers = options.headers || {};
headers.accept = headers.accept || 'application/json';
headers['user-agent'] = headers['user-agent'] || this.userAgent;
headers.authorization = headers.authorization || this.authstr;
return request({
url: this.url + options.uri,
method: options.method || 'GET',
headers: headers,
encoding: options.encoding,
json: options.json != null ? options.json : true,
});
};
Server.prototype.auth = function(user, pass) {
this.authstr = 'Basic '+(new Buffer(user+':'+pass)).toString('base64');
return this.request({
uri: '/-/user/org.couchdb.user:'+encodeURIComponent(user)+'/-rev/undefined',
method: 'PUT',
json: {
name: user,
password: pass,
email: 'test@example.com',
_id: 'org.couchdb.user:' + user,
type: 'user',
roles: [],
date: new Date(),
},
});
};
Server.prototype.logout = function(token) {
return this.request({
uri: '/-/user/token/'+encodeURIComponent(token),
method: 'DELETE',
});
};
Server.prototype.get_package = function(name) {
return this.request({
uri: '/'+encodeURIComponent(name),
method: 'GET',
});
};
Server.prototype.put_package = function(name, data) {
if (typeof(data) === 'object' && !Buffer.isBuffer(data)) data = JSON.stringify(data);
return this.request({
uri: '/'+encodeURIComponent(name),
method: 'PUT',
headers: {
'content-type': 'application/json',
},
}).send(data);
};
Server.prototype.put_version = function(name, version, data) {
if (typeof(data) === 'object' && !Buffer.isBuffer(data)) data = JSON.stringify(data);
return this.request({
uri: '/'+encodeURIComponent(name)+'/'+encodeURIComponent(version)+'/-tag/latest',
method: 'PUT',
headers: {
'content-type': 'application/json',
},
}).send(data);
};
Server.prototype.get_tarball = function(name, filename) {
return this.request({
uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename),
method: 'GET',
encoding: null,
});
};
Server.prototype.put_tarball = function(name, filename, data) {
return this.request({
uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename)+'/whatever',
method: 'PUT',
headers: {
'content-type': 'application/octet-stream',
},
}).send(data);
};
Server.prototype.add_tag = function(name, tag, version) {
return this.request({
uri: '/'+encodeURIComponent(name)+'/'+encodeURIComponent(tag),
method: 'PUT',
headers: {
'content-type': 'application/json',
},
}).send(JSON.stringify(version));
};
Server.prototype.put_tarball_incomplete = function(name, filename, data, size, cb) {
let promise = this.request({
uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename)+'/whatever',
method: 'PUT',
headers: {
'content-type': 'application/octet-stream',
'content-length': size,
},
timeout: 1000,
});
promise.request(function(req) {
req.write(data);
setTimeout(function() {
req.req.abort();
}, 20);
});
return new Promise(function(resolve, reject) {
promise
.then(function() {
reject(Error('no error'));
})
.catch(function(err) {
if (err.code === 'ECONNRESET') {
resolve();
} else {
reject(err);
}
});
});
};
Server.prototype.add_package = function(name) {
return this.put_package(name, require('./package')(name))
.status(201)
.body_ok('created new package');
};
Server.prototype.whoami = function() {
return this.request({uri: '/-/whoami'})
.status(200)
.then(function(x) {
return x.username;
});
};
Server.prototype.debug = function() {
return this.request({
uri: '/-/_debug',
method: 'GET',
headers: {
'content-type': 'application/json',
},
});
};
module.exports = Server;

@ -1,104 +0,0 @@
'use strict';
const assert = require('assert');
const request = require('request');
const sym = Symbol('smart_request_data');
function smart_request(options) {
let self = {};
self[sym] = {};
self[sym].error = Error();
Error.captureStackTrace(self[sym].error, smart_request);
let result = new Promise(function(resolve, reject) {
self[sym].request = request(options, function(err, res, body) {
if (err) return reject(err);
self[sym].response = res;
resolve(body);
});
});
return extend(self, result);
}
function extend(self, promise) {
promise[sym] = self[sym];
Object.setPrototypeOf(promise, extensions);
return promise;
}
var extensions = Object.create(Promise.prototype);
extensions.status = function(expected) {
let self_data = this[sym];
return extend(this, this.then(function(body) {
try {
assert.equal(self_data.response.statusCode, expected);
} catch(err) {
self_data.error.message = err.message;
throw self_data.error;
}
return body;
}));
};
extensions.body_ok = function(expected) {
let self_data = this[sym];
return extend(this, this.then(function(body) {
try {
if (Object.prototype.toString.call(expected) === '[object RegExp]') {
assert(body.ok.match(expected), '\'' + body.ok + '\' doesn\'t match ' + expected);
} else {
assert.equal(body.ok, expected);
}
assert.equal(body.error, null);
} catch(err) {
self_data.error.message = err.message;
throw self_data.error;
}
return body;
}));
};
extensions.body_error = function(expected) {
let self_data = this[sym];
return extend(this, this.then(function(body) {
try {
if (Object.prototype.toString.call(expected) === '[object RegExp]') {
assert(body.error.match(expected), body.error + ' doesn\'t match ' + expected);
} else {
assert.equal(body.error, expected);
}
assert.equal(body.ok, null);
} catch(err) {
self_data.error.message = err.message;
throw self_data.error;
}
return body;
}));
};
extensions.request = function(cb) {
cb(this[sym].request);
return this;
};
extensions.response = function(cb) {
let self_data = this[sym];
return extend(this, this.then(function(body) {
cb(self_data.response);
return body;
}));
};
extensions.send = function(data) {
this[sym].request.end(data);
return this;
};
module.exports = smart_request;

@ -1,52 +1,79 @@
'use strict';
const _ = require('lodash');
const fork = require('child_process').fork;
const bodyParser = require('body-parser');
const express = require('express');
const rimraf = require('rimraf');
const rimRaf = require('rimraf');
const path = require('path');
const Server = require('./server');
const forks = process.forks = [];
process.server = Server('http://localhost:55551/');
process.server2 = Server('http://localhost:55552/');
process.server3 = Server('http://localhost:55553/');
process.express = express();
process.server = new Server('http://localhost:55551/');
process.server2 = new Server('http://localhost:55552/');
process.server3 = new Server('http://localhost:55553/');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
process.express = app;
process.express.listen(55550);
module.exports.start = function(dir, conf) {
return new Promise(function(resolve, reject) {
rimraf(__dirname + '/../' + dir, function() {
// filter out --debug-brk
let oldArgv = process.execArgv;
process.execArgv = process.execArgv.filter(function(x) {
return x !== '--debug-brk';
const storageDir = path.join(__dirname, `/../${dir}`);
const configPath = path.join(__dirname, '../', conf);
rimRaf(storageDir, function(err) {
if(_.isNil(err) === false) {
reject(err);
}
const filteredArguments = process.execArgv = process.execArgv.filter(function(x) {
// filter out --debug-brk and --inspect-brk since Node7
return (x.indexOf('--debug-brk') === -1 && x.indexOf('--inspect-brk') === -1);
});
const f = fork(__dirname + '/../../../bin/verdaccio'
, ['-c', __dirname + '/../' + conf]
, {silent: !process.env.TRAVIS}
const childFork = fork(__dirname + '/../../../bin/verdaccio',
['-c', configPath],
{
silent: !process.env.TRAVIS
// silent: false
}
);
forks.push(f);
f.on('message', function(msg) {
forks.push(childFork);
childFork.on('message', function(msg) {
if ('verdaccio_started' in msg) {
resolve();
}
});
f.on('error', function(err) {
childFork.on('error', function(err) {
reject(err);
});
process.execArgv = oldArgv;
childFork.on('disconnect', function(err) {
reject(err);
});
childFork.on('exit', function(err) {
reject(err);
});
process.execArgv = filteredArguments;
});
});
};
process.on('exit', function() {
if (forks[0]) {
if (_.isNil(forks[0]) === false) {
forks[0].kill();
}
if (forks[1]) {
if (_.isNil(forks[1]) === false) {
forks[1].kill();
}
if (forks[2]) {
if (_.isNil(forks[2]) === false) {
forks[2].kill();
}
});

@ -11,7 +11,7 @@ module.exports = function() {
let server2 = process.server2;
it('testing anti-loop', function() {
return server2.get_package('testloop')
return server2.getPackage('testloop')
.status(404)
.body_error(/no such package/);
})
@ -22,7 +22,7 @@ module.exports = function() {
describe(pkg, function() {
before(function() {
return server.put_package(pkg, require('./lib/package')(pkg))
return server.putPackage(pkg, require('./lib/package')(pkg))
.status(201)
.body_ok(/created new package/);
});
@ -31,7 +31,7 @@ module.exports = function() {
describe(pkg, function() {
before(function() {
return server.put_version(pkg, '0.1.1', require('./lib/package')(pkg))
return server.putVersion(pkg, '0.1.1', require('./lib/package')(pkg))
.status(201)
.body_ok(/published/);
});
@ -39,12 +39,12 @@ module.exports = function() {
it(prefix+'uploading new package version', function() {});
it(prefix+'uploading incomplete tarball', function() {
return server.put_tarball_incomplete(pkg, pkg+'.bad', readfile('fixtures/binary'), 3000);
return server.putTarballIncomplete(pkg, pkg+'.bad', readfile('fixtures/binary'), 3000);
});
describe('tarball', function() {
before(function() {
return server.put_tarball(pkg, pkg+'.file', readfile('fixtures/binary'))
return server.putTarball(pkg, pkg+'.file', readfile('fixtures/binary'))
.status(201)
.body_ok(/.*/);
});
@ -52,7 +52,7 @@ module.exports = function() {
it(prefix+'uploading new tarball', function() {});
it(prefix+'downloading tarball from server1', function() {
return server.get_tarball(pkg, pkg+'.file')
return server.getTarball(pkg, pkg+'.file')
.status(200)
.then(function(body) {
assert.deepEqual(body, readfile('fixtures/binary'));

@ -30,7 +30,7 @@ module.exports = function() {
it('add pkg', function() {});
it('server1 - tarball', function() {
return server.get_tarball('testpkg-newnpmreg', 'testpkg-newnpmreg-0.0.0.tgz')
return server.getTarball('testpkg-newnpmreg', 'testpkg-newnpmreg-0.0.0.tgz')
.status(200)
.then(function(body) {
// not real sha due to utf8 conversion
@ -39,7 +39,7 @@ module.exports = function() {
});
it('server2 - tarball', function() {
return server2.get_tarball('testpkg-newnpmreg', 'testpkg-newnpmreg-0.0.0.tgz')
return server2.getTarball('testpkg-newnpmreg', 'testpkg-newnpmreg-0.0.0.tgz')
.status(200)
.then(function(body) {
// not real sha due to utf8 conversion
@ -48,7 +48,7 @@ module.exports = function() {
});
it('server1 - package', function() {
return server.get_package('testpkg-newnpmreg')
return server.getPackage('testpkg-newnpmreg')
.status(200)
.then(function(body) {
assert.equal(body.name, 'testpkg-newnpmreg');
@ -59,7 +59,7 @@ module.exports = function() {
});
it('server2 - package', function() {
return server2.get_package('testpkg-newnpmreg')
return server2.getPackage('testpkg-newnpmreg')
.status(200)
.then(function(body) {
assert.equal(body.name, 'testpkg-newnpmreg');

116
test/functional/notify.js Normal file

@ -0,0 +1,116 @@
'use strict';
const assert = require('assert');
const _ = require('lodash');
const notify = require('../../lib/notify').notify;
module.exports = function() {
const express = process.express;
const config = {
notify: {
method: 'POST',
headers: [{
'Content-Type': 'application/json'
}],
endpoint: "http://localhost:55550/api/notify",
content: '{"color":"green","message":"New package published: * {{ name }}*","notify":true,"message_format":"text"}'
}
};
describe('notifications', function () {
before(function () {
express.post('/api/notify', function (req, res) {
res.send(req.body);
});
express.post('/api/notify/bad', function (req, res) {
res.status(400);
res.send('bad response');
});
});
it('notification should be send', function (done) {
const metadata = {
name: "pkg-test"
};
notify(metadata, config).then(function (body) {
const jsonBody = JSON.parse(body);
assert.ok(`New package published: * ${metadata.name}*` === jsonBody.message,
'Body notify message should be equal');
done();
}, function (err) {
assert.fail(err);
done();
});
});
it('notification should be send single header', function (done) {
const metadata = {
name: "pkg-test"
};
const configMultipleHeader = _.cloneDeep(config);
configMultipleHeader.notify.headers = {
'Content-Type': 'application/json'
};
notify(metadata, configMultipleHeader).then(function (body) {
const jsonBody = JSON.parse(body);
assert.ok(`New package published: * ${metadata.name}*` === jsonBody.message,
'Body notify message should be equal');
done();
}, function (err) {
assert.fail(err);
done();
});
});
it('notification should be send multiple notifications endpoints', function (done) {
const metadata = {
name: "pkg-test"
};
// let notificationsCounter = 0;
const multipleNotificationsEndpoint = {
notify: []
};
for (let i = 0; i < 10; i++) {
const notificationSettings = _.cloneDeep(config.notify);
// basically we allow al notifications
notificationSettings.packagePattern = /^pkg-test$/;
// notificationSettings.packagePatternFlags = 'i';
multipleNotificationsEndpoint.notify.push(notificationSettings);
}
notify(metadata, multipleNotificationsEndpoint).then(function (body) {
const jsonBody = JSON.parse(body);
assert.ok(`New package published: * ${metadata.name}*` === jsonBody.message,
'Body notify message should be equal');
done();
}, function (err) {
assert.fail(err);
done();
});
});
it('notification should fails', function (done) {
const metadata = {
name: "pkg-test"
};
const configFail = _.cloneDeep(config);
configFail.notify.endpoint = "http://localhost:55550/api/notify/bad";
notify(metadata, configFail).then(function () {
assert.equal(false, 'This service should fails with status code 400');
done();
}, function (err) {
assert.ok('Bad Request' === err, 'The error message should be "Bad Request');
done();
});
});
});
};

@ -14,27 +14,27 @@ module.exports = function() {
let server2 = process.server2;
it('trying to fetch non-existent package / null storage', function() {
return server.get_package('test-nullstorage-nonexist')
return server.getPackage('test-nullstorage-nonexist')
.status(404)
.body_error(/no such package/);
});
describe('test-nullstorage on server2', function() {
before(function() {
return server2.add_package('test-nullstorage2');
return server2.addPackage('test-nullstorage2');
});
it('creating new package - server2', function() {/* test for before() */});
it('downloading non-existent tarball', function() {
return server.get_tarball('test-nullstorage2', 'blahblah')
return server.getTarball('test-nullstorage2', 'blahblah')
.status(404)
.body_error(/no such file/);
});
describe('tarball', function() {
before(function() {
return server2.put_tarball('test-nullstorage2', 'blahblah', readfile('fixtures/binary'))
return server2.putTarball('test-nullstorage2', 'blahblah', readfile('fixtures/binary'))
.status(201)
.body_ok(/.*/);
});
@ -42,7 +42,7 @@ module.exports = function() {
before(function() {
let pkg = require('./lib/package')('test-nullstorage2');
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex');
return server2.put_version('test-nullstorage2', '0.0.1', pkg)
return server2.putVersion('test-nullstorage2', '0.0.1', pkg)
.status(201)
.body_ok(/published/);
});
@ -50,7 +50,7 @@ module.exports = function() {
it('uploading new tarball', function() {/* test for before() */});
it('downloading newly created tarball', function() {
return server.get_tarball('test-nullstorage2', 'blahblah')
return server.getTarball('test-nullstorage2', 'blahblah')
.status(200)
.then(function(body) {
assert.deepEqual(body, readfile('fixtures/binary'));
@ -58,7 +58,7 @@ module.exports = function() {
});
it('downloading newly created package', function() {
return server.get_package('test-nullstorage2')
return server.getPackage('test-nullstorage2')
.status(200)
.then(function(body) {
assert.equal(body.name, 'test-nullstorage2');

@ -70,19 +70,19 @@ module.exports = function() {
});
it('access test-auth-allow', function() {
return server2.get_package('test-auth-allow')
return server2.getPackage('test-auth-allow')
.status(404)
.body_error('no such package available');
});
it('access test-auth-deny', function() {
return server2.get_package('test-auth-deny')
return server2.getPackage('test-auth-deny')
.status(403)
.body_error('you\'re not allowed here');
});
it('access test-auth-regular', function() {
return server2.get_package('test-auth-regular')
return server2.getPackage('test-auth-regular')
.status(404)
.body_error('no such package available');
});
@ -96,19 +96,19 @@ module.exports = function() {
});
it('access test-auth-allow', function() {
return server2.get_package('test-auth-allow')
return server2.getPackage('test-auth-allow')
.status(403)
.body_error('i don\'t know anything about you');
});
it('access test-auth-deny', function() {
return server2.get_package('test-auth-deny')
return server2.getPackage('test-auth-deny')
.status(403)
.body_error('i don\'t know anything about you');
});
it('access test-auth-regular', function() {
return server2.get_package('test-auth-regular')
return server2.getPackage('test-auth-regular')
.status(404)
.body_error('no such package available');
});

@ -2,7 +2,7 @@
module.exports = Plugin;
function Plugin(config, stuff) {
function Plugin(config) {
let self = Object.create(Plugin.prototype);
self._config = config;
return self;
@ -12,13 +12,12 @@ function Plugin(config, stuff) {
Plugin.prototype.verdaccio_version = '1.1.0';
Plugin.prototype.authenticate = function(user, password, cb) {
let self = this;
if (user !== self._config.accept_user) {
if (user !== this._config.accept_user) {
// delegate to next plugin
return cb(null, false);
}
if (password !== self._config.with_password) {
let err = Error('i don\'t like your password');
if (password !== this._config.with_password) {
const err = Error('i don\'t like your password');
err.status = 403;
return cb(err);
}

@ -2,7 +2,7 @@
module.exports = Plugin;
function Plugin(config, stuff) {
function Plugin(config) {
let self = Object.create(Plugin.prototype);
self._config = config;
return self;
@ -12,17 +12,16 @@ function Plugin(config, stuff) {
Plugin.prototype.verdaccio_version = '1.1.0';
Plugin.prototype.allow_access = function(user, pkg, cb) {
let self = this;
if (!pkg.handled_by_auth_plugin) {
// delegate to next plugin
return cb(null, false);
}
if (user.name !== self._config.allow_user) {
if (user.name !== this._config.allow_user) {
let err = Error('i don\'t know anything about you');
err.status = 403;
return cb(err);
}
if (pkg.name !== self._config.to_access) {
if (pkg.name !== this._config.to_access) {
let err = Error('you\'re not allowed here');
err.status = 403;
return cb(err);

@ -9,7 +9,7 @@ module.exports = function() {
describe('race', function() {
before(function() {
return server.put_package('race', require('./lib/package')('race'))
return server.putPackage('race', require('./lib/package')('race'))
.status(201)
.body_ok(/created new package/);
});
@ -24,7 +24,7 @@ module.exports = function() {
data.rand = Math.random();
let _res;
server.put_version('race', '0.0.1', data)
server.putVersion('race', '0.0.1', data)
.response(function(res) {
_res = res;
})
@ -62,7 +62,7 @@ module.exports = function() {
(function(i) {
fns.push(function(cb_) {
let _res;
server.put_version('race', '0.1.'+String(i), require('./lib/package')('race'))
server.putVersion('race', '0.1.'+String(i), require('./lib/package')('race'))
.response(function(res) {
_res = res;
})
@ -94,7 +94,7 @@ module.exports = function() {
});
after('downloading package', function() {
return server.get_package('race')
return server.getPackage('race')
.status(200)
.then(function(body) {
assert.equal(Object.keys(body.versions).length, _oksum);

@ -29,7 +29,7 @@ module.exports = function() {
it('add pkg', function() {});
it('server1 - tarball', function() {
return server.get_tarball('@test/scoped', 'scoped-1.0.0.tgz')
return server.getTarball('@test/scoped', 'scoped-1.0.0.tgz')
.status(200)
.then(function(body) {
// not real sha due to utf8 conversion
@ -38,7 +38,7 @@ module.exports = function() {
});
it('server2 - tarball', function() {
return server2.get_tarball('@test/scoped', 'scoped-1.0.0.tgz')
return server2.getTarball('@test/scoped', 'scoped-1.0.0.tgz')
.status(200)
.then(function(body) {
// not real sha due to utf8 conversion
@ -47,7 +47,7 @@ module.exports = function() {
});
it('server1 - package', function() {
return server.get_package('@test/scoped')
return server.getPackage('@test/scoped')
.status(200)
.then(function(body) {
assert.equal(body.name, '@test/scoped');
@ -58,7 +58,7 @@ module.exports = function() {
});
it('server2 - package', function() {
return server2.get_package('@test/scoped')
return server2.getPackage('@test/scoped')
.status(200)
.then(function(body) {
assert.equal(body.name, '@test/scoped');

@ -7,17 +7,17 @@ module.exports = function() {
describe('Security', function() {
before(function() {
return server.add_package('testpkg-sec');
return server.addPackage('testpkg-sec');
});
it('bad pkg #1', function() {
return server.get_package('package.json')
return server.getPackage('package.json')
.status(403)
.body_error(/invalid package/);
});
it('bad pkg #2', function() {
return server.get_package('__proto__')
return server.getPackage('__proto__')
.status(403)
.body_error(/invalid package/);
});
@ -51,19 +51,19 @@ module.exports = function() {
});
it('silly things - writing #1', function() {
return server.put_tarball('testpkg-sec', 'package.json', '{}')
return server.putTarball('testpkg-sec', 'package.json', '{}')
.status(403)
.body_error(/invalid filename/);
});
it('silly things - writing #3', function() {
return server.put_tarball('testpkg-sec', 'node_modules', '{}')
return server.putTarball('testpkg-sec', 'node_modules', '{}')
.status(403)
.body_error(/invalid filename/);
});
it('silly things - writing #4', function() {
return server.put_tarball('testpkg-sec', '../testpkg.tgz', '{}')
return server.putTarball('testpkg-sec', '../testpkg.tgz', '{}')
.status(403)
.body_error(/invalid filename/);
});

@ -15,11 +15,11 @@ web:
enable: true
auth:
./plugins/authenticate:
../plugins/authenticate:
accept_user: authtest2
with_password: blahblah
./plugins/authorize:
../plugins/authorize:
allow_user: authtest
to_access: test-auth-allow

@ -10,7 +10,6 @@ web:
uplinks:
server1:
url: http://localhost:55551/
# cache: true
server2:
url: http://localhost:55552/
cache: false

@ -11,7 +11,7 @@ module.exports = function() {
let express = process.express;
it('tags - testing for 404', function() {
return server.get_package('testexp_tags')
return server.getPackage('testexp_tags')
// shouldn't exist yet
.status(404)
.body_error(/no such package/);
@ -26,7 +26,7 @@ module.exports = function() {
});
it('fetching package again', function() {
return server.get_package('testexp_tags')
return server.getPackage('testexp_tags')
.status(200)
.then(function(body) {
assert.equal(typeof(body.versions['1.1']), 'object');
@ -58,7 +58,7 @@ module.exports = function() {
// populate cache
before(function() {
return server.get_package('testexp_tags2')
return server.getPackage('testexp_tags2')
.status(200);
});

@ -0,0 +1,98 @@
'use strict';
const fs = require('fs');
const path = require('path');
const assert = require('assert');
const crypto = require('crypto');
const STORAGE = 'store/test-storage3';
const TARBALL = 'blahblah';
const PKG_GH131 = 'pkg-gh131';
const PKG_GH1312 = 'pkg-gh1312';
function isCached(pkgName, tarballName) {
return fs.existsSync(path.join(__dirname, STORAGE, pkgName, tarballName));
}
function readfile(x) {
return fs.readFileSync(path.join(__dirname, x));
}
module.exports = function() {
const server = process.server;
const server2 = process.server2;
const server3 = process.server3;
describe('storage tarball cache test', function() {
//more info #131
before(function () {
return server.addPackage(PKG_GH131);
});
before(function () {
return server.putTarball(PKG_GH131, TARBALL, readfile('fixtures/binary'))
.status(201)
.body_ok(/.*/);
});
before(function () {
const pkg = require('./lib/package')(PKG_GH131);
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex');
return server.putVersion(PKG_GH131, '0.0.1', pkg)
.status(201)
.body_ok(/published/);
});
before(function () {
return server3.getPackage(PKG_GH131)
.status(200);
});
before(function () {
return server3.getTarball(PKG_GH131, TARBALL)
.status(200);
});
it('should be caching packages from uplink server1', function () {
assert.equal(isCached(PKG_GH131, TARBALL), true);
});
before(function () {
return server2.addPackage(PKG_GH1312);
});
before(function () {
return server2.putTarball(PKG_GH1312, TARBALL, readfile('fixtures/binary'))
.status(201)
.body_ok(/.*/);
});
before(function () {
const pkg = require('./lib/package')(PKG_GH1312);
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex');
return server2.putVersion(PKG_GH1312, '0.0.1', pkg)
.status(201)
.body_ok(/published/);
});
before(function () {
return server3.getPackage(PKG_GH1312)
.status(200);
});
before(function () {
return server3.getTarball(PKG_GH1312, TARBALL)
.status(200);
});
it('must not be caching packages from uplink server2', function () {
assert.equal(isCached(PKG_GH1312, TARBALL), false);
});
});
};

@ -1,3 +1,4 @@
--reporter dot
--timeout 5000
--inline-diffs
--reporter spec
--timeout 20000
--full-trace
--colors

@ -1,7 +1,7 @@
'use strict';
const config = {
storage: __dirname + '/test-storage',
storage: __dirname + '/store/test-storage',
uplinks: {
'npmjs': {
'url': 'https://registry.npmjs.org/'

@ -11,7 +11,7 @@ describe('toplevel', function() {
let port;
before(function(done) {
rimraf(__dirname + '/test-storage', done);
rimraf(__dirname + '/store/test-storage', done);
});
before(function(done) {