mirror of
https://github.com/verdaccio/verdaccio.git
synced 2025-01-06 22:40:26 -05:00
test: Increase coverage publish api tests (#1056)
* refactor: ES6 sugar * refactor: improves code in publish * refactor: add tests for upload tarball and add a version flow * refactor: unpublish endpoint tests * refactor: publish endpoint test * docs: adds code blocks to publish functionality * refactor: rename tests file name * refactor: improves logic for npm star command * refactor: replaces assert equal with strictEqual
This commit is contained in:
parent
bfbb58fef4
commit
cbcfc9a48b
6 changed files with 449 additions and 108 deletions
|
@ -7,57 +7,84 @@ import _ from 'lodash';
|
||||||
import Path from 'path';
|
import Path from 'path';
|
||||||
import mime from 'mime';
|
import mime from 'mime';
|
||||||
|
|
||||||
import { API_MESSAGE, HEADERS, DIST_TAGS, API_ERROR } from '../../../lib/constants';
|
import { API_MESSAGE, HEADERS, DIST_TAGS, API_ERROR, HTTP_STATUS } from '../../../lib/constants';
|
||||||
import { validateMetadata, isObject, ErrorCode } from '../../../lib/utils';
|
import { validateMetadata, isObject, ErrorCode } from '../../../lib/utils';
|
||||||
import { media, expectJson, allow } from '../../middleware';
|
import { media, expectJson, allow } from '../../middleware';
|
||||||
import { notify } from '../../../lib/notify';
|
import { notify } from '../../../lib/notify';
|
||||||
|
|
||||||
import type { Router } from 'express';
|
import type { Router } from 'express';
|
||||||
import type { Config, Callback } from '@verdaccio/types';
|
import type { Config, Callback, MergeTags, Version } from '@verdaccio/types';
|
||||||
import type { IAuth, $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler } from '../../../../types';
|
import type { IAuth, $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler } from '../../../../types';
|
||||||
import logger from '../../../lib/logger';
|
import logger from '../../../lib/logger';
|
||||||
|
|
||||||
export default function(router: Router, auth: IAuth, storage: IStorageHandler, config: Config) {
|
export default function publish(router: Router, auth: IAuth, storage: IStorageHandler, config: Config) {
|
||||||
const can = allow(auth);
|
const can = allow(auth);
|
||||||
|
|
||||||
// publishing a package
|
// publishing a package
|
||||||
router.put('/:package/:_rev?/:revision?', can('publish'), media(mime.getType('json')), expectJson, function(
|
router.put('/:package/:_rev?/:revision?', can('publish'), media(mime.getType('json')), expectJson, publishPackage(storage, config));
|
||||||
req: $RequestExtend,
|
|
||||||
res: $ResponseExtend,
|
// un-publishing an entire package
|
||||||
next: $NextFunctionVer
|
router.delete('/:package/-rev/*', can('publish'), unPublishPackage(storage));
|
||||||
) {
|
|
||||||
const name = req.params.package;
|
// removing a tarball
|
||||||
let metadata;
|
router.delete('/:package/-/:filename/-rev/:revision', can('publish'), removeTarball(storage));
|
||||||
const create_tarball = function(filename: string, data, cb: Callback) {
|
|
||||||
let stream = storage.addTarball(name, filename);
|
// uploading package tarball
|
||||||
|
router.put('/:package/-/:filename/*', can('publish'), media(HEADERS.OCTET_STREAM), uploadPackageTarball(storage));
|
||||||
|
|
||||||
|
// adding a version
|
||||||
|
router.put('/:package/:version/-tag/:tag', can('publish'), media(mime.getType('json')), expectJson, addVersion(storage));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publish a package
|
||||||
|
*/
|
||||||
|
export function publishPackage(storage: IStorageHandler, config: Config) {
|
||||||
|
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||||
|
const packageName = req.params.package;
|
||||||
|
/**
|
||||||
|
* Write tarball of stream data from package clients.
|
||||||
|
*/
|
||||||
|
const createTarball = function(filename: string, data, cb: Callback) {
|
||||||
|
let stream = storage.addTarball(packageName, filename);
|
||||||
stream.on('error', function(err) {
|
stream.on('error', function(err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
});
|
});
|
||||||
stream.on('success', function() {
|
stream.on('success', function() {
|
||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
|
|
||||||
// this is dumb and memory-consuming, but what choices do we have?
|
// this is dumb and memory-consuming, but what choices do we have?
|
||||||
// flow: we need first refactor this file before decides which type use here
|
// flow: we need first refactor this file before decides which type use here
|
||||||
stream.end(new Buffer(data.data, 'base64'));
|
stream.end(new Buffer(data.data, 'base64'));
|
||||||
stream.done();
|
stream.done();
|
||||||
};
|
};
|
||||||
|
|
||||||
const create_version = function(version, data, cb) {
|
/**
|
||||||
storage.addVersion(name, version, data, null, cb);
|
* Add new package version in storage
|
||||||
|
*/
|
||||||
|
const createVersion = function(version: string, metadata: Version, cb: Callback) {
|
||||||
|
storage.addVersion(packageName, version, metadata, null, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
const add_tags = function(tags, cb) {
|
/**
|
||||||
storage.mergeTags(name, tags, cb);
|
* Add new tags in storage
|
||||||
|
*/
|
||||||
|
const addTags = function(tags: MergeTags, cb: Callback) {
|
||||||
|
storage.mergeTags(packageName, tags, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
const after_change = function(err, ok_message) {
|
const afterChange = function(error, okMessage, metadata) {
|
||||||
// old npm behaviour
|
let metadataCopy = { ...metadata };
|
||||||
if (_.isNil(metadata._attachments)) {
|
const { _attachments, versions } = metadataCopy;
|
||||||
if (err) return next(err);
|
|
||||||
res.status(201);
|
// old npm behavior, if there is no attachments
|
||||||
|
if (_.isNil(_attachments)) {
|
||||||
|
if (error) {
|
||||||
|
return next(error);
|
||||||
|
}
|
||||||
|
res.status(HTTP_STATUS.CREATED);
|
||||||
return next({
|
return next({
|
||||||
ok: ok_message,
|
ok: okMessage,
|
||||||
success: true,
|
success: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -65,106 +92,129 @@ export default function(router: Router, auth: IAuth, storage: IStorageHandler, c
|
||||||
// npm-registry-client 0.3+ embeds tarball into the json upload
|
// npm-registry-client 0.3+ embeds tarball into the json upload
|
||||||
// https://github.com/isaacs/npm-registry-client/commit/e9fbeb8b67f249394f735c74ef11fe4720d46ca0
|
// https://github.com/isaacs/npm-registry-client/commit/e9fbeb8b67f249394f735c74ef11fe4720d46ca0
|
||||||
// issue https://github.com/rlidwka/sinopia/issues/31, dealing with it here:
|
// issue https://github.com/rlidwka/sinopia/issues/31, dealing with it here:
|
||||||
|
if (isObject(_attachments) === false || Object.keys(_attachments).length !== 1 || isObject(versions) === false || Object.keys(versions).length !== 1) {
|
||||||
if (
|
|
||||||
typeof metadata._attachments !== 'object' ||
|
|
||||||
Object.keys(metadata._attachments).length !== 1 ||
|
|
||||||
typeof metadata.versions !== 'object' ||
|
|
||||||
Object.keys(metadata.versions).length !== 1
|
|
||||||
) {
|
|
||||||
// npm is doing something strange again
|
// npm is doing something strange again
|
||||||
// if this happens in normal circumstances, report it as a bug
|
// if this happens in normal circumstances, report it as a bug
|
||||||
return next(ErrorCode.getBadRequest('unsupported registry call'));
|
return next(ErrorCode.getBadRequest('unsupported registry call'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err && err.status != 409) {
|
if (error && error.status !== HTTP_STATUS.CONFLICT) {
|
||||||
return next(err);
|
return next(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// at this point document is either created or existed before
|
// at this point document is either created or existed before
|
||||||
const t1 = Object.keys(metadata._attachments)[0];
|
const firstAttachmentKey = Object.keys(_attachments)[0];
|
||||||
create_tarball(Path.basename(t1), metadata._attachments[t1], function(err) {
|
|
||||||
if (err) {
|
createTarball(Path.basename(firstAttachmentKey), _attachments[firstAttachmentKey], function(error) {
|
||||||
return next(err);
|
if (error) {
|
||||||
|
return next(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const versionToPublish = Object.keys(metadata.versions)[0];
|
const versionToPublish = Object.keys(versions)[0];
|
||||||
metadata.versions[versionToPublish].readme = _.isNil(metadata.readme) === false ? String(metadata.readme) : '';
|
|
||||||
create_version(versionToPublish, metadata.versions[versionToPublish], function(err) {
|
versions[versionToPublish].readme = _.isNil(metadataCopy.readme) === false ? String(metadataCopy.readme) : '';
|
||||||
if (err) {
|
|
||||||
return next(err);
|
createVersion(versionToPublish, versions[versionToPublish], function(error) {
|
||||||
|
if (error) {
|
||||||
|
return next(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
add_tags(metadata[DIST_TAGS], async function(err) {
|
addTags(metadataCopy[DIST_TAGS], async function(error) {
|
||||||
if (err) {
|
if (error) {
|
||||||
return next(err);
|
return next(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await notify(metadata, config, req.remote_user, `${metadata.name}@${versionToPublish}`);
|
await notify(metadataCopy, config, req.remote_user, `${metadataCopy.name}@${versionToPublish}`);
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
logger.logger.error({ err }, 'notify batch service has failed: @{err}');
|
logger.logger.error({ error }, 'notify batch service has failed: @{error}');
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(201);
|
res.status(HTTP_STATUS.CREATED);
|
||||||
return next({ ok: ok_message, success: true });
|
return next({ ok: okMessage, success: true });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Object.keys(req.body).length === 1 && isObject(req.body.users)) {
|
if (Object.prototype.hasOwnProperty.call(req.body, '_rev') && isObject(req.body.users)) {
|
||||||
// 501 status is more meaningful, but npm doesn't show error message for 5xx
|
return next(ErrorCode.getNotFound('npm star| un-star calls are not implemented'));
|
||||||
return next(ErrorCode.getNotFound('npm star|unstar calls are not implemented'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
metadata = validateMetadata(req.body, name);
|
const metadata = validateMetadata(req.body, packageName);
|
||||||
} catch (err) {
|
if (req.params._rev) {
|
||||||
|
storage.changePackage(packageName, metadata, req.params.revision, function(error) {
|
||||||
|
afterChange(error, API_MESSAGE.PKG_CHANGED, metadata);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
storage.addPackage(packageName, metadata, function(error) {
|
||||||
|
afterChange(error, API_MESSAGE.PKG_CREATED, metadata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
return next(ErrorCode.getBadData(API_ERROR.BAD_PACKAGE_DATA));
|
return next(ErrorCode.getBadData(API_ERROR.BAD_PACKAGE_DATA));
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (req.params._rev) {
|
/**
|
||||||
storage.changePackage(name, metadata, req.params.revision, function(err) {
|
* un-publish a package
|
||||||
after_change(err, API_MESSAGE.PKG_CHANGED);
|
*/
|
||||||
});
|
export function unPublishPackage(storage: IStorageHandler) {
|
||||||
} else {
|
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||||
storage.addPackage(name, metadata, function(err) {
|
|
||||||
after_change(err, API_MESSAGE.PKG_CREATED);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// unpublishing an entire package
|
|
||||||
router.delete('/:package/-rev/*', can('publish'), function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
|
||||||
storage.removePackage(req.params.package, function(err) {
|
storage.removePackage(req.params.package, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
res.status(201);
|
res.status(HTTP_STATUS.CREATED);
|
||||||
return next({ ok: API_MESSAGE.PKG_REMOVED });
|
return next({ ok: API_MESSAGE.PKG_REMOVED });
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// removing a tarball
|
/**
|
||||||
router.delete('/:package/-/:filename/-rev/:revision', can('publish'), function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
* Delete tarball
|
||||||
|
*/
|
||||||
|
export function removeTarball(storage: IStorageHandler) {
|
||||||
|
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||||
storage.removeTarball(req.params.package, req.params.filename, req.params.revision, function(err) {
|
storage.removeTarball(req.params.package, req.params.filename, req.params.revision, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
res.status(201);
|
res.status(HTTP_STATUS.CREATED);
|
||||||
return next({ ok: API_MESSAGE.TARBALL_REMOVED });
|
return next({ ok: API_MESSAGE.TARBALL_REMOVED });
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Adds a new version
|
||||||
|
*/
|
||||||
|
export function addVersion(storage: IStorageHandler) {
|
||||||
|
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||||
|
const { version, tag } = req.params;
|
||||||
|
const packageName = req.params.package;
|
||||||
|
|
||||||
// uploading package tarball
|
storage.addVersion(packageName, version, req.body, tag, function(error) {
|
||||||
router.put('/:package/-/:filename/*', can('publish'), media(HEADERS.OCTET_STREAM), function(
|
if (error) {
|
||||||
req: $RequestExtend,
|
return next(error);
|
||||||
res: $ResponseExtend,
|
}
|
||||||
next: $NextFunctionVer
|
|
||||||
) {
|
res.status(HTTP_STATUS.CREATED);
|
||||||
const name = req.params.package;
|
return next({
|
||||||
const stream = storage.addTarball(name, req.params.filename);
|
ok: API_MESSAGE.PKG_PUBLISHED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* uploadPackageTarball
|
||||||
|
*/
|
||||||
|
export function uploadPackageTarball(storage: IStorageHandler) {
|
||||||
|
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||||
|
const packageName = req.params.package;
|
||||||
|
const stream = storage.addTarball(packageName, req.params.filename);
|
||||||
req.pipe(stream);
|
req.pipe(stream);
|
||||||
|
|
||||||
// checking if end event came before closing
|
// checking if end event came before closing
|
||||||
|
@ -185,31 +235,10 @@ export default function(router: Router, auth: IAuth, storage: IStorageHandler, c
|
||||||
});
|
});
|
||||||
|
|
||||||
stream.on('success', function() {
|
stream.on('success', function() {
|
||||||
res.status(201);
|
res.status(HTTP_STATUS.CREATED);
|
||||||
return next({
|
return next({
|
||||||
ok: API_MESSAGE.TARBALL_UPLOADED,
|
ok: API_MESSAGE.TARBALL_UPLOADED,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
|
|
||||||
// adding a version
|
|
||||||
router.put('/:package/:version/-tag/:tag', can('publish'), media(mime.getType('json')), expectJson, function(
|
|
||||||
req: $RequestExtend,
|
|
||||||
res: $ResponseExtend,
|
|
||||||
next: $NextFunctionVer
|
|
||||||
) {
|
|
||||||
const { version, tag } = req.params;
|
|
||||||
const name = req.params.package;
|
|
||||||
|
|
||||||
storage.addVersion(name, version, req.body, tag, function(err) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(201);
|
|
||||||
return next({
|
|
||||||
ok: API_MESSAGE.PKG_PUBLISHED,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ export default (async function(configHash: any) {
|
||||||
setup(configHash.logs);
|
setup(configHash.logs);
|
||||||
const config: IConfig = new AppConfig(configHash);
|
const config: IConfig = new AppConfig(configHash);
|
||||||
const storage: IStorageHandler = new Storage(config);
|
const storage: IStorageHandler = new Storage(config);
|
||||||
// waits until init calls have been intialized
|
// waits until init calls have been initialized
|
||||||
await storage.init(config);
|
await storage.init(config);
|
||||||
return defineAPI(config, storage);
|
return defineAPI(config, storage);
|
||||||
});
|
});
|
||||||
|
|
|
@ -92,7 +92,7 @@ export function isObject(obj: any): boolean {
|
||||||
*/
|
*/
|
||||||
export function validateMetadata(object: Package, name: string): Object {
|
export function validateMetadata(object: Package, name: string): Object {
|
||||||
assert(isObject(object), 'not a json object');
|
assert(isObject(object), 'not a json object');
|
||||||
assert.equal(object.name, name);
|
assert.strictEqual(object.name, name);
|
||||||
|
|
||||||
if (!isObject(object[DIST_TAGS])) {
|
if (!isObject(object[DIST_TAGS])) {
|
||||||
object[DIST_TAGS] = {};
|
object[DIST_TAGS] = {};
|
||||||
|
|
48
test/unit/api/__snapshots__/api.publish.spec.js.snap
Normal file
48
test/unit/api/__snapshots__/api.publish.spec.js.snap
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Publish endpoints - publish package should add a new package 1`] = `
|
||||||
|
[MockFunction] {
|
||||||
|
"calls": Array [
|
||||||
|
Array [
|
||||||
|
"verdaccio",
|
||||||
|
Object {
|
||||||
|
"dist-tags": Object {},
|
||||||
|
"name": "verdaccio",
|
||||||
|
"time": Object {},
|
||||||
|
"versions": Object {},
|
||||||
|
},
|
||||||
|
[Function],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
"results": Array [
|
||||||
|
Object {
|
||||||
|
"isThrow": false,
|
||||||
|
"value": undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Publish endpoints - publish package should change the existing package 1`] = `
|
||||||
|
[MockFunction] {
|
||||||
|
"calls": Array [
|
||||||
|
Array [
|
||||||
|
"verdaccio",
|
||||||
|
Object {
|
||||||
|
"dist-tags": Object {},
|
||||||
|
"name": "verdaccio",
|
||||||
|
"time": Object {},
|
||||||
|
"versions": Object {},
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
[Function],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
"results": Array [
|
||||||
|
Object {
|
||||||
|
"isThrow": false,
|
||||||
|
"value": undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`;
|
264
test/unit/api/api.publish.spec.js
Normal file
264
test/unit/api/api.publish.spec.js
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
*/
|
||||||
|
import { addVersion, uploadPackageTarball, removeTarball, unPublishPackage, publishPackage } from '../../../src/api/endpoint/api/publish';
|
||||||
|
import { HTTP_STATUS, API_ERROR } from '../../../src/lib/constants';
|
||||||
|
|
||||||
|
const REVISION_MOCK = '15-e53a77096b0ee33e';
|
||||||
|
|
||||||
|
describe('Publish endpoints - add a tag', () => {
|
||||||
|
let req;
|
||||||
|
let res;
|
||||||
|
let next;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
req = {
|
||||||
|
params: {
|
||||||
|
version: '1.0.0',
|
||||||
|
tag: 'tag',
|
||||||
|
package: 'verdaccio',
|
||||||
|
},
|
||||||
|
body: '',
|
||||||
|
};
|
||||||
|
res = {
|
||||||
|
status: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
next = jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should add a version', done => {
|
||||||
|
const storage = {
|
||||||
|
addVersion: (packageName, version, body, tag, cb) => {
|
||||||
|
expect(packageName).toEqual(req.params.package);
|
||||||
|
expect(version).toEqual(req.params.version);
|
||||||
|
expect(body).toEqual(req.body);
|
||||||
|
expect(tag).toEqual(req.params.tag);
|
||||||
|
cb();
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
addVersion(storage)(req, res, next);
|
||||||
|
|
||||||
|
expect(res.status).toHaveBeenLastCalledWith(HTTP_STATUS.CREATED);
|
||||||
|
expect(next).toHaveBeenLastCalledWith({ ok: 'package published' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('when failed to add a version', done => {
|
||||||
|
const storage = {
|
||||||
|
addVersion: (packageName, version, body, tag, cb) => {
|
||||||
|
const error = {
|
||||||
|
message: 'failure',
|
||||||
|
};
|
||||||
|
cb(error);
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
addVersion(storage)(req, res, next);
|
||||||
|
|
||||||
|
expect(next).toHaveBeenLastCalledWith({ message: 'failure' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* upload package: '/:package/-/:filename/*'
|
||||||
|
*/
|
||||||
|
describe('Publish endpoints - upload package tarball', () => {
|
||||||
|
let req;
|
||||||
|
let res;
|
||||||
|
let next;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
req = {
|
||||||
|
params: {
|
||||||
|
filename: 'verdaccio.gzip',
|
||||||
|
package: 'verdaccio',
|
||||||
|
},
|
||||||
|
pipe: jest.fn(),
|
||||||
|
on: jest.fn(),
|
||||||
|
};
|
||||||
|
res = { status: jest.fn(), report_error: jest.fn() };
|
||||||
|
next = jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should upload package tarball successfully', () => {
|
||||||
|
const stream = {
|
||||||
|
done: jest.fn(),
|
||||||
|
abort: jest.fn(),
|
||||||
|
on: jest.fn(() => (status, cb) => cb()),
|
||||||
|
};
|
||||||
|
const storage = {
|
||||||
|
addTarball(packageName, filename) {
|
||||||
|
expect(packageName).toEqual(req.params.package);
|
||||||
|
expect(filename).toEqual(req.params.filename);
|
||||||
|
return stream;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
uploadPackageTarball(storage)(req, res, next);
|
||||||
|
expect(req.pipe).toHaveBeenCalled();
|
||||||
|
expect(req.on).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete tarball: '/:package/-/:filename/-rev/:revision'
|
||||||
|
*/
|
||||||
|
describe('Publish endpoints - delete tarball', () => {
|
||||||
|
let req;
|
||||||
|
let res;
|
||||||
|
let next;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
req = {
|
||||||
|
params: {
|
||||||
|
filename: 'verdaccio.gzip',
|
||||||
|
package: 'verdaccio',
|
||||||
|
revision: REVISION_MOCK,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
res = { status: jest.fn() };
|
||||||
|
next = jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should delete tarball successfully', done => {
|
||||||
|
const storage = {
|
||||||
|
removeTarball(packageName, filename, revision, cb) {
|
||||||
|
expect(packageName).toEqual(req.params.package);
|
||||||
|
expect(filename).toEqual(req.params.filename);
|
||||||
|
expect(revision).toEqual(req.params.revision);
|
||||||
|
cb();
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
removeTarball(storage)(req, res, next);
|
||||||
|
expect(res.status).toHaveBeenCalledWith(HTTP_STATUS.CREATED);
|
||||||
|
expect(next).toHaveBeenCalledWith({ ok: 'tarball removed' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('failed while deleting the tarball', done => {
|
||||||
|
const error = {
|
||||||
|
message: 'deletion failed',
|
||||||
|
};
|
||||||
|
const storage = {
|
||||||
|
removeTarball(packageName, filename, revision, cb) {
|
||||||
|
cb(error);
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
removeTarball(storage)(req, res, next);
|
||||||
|
expect(next).toHaveBeenCalledWith(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Un-publish package: '/:package/-rev/*'
|
||||||
|
*/
|
||||||
|
describe('Publish endpoints - un-publish package', () => {
|
||||||
|
let req;
|
||||||
|
let res;
|
||||||
|
let next;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
req = {
|
||||||
|
params: {
|
||||||
|
package: 'verdaccio',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
res = { status: jest.fn() };
|
||||||
|
next = jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should un-publish package successfully', done => {
|
||||||
|
const storage = {
|
||||||
|
removePackage(packageName, cb) {
|
||||||
|
expect(packageName).toEqual(req.params.package);
|
||||||
|
cb();
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
unPublishPackage(storage)(req, res, next);
|
||||||
|
expect(res.status).toHaveBeenCalledWith(HTTP_STATUS.CREATED);
|
||||||
|
expect(next).toHaveBeenCalledWith({ ok: 'package removed' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('un-publish failed', done => {
|
||||||
|
const error = {
|
||||||
|
message: 'un-publish failed',
|
||||||
|
};
|
||||||
|
const storage = {
|
||||||
|
removePackage(packageName, cb) {
|
||||||
|
cb(error);
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
unPublishPackage(storage)(req, res, next);
|
||||||
|
expect(next).toHaveBeenCalledWith(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publish package: '/:package/:_rev?/:revision?'
|
||||||
|
*/
|
||||||
|
describe('Publish endpoints - publish package', () => {
|
||||||
|
let req;
|
||||||
|
let res;
|
||||||
|
let next;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
req = {
|
||||||
|
body: {
|
||||||
|
name: 'verdaccio',
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
package: 'verdaccio',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
res = { status: jest.fn() };
|
||||||
|
next = jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should change the existing package', () => {
|
||||||
|
const storage = {
|
||||||
|
changePackage: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
req.params._rev = REVISION_MOCK;
|
||||||
|
|
||||||
|
publishPackage(storage)(req, res, next);
|
||||||
|
expect(storage.changePackage).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should add a new package', () => {
|
||||||
|
const storage = {
|
||||||
|
addPackage: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
publishPackage(storage)(req, res, next);
|
||||||
|
expect(storage.addPackage).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should throw an error while publishing package', () => {
|
||||||
|
const storage = {
|
||||||
|
addPackage() {
|
||||||
|
throw new Error();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
publishPackage(storage)(req, res, next);
|
||||||
|
expect(next).toHaveBeenCalledWith(new Error(API_ERROR.BAD_PACKAGE_DATA));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should throw an error for un-implemented star calls', () => {
|
||||||
|
const storage = {};
|
||||||
|
req.body._rev = REVISION_MOCK;
|
||||||
|
req.body.users = {};
|
||||||
|
publishPackage(storage)(req, res, next);
|
||||||
|
expect(next).toHaveBeenCalledWith(new Error('npm star| un-star calls are not implemented'));
|
||||||
|
});
|
||||||
|
});
|
|
@ -75,7 +75,7 @@ describe('notifyRequest', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const notification = require('../../../src/lib/notify/notify-request');
|
const notification = require('../../../src/lib/notify/notify-request');
|
||||||
const infoArgs = [{ content: 'Verdaccio@x.x.x successfully published' }, 'A notification has been shipped: @{content}'];
|
const infoArgs = [{ content }, 'A notification has been shipped: @{content}'];
|
||||||
const debugArgs = [{ body: 'Successfully delivered' }, ' body: @{body}'];
|
const debugArgs = [{ body: 'Successfully delivered' }, ' body: @{body}'];
|
||||||
|
|
||||||
await expect(notification.notifyRequest(options, content)).resolves.toEqual('Successfully delivered');
|
await expect(notification.notifyRequest(options, content)).resolves.toEqual('Successfully delivered');
|
||||||
|
@ -93,7 +93,7 @@ describe('notifyRequest', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const notification = require('../../../src/lib/notify/notify-request');
|
const notification = require('../../../src/lib/notify/notify-request');
|
||||||
const infoArgs = [{ content: 'Verdaccio@x.x.x successfully published' }, 'A notification has been shipped: @{content}'];
|
const infoArgs = [{ content }, 'A notification has been shipped: @{content}'];
|
||||||
|
|
||||||
await expect(notification.notifyRequest(options, content)).rejects.toThrowError('body is missing');
|
await expect(notification.notifyRequest(options, content)).rejects.toThrowError('body is missing');
|
||||||
expect(logger.logger.info).toHaveBeenCalledWith(...infoArgs);
|
expect(logger.logger.info).toHaveBeenCalledWith(...infoArgs);
|
||||||
|
|
Loading…
Reference in a new issue