diff --git a/test/functional/index.js b/test/functional/index.js index dfdb0e9a5..a8ae27458 100644 --- a/test/functional/index.js +++ b/test/functional/index.js @@ -1,21 +1,26 @@ 'use strict'; -require('./lib/startup'); +// require('./lib/startup'); +//import _ from 'lodash'; + +import {VerdaccioServer, VerdaccioConfig} from './lib/verdaccio-server'; -const _ = require('lodash'); const assert = require('assert'); -const exec = require('child_process').exec; describe('functional test verdaccio', function() { - const server = process.server; - const server2 = process.server2; - const server3 = process.server3; + + const config1 = new VerdaccioConfig('./store/test-storage', '/store/config-1.yaml', 'http://localhost:55551/'); + const config2 = new VerdaccioConfig('./store/test-storage2', '/store/config-2.yaml', 'http://localhost:55552/'); + const config3 = new VerdaccioConfig('./store/test-storage3', '/store/config-3.yaml', 'http://localhost:55553/'); + const server1 = new VerdaccioServer(config1); + const server2 = new VerdaccioServer(config2); + const server3 = new VerdaccioServer(config3); before(function(done) { Promise.all([ - 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'), + server1.start(), + server2.start(), + server3.start(), ]).then(() => { done(); }).catch(function(error) { @@ -24,91 +29,39 @@ describe('functional test verdaccio', function() { }); - 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(); - }); - }); - }); - })); - }); - - 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() */ }); require('./package/access')(); - require('./basic')(); - require('./gh29')(); - require('./tags/tags')(); - require('./package/gzip.spec')(); - require('./sanity/incomplete')(); - require('./sanity/mirror')(); - require('./tags/preserve_tags.spec')(); - require('./readme/readme.spec')(); - require('./sanity/nullstorage')(); - require('./performance/race')(); - require('./sanity/racycrash')(); - require('./package/scoped.spec')(); - require('./sanity/security')(); - require('./adduser/adduser')(); - require('./adduser/logout')(); - require('./tags/addtag.spec')(); - require('./plugins/auth.spec')(); - require('./plugins/middleware.spec')(); - require('./notifications/notify')(); - // // requires packages published to server1/server2 - require('./uplink.cache.spec')(); - require('./uplink.auth.spec')(); + // require('./basic')(); + // require('./gh29')(); + // require('./tags/tags')(); + // require('./package/gzip.spec')(); + // require('./sanity/incomplete')(); + // require('./sanity/mirror')(); + // require('./tags/preserve_tags.spec')(); + // require('./readme/readme.spec')(); + // require('./sanity/nullstorage')(); + // require('./performance/race')(); + // require('./sanity/racycrash')(); + // require('./package/scoped.spec')(); + // require('./sanity/security')(); + // require('./adduser/adduser')(); + // require('./adduser/logout')(); + // require('./tags/addtag.spec')(); + // require('./plugins/auth.spec')(); + // require('./notifications/notify')(); + // // // requires packages published to server1/server2 + // require('./uplink.cache.spec')(); + // require('./uplink.auth.spec')(); after(function(done) { const check = (server) => { - return new Promise(function(resolve, reject) { - exec(`lsof -p ${parseInt(server.pid, 10)}`, function(err, result) { - if (err) { - reject(); - } else { - 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); - resolve(); - } - }); - }); + return server.stop(); }; - Promise.all([check(server), check(server2), check(server3)]).then(function() { + Promise.all([check(server1), check(server2), check(server3)]).then(function() { done(); }, (reason) => { assert.equal(reason, null); diff --git a/test/functional/lib/request.js b/test/functional/lib/request.js index 4402350c1..d7250c340 100644 --- a/test/functional/lib/request.js +++ b/test/functional/lib/request.js @@ -1,15 +1,20 @@ -const assert = require('assert'); -const request = require('request'); +// @flow + +import assert from 'assert'; +import request from 'request'; +import _ from 'lodash'; +import type {IRequestPromise} from './types'; + const requestData = Symbol('smart_request_data'); -const _ = require('lodash'); -export class PromiseAssert extends Promise { +export class PromiseAssert extends Promise implements IRequestPromise{ - constructor(options) { + constructor(options: any) { super(options); } - status(expected) { + status(expected: number) { + // $FlowFixMe const selfData = this[requestData]; return injectResponse(this, this.then(function(body) { @@ -23,8 +28,9 @@ export class PromiseAssert extends Promise { })); } - body_ok(expected) { - const self_data = this[requestData]; + body_ok(expected: any) { + // $FlowFixMe + const selfData = this[requestData]; return injectResponse(this, this.then(function(body) { try { @@ -35,16 +41,17 @@ export class PromiseAssert extends Promise { } assert.equal(body.error, null); } catch(err) { - self_data.error.message = err.message; - throw self_data.error; + selfData.error.message = err.message; + throw selfData.error; } return body; })); } - body_error(expected) { - const self_data = this[requestData]; + body_error(expected: any) { + // $FlowFixMe + const selfData = this[requestData]; return injectResponse(this, this.then(function(body) { try { @@ -55,19 +62,21 @@ export class PromiseAssert extends Promise { } assert.equal(body.ok, null); } catch(err) { - self_data.error.message = err.message; - throw self_data.error; + selfData.error.message = err.message; + throw selfData.error; } return body; })); } - request(callback) { + request(callback: any) { + // $FlowFixMe callback(this[requestData].request); return this; } - response(cb) { + response(cb: any) { + // $FlowFixMe const selfData = this[requestData]; return injectResponse(this, this.then(function(body) { @@ -76,25 +85,30 @@ export class PromiseAssert extends Promise { })); } - send(data) { + send(data: any) { + // $FlowFixMe this[requestData].request.end(data); return this; } } -function injectResponse(smartObject, promise) { +function injectResponse(smartObject: any, promise: Promise): Promise { + // $FlowFixMe promise[requestData] = smartObject[requestData]; return promise; } -function smartRequest(options) { - const smartObject = {}; + +function smartRequest(options: any): Promise { + const smartObject: any = {}; smartObject[requestData] = {}; smartObject[requestData].error = Error(); - Error.captureStackTrace(smartObject[requestData].error, smartRequest); - const result = new PromiseAssert(function(resolve, reject) { + Error.captureStackTrace(smartObject[requestData].error, smartRequest); + + const promiseResult: Promise = new PromiseAssert(function(resolve, reject) { + // store request reference on symbol smartObject[requestData].request = request(options, function(err, res, body) { if (err) { return reject(err); @@ -106,9 +120,7 @@ function smartRequest(options) { }); }); - // console.log("--result->", result); - - return injectResponse(smartObject, result); + return injectResponse(smartObject, promiseResult); } export default smartRequest; diff --git a/test/functional/lib/server.js b/test/functional/lib/server.js index a49d699de..04146768e 100644 --- a/test/functional/lib/server.js +++ b/test/functional/lib/server.js @@ -1,28 +1,34 @@ +// @flow -const _ = require('lodash'); +import _ from 'lodash'; import assert from 'assert'; -import request from './request'; +import smartRequest from './request'; +import type {IServerBridge} from './types'; -const buildAuthHeader = (user, pass) => { +const buildAuthHeader = (user, pass): string => { return `Basic ${(new Buffer(`${user}:${pass}`)).toString('base64')}`; }; -class Server { +export default class Server implements IServerBridge{ + url: string; + userAgent: string; + authstr: string; - constructor(url) { + constructor(url: string) { this.url = url.replace(/\/$/, ''); this.userAgent = 'node/v8.1.2 linux x64'; this.authstr = buildAuthHeader('test', 'test'); } - request(options) { + request(options: any): any { assert(options.uri); - const headers = options.headers || {}; + const headers: any = options.headers || {}; + headers.accept = headers.accept || 'application/json'; headers['user-agent'] = headers['user-agent'] || this.userAgent; headers.authorization = headers.authorization || this.authstr; - return request({ + return smartRequest({ url: this.url + options.uri, method: options.method || 'GET', headers: headers, @@ -31,7 +37,7 @@ class Server { }); } - auth(name, password) { + auth(name: string, password: string) { this.authstr = buildAuthHeader(name, password); return this.request({ uri: `/-/user/org.couchdb.user:${encodeURIComponent(name)}/-rev/undefined`, @@ -48,7 +54,7 @@ class Server { }); } - logout(token) { + logout(token: string) { return this.request({ uri: `/-/user/token/${encodeURIComponent(token)}`, method: 'DELETE', @@ -56,14 +62,14 @@ class Server { } - getPackage(name) { + getPackage(name: string) { return this.request({ uri: `/${encodeURIComponent(name)}`, method: 'GET', }); } - putPackage(name, data) { + putPackage(name: string, data) { if (_.isObject(data) && !Buffer.isBuffer(data)) { data = JSON.stringify(data); } @@ -76,10 +82,11 @@ class Server { }).send(data); } - putVersion(name, version, data) { + putVersion(name: string, version: string, data: any) { if (_.isObject(data) && !Buffer.isBuffer(data)) { data = JSON.stringify(data); } + return this.request({ uri: `/${encodeURIComponent(name)}/${encodeURIComponent(version)}/-tag/latest`, method: 'PUT', @@ -89,7 +96,7 @@ class Server { }).send(data); } - getTarball(name, filename) { + getTarball(name: string, filename: string) { return this.request({ uri: `/${encodeURIComponent(name)}/-/${encodeURIComponent(filename)}`, method: 'GET', @@ -97,7 +104,7 @@ class Server { }); } - putTarball(name, filename, data) { + putTarball(name: string, filename: string, data: any) { return this.request({ uri: `/${encodeURIComponent(name)}/-/${encodeURIComponent(filename)}/whatever`, method: 'PUT', @@ -107,7 +114,7 @@ class Server { }).send(data); } - removeTarball(name) { + removeTarball(name: string) { return this.request({ uri: `/${encodeURIComponent(name)}/-rev/whatever`, method: 'DELETE', @@ -117,7 +124,7 @@ class Server { }); } - removeSingleTarball(name, filename) { + removeSingleTarball(name: string, filename: string) { return this.request({ uri: `/${encodeURIComponent(name)}/-/${filename}/-rev/whatever`, method: 'DELETE', @@ -128,7 +135,7 @@ class Server { } - addTag(name, tag, version) { + addTag(name: string, tag: string, version: string) { return this.request({ uri: `/${encodeURIComponent(name)}/${encodeURIComponent(tag)}`, method: 'PUT', @@ -138,7 +145,7 @@ class Server { }).send(JSON.stringify(version)); } - putTarballIncomplete(name, filename, data, size, cb) { + putTarballIncomplete(name: string, filename: string, data: any, size: number, cb: Function) { let promise = this.request({ uri: `/${encodeURIComponent(name)}/-/${encodeURIComponent(filename)}/whatever`, method: 'PUT', @@ -171,7 +178,7 @@ class Server { }); } - addPackage(name) { + addPackage(name: string) { return this.putPackage(name, require('../fixtures/package')(name)) .status(201) .body_ok('created new package'); @@ -204,6 +211,4 @@ class Server { }, }) } - } -export default Server; diff --git a/test/functional/lib/types.js b/test/functional/lib/types.js new file mode 100644 index 000000000..ad036b695 --- /dev/null +++ b/test/functional/lib/types.js @@ -0,0 +1,52 @@ +// @flow + +export interface IVerdaccioConfig { + storagePath: string; + configPath: string; + domainPath: string; +} + +export interface IRequestPromise { + status(reason: any): any; + body_ok(reason: any): any; + body_error(reason: any): any; + request(reason: any): any; + response(reason: any): any; + send(reason: any): any; +} + +export interface IServerProcess { + bridge: IServerBridge; + init(): Promise; + stop(): Promise; + getBridge(): IServerBridge; + notify(callback: Function): void; + cleanStorage(): Promise; +} + +declare class verdaccio$PromiseAssert extends Promise { + constructor(options: any): IRequestPromise; +} + +export interface IServerBridge { + url: string; + userAgent: string; + authstr: string; + request(options: any): typeof verdaccio$PromiseAssert; + auth(name: string, password: string): IRequestPromise; + logout(token: string): Promise; + auth(name: string, password: string): IRequestPromise; + getPackage(name: string): Promise; + putPackage(name: string, data: any): Promise; + putVersion(name: string, version: string, data: any): Promise; + getTarball(name: string, filename: string): Promise; + putTarball(name: string, filename: string, data: any): Promise; + removeTarball(name: string): Promise; + removeSingleTarball(name: string, filename: string): Promise; + addTag(name: string, tag: string, version: string): Promise; + putTarballIncomplete(name: string, filename: string, data: any, size: number, cb: Function): Promise; + addPackage(name: string): Promise; + whoami(): Promise; + ping(): Promise; + debug(): IRequestPromise; +} \ No newline at end of file diff --git a/test/functional/lib/verdaccio-server.js b/test/functional/lib/verdaccio-server.js new file mode 100644 index 000000000..f3f048d4a --- /dev/null +++ b/test/functional/lib/verdaccio-server.js @@ -0,0 +1,172 @@ +// @flow +import _ from 'lodash'; +import express from 'express'; +import rimRaf from 'rimraf'; +import path from 'path'; +import {fork} from 'child_process'; +import bodyParser from 'body-parser'; +import Server from './server'; +import type {IVerdaccioConfig, IServerBridge, IServerProcess} from './types'; + +export class ExpressServer { + static start(): Promise { + return new Promise(function(resolve, reject) { + const app = express(); + + app.use(bodyParser.json()); + app.use(bodyParser.urlencoded({ + extended: true + })); + + app.listen(55550, function starExpressServer() { + resolve(); + }); + }); + } +} + +export class VerdaccioConfig implements IVerdaccioConfig { + + storagePath: string; + configPath: string; + domainPath: string; + + constructor(storagePath: string, configPath: string, domainPath: string) { + this.storagePath = storagePath; + this.configPath = configPath; + this.domainPath = domainPath; + } +} + +class VerdaccioProcess implements IServerProcess { + + bridge: IServerBridge; + config: IVerdaccioConfig; + childFork: any; + + constructor(config: IVerdaccioConfig) { + this.config = config; + this.bridge = new Server(config.domainPath); + } + + init(): Promise { + return new Promise(function(resolve, reject) { + const verdaccioRegisterWrap = path.join(__dirname, '/../../helper/verdaccio-test'); + const storageDir: string = path.join(__dirname, `/../${this.config.storagePath}`); + const configPath: string = path.join(__dirname, '../', this.config.configPath); + + 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); + // }); + + this.childFork = fork(verdaccioRegisterWrap, + ['-c', configPath], + { + // silent: !process.env.TRAVIS, + silent: false, + env: { + BABEL_ENV: 'registry' + } + } + ); + + // forks.push(childFork); + + this.childFork.on('message', function(msg) { + if ('verdaccio_started' in msg) { + resolve(this.childFork); + } + }); + + this.childFork.on('error', function(err) { + reject(err); + }); + + this.childFork.on('disconnect', function(err) { + reject(err); + }); + + this.childFork.on('exit', function(err) { + reject(err); + }); + + //process.execArgv = filteredArguments; + }); + + }); + } + + getBridge(): IServerBridge { + return this.bridge; + } + + stop(): Promise { + return new Promise(function(resolve, reject) { + + }); + } + + notify(callback: Function): void { + callback(); + } + + cleanStorage(): Promise { + return new Promise(function(resolve, reject) { + rimRaf(this.config.storagePath, function(err) { + if(_.isNil(err) === false) { + reject(err); + } + }); + }); + } +} + +export class VerdaccioServer { + + serverProcess: IServerProcess; + pid: number; + + constructor(config: IVerdaccioConfig) { + this.serverProcess = new VerdaccioProcess(config); + } + + start(): Promise { + return this.serverProcess.init().then(this.debugCheck); + } + + debugCheck(): Promise{ + return new Promise((resolve, reject) => { + this.serverProcess.getBridge().debug().status(200).then((body) => { + if (_.isNil(body.pid)) { + reject(); + } + this.pid = body.pid; + return this.authTestUser().catch(function(reason: any) { + reject(reason); + }); + }).catch(function(reason: any) { + reject(reason); + }); + }); + } + + authTestUser(): Promise { + return this.serverProcess.getBridge().auth('test', 'test') + .status(201) + .body_ok(/'test'/); + } + + stop() { + + } + + notify() { + + } +} \ No newline at end of file