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

Merge pull request #787 from verdaccio/refactor-unit-test

refactor: unit test
This commit is contained in:
Juan Picado @jotadeveloper 2018-06-29 00:36:36 +02:00 committed by GitHub
commit 197aecbe4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 30718 additions and 503 deletions

@ -9,6 +9,10 @@
# path to a directory with all packages
storage: ./storage
web:
# WebUI is enabled as default, if you want disable it, just uncomment this line
title: Verdaccio
auth:
htpasswd:
file: ./htpasswd

@ -13,6 +13,10 @@
# path to a directory with all packages
storage: /verdaccio/storage
web:
# WebUI is enabled as default, if you want disable it, just uncomment this line
title: Verdaccio
auth:
htpasswd:
file: /verdaccio/conf/htpasswd

@ -16,10 +16,11 @@ auth:
# You can set this to -1 to disable registration.
#max_users: 1000
# Experimental built-in middlewares
#middlewares:
# audit:
# enabled: true
# Configure plugins that can register custom middlewares
# To use `npm audit` uncomment the following section
middlewares:
audit:
enabled: true
# a list of other known repositories we can talk to
uplinks:
@ -141,34 +142,34 @@ logs:
# webhooks, but will also deliver a simple payload to
# any endpoint. Currently only active for publish / create
# commands.
notify:
#notify:
# Choose a method. Technically this will accept any HTTP
# request method, but probably stick to GET or POST
method: POST
# method: POST
# Only run this notification if the package name matches the regular
# expression
packagePattern: ^example-package$
# packagePattern: ^example-package$
# Any flags to be used with the regular expression
packagePatternFlags: i
# 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: [{'Content-type': 'application/x-www-form-urlencoded'}]
# set the URL endpoint for this call
endpoint: https://hooks.slack.com/...
# endpoint: https://hooks.slack.com/...
# Finally, the content you will be sending in the body.
# This data will first be run through Handlebars to parse
# any Handlebar expressions. All data housed in the metadata object
# is available for use within the expressions.
content: ' {{ handlebar-expression }}'
# content: ' {{ handlebar-expression }}'
# For Slack, follow the following format:
# content: '{ "text": "Package *{{ name }}* published to version *{{ dist-tags.latest }}*", "username": "Verdaccio", "icon_emoji": ":package:" }'
# Multiple notification endpoints can be created by specifying a collection
'example-package-1':
method: POST
# 'example-package-1':
# method: POST
# Only run this notification if the package name matches the regular
# expression
packagePattern: ^example-package-regex$
# packagePattern: ^example-package-regex$
# Any flags to be used with the regular expression
# since verdaccio 2.2.2 this property has been disabled read #108
# it will be re-enabled after 2.5.0
@ -176,18 +177,14 @@ notify:
# If this endpoint requires specific headers, set them here
# as an array of key: value objects.
# headers supports as well a literal object
headers: {'Content-type': 'application/x-www-form-urlencoded'}
# headers: {'Content-type': 'application/x-www-form-urlencoded'}
# set the URL endpoint for this call
endpoint: https://hooks.slack.com/...
# endpoint: https://hooks.slack.com/...
# Finally, the content you will be sending in the body.
# This data will first be run through Handlebars to parse
# any Handlebar expressions. All data housed in the metadata object
# is available for use within the expressions.
content: ' {{ handlebar-expression }}'
# content: ' {{ handlebar-expression }}'
# For Slack, follow the following format:
# content: '{ "text": "Package *{{ name }}* published to version *{{ dist-tags.latest }}*", "username": "Verdaccio", "icon_emoji": ":package:" }'
# Configure plugins that can register custom middlewares
#middlewares:
# plugin-name:
# setting: true

@ -1,143 +0,0 @@
// flow-typed signature: 9fd7b9287df55ee8cfa980889d107499
// flow-typed version: a8b5058d19/axios_v0.17.x/flow_>=v0.25.x
declare module "axios" {
declare interface ProxyConfig {
host: string;
port: number;
}
declare interface Cancel {
constructor(message?: string): Cancel;
message: string;
}
declare interface Canceler {
(message?: string): void;
}
declare interface CancelTokenSource {
token: CancelToken;
cancel: Canceler;
}
declare class CancelToken {
constructor(executor: (cancel: Canceler) => void): CancelToken;
static source(): CancelTokenSource;
promise: Promise<Cancel>;
reason?: Cancel;
throwIfRequested(): void;
}
declare interface AxiosXHRConfigBase<T> {
adapter?: <T>(config: AxiosXHRConfig<T>) => Promise<AxiosXHR<T>>;
auth?: {
username: string,
password: string
};
baseURL?: string;
cancelToken?: CancelToken;
headers?: Object;
httpAgent?: mixed; // Missing the type in the core flow node libdef
httpsAgent?: mixed; // Missing the type in the core flow node libdef
maxContentLength?: number;
maxRedirects?: 5;
params?: Object;
paramsSerializer?: (params: Object) => string;
progress?: (progressEvent: Event) => void | mixed;
proxy?: ProxyConfig | false;
responseType?:
| "arraybuffer"
| "blob"
| "document"
| "json"
| "text"
| "stream";
timeout?: number;
transformRequest?: Array<<U>(data: T) => U | Array<<U>(data: T) => U>>;
transformResponse?: Array<<U>(data: T) => U>;
validateStatus?: (status: number) => boolean;
withCredentials?: boolean;
xsrfCookieName?: string;
xsrfHeaderName?: string;
}
declare type $AxiosXHRConfigBase<T> = AxiosXHRConfigBase<T>;
declare interface AxiosXHRConfig<T> extends AxiosXHRConfigBase<T> {
data?: T;
method?: string;
url: string;
}
declare type $AxiosXHRConfig<T> = AxiosXHRConfig<T>;
declare class AxiosXHR<T> {
config: AxiosXHRConfig<T>;
data: T;
headers?: Object;
status: number;
statusText: string;
request: http$ClientRequest | XMLHttpRequest;
}
declare type $AxiosXHR<T> = AxiosXHR<T>;
declare class AxiosInterceptorIdent extends String {}
declare class AxiosRequestInterceptor<T> {
use(
successHandler: ?(
response: AxiosXHRConfig<T>
) => Promise<AxiosXHRConfig<*>> | AxiosXHRConfig<*>,
errorHandler: ?(error: mixed) => mixed
): AxiosInterceptorIdent;
eject(ident: AxiosInterceptorIdent): void;
}
declare class AxiosResponseInterceptor<T> {
use(
successHandler: ?(response: AxiosXHR<T>) => mixed,
errorHandler: ?(error: $AxiosError<any>) => mixed
): AxiosInterceptorIdent;
eject(ident: AxiosInterceptorIdent): void;
}
declare type AxiosPromise<T> = Promise<AxiosXHR<T>>;
declare class Axios {
constructor<T>(config?: AxiosXHRConfigBase<T>): void;
$call: <T>(
config: AxiosXHRConfig<T> | string,
config?: AxiosXHRConfig<T>
) => AxiosPromise<T>;
request<T>(config: AxiosXHRConfig<T>): AxiosPromise<T>;
delete<T>(url: string, config?: AxiosXHRConfigBase<T>): AxiosPromise<T>;
get<T>(url: string, config?: AxiosXHRConfigBase<T>): AxiosPromise<T>;
head<T>(url: string, config?: AxiosXHRConfigBase<T>): AxiosPromise<T>;
post<T>(
url: string,
data?: mixed,
config?: AxiosXHRConfigBase<T>
): AxiosPromise<T>;
put<T>(
url: string,
data?: mixed,
config?: AxiosXHRConfigBase<T>
): AxiosPromise<T>;
patch<T>(
url: string,
data?: mixed,
config?: AxiosXHRConfigBase<T>
): AxiosPromise<T>;
interceptors: {
request: AxiosRequestInterceptor<mixed>,
response: AxiosResponseInterceptor<mixed>
};
defaults: { headers: Object } & AxiosXHRConfig<*>;
}
declare class AxiosError<T> extends Error {
config: AxiosXHRConfig<T>;
response: AxiosXHR<T>;
code?: string;
}
declare type $AxiosError<T> = AxiosError<T>;
declare interface AxiosExport extends Axios {
Axios: typeof Axios;
Cancel: Class<Cancel>;
CancelToken: Class<CancelToken>;
isCancel(value: any): boolean;
create(config?: AxiosXHRConfigBase<any>): Axios;
all: typeof Promise.all;
spread(callback: Function): (arr: Array<any>) => Function;
}
declare module.exports: AxiosExport;
}

@ -4,20 +4,39 @@ module.exports = {
name: 'verdaccio-unit-jest',
verbose: true,
collectCoverage: true,
coveragePathIgnorePatterns: [
'node_modules',
'fixtures'
],
testEnvironment: 'jest-environment-jsdom-global',
testRegex: '(test/unit.*\\.spec|test/unit/webui/.*\\.spec)\\.js',
setupFiles: [
'./test/unit/setup.js'
],
// Some unit tests rely on data folders that look like packages. This confuses jest-hast-map
// when it tries to scan for package.json files.
modulePathIgnorePatterns: [
'setup.js'
'<rootDir>/test/unit/partials/mock-store/.*/package.json',
'<rootDir>/test/functional/store/.*/package.json',
'<rootDir>/test/unit/partials/store/.*/package.json',
'<rootDir>/coverage',
'<rootDir>/docs',
'<rootDir>/debug',
'<rootDir>/scripts',
'<rootDir>/.circleci',
'<rootDir>/tools',
'<rootDir>/wiki',
'<rootDir>/systemd',
'<rootDir>/flow-typed',
'<rootDir>test/unit/partials/mock-store/.*/package.json',
'<rootDir>/test/functional/store/.*/package.json',
'<rootDir>/build',
'<rootDir>/.vscode/',
],
testPathIgnorePatterns: [
'__snapshots__'
'__snapshots__',
'<rootDir>/build',
],
coveragePathIgnorePatterns: [
'node_modules',
'fixtures',
'<rootDir>/test',
],
moduleNameMapper: {
'\\.(scss)$': '<rootDir>/node_modules/identity-obj-proxy',

@ -6,6 +6,7 @@ import {media, allow} from '../../middleware';
import {DIST_TAGS} from '../../../lib/utils';
import type {Router} from 'express';
import type {IAuth, $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler} from '../../../../types';
import {API_MESSAGE, HTTP_STATUS} from '../../../lib/constants';
export default function(route: Router, auth: IAuth, storage: IStorageHandler) {
const can = allow(auth);
@ -20,8 +21,8 @@ export default function(route: Router, auth: IAuth, storage: IStorageHandler) {
if (err) {
return next(err);
}
res.status(201);
return next({ok: 'package tagged'});
res.status(HTTP_STATUS.CREATED);
return next({ok: API_MESSAGE.TAG_ADDED});
});
};
@ -39,9 +40,9 @@ export default function(route: Router, auth: IAuth, storage: IStorageHandler) {
if (err) {
return next(err);
}
res.status(201);
res.status(HTTP_STATUS.CREATED);
return next({
ok: 'tag removed',
ok: API_MESSAGE.TAG_REMOVED,
});
});
});
@ -65,8 +66,10 @@ export default function(route: Router, auth: IAuth, storage: IStorageHandler) {
if (err) {
return next(err);
}
res.status(201);
return next({ok: 'tags updated'});
res.status(HTTP_STATUS.CREATED);
return next({
ok: API_MESSAGE.TAG_UPDATED,
});
});
});
}

@ -1,9 +1,8 @@
// @flow
import express from 'express';
import Error from 'http-errors';
import compression from 'compression';
import _ from 'lodash';
import express from 'express';
import compression from 'compression';
import cors from 'cors';
import Storage from '../lib/storage';
import {loadPlugin} from '../lib/plugin-loader';
@ -14,6 +13,8 @@ import apiEndpoint from './endpoint';
import type {$Application} from 'express';
import type {$ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler, IAuth} from '../../types';
import type {Config as IConfig} from '@verdaccio/types';
import {ErrorCode} from '../lib/utils';
import {API_ERROR, HTTP_STATUS} from '../lib/constants';
const LoggerApp = require('../lib/logger');
const Config = require('../lib/config');
@ -69,18 +70,18 @@ const defineAPI = function(config: Config, storage: IStorageHandler) {
app.use('/-/verdaccio/', require('./web/api')(config, auth, storage));
} else {
app.get('/', function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
next(Error[404]('Web interface is disabled in the config file'));
next(ErrorCode.getNotFound(API_ERROR.WEB_DISABLED));
});
}
// Catch 404
app.get('/*', function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
next(Error[404]('File not found'));
next(ErrorCode.getNotFound(API_ERROR.FILE_NOT_FOUND));
});
app.use(function(err, req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
if (_.isError(err)) {
if (err.code === 'ECONNABORT' && res.statusCode === 304) {
if (err.code === 'ECONNABORT' && res.statusCode === HTTP_STATUS.NOT_MODIFIED) {
return next();
}
if (_.isFunction(res.report_error) === false) {

@ -3,6 +3,7 @@ import _ from 'lodash';
import fs from 'fs';
import Search from '../../lib/search';
import * as Utils from '../../lib/utils';
import {WEB_TITLE} from '../../lib/constants';
const {securityIframe} = require('../middleware');
/* eslint new-cap:off */
@ -40,10 +41,9 @@ module.exports = function(config, auth, storage) {
router.get('/', function(req, res) {
const base = Utils.combineBaseUrl(Utils.getWebProtocol(req), req.get('host'), config.url_prefix);
const defaultTitle = 'Verdaccio';
let webPage = template
.replace(/ToReplaceByVerdaccio/g, base)
.replace(/ToReplaceByTitle/g, _.get(config, 'web.title') ? config.web.title : defaultTitle);
.replace(/ToReplaceByTitle/g, _.get(config, 'web.title') ? config.web.title : WEB_TITLE);
res.setHeader('Content-Type', 'text/html');

@ -8,6 +8,7 @@ import {aesDecrypt, aesEncrypt, signPayload, verifyPayload} from './crypto-utils
import type {Config, Logger, Callback} from '@verdaccio/types';
import type {$Response, NextFunction} from 'express';
import type {$RequestExtend, JWTPayload} from '../../types';
import {ROLES} from './constants';
const LoggerApi = require('./logger');
@ -148,13 +149,13 @@ class Auth {
let pkg = Object.assign({name: packageName}, this.config.getMatchedPackagesSpec(packageName));
(function next() {
let p = plugins.shift();
const plugin = plugins.shift();
if (typeof(p.allow_access) !== 'function') {
if (typeof(plugin.allow_access) !== 'function') {
return next();
}
p.allow_access(user, pkg, function(err, ok) {
plugin.allow_access(user, pkg, function(err, ok) {
if (err) {
return callback(err);
}
@ -360,7 +361,7 @@ function buildAnonymousUser() {
*/
function authenticatedUser(name: string, pluginGroups: Array<any>) {
const isGroupValid: boolean = _.isArray(pluginGroups);
const groups = (isGroupValid ? pluginGroups : []).concat(['$all', '$authenticated', '@all', '@authenticated', 'all']);
const groups = (isGroupValid ? pluginGroups : []).concat([ROLES.$ALL, ROLES.$AUTH, ROLES.DEPRECATED_ALL, ROLES.DEPRECATED_AUTH, ROLES.ALL]);
return {
name,

@ -13,6 +13,7 @@ import {parse_address} from './utils';
import type {Callback} from '@verdaccio/types';
import type {$Application} from 'express';
import {DEFAULT_PORT} from './constants';
const logger = require('./logger');
@ -37,7 +38,7 @@ export function getListListenAddresses(argListen: string, configListen: mixed) {
} else if (configListen) {
addresses = [configListen];
} else {
addresses = ['4873'];
addresses = [DEFAULT_PORT];
}
addresses = addresses.map(function(addr) {
const parsedAddr = parse_address(addr);

@ -1,9 +1,13 @@
// @flow
export const DEFAULT_PORT = '4873';
export const DEFAULT_DOMAIN = 'localhost';
export const HEADERS = {
JSON: 'application/json',
JSON_CHARSET: 'application/json; charset=utf-8',
OCTET_STREAM: 'application/octet-stream',
OCTET_STREAM: 'application/octet-stream; charset=utf-8',
TEXT_CHARSET: 'text/plain; charset=utf-8',
GZIP: 'gzip',
};
@ -20,13 +24,22 @@ export const ERROR_CODE = {
export const TOKEN_BASIC = 'Basic';
export const TOKEN_BEARER = 'Bearer';
export const DEFAULT_REGISTRY = 'https://registry.npmjs.org/';
export const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
export const DEFAULT_UPLINK = 'npmjs';
export const ROLES = {
$ALL: '$all',
$AUTH: '$authenticated',
DEPRECATED_ALL: '@all',
DEPRECATED_AUTH: '@authenticated',
ALL: 'all',
};
export const HTTP_STATUS = {
OK: 200,
CREATED: 201,
MULTIPLE_CHOICES: 300,
NOT_MODIFIED: 304,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
@ -45,6 +58,10 @@ export const API_MESSAGE = {
PKG_REMOVED: 'package removed',
PKG_PUBLISHED: 'package published',
TARBALL_REMOVED: 'tarball removed',
TAG_UPDATED: 'tags updated',
TAG_REMOVED: 'tag removed',
TAG_ADDED: 'package tagged',
};
export const API_ERROR = {
@ -57,6 +74,12 @@ export const API_ERROR = {
NOT_FILE_UPLINK: 'file doesn\'t exist on uplink',
MAX_USERS_REACHED: 'maximum amount of users reached',
VERSION_NOT_EXIST: 'this version doesn\'t exist',
FILE_NOT_FOUND: 'File not found',
BAD_STATUS_CODE: 'bad status code',
WEB_DISABLED: 'Web interface is disabled in the config file',
};
export const DEFAULT_NO_README = 'ERROR: No README data found!';
export const WEB_TITLE = 'Verdaccio';

@ -745,7 +745,10 @@ class LocalStorage implements IStorage {
json._rev = DEFAULT_REVISION;
}
json._rev = generateRevision(json._rev);
// this is intended in debug mode we do not want modify the store revision
if (_.isNil(this.config._debug)) {
json._rev = generateRevision(json._rev);
}
return json;
}

@ -411,7 +411,7 @@ class ProxyStorage implements IProxy {
return callback( ErrorCode.getNotFound(API_ERROR.NOT_PACKAGE_UPLINK));
}
if (!(res.statusCode >= HTTP_STATUS.OK && res.statusCode < HTTP_STATUS.MULTIPLE_CHOICES)) {
const error = ErrorCode.getInternalError(`bad status code: ${res.statusCode}`);
const error = ErrorCode.getInternalError(`${API_ERROR.BAD_STATUS_CODE}: ${res.statusCode}`);
// $FlowFixMe
error.remoteStatus = res.statusCode;
return callback(error);

@ -1,16 +1,16 @@
// @flow
import {generateGravatarUrl} from '../utils/user';
import _ from 'lodash';
import fs from 'fs';
import assert from 'assert';
import semver from 'semver';
import YAML from 'js-yaml';
import URL from 'url';
import fs from 'fs';
import _ from 'lodash';
import asciidoctor from 'asciidoctor.js';
import createError from 'http-errors';
import marked from 'marked';
import {HTTP_STATUS, API_ERROR} from './constants';
import {HTTP_STATUS, API_ERROR, DEFAULT_PORT, DEFAULT_DOMAIN} from './constants';
import {generateGravatarUrl} from '../utils/user';
import type {Package} from '@verdaccio/types';
import type {$Request} from 'express';
@ -219,8 +219,8 @@ function parse_address(urlAddress: any) {
if (urlPattern) {
return {
proto: urlPattern[2] || 'http',
host: urlPattern[6] || urlPattern[7] || 'localhost',
port: urlPattern[8] || '4873',
host: urlPattern[6] || urlPattern[7] || DEFAULT_DOMAIN,
port: urlPattern[8] || DEFAULT_PORT,
};
}

@ -1,3 +1,5 @@
import {DOMAIN_SERVERS as localhost} from '../test.conf';
export const CREDENTIALS = {
user: 'test',
password: 'test'
@ -8,4 +10,5 @@ export const PORT_SERVER_APP = '55550';
export const PORT_SERVER_1 = '55551';
export const PORT_SERVER_2 = '55552';
export const PORT_SERVER_3 = '55553';
export const DOMAIN_SERVERS = 'localhost';
export const DOMAIN_SERVERS = localhost;

@ -77,9 +77,7 @@ class FunctionalEnvironment extends NodeEnvironment {
async teardown() {
await super.teardown();
console.log(chalk.yellow('Teardown Test Environment.'));
// this.global.__VERDACCIO_E2E__.stop();
// this.global.__VERDACCIO__PROTECTED_E2E__.stop();
// close verdaccios
// shutdown verdaccio
for (let server of this.global.__SERVERS_PROCESS__) {
server[0].stop();
}

@ -120,7 +120,5 @@ packages:
access: test $anonymous
publish: test $anonymous
listen: 55551
# expose internal methods
_debug: true

@ -93,7 +93,5 @@ packages:
access: test $anonymous
publish: test $anonymous
listen: 55552
# expose internal methods
_debug: true

@ -38,7 +38,5 @@ packages:
'*':
access: $all
listen: 55553
# expose internal methods
_debug: true

@ -7,5 +7,23 @@ module.exports = {
globalSetup: './e2e/pre-setup.js',
globalTeardown: './e2e/teardown.js',
testEnvironment: './e2e/puppeteer_environment.js',
testRegex: '(/e2e.*\\.spec)\\.js'
testRegex: '(/e2e.*\\.spec)\\.js',
modulePathIgnorePatterns: [
'<rootDir>/unit/partials/mock-store/.*/package.json',
'<rootDir>/functional/store/.*/package.json',
'<rootDir>/unit/partials/store/.*/package.json',
'<rootDir>/../coverage',
'<rootDir>/../docs',
'<rootDir>/../debug',
'<rootDir>/../scripts',
'<rootDir>/../.circleci',
'<rootDir>/../tools',
'<rootDir>/../wiki',
'<rootDir>/../systemd',
'<rootDir>/../flow-typed',
'<rootDir>unit/partials/mock-store/.*/package.json',
'<rootDir>functional/store/.*/package.json',
'<rootDir>/../build',
'<rootDir>/../.vscode/',
]
};

@ -6,5 +6,25 @@ module.exports = {
globalSetup: './functional/pre-setup.js',
globalTeardown: './functional/teardown.js',
testEnvironment: './functional/test-environment.js',
// Some unit tests rely on data folders that look like packages. This confuses jest-hast-map
// when it tries to scan for package.json files.
modulePathIgnorePatterns: [
'<rootDir>/unit/partials/mock-store/.*/package.json',
'<rootDir>/functional/store/.*/package.json',
'<rootDir>/unit/partials/store/.*/package.json',
'<rootDir>/../coverage',
'<rootDir>/../docs',
'<rootDir>/../debug',
'<rootDir>/../scripts',
'<rootDir>/../.circleci',
'<rootDir>/../tools',
'<rootDir>/../wiki',
'<rootDir>/../systemd',
'<rootDir>/../flow-typed',
'<rootDir>unit/partials/mock-store/.*/package.json',
'<rootDir>functional/store/.*/package.json',
'<rootDir>/../build',
'<rootDir>/../.vscode/',
],
collectCoverage: false
};

@ -14,65 +14,78 @@ export default class VerdaccioProcess implements IServerProcess {
childFork: any;
isDebug: boolean;
silence: boolean;
cleanStore: boolean;
constructor(config: IVerdaccioConfig, bridge: IServerBridge, silence: boolean = true, isDebug: boolean = false) {
constructor(config: IVerdaccioConfig,
bridge: IServerBridge,
silence: boolean = true,
isDebug: boolean = false,
cleanStore: boolean = true) {
this.config = config;
this.bridge = bridge;
this.silence = silence;
this.isDebug = isDebug;
this.cleanStore = cleanStore;
}
init(): Promise<any> {
init(verdaccioPath: string = '../../bin/verdaccio'): Promise<any> {
return new Promise((resolve, reject) => {
const verdaccioRegisterWrap: string = path.join(__dirname, '../../bin/verdaccio');
rimRaf(this.config.storagePath, (err) => {
if (_.isNil(err) === false) {
reject(err);
}
let childOptions = {
silent: this.silence
};
if (this.isDebug) {
const debugPort = parseInt(this.config.port, 10) + 5;
childOptions = Object.assign({}, childOptions, {
execArgv: [`--inspect=${debugPort}`]
});
}
this.childFork = fork(verdaccioRegisterWrap, ['-c', this.config.configPath], childOptions);
this.childFork.on('message', (msg) => {
if ('verdaccio_started' in msg) {
this.bridge.debug().status(HTTP_STATUS.OK).then((body) => {
this.bridge.auth(CREDENTIALS.user, CREDENTIALS.password)
.status(HTTP_STATUS.CREATED)
.body_ok(new RegExp(CREDENTIALS.user))
.then(() => {
resolve([this, body.pid]);
}, reject)
}, reject);
if(this.cleanStore) {
rimRaf(this.config.storagePath, (err) => {
if (_.isNil(err) === false) {
reject(err);
}
});
this.childFork.on('error', (err) => {
console.log('error process', err);
reject([err, this]);
this._start(verdaccioPath, resolve, reject);
});
} else {
this._start(verdaccioPath, resolve, reject);
}
});
}
this.childFork.on('disconnect', (err) => {
reject([err, this]);
});
_start(verdaccioPath: string, resolve: Function, reject: Function) {
const verdaccioRegisterWrap: string = path.join(__dirname, verdaccioPath);
let childOptions = {
silent: this.silence
};
this.childFork.on('exit', (err) => {
reject([err, this]);
});
if (this.isDebug) {
const debugPort = parseInt(this.config.port, 10) + 5;
childOptions = Object.assign({}, childOptions, {
execArgv: [`--inspect=${debugPort}`]
});
}
const {configPath, port} = this.config;
// $FlowFixMe
this.childFork = fork(verdaccioRegisterWrap, ['-c', configPath, '-l', port], childOptions);
this.childFork.on('message', (msg) => {
if ('verdaccio_started' in msg) {
this.bridge.debug().status(HTTP_STATUS.OK).then((body) => {
this.bridge.auth(CREDENTIALS.user, CREDENTIALS.password)
.status(HTTP_STATUS.CREATED)
.body_ok(new RegExp(CREDENTIALS.user))
.then(() => {
resolve([this, body.pid]);
}, reject)
}, reject);
}
});
this.childFork.on('error', (err) => {
console.log('error process', err);
reject([err, this]);
});
this.childFork.on('disconnect', (err) => {
reject([err, this]);
});
this.childFork.on('exit', (err) => {
reject([err, this]);
});
}

1
test/test.conf.js Normal file

@ -0,0 +1 @@
export const DOMAIN_SERVERS = '0.0.0.0';

@ -8,7 +8,11 @@ import publishMetadata from '../partials/publish-api';
import forbiddenPlace from '../partials/forbidden-place';
import Config from '../../../src/lib/config';
import endPointAPI from '../../../src/api/index';
import {HEADERS, API_ERROR} from '../../../src/lib/constants';
import {HEADERS, API_ERROR, HTTP_STATUS, HEADER_TYPE, API_MESSAGE} from '../../../src/lib/constants';
import {mockServer} from './mock';
import {DOMAIN_SERVERS} from '../../functional/config.functional';
import {DIST_TAGS} from '../../../src/lib/utils';
require('../../../src/lib/logger').setup([]);
const credentials = { name: 'Jota', password: 'secretPass' };
@ -16,10 +20,11 @@ const credentials = { name: 'Jota', password: 'secretPass' };
describe('endpoint unit test', () => {
let config;
let app;
jest.setTimeout(10000);
let mockRegistry;
beforeAll(function(done) {
const store = path.join(__dirname, '../partials/store/test-storage');
const mockServerPort = 55549;
rimraf(store, async () => {
const configForTest = _.clone(configDefault);
configForTest.auth = {
@ -27,22 +32,33 @@ describe('endpoint unit test', () => {
file: './test-storage/htpasswd-test'
}
};
configForTest.uplinks = {
npmjs: {
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`
}
};
configForTest.self_path = store;
config = new Config(configForTest);
app = await endPointAPI(config);
mockRegistry = await mockServer(mockServerPort).init();
done();
});
});
afterAll(function(done) {
mockRegistry[0].stop();
done();
});
describe('Registry API Endpoints', () => {
describe('should test ping api', () => {
test('should test endpoint /-/ping', (done) => {
request(app)
.get('/-/ping')
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.OK)
.end(function(err) {
if (err) {
return done(err);
}
@ -55,8 +71,8 @@ describe('endpoint unit test', () => {
test('should test /-/whoami endpoint', (done) => {
request(app)
.get('/-/whoami')
.expect('Content-Type', /json/)
.expect(200)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.OK)
.end(function(err, res) {
if (err) {
return done(err);
@ -68,8 +84,8 @@ describe('endpoint unit test', () => {
test('should test /whoami endpoint', (done) => {
request(app)
.get('/-/whoami')
.expect('Content-Type', /json/)
.expect(200)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.OK)
.end(function(err, res) {
if (err) {
return done(err);
@ -86,8 +102,8 @@ describe('endpoint unit test', () => {
request(app)
.get('/auth-package')
.set('authorization', 'FakeHader')
.expect('Content-Type', /json/)
.expect(403)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.expect(HTTP_STATUS.FORBIDDEN)
.end(function(err, res) {
expect(res.body.error).toBeDefined();
expect(res.body.error).toMatch(/unregistered users are not allowed to access package auth-package/);
@ -99,8 +115,8 @@ describe('endpoint unit test', () => {
request(app)
.get('/auth-package')
.set('authorization', 'Bearer')
.expect('Content-Type', /json/)
.expect(403)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.expect(HTTP_STATUS.FORBIDDEN)
.end(function(err, res) {
expect(res.body.error).toBeDefined();
expect(res.body.error).toMatch(/unregistered users are not allowed to access package auth-package/);
@ -112,8 +128,8 @@ describe('endpoint unit test', () => {
request(app)
.get('/auth-package')
.set('authorization', 'Bearer 12345')
.expect('Content-Type', /json/)
.expect(403)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.expect(HTTP_STATUS.FORBIDDEN)
.end(function(err, res) {
expect(res.body.error).toBeDefined();
expect(res.body.error).toMatch(/unregistered users are not allowed to access package auth-package/);
@ -127,8 +143,8 @@ describe('endpoint unit test', () => {
request(app)
.put('/-/user/org.couchdb.user:jota')
.send(credentials)
.expect('Content-Type', /json/)
.expect(201)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.CREATED)
.end(function(err, res) {
if (err) {
return done(err);
@ -144,8 +160,8 @@ describe('endpoint unit test', () => {
request(app)
.get('/vue')
.set('authorization', `Bearer ${token}`)
.expect('Content-Type', /json/)
.expect(200)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.OK)
.end(function(err, res) {
expect(err).toBeNull();
expect(res.body).toBeDefined();
@ -163,8 +179,8 @@ describe('endpoint unit test', () => {
request(app)
.put('/-/user/org.couchdb.user:jota')
.send(credentialsShort)
.expect('Content-Type', /json/)
.expect(400)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.BAD_REQUEST)
.end(function(err, res) {
if (err) {
return done(err);
@ -184,8 +200,8 @@ describe('endpoint unit test', () => {
request(app)
.put('/-/user/org.couchdb.user:jota')
.send(credentialsShort)
.expect('Content-Type', /json/)
.expect(400)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.BAD_REQUEST)
.end(function(err, res) {
if (err) {
return done(err);
@ -205,8 +221,8 @@ describe('endpoint unit test', () => {
request(app)
.put('/-/user/org.couchdb.user:jotaNew')
.send(newCredentials)
.expect('Content-Type', /json/)
.expect(201)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.CREATED)
.end(function(err, res) {
if (err) {
return done(err);
@ -220,8 +236,8 @@ describe('endpoint unit test', () => {
request(app)
.put('/-/user/org.couchdb.user:jotaNew')
.send(credentials)
.expect('Content-Type', /json/)
.expect(409)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.CONFLICT)
.end(function(err, res) {
if (err) {
return done(err);
@ -240,8 +256,8 @@ describe('endpoint unit test', () => {
request(app)
.put('/-/user/org.couchdb.user:jota')
.send(credentialsShort)
.expect('Content-Type', /json/)
.expect(401)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.UNAUTHORIZED)
.end(function(err, res) {
if (err) {
return done(err);
@ -261,9 +277,9 @@ describe('endpoint unit test', () => {
request(app)
.get('/jquery')
.set('content-type', HEADERS.JSON_CHARSET)
.expect('Content-Type', /json/)
.expect(200)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.OK)
.end(function(err, res) {
if (err) {
return done(err);
@ -279,9 +295,9 @@ describe('endpoint unit test', () => {
request(app)
.get('/jquery/1.5.1')
.set('content-type', HEADERS.JSON_CHARSET)
.expect('Content-Type', /json/)
.expect(200)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.OK)
.end(function(err, res) {
if (err) {
return done(err);
@ -297,9 +313,9 @@ describe('endpoint unit test', () => {
request(app)
.get('/jquery/latest')
.set('content-type', HEADERS.JSON_CHARSET)
.expect('Content-Type', /json/)
.expect(200)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.OK)
.end(function(err, res) {
if (err) {
return done(err);
@ -315,10 +331,10 @@ describe('endpoint unit test', () => {
request(app)
.get('/jquery/never-will-exist-this-tag')
.set('content-type', HEADERS.JSON_CHARSET)
.expect('Content-Type', /json/)
.expect(404)
.end(function(err, res) {
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.NOT_FOUND)
.end(function(err) {
if (err) {
return done(err);
}
@ -330,9 +346,9 @@ describe('endpoint unit test', () => {
request(app)
.get('/@verdaccio/not-found')
.set('content-type', HEADERS.JSON_CHARSET)
.expect('Content-Type', /json/)
.expect(404)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.NOT_FOUND)
.end(function(err, res) {
if (err) {
return done(err);
@ -345,10 +361,10 @@ describe('endpoint unit test', () => {
request(app)
.get('/forbidden-place')
.set('content-type', HEADERS.JSON_CHARSET)
.expect('Content-Type', /json/)
.expect(403)
.end(function(err, res) {
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.FORBIDDEN)
.end(function(err) {
if (err) {
return done(err);
}
@ -360,8 +376,8 @@ describe('endpoint unit test', () => {
request(app)
.get('/jquery/-/jquery-1.5.1.tgz')
.expect('Content-Type', /application\/octet-stream/)
.expect(200)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.OCTET_STREAM)
.expect(HTTP_STATUS.OK)
.end(function(err, res) {
if (err) {
return done(err);
@ -376,10 +392,11 @@ describe('endpoint unit test', () => {
request(app)
.get('/jquery/-/jquery-0.0.1.tgz')
.expect('Content-Type', /application\/octet-stream/)
.expect(404)
.end(function(err, res) {
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.OCTET_STREAM)
.expect(HTTP_STATUS.NOT_FOUND)
.end(function(err) {
if (err) {
expect(err).not.toBeNull();
return done(err);
}
@ -403,10 +420,11 @@ describe('endpoint unit test', () => {
.send(JSON.stringify(jqueryVersion))
.set('accept', 'gzip')
.set('accept-encoding', HEADERS.JSON)
.set('content-type', HEADERS.JSON)
.expect(201)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.expect(HTTP_STATUS.CREATED)
.end(function(err, res) {
if (err) {
expect.toBeNull();
return done(err);
}
@ -421,10 +439,11 @@ describe('endpoint unit test', () => {
request(app)
.get('/-/package/jquery/dist-tags')
.set('accept-encoding', HEADERS.JSON)
.set('content-type', HEADERS.JSON)
.expect(200)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.expect(HTTP_STATUS.OK)
.end(function(err, res) {
if (err) {
expect.toBeNull();
return done(err);
}
@ -439,15 +458,16 @@ describe('endpoint unit test', () => {
request(app)
.post('/-/package/jquery/dist-tags')
.send(JSON.stringify(jqueryUpdatedVersion))
.set('content-type', HEADERS.JSON)
.expect(201)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.expect(HTTP_STATUS.CREATED)
.end(function(err, res) {
if (err) {
expect.toBeNull();
return done(err);
}
expect(res.body.ok).toBeDefined();
expect(res.body.ok).toMatch(/tags updated/);
expect(res.body.ok).toMatch(API_MESSAGE.TAG_UPDATED);
done();
});
});
@ -457,10 +477,11 @@ describe('endpoint unit test', () => {
request(app)
.get('/-/package/jquery/dist-tags')
.set('accept-encoding', HEADERS.JSON)
.set('content-type', HEADERS.JSON)
.expect(200)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.expect(HTTP_STATUS.OK)
.end(function(err, res) {
if (err) {
expect.toBeNull();
return done(err);
}
@ -475,16 +496,17 @@ describe('endpoint unit test', () => {
request(app)
.del('/-/package/jquery/dist-tags/verdaccio-tag')
.set('accept-encoding', HEADERS.JSON)
.set('content-type', HEADERS.JSON)
//.expect('Content-Type', /json/)
.expect(201)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
//.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.CREATED)
.end(function(err, res) {
if (err) {
expect.toBeNull();
return done(err);
}
expect(res.body.ok).toBeDefined();
expect(res.body.ok).toMatch(/tag removed/);
expect(res.body.ok).toMatch(API_MESSAGE.TAG_REMOVED);
done();
});
});
@ -498,11 +520,12 @@ describe('endpoint unit test', () => {
request(app)
.get('/-/all/since?stale=update_after&startkey=' + cacheTime)
// .set('accept-encoding', HEADERS.JSON)
// .set('content-type', HEADERS.JSON)
//.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
// .set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
//.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.OK)
.end(function(err) {
if (err) {
expect.toBeNull();
return done(err);
}
//TODO: we have to catch the stream check whether it returns something
@ -517,17 +540,18 @@ describe('endpoint unit test', () => {
test('should publish a new package', (done) => {
request(app)
.put('/@scope%2fpk1-test')
.set('content-type', HEADERS.JSON)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.send(JSON.stringify(publishMetadata))
.expect(201)
.expect(HTTP_STATUS.CREATED)
.end(function(err, res) {
if (err) {
expect.toBeNull();
return done(err);
}
expect(res.body.ok).toBeDefined();
expect(res.body.success).toBeDefined();
expect(res.body.success).toBeTruthy();
expect(res.body.ok).toMatch(/created new package/);
expect(res.body.ok).toMatch(API_MESSAGE.PKG_CREATED);
done();
});
});
@ -536,14 +560,15 @@ describe('endpoint unit test', () => {
//FUTURE: for some reason it does not remove the scope folder
request(app)
.del('/@scope%2fpk1-test/-rev/4-6abcdb4efd41a576')
.set('content-type', HEADERS.JSON)
.expect(201)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.expect(HTTP_STATUS.CREATED)
.end(function(err, res) {
if (err) {
expect.toBeNull();
return done(err);
}
expect(res.body.ok).toBeDefined();
expect(res.body.ok).toMatch(/package removed/);
expect(res.body.ok).toMatch(API_MESSAGE.PKG_REMOVED);
done();
});
});
@ -554,15 +579,15 @@ describe('endpoint unit test', () => {
beforeAll(async function() {
await request(app)
.put('/@scope%2fpk1-test')
.set('content-type', HEADERS.JSON)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.send(JSON.stringify(publishMetadata))
.expect(201);
.expect(HTTP_STATUS.CREATED);
await request(app)
.put('/forbidden-place')
.set('content-type', HEADERS.JSON)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.send(JSON.stringify(forbiddenPlace))
.expect(201);
.expect(HTTP_STATUS.CREATED);
});
describe('Packages', () => {
@ -570,7 +595,7 @@ describe('endpoint unit test', () => {
test('should display all packages', (done) => {
request(app)
.get('/-/verdaccio/packages' )
.expect(200)
.expect(HTTP_STATUS.OK)
.end(function(err, res) {
expect(res.body).toHaveLength(1);
done();
@ -580,8 +605,8 @@ describe('endpoint unit test', () => {
test.skip('should display scoped readme', (done) => {
request(app)
.get('/-/verdaccio/package/readme/@scope/pk1-test')
.expect(200)
.expect('Content-Type', 'text/plain; charset=utf-8')
.expect(HTTP_STATUS.OK)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_CHARSET)
.end(function(err, res) {
expect(res.text).toMatch('<h1 id="test">test</h1>\n');
done();
@ -592,8 +617,8 @@ describe('endpoint unit test', () => {
test.skip('should display scoped readme 404', (done) => {
request(app)
.get('/-/verdaccio/package/readme/@scope/404')
.expect(200)
.expect('Content-Type', 'text/plain; charset=utf-8')
.expect(HTTP_STATUS.OK)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_CHARSET)
.end(function(err, res) {
expect(res.body.error).toMatch(API_ERROR.NO_PACKAGE);
done();
@ -603,11 +628,11 @@ describe('endpoint unit test', () => {
test('should display sidebar info', (done) => {
request(app)
.get('/-/verdaccio/sidebar/@scope/pk1-test')
.expect(200)
.expect('Content-Type', /json/)
.expect(HTTP_STATUS.OK)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.end(function(err, res) {
const sideBarInfo = res.body;
const latestVersion = publishMetadata.versions[publishMetadata['dist-tags'].latest];
const latestVersion = publishMetadata.versions[publishMetadata[DIST_TAGS].latest];
expect(sideBarInfo.latest.author).toBeDefined();
expect(sideBarInfo.latest.author.avatar).toMatch(/www.gravatar.com/);
@ -620,9 +645,9 @@ describe('endpoint unit test', () => {
test('should display sidebar info 404', (done) => {
request(app)
.get('/-/verdaccio/sidebar/@scope/404')
.expect(404)
.expect('Content-Type', /json/)
.end(function(err, res) {
.expect(HTTP_STATUS.NOT_FOUND)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.end(function() {
done();
});
});
@ -633,7 +658,7 @@ describe('endpoint unit test', () => {
test('should search pk1-test', (done) => {
request(app)
.get('/-/verdaccio/search/scope')
.expect(200)
.expect(HTTP_STATUS.OK)
.end(function(err, res) {
expect(res.body).toHaveLength(1);
done();
@ -643,7 +668,7 @@ describe('endpoint unit test', () => {
test('should search with 404', (done) => {
request(app)
.get('/-/verdaccio/search/@')
.expect(200)
.expect(HTTP_STATUS.OK)
.end(function(err, res) {
// in a normal world, the output would be 1
// https://github.com/verdaccio/verdaccio/issues/345
@ -656,7 +681,7 @@ describe('endpoint unit test', () => {
test('should not find forbidden-place', (done) => {
request(app)
.get('/-/verdaccio/search/forbidden-place')
.expect(200)
.expect(HTTP_STATUS.OK)
.end(function(err, res) {
//this is expected since we are not logged
// and forbidden-place is allow_access: 'nobody'
@ -675,7 +700,7 @@ describe('endpoint unit test', () => {
username: credentials.name,
password: credentials.password
})
.expect(200)
.expect(HTTP_STATUS.OK)
.end(function(err, res) {
expect(res.body.error).toBeUndefined();
expect(res.body.token).toBeDefined();
@ -693,7 +718,7 @@ describe('endpoint unit test', () => {
password: 'fake'
}))
//FIXME: there should be 401
.expect(200)
.expect(HTTP_STATUS.OK)
.end(function(err, res) {
expect(res.body.error).toMatch(/bad username\/password, access denied/);
done();

@ -1,11 +1,10 @@
import endPointAPI from '../../../src/api/index';
import {API_ERROR} from '../../../src/lib/constants';
const assert = require('assert');
const express = require('express');
const request = require('request');
const rimraf = require('rimraf');
const config = require('../partials/config/index');
import express from 'express';
import request from 'request';
import rimraf from 'rimraf';
import config from '../partials/config/index';
const app = express();
const server = require('http').createServer(app);
@ -35,18 +34,18 @@ describe('basic system test', () => {
request({
url: 'http://localhost:' + port + '/',
}, function(err, res, body) {
assert.equal(err, null);
assert(body.match(/<title>Verdaccio<\/title>/));
expect(err).toBeNull();
expect(body).toMatch(/<title>Verdaccio<\/title>/);
done();
});
});
test('server should respond on /whatever', done => {
request({
url: 'http://localhost:' + port + '/whatever',
url: `http://localhost:${port}/whatever`,
}, function(err, res, body) {
assert.equal(err, null);
assert(body.match(/no such package available/));
expect(err).toBeNull();
expect(body).toMatch(API_ERROR.NO_PACKAGE);
done();
});
});

@ -1,8 +1,10 @@
import path from 'path';
import _ from 'lodash';
import startServer from '../../../src/index';
import {getListListenAddresses} from '../../../src/lib/bootstrap';
import config from '../partials/config/index';
import path from 'path';
import _ from 'lodash';
import {DEFAULT_DOMAIN, DEFAULT_PORT} from '../../../src/lib/constants';
require('../../../src/lib/logger').setup([]);
@ -11,18 +13,21 @@ describe('startServer via API', () => {
describe('startServer launcher', () => {
test('should provide all server data await/async', async (done) => {
const store = path.join(__dirname, 'partials/store');
const serverName = 'verdaccio-test';
const version = '1.0.0';
const port = '6000';
await startServer(config, 6000, store, '1.0.0', 'verdaccio-test',
await startServer(config, port, store, version, serverName,
(webServer, addrs, pkgName, pkgVersion) => {
expect(webServer).toBeDefined();
expect(addrs).toBeDefined();
expect(addrs.proto).toBe('http');
expect(addrs.host).toBe('localhost');
expect(addrs.port).toBe('6000');
expect(addrs.port).toBe(port);
expect(pkgName).toBeDefined();
expect(pkgVersion).toBeDefined();
expect(pkgVersion).toBe('1.0.0');
expect(pkgName).toBe('verdaccio-test');
expect(pkgVersion).toBe(version);
expect(pkgName).toBe(serverName);
done();
});
});
@ -38,12 +43,12 @@ describe('startServer via API', () => {
});
describe('getListListenAddresses test', () => {
test('should return by default 4873', () => {
test(`should return by default ${DEFAULT_PORT}`, () => {
const addrs = getListListenAddresses()[0];
expect(addrs.proto).toBe('http');
expect(addrs.host).toBe('localhost');
expect(addrs.port).toBe('4873');
expect(addrs.host).toBe(DEFAULT_DOMAIN);
expect(addrs.port).toBe(DEFAULT_PORT);
});
test('should return a list of address and no cli argument provided', () => {

@ -1,55 +1,91 @@
const assert = require('assert');
const Utils = require('../../../src/lib/utils');
const Config = require('../../../src/lib/config');
const path = require('path');
const _ = require('lodash');
import path from 'path';
import _ from 'lodash';
import Config from '../../../src/lib/config';
import {parseConfigFile} from '../../../src/lib/utils';
import {DEFAULT_REGISTRY, DEFAULT_UPLINK, ROLES, WEB_TITLE} from '../../../src/lib/constants';
const resolveConf = (conf) => path.join(__dirname, `../../../conf/${conf}.yaml`);
const checkUplink = (config) => {
assert.equal(_.isObject(config.uplinks['npmjs']), true);
assert.equal(config.uplinks['npmjs'].url, 'https://registry.npmjs.org');
const checkDefaultUplink = (config) => {
expect(_.isObject(config.uplinks[DEFAULT_UPLINK])).toBeTruthy();
expect(config.uplinks[DEFAULT_UPLINK].url).toMatch(DEFAULT_REGISTRY);
};
const checkPackages = (config) => {
assert.equal(_.isObject(config.packages), true);
assert.equal(Object.keys(config.packages).join('|'), '@*/*|**');
assert.equal(config.packages['@*/*'].access, '$all');
assert.equal(config.packages['@*/*'].publish, '$authenticated');
assert.equal(config.packages['@*/*'].proxy, 'npmjs');
assert.equal(config.packages['**'].access, '$all');
assert.equal(config.packages['**'].publish, '$authenticated');
assert.equal(config.packages['**'].proxy, 'npmjs');
assert.equal(config.uplinks['npmjs'].url, 'https://registry.npmjs.org');
const checkDefaultConfPackages = (config) => {
//auth
expect(_.isObject(config.auth)).toBeTruthy();
expect(_.isObject(config.auth.htpasswd)).toBeTruthy();
expect(config.auth.htpasswd.file).toMatch(/htpasswd/);
//web
expect(_.isObject(config.web)).toBeTruthy();
expect(config.web.title).toBe(WEB_TITLE);
expect(config.web.enable).toBeUndefined();
// packages
expect(_.isObject(config.packages)).toBeTruthy();
expect(Object.keys(config.packages).join('|')).toBe('@*/*|**');
expect(config.packages['@*/*'].access).toBeDefined();
expect(config.packages['@*/*'].access).toContainEqual(ROLES.$ALL);
expect(config.packages['@*/*'].publish).toBeDefined();
expect(config.packages['@*/*'].publish).toContainEqual(ROLES.$AUTH);
expect(config.packages['@*/*'].proxy).toBeDefined();
expect(config.packages['@*/*'].proxy).toContainEqual(DEFAULT_UPLINK);
expect(config.packages['**'].access).toBeDefined();
expect(config.packages['**'].access).toContainEqual(ROLES.$ALL);
expect(config.packages['**'].publish).toBeDefined();
expect(config.packages['**'].publish).toContainEqual(ROLES.$AUTH);
expect(config.packages['**'].proxy).toBeDefined();
expect(config.packages['**'].proxy,).toContainEqual(DEFAULT_UPLINK);
// uplinks
expect(config.uplinks[DEFAULT_UPLINK]).toBeDefined();
expect(config.uplinks[DEFAULT_UPLINK].url).toEqual(DEFAULT_REGISTRY);
// audit
expect(config.middlewares).toBeDefined();
expect(config.middlewares.audit).toBeDefined();
expect(config.middlewares.audit.enabled).toBeTruthy();
// logs
expect(config.logs).toBeDefined();
expect(config.logs[0].type).toEqual('stdout');
expect(config.logs[0].format).toEqual('pretty');
expect(config.logs[0].level).toEqual('http');
//must not be enabled by default
expect(config.notify).toBeUndefined();
expect(config.store).toBeUndefined();
expect(config.publish).toBeUndefined();
expect(config.url_prefix).toBeUndefined();
expect(config.url_prefix).toBeUndefined();
};
describe('Config file', () => {
beforeAll(function() {
this.config = new Config(Utils.parseConfigFile(resolveConf('full')));
this.config = new Config(parseConfigFile(resolveConf('full')));
});
describe('Config file', () => {
test('parse full.yaml', () => {
const config = new Config(Utils.parseConfigFile(resolveConf('full')));
checkUplink(config);
assert.equal(config.storage, './storage');
assert.equal(config.web.title, 'Verdaccio');
checkPackages(config);
const config = new Config(parseConfigFile(resolveConf('full')));
checkDefaultUplink(config);
expect(config.storage).toBe('./storage');
checkDefaultConfPackages(config);
});
test('parse docker.yaml', () => {
const config = new Config(Utils.parseConfigFile(resolveConf('docker')));
checkUplink(config);
assert.equal(config.storage, '/verdaccio/storage');
assert.equal(config.auth.htpasswd.file, '/verdaccio/conf/htpasswd');
const config = new Config(parseConfigFile(resolveConf('docker')));
checkDefaultUplink(config);
expect(config.storage).toBe('/verdaccio/storage');
expect(config.auth.htpasswd.file).toBe('/verdaccio/conf/htpasswd');
checkDefaultConfPackages(config);
});
test('parse default.yaml', () => {
const config = new Config(Utils.parseConfigFile(resolveConf('default')));
checkUplink(config);
assert.equal(config.storage, './storage');
assert.equal(config.auth.htpasswd.file, './htpasswd');
const config = new Config(parseConfigFile(resolveConf('default')));
checkDefaultUplink(config);
expect(config.storage).toBe('./storage');
expect(config.auth.htpasswd.file).toBe('./htpasswd');
checkDefaultConfPackages(config);
});
});

@ -1,40 +1,44 @@
const assert = require('assert');
const _ = require('lodash');
const parse = require('../../../src/lib/utils').parse_address;
import _ from 'lodash';
import {parse_address as parse} from '../../../src/lib/utils';
import {DEFAULT_DOMAIN, DEFAULT_PORT} from '../../../src/lib/constants';
describe('Parse listen address', () => {
function addTest(what, proto, host, port) {
test(what, () => {
function addTest(uri, proto, host, port) {
test(`should parse ${uri}`, () => {
const parsed = parse(uri);
if (_.isNull(proto)) {
assert.strictEqual(parse(what), null);
expect(parsed).toBeNull();
} else if (port) {
assert.deepEqual(parse(what), {
proto: proto,
host: host,
port: port,
expect(parsed).toEqual({
proto,
host,
port,
});
} else {
assert.deepEqual(parse(what), {
proto: proto,
expect(parsed).toEqual({
proto,
path: host,
});
}
});
}
addTest('4873', 'http', 'localhost', '4873');
addTest(':4873', 'http', 'localhost', '4873');
addTest('blah:4873', 'http', 'blah', '4873');
addTest('http://:4873', 'http', 'localhost', '4873');
addTest('https::4873', 'https', 'localhost', '4873');
addTest('https:blah:4873', 'https', 'blah', '4873');
addTest('https://blah:4873/', 'https', 'blah', '4873');
addTest('[::1]:4873', 'http', '::1', '4873');
addTest('https:[::1]:4873', 'https', '::1', '4873');
addTest(DEFAULT_PORT, 'http', DEFAULT_DOMAIN, DEFAULT_PORT);
addTest(':4873', 'http', DEFAULT_DOMAIN, DEFAULT_PORT);
addTest('blah:4873', 'http', 'blah', DEFAULT_PORT);
addTest('http://:4873', 'http', DEFAULT_DOMAIN, DEFAULT_PORT);
addTest('https::4873', 'https', DEFAULT_DOMAIN, DEFAULT_PORT);
addTest('https:blah:4873', 'https', 'blah', DEFAULT_PORT);
addTest('https://blah:4873/', 'https', 'blah', DEFAULT_PORT);
addTest('[::1]:4873', 'http', '::1', DEFAULT_PORT);
addTest('https:[::1]:4873', 'https', '::1', DEFAULT_PORT);
addTest('unix:/tmp/foo.sock', 'http', '/tmp/foo.sock');
addTest('http:unix:foo.sock', 'http', 'foo.sock');
addTest('https://unix:foo.sock', 'https', 'foo.sock');
addTest('https://unix:foo.sock:34', 'https', 'foo.sock:34');
addTest('http://foo.sock:34', 'http', 'foo.sock', '34');
addTest('blah', null);
addTest('blah://4873', null);

18
test/unit/api/mock.js Normal file

@ -0,0 +1,18 @@
// @flow
import path from 'path';
import {DOMAIN_SERVERS} from '../../functional/config.functional';
import VerdaccioProcess from '../../lib/server_process';
import {VerdaccioConfig} from '../../lib/verdaccio-server';
import Server from '../../lib/server';
import type {IServerBridge} from '../../types';
export function mockServer(port: number) {
const pathStore = path.join(__dirname, '../partials');
const verdaccioConfig = new VerdaccioConfig(
path.join(pathStore, '/mock-store'),
path.join(pathStore, '/config-unit-test.yaml'), `http://${DOMAIN_SERVERS}:${port}/`, port);
const server: IServerBridge = new Server(verdaccioConfig.domainPath);
return new VerdaccioProcess(verdaccioConfig, server, false, false, false);
}

@ -1,7 +1,7 @@
// @flow
import _ from 'lodash';
import httpMocks from 'node-mocks-http';
import path from 'path';
// $FlowFixMe
import configExample from '../partials/config/index';
import AppConfig from '../../../src/lib/config';
@ -10,74 +10,85 @@ import {setup} from '../../../src/lib/logger';
import type {Config} from '@verdaccio/types';
import type {IStorageHandler} from '../../../types/index';
import {API_ERROR} from '../../../src/lib/constants';
import {API_ERROR, HTTP_STATUS} from '../../../src/lib/constants';
import {mockServer} from './mock';
import {DOMAIN_SERVERS} from '../../functional/config.functional';
setup(configExample.logs);
const generateStorage = async function() {
const storageConfig = _.clone(configExample);
const storage = `./unit/partials/store/test-storage-store.spec`;
const mockServerPort: number = 55548;
const generateStorage = async function(port = mockServerPort, configDefault = configExample) {
const storageConfig = _.clone(configDefault);
const storage = path.join(__dirname, '../partials/store/test-storage-store.spec');
storageConfig.self_path = __dirname;
storageConfig.storage = storage;
const config: Config = new AppConfig(storageConfig);
storageConfig.uplinks = {
npmjs: {
url: `http://${DOMAIN_SERVERS}:${port}`
}
};
const config: Config = new AppConfig(storageConfig);
const store: IStorageHandler = new Storage(config);
await store.init(config);
return store;
return store;
}
describe('StorageTest', () => {
let mockRegistry;
jest.setTimeout(10000);
beforeAll(async () => {
mockRegistry = await mockServer(mockServerPort).init();
});
beforeAll(async (done)=> {
const storage: IStorageHandler = await generateStorage();
var request = httpMocks.createRequest({
method: 'GET',
url: '/react',
params: {}
});
afterAll(function(done) {
mockRegistry[0].stop();
done();
});
storage.getPackage({
name: 'react',
req: request,
callback: () => {
const stream = storage.getTarball('react', 'react-16.1.0.tgz');
stream.on('content-length', function(content) {
if (content) {
expect(content).toBeTruthy();
done();
}
});
},
});
});
test('should be defined', async () => {
const storage: IStorageHandler = await generateStorage();
test('should be defined', async () => {
const storage: IStorageHandler = await generateStorage();
expect(storage).toBeDefined();
});
});
test('should fetch from uplink react metadata from nmpjs', async (done) => {
const storage: IStorageHandler = await generateStorage();
describe('test _syncUplinksMetadata', () => {
test('should fetch from uplink jquery metadata from registry', async (done) => {
const storage: IStorageHandler = await generateStorage();
// $FlowFixMe
storage._syncUplinksMetadata('react', null, {}, (err, metadata, errors) => {
expect(metadata).toBeInstanceOf(Object);
done();
});
});
// $FlowFixMe
storage._syncUplinksMetadata('jquery', null, {}, (err, metadata, errors) => {
expect(err).toBeNull();
expect(metadata).toBeDefined();
expect(metadata).toBeInstanceOf(Object);
done();
});
});
test('should fails on fetch from uplink metadata from nmpjs', async (done) => {
const storage: IStorageHandler = await generateStorage();
test('should fails on fetch from uplink non existing from registry', async (done) => {
const storage: IStorageHandler = await generateStorage();
// $FlowFixMe
storage._syncUplinksMetadata('@verdaccio/404', null, {}, (err, metadata, errors) => {
expect(errors).toBeInstanceOf(Array);
expect(errors[0][0].statusCode).toBe(404);
expect(errors[0][0].message).toMatch(API_ERROR.NOT_PACKAGE_UPLINK);
done();
});
// $FlowFixMe
storage._syncUplinksMetadata('@verdaccio/404', null, {}, (err, metadata, errors) => {
expect(err).not.toBeNull();
expect(errors).toBeInstanceOf(Array);
expect(errors[0][0].statusCode).toBe(HTTP_STATUS.NOT_FOUND);
expect(errors[0][0].message).toMatch(API_ERROR.NOT_PACKAGE_UPLINK);
done();
});
});
test('should fails on fetch from uplink corrupted pkg from registry', async (done) => {
const storage: IStorageHandler = await generateStorage();
// $FlowFixMe
storage._syncUplinksMetadata('corrupted-package', null, {}, (err, metadata, errors) => {
expect(err).not.toBeNull();
expect(errors).toBeInstanceOf(Array);
expect(errors[0][0].statusCode).toBe(HTTP_STATUS.INTERNAL_ERROR);
expect(errors[0][0].message).toMatch(API_ERROR.BAD_STATUS_CODE);
done();
});
});
});
});

@ -8,15 +8,17 @@ import {setup} from '../../../src/lib/logger';
import type {Config, UpLinkConf} from '@verdaccio/types';
import type {IProxy} from '../../../types/index';
import {API_ERROR, DEFAULT_REGISTRY} from "../../../src/lib/constants";
import {API_ERROR} from "../../../src/lib/constants";
import {mockServer} from './mock';
import {DOMAIN_SERVERS} from '../../functional/config.functional';
setup([]);
describe('UpStorge', () => {
jest.setTimeout(10000);
const mockServerPort: number = 55547;
let mockRegistry;
const uplinkDefault = {
url: DEFAULT_REGISTRY
url: `http://0.0.0.0:${mockServerPort}`
};
const generateProxy = (config: UpLinkConf = uplinkDefault) => {
const appConfig: Config = new AppConfig(configExample);
@ -24,6 +26,15 @@ describe('UpStorge', () => {
return new ProxyStorage(config, appConfig);
};
beforeAll(async () => {
mockRegistry = await mockServer(mockServerPort).init();
});
afterAll(function(done) {
mockRegistry[0].stop();
done();
});
test('should be defined', () => {
const proxy = generateProxy();
@ -69,7 +80,7 @@ describe('UpStorge', () => {
describe('UpStorge::fetchTarball', () => {
test('should fetch a tarball from uplink', (done) => {
const proxy = generateProxy();
const tarball:string = 'https://registry.npmjs.org/aaa/-/aaa-0.0.1.tgz';
const tarball:string = `http://${DOMAIN_SERVERS}:${mockServerPort}/jquery/-/jquery-1.5.1.tgz`;
const stream = proxy.fetchTarball(tarball);
stream.on('error', function(err) {
@ -86,7 +97,7 @@ describe('UpStorge', () => {
test('should throw a 404 on fetch a tarball from uplink', (done) => {
const proxy = generateProxy();
const tarball:string = 'https://google.es/aaa/-/aaa-0.0.1.tgz';
const tarball:string = `http://${DOMAIN_SERVERS}:${mockServerPort}/jquery/-/no-exist-1.5.1.tgz`;
const stream = proxy.fetchTarball(tarball);
stream.on('error', function(err) {

@ -2,11 +2,14 @@
import _ from 'lodash';
import smartRequest, {PromiseAssert} from '../../lib/request';
import {mockServer} from '../api/mock';
import {HTTP_STATUS} from '../../../src/lib/constants';
import type {IRequestPromise} from '../../types';
describe('Request Functional', () => {
const restTest: string = "http://registry.npmjs.org/aaa";
const mockServerPort = 55547;
const restTest: string = `http://localhost:${55547}/jquery`;
let mockRegistry;
describe('Request Functional', () => {
test('PromiseAssert', () => {
@ -26,6 +29,15 @@ describe('Request Functional', () => {
});
describe('smartRequest Rest', () => {
beforeAll(async () => {
mockRegistry = await mockServer(mockServerPort).init();
});
afterAll(function(done) {
mockRegistry[0].stop();
done();
});
test('basic rest', (done) => {
const options: any = {
url: restTest,
@ -46,8 +58,8 @@ describe('Request Functional', () => {
method: 'GET'
};
// $FlowFixMe
smartRequest(options).status(200).then((result)=> {
expect(JSON.parse(result).name).toBe('aaa');
smartRequest(options).status(HTTP_STATUS.OK).then((result)=> {
expect(JSON.parse(result).name).toBe('jquery');
done();
})
});

@ -0,0 +1,25 @@
storage: ./mock-store
web:
enable: false
title: verdaccio-server-unit-test
auth:
auth-memory:
users:
test:
name: test
password: test
logs:
- {type: stdout, format: pretty, level: warn}
packages:
'@*/*':
access: $all
publish: none
'**':
access: $all
publish: none
_debug: true

@ -1,10 +1,11 @@
import path from 'path';
import {DEFAULT_REGISTRY} from '../../../../src/lib/constants';
const config = {
storage: path.join(__dirname, '../store/access-storage'),
uplinks: {
'npmjs': {
'url': 'https://registry.npmjs.org/'
'url': DEFAULT_REGISTRY
}
},
packages: {

@ -5,48 +5,54 @@ const config = {
storage: path.join(__dirname, '../store/test-storage'),
uplinks: {
'npmjs': {
'url': 'https://registry.npmjs.org/'
'url': 'http://localhost:4873/'
}
},
packages: {
'@*/*': {
allow_access: '$all',
allow_publish: '$all',
access: '$all',
publish: '$all',
proxy: 'npmjs'
},
'forbidden-place': {
allow_access: 'nobody',
allow_publish: '$all'
access: 'nobody',
publish: '$all'
},
'react': {
allow_access: '$all',
allow_publish: '$all',
access: '$all',
publish: '$all',
proxy: 'npmjs'
},
'corrupted-package': {
access: '$all',
publish: '$all',
proxy: 'npmjs'
},
'jquery': {
allow_access: '$all',
allow_publish: '$all',
access: '$all',
publish: '$all',
proxy: 'npmjs'
},
'auth-package': {
allow_access: '$authenticated',
allow_publish: '$authenticated'
access: '$authenticated',
publish: '$authenticated'
},
'vue': {
allow_access: '$authenticated',
allow_publish: '$authenticated',
access: '$authenticated',
publish: '$authenticated',
proxy: 'npmjs'
},
'*': {
allow_access: '$all',
allow_publish: '$all'
access: '$all',
publish: '$all'
},
},
logs: [
{type: 'stdout', format: 'pretty', level: 'fatal'},
{type: 'stdout', format: 'pretty', level: 'warn'},
],
};

@ -0,0 +1 @@
{"list":[],"secret":"12c39716d7c75d50b9988255fff332e1b066bad04e10fff9cba42434bc5fe19e"}

@ -0,0 +1,12 @@
{
"name": "corrupted-package"
"version": {},
"dist-tags": {},
"_distfiles": {},
"_attachments": {},
"_uplinks": {},
"time": {},
"_rev": "0-0000000000000000",
"readme": "",
"versions": {}
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -5,6 +5,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import PackageDetail from '../../../../src/webui/src/components/PackageDetail/index';
import Readme from '../../../../src/webui/src/components/Readme/index';
import {WEB_TITLE} from '../../../../src/lib/constants';
console.error = jest.fn();
@ -17,11 +18,11 @@ describe('<PackageDetail /> component', () => {
it('should load the component', () => {
const props = {
readMe: 'Test readme',
package: 'Verdaccio'
package: WEB_TITLE
};
const wrapper = shallow(<PackageDetail {...props} />);
expect(wrapper.find('h1').text()).toEqual('Verdaccio');
expect(wrapper.find('h1').text()).toEqual(WEB_TITLE);
expect(
wrapper
.find(Readme)