refactor: refactor functional test server launch (wip)

This commit is contained in:
Juan Picado @jotadeveloper 2017-11-19 16:08:04 +01:00 committed by juanpicado
parent 5cc8405d4f
commit adf7f3adb3
5 changed files with 325 additions and 131 deletions

View File

@ -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);

View File

@ -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<any> 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<any>): Promise<any> {
// $FlowFixMe
promise[requestData] = smartObject[requestData];
return promise;
}
function smartRequest(options) {
const smartObject = {};
function smartRequest(options: any): Promise<any> {
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<any> = 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;

View File

@ -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;

View File

@ -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<any>;
stop(): Promise<any>;
getBridge(): IServerBridge;
notify(callback: Function): void;
cleanStorage(): Promise<any>;
}
declare class verdaccio$PromiseAssert<IRequestPromise> extends Promise<any> {
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<any>;
auth(name: string, password: string): IRequestPromise;
getPackage(name: string): Promise<any>;
putPackage(name: string, data: any): Promise<any>;
putVersion(name: string, version: string, data: any): Promise<any>;
getTarball(name: string, filename: string): Promise<any>;
putTarball(name: string, filename: string, data: any): Promise<any>;
removeTarball(name: string): Promise<any>;
removeSingleTarball(name: string, filename: string): Promise<any>;
addTag(name: string, tag: string, version: string): Promise<any>;
putTarballIncomplete(name: string, filename: string, data: any, size: number, cb: Function): Promise<any>;
addPackage(name: string): Promise<any>;
whoami(): Promise<any>;
ping(): Promise<any>;
debug(): IRequestPromise;
}

View File

@ -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<any> {
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<any> {
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<any> {
return new Promise(function(resolve, reject) {
});
}
notify(callback: Function): void {
callback();
}
cleanStorage(): Promise<any> {
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<any> {
return this.serverProcess.init().then(this.debugCheck);
}
debugCheck(): Promise<any>{
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<any> {
return this.serverProcess.getBridge().auth('test', 'test')
.status(201)
.body_ok(/'test'/);
}
stop() {
}
notify() {
}
}