mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-12-16 21:56:25 -05:00
refactor: relocate verdaccio-memory plugin (#1974)
This commit is contained in:
parent
1d11128051
commit
827376e85c
15 changed files with 1813 additions and 20 deletions
3
packages/plugins/memory/.babelrc
Normal file
3
packages/plugins/memory/.babelrc
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"extends": "../../../.babelrc"
|
||||
}
|
5
packages/plugins/memory/jest.config.js
Normal file
5
packages/plugins/memory/jest.config.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
const config = require('../../../jest/config');
|
||||
|
||||
module.exports = Object.assign({}, config, {
|
||||
collectCoverage: true,
|
||||
});
|
44
packages/plugins/memory/package.json
Normal file
44
packages/plugins/memory/package.json
Normal file
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"name": "verdaccio-memory",
|
||||
"version": "10.0.0-beta",
|
||||
"description": "Storage implementation in memory",
|
||||
"keywords": [
|
||||
"verdaccio",
|
||||
"plugin",
|
||||
"storage",
|
||||
"memory"
|
||||
],
|
||||
"author": "Juan Picado <juanpicado19@gmail.com>",
|
||||
"license": "MIT",
|
||||
"homepage": "https://verdaccio.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/verdaccio/verdaccio",
|
||||
"directory": "packages/plugins/memory"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/verdaccio/verdaccio/issues"
|
||||
},
|
||||
"main": "build/index.js",
|
||||
"types": "build/index.d.ts",
|
||||
"dependencies": {
|
||||
"@verdaccio/commons-api": "workspace:*",
|
||||
"@verdaccio/streams": "workspace:*",
|
||||
"memory-fs": "^0.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@verdaccio/types": "workspace:*"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rimraf ./build",
|
||||
"type-check": "tsc --noEmit -p tsconfig.build.json",
|
||||
"build:types": "tsc --emitDeclarationOnly -p tsconfig.build.json",
|
||||
"build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps",
|
||||
"build": "pnpm run build:js && pnpm run build:types",
|
||||
"test": "cross-env NODE_ENV=test BABEL_ENV=test jest"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/verdaccio"
|
||||
}
|
||||
}
|
5
packages/plugins/memory/src/index.ts
Normal file
5
packages/plugins/memory/src/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import LocalMemory from './local-memory';
|
||||
|
||||
export { LocalMemory };
|
||||
|
||||
export default LocalMemory;
|
124
packages/plugins/memory/src/local-memory.ts
Normal file
124
packages/plugins/memory/src/local-memory.ts
Normal file
|
@ -0,0 +1,124 @@
|
|||
import { getServiceUnavailable } from '@verdaccio/commons-api';
|
||||
import {
|
||||
Logger,
|
||||
Callback,
|
||||
Config,
|
||||
IPluginStorage,
|
||||
Token,
|
||||
TokenFilter,
|
||||
PluginOptions,
|
||||
} from '@verdaccio/types';
|
||||
|
||||
import MemoryHandler, { DataHandler } from './memory-handler';
|
||||
|
||||
export type ConfigMemory = Config & { limit?: number };
|
||||
export interface MemoryLocalStorage {
|
||||
secret: string;
|
||||
list: string[];
|
||||
files: DataHandler;
|
||||
}
|
||||
|
||||
const DEFAULT_LIMIT = 1000;
|
||||
class LocalMemory implements IPluginStorage<ConfigMemory> {
|
||||
private path: string;
|
||||
private limit: number;
|
||||
public logger: Logger;
|
||||
private data: MemoryLocalStorage;
|
||||
public config: ConfigMemory;
|
||||
|
||||
public constructor(config: ConfigMemory, options: PluginOptions<ConfigMemory>) {
|
||||
this.config = config;
|
||||
this.limit = config.limit || DEFAULT_LIMIT;
|
||||
this.logger = options.logger;
|
||||
this.data = this._createEmtpyDatabase();
|
||||
this.path = '/';
|
||||
}
|
||||
|
||||
public getSecret(): Promise<string> {
|
||||
return Promise.resolve(this.data.secret);
|
||||
}
|
||||
|
||||
public setSecret(secret: string): Promise<string | null> {
|
||||
return new Promise((resolve): void => {
|
||||
this.data.secret = secret;
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
|
||||
public add(name: string, cb: Callback): void {
|
||||
const { list } = this.data;
|
||||
|
||||
if (list.length < this.limit) {
|
||||
if (list.indexOf(name) === -1) {
|
||||
list.push(name);
|
||||
}
|
||||
cb(null);
|
||||
} else {
|
||||
this.logger.info(
|
||||
{ limit: this.limit },
|
||||
'Storage memory has reached limit of @{limit} packages'
|
||||
);
|
||||
cb(new Error('Storage memory has reached limit of limit packages'));
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public search(onPackage: Callback, onEnd: Callback, validateName: Function): void {
|
||||
this.logger.warn('[verdaccio/memory]: search method not implemented, PR is welcome');
|
||||
onEnd();
|
||||
}
|
||||
|
||||
public remove(name: string, cb: Callback): void {
|
||||
const { list } = this.data;
|
||||
const item = list.indexOf(name);
|
||||
|
||||
if (item !== -1) {
|
||||
list.splice(item, 1);
|
||||
}
|
||||
|
||||
cb(null);
|
||||
}
|
||||
|
||||
public get(cb: Callback): void {
|
||||
cb(null, this.data.list);
|
||||
}
|
||||
|
||||
public getPackageStorage(packageInfo: string): MemoryHandler {
|
||||
return new MemoryHandler(packageInfo, this.data.files, this.logger);
|
||||
}
|
||||
|
||||
private _createEmtpyDatabase(): MemoryLocalStorage {
|
||||
const list: string[] = [];
|
||||
const files = {};
|
||||
const emptyDatabase = {
|
||||
list,
|
||||
files,
|
||||
secret: '',
|
||||
};
|
||||
|
||||
return emptyDatabase;
|
||||
}
|
||||
|
||||
public saveToken(token: Token): Promise<void> {
|
||||
this.logger.warn('[verdaccio/memory][saveToken] save token has not been implemented yet');
|
||||
|
||||
return Promise.reject(getServiceUnavailable('method not implemented'));
|
||||
}
|
||||
|
||||
public deleteToken(user: string, tokenKey: string): Promise<void> {
|
||||
this.logger.warn(
|
||||
{ tokenKey, user },
|
||||
'[verdaccio/memory][deleteToken] delete token has not been implemented yet @{user}'
|
||||
);
|
||||
|
||||
return Promise.reject(getServiceUnavailable('method not implemented'));
|
||||
}
|
||||
|
||||
public readTokens(filter: TokenFilter): Promise<Token[]> {
|
||||
this.logger.warn('[verdaccio/memory][readTokens] read tokens has not been implemented yet ');
|
||||
|
||||
return Promise.reject(getServiceUnavailable('method not implemented'));
|
||||
}
|
||||
}
|
||||
|
||||
export default LocalMemory;
|
190
packages/plugins/memory/src/memory-handler.ts
Normal file
190
packages/plugins/memory/src/memory-handler.ts
Normal file
|
@ -0,0 +1,190 @@
|
|||
import {
|
||||
VerdaccioError,
|
||||
getBadRequest,
|
||||
getInternalError,
|
||||
getConflict,
|
||||
getNotFound,
|
||||
} from '@verdaccio/commons-api';
|
||||
import MemoryFileSystem from 'memory-fs';
|
||||
import { UploadTarball, ReadTarball } from '@verdaccio/streams';
|
||||
import {
|
||||
Callback,
|
||||
Logger,
|
||||
IPackageStorageManager,
|
||||
IUploadTarball,
|
||||
IReadTarball,
|
||||
CallbackAction,
|
||||
StorageUpdateCallback,
|
||||
StorageWriteCallback,
|
||||
PackageTransformer,
|
||||
Package,
|
||||
ReadPackageCallback,
|
||||
} from '@verdaccio/types';
|
||||
|
||||
import { parsePackage, stringifyPackage } from './utils';
|
||||
|
||||
const fs = new MemoryFileSystem();
|
||||
|
||||
export type DataHandler = {
|
||||
[key: string]: string;
|
||||
};
|
||||
|
||||
class MemoryHandler implements IPackageStorageManager {
|
||||
private data: DataHandler;
|
||||
private name: string;
|
||||
private path: string;
|
||||
public logger: Logger;
|
||||
|
||||
public constructor(packageName: string, data: DataHandler, logger: Logger) {
|
||||
// this is not need it
|
||||
this.data = data;
|
||||
this.name = packageName;
|
||||
this.logger = logger;
|
||||
this.path = '/';
|
||||
}
|
||||
|
||||
public updatePackage(
|
||||
pkgFileName: string,
|
||||
updateHandler: StorageUpdateCallback,
|
||||
onWrite: StorageWriteCallback,
|
||||
transformPackage: PackageTransformer,
|
||||
onEnd: CallbackAction
|
||||
): void {
|
||||
const json: string = this._getStorage(pkgFileName);
|
||||
let pkg: Package;
|
||||
|
||||
try {
|
||||
pkg = parsePackage(json) as Package;
|
||||
} catch (err) {
|
||||
return onEnd(err);
|
||||
}
|
||||
|
||||
updateHandler(pkg, (err: VerdaccioError) => {
|
||||
if (err) {
|
||||
return onEnd(err);
|
||||
}
|
||||
try {
|
||||
onWrite(pkgFileName, transformPackage(pkg), onEnd);
|
||||
} catch (err) {
|
||||
return onEnd(getInternalError('error on parse the metadata'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public deletePackage(pkgName: string, callback: Callback): void {
|
||||
delete this.data[pkgName];
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
public removePackage(callback: CallbackAction): void {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
public createPackage(name: string, value: Package, cb: CallbackAction): void {
|
||||
this.savePackage(name, value, cb);
|
||||
}
|
||||
|
||||
public savePackage(name: string, value: Package, cb: CallbackAction): void {
|
||||
try {
|
||||
const json: string = stringifyPackage(value);
|
||||
|
||||
this.data[name] = json;
|
||||
return cb(null);
|
||||
} catch (err) {
|
||||
return cb(getInternalError(err.message));
|
||||
}
|
||||
}
|
||||
|
||||
public readPackage(name: string, cb: ReadPackageCallback): void {
|
||||
const json = this._getStorage(name);
|
||||
const isJson = typeof json === 'undefined';
|
||||
|
||||
try {
|
||||
return cb(isJson ? getNotFound() : null, parsePackage(json));
|
||||
} catch (err) {
|
||||
return cb(getNotFound());
|
||||
}
|
||||
}
|
||||
|
||||
public writeTarball(name: string): IUploadTarball {
|
||||
const uploadStream: IUploadTarball = new UploadTarball({});
|
||||
const temporalName = `/${name}`;
|
||||
|
||||
process.nextTick(function () {
|
||||
fs.stat(temporalName, function (fileError, stats) {
|
||||
if (!fileError && stats) {
|
||||
return uploadStream.emit('error', getConflict());
|
||||
}
|
||||
|
||||
try {
|
||||
const file = fs.createWriteStream(temporalName);
|
||||
|
||||
uploadStream.pipe(file);
|
||||
|
||||
uploadStream.done = function (): void {
|
||||
const onEnd = function (): void {
|
||||
uploadStream.emit('success');
|
||||
};
|
||||
|
||||
uploadStream.on('end', onEnd);
|
||||
};
|
||||
|
||||
uploadStream.abort = function (): void {
|
||||
uploadStream.emit('error', getBadRequest('transmision aborted'));
|
||||
file.end();
|
||||
};
|
||||
|
||||
uploadStream.emit('open');
|
||||
return;
|
||||
} catch (err) {
|
||||
uploadStream.emit('error', err);
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return uploadStream;
|
||||
}
|
||||
|
||||
public readTarball(name: string): IReadTarball {
|
||||
const pathName = `/${name}`;
|
||||
|
||||
const readTarballStream: IReadTarball = new ReadTarball({});
|
||||
|
||||
process.nextTick(function () {
|
||||
fs.stat(pathName, function (fileError, stats) {
|
||||
if (fileError && !stats) {
|
||||
return readTarballStream.emit('error', getNotFound());
|
||||
}
|
||||
|
||||
try {
|
||||
const readStream = fs.createReadStream(pathName);
|
||||
|
||||
const contentLength: number = (fs.data[name] && fs.data[name].length) || 0;
|
||||
readTarballStream.emit('content-length', contentLength);
|
||||
readTarballStream.emit('open');
|
||||
readStream.pipe(readTarballStream);
|
||||
readStream.on('error', (error: VerdaccioError) => {
|
||||
readTarballStream.emit('error', error);
|
||||
});
|
||||
|
||||
readTarballStream.abort = function (): void {
|
||||
readStream.destroy(getBadRequest('read has been aborted'));
|
||||
};
|
||||
return;
|
||||
} catch (err) {
|
||||
readTarballStream.emit('error', err);
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return readTarballStream;
|
||||
}
|
||||
|
||||
private _getStorage(name = ''): string {
|
||||
return this.data[name];
|
||||
}
|
||||
}
|
||||
|
||||
export default MemoryHandler;
|
9
packages/plugins/memory/src/utils.ts
Normal file
9
packages/plugins/memory/src/utils.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { Package } from '@verdaccio/types';
|
||||
|
||||
export function stringifyPackage(pkg: Package) {
|
||||
return JSON.stringify(pkg, null, '\t');
|
||||
}
|
||||
|
||||
export function parsePackage(pkg: string) {
|
||||
return JSON.parse(pkg);
|
||||
}
|
75
packages/plugins/memory/test/local-memory.spec.ts
Normal file
75
packages/plugins/memory/test/local-memory.spec.ts
Normal file
|
@ -0,0 +1,75 @@
|
|||
import { Logger, IPluginStorage } from '@verdaccio/types';
|
||||
import { VerdaccioError } from '@verdaccio/commons-api';
|
||||
|
||||
import { ConfigMemory } from '../src/local-memory';
|
||||
import { DataHandler } from '../src/memory-handler';
|
||||
import LocalMemory from '../src/index';
|
||||
|
||||
import config from './partials/config';
|
||||
|
||||
const logger: Logger = {
|
||||
error: (e) => console.warn(e),
|
||||
info: (e) => console.warn(e),
|
||||
debug: (e) => console.warn(e),
|
||||
child: (e) => console.warn(e),
|
||||
warn: (e) => console.warn(e),
|
||||
http: (e) => console.warn(e),
|
||||
trace: (e) => console.warn(e),
|
||||
};
|
||||
|
||||
const defaultConfig = { logger, config: null };
|
||||
|
||||
describe('memory unit test .', () => {
|
||||
describe('LocalMemory', () => {
|
||||
test('should create an LocalMemory instance', () => {
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
|
||||
expect(localMemory).toBeDefined();
|
||||
});
|
||||
|
||||
test('should create add a package', (done) => {
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
localMemory.add('test', (err: VerdaccioError) => {
|
||||
expect(err).toBeNull();
|
||||
localMemory.get((err: VerdaccioError, data: DataHandler) => {
|
||||
expect(err).toBeNull();
|
||||
expect(data).toHaveLength(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('should reach max limit', (done) => {
|
||||
config.limit = 2;
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
|
||||
localMemory.add('test1', (err) => {
|
||||
expect(err).toBeNull();
|
||||
localMemory.add('test2', (err) => {
|
||||
expect(err).toBeNull();
|
||||
localMemory.add('test3', (err) => {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.message).toMatch(/Storage memory has reached limit of limit packages/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('should remove a package', (done) => {
|
||||
const pkgName = 'test';
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
localMemory.add(pkgName, (err) => {
|
||||
expect(err).toBeNull();
|
||||
localMemory.remove(pkgName, (err) => {
|
||||
expect(err).toBeNull();
|
||||
localMemory.get((err, data) => {
|
||||
expect(err).toBeNull();
|
||||
expect(data).toHaveLength(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
380
packages/plugins/memory/test/memory.spec.ts
Normal file
380
packages/plugins/memory/test/memory.spec.ts
Normal file
|
@ -0,0 +1,380 @@
|
|||
import { Logger, IPluginStorage, IPackageStorage, ILocalPackageManager } from '@verdaccio/types';
|
||||
import { getInternalError } from '@verdaccio/commons-api';
|
||||
|
||||
import { ConfigMemory } from '../src/local-memory';
|
||||
import MemoryHandler from '../src/memory-handler';
|
||||
import LocalMemory from '../src/index';
|
||||
|
||||
import config from './partials/config';
|
||||
import pkgExample from './partials/pkg';
|
||||
|
||||
const logger: Logger = {
|
||||
error: (e) => console.warn(e),
|
||||
info: (e) => console.warn(e),
|
||||
debug: (e) => console.warn(e),
|
||||
child: (e) => console.warn(e),
|
||||
warn: (e) => console.warn(e),
|
||||
http: (e) => console.warn(e),
|
||||
trace: (e) => console.warn(e),
|
||||
};
|
||||
|
||||
const defaultConfig = { logger, config: null };
|
||||
|
||||
const mockStringify = jest.fn((value) => {
|
||||
return jest.requireActual('../src/utils.ts').stringifyPackage(value);
|
||||
});
|
||||
|
||||
const mockParsePackage = jest.fn((value) => {
|
||||
return jest.requireActual('../src/utils.ts').parsePackage(value);
|
||||
});
|
||||
|
||||
jest.mock('../src/utils.ts', () => ({
|
||||
stringifyPackage: (value) => mockStringify(value),
|
||||
parsePackage: (value) => mockParsePackage(value),
|
||||
}));
|
||||
|
||||
describe('memory unit test .', () => {
|
||||
describe('MemoryHandler', () => {
|
||||
test('should create an MemoryHandler instance', () => {
|
||||
const memoryHandler = new MemoryHandler(
|
||||
'test',
|
||||
{
|
||||
['foo']: 'bar',
|
||||
},
|
||||
logger
|
||||
);
|
||||
|
||||
expect(memoryHandler).toBeDefined();
|
||||
});
|
||||
|
||||
test('should save a package', (done) => {
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
const pkgName = 'test';
|
||||
|
||||
const handler = localMemory.getPackageStorage(pkgName);
|
||||
expect(handler).toBeDefined();
|
||||
|
||||
if (handler) {
|
||||
handler.savePackage(pkgName, pkgExample, (err) => {
|
||||
expect(err).toBeNull();
|
||||
handler.readPackage(pkgName, (err, data) => {
|
||||
expect(err).toBeNull();
|
||||
expect(data).toEqual(pkgExample);
|
||||
done();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('should fails on save a package', (done) => {
|
||||
mockStringify.mockImplementationOnce(() => {
|
||||
throw new Error('error on parse');
|
||||
});
|
||||
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
const pkgName = 'test';
|
||||
|
||||
const handler: IPackageStorage = localMemory.getPackageStorage(
|
||||
pkgName
|
||||
) as ILocalPackageManager;
|
||||
|
||||
handler.savePackage(pkgName, pkgExample, (err) => {
|
||||
expect(err).toEqual(getInternalError('error on parse'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should fails on read a package', (done) => {
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
const pkgName = 'test';
|
||||
|
||||
const handler = localMemory.getPackageStorage(pkgName);
|
||||
expect(handler).toBeDefined();
|
||||
|
||||
if (handler) {
|
||||
handler.readPackage(pkgName, (err) => {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.code).toBe(404);
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('should update a package', (done) => {
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
const pkgName = 'test';
|
||||
|
||||
const handler = localMemory.getPackageStorage(pkgName);
|
||||
expect(handler).toBeDefined();
|
||||
const onEnd = jest.fn();
|
||||
|
||||
if (handler) {
|
||||
handler.savePackage(pkgName, pkgExample, (err) => {
|
||||
expect(err).toBeNull();
|
||||
|
||||
handler.updatePackage(
|
||||
pkgName,
|
||||
(json, callback) => {
|
||||
expect(json).toBeDefined();
|
||||
expect(json.name).toBe(pkgExample.name);
|
||||
expect(callback).toBeDefined();
|
||||
callback(null);
|
||||
},
|
||||
(name, data, onEnd) => {
|
||||
expect(name).toBe(pkgName);
|
||||
expect(data.name).toBe(pkgExample.name);
|
||||
onEnd();
|
||||
expect(onEnd).toHaveBeenCalled();
|
||||
done();
|
||||
},
|
||||
(data) => {
|
||||
expect(data).toBeDefined();
|
||||
return data;
|
||||
},
|
||||
onEnd
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('should parse fails on update a package', (done) => {
|
||||
mockParsePackage.mockImplementationOnce(() => {
|
||||
throw new Error('error on parse');
|
||||
});
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
|
||||
const pkgName = 'test';
|
||||
|
||||
const handler = localMemory.getPackageStorage(pkgName);
|
||||
expect(handler).toBeDefined();
|
||||
const onEnd = jest.fn((err) => {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err).toEqual(getInternalError('error on parse'));
|
||||
done();
|
||||
});
|
||||
|
||||
if (handler) {
|
||||
handler.savePackage(pkgName, pkgExample, (err) => {
|
||||
expect(err).toBeNull();
|
||||
handler.updatePackage(
|
||||
pkgName,
|
||||
() => {},
|
||||
() => {},
|
||||
// @ts-ignore
|
||||
() => {},
|
||||
onEnd
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('should fail updateHandler update a package', (done) => {
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
const pkgName = 'test';
|
||||
|
||||
const handler = localMemory.getPackageStorage(pkgName);
|
||||
expect(handler).toBeDefined();
|
||||
const onEnd = jest.fn((err) => {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err).toEqual(getInternalError('some error'));
|
||||
done();
|
||||
});
|
||||
|
||||
if (handler) {
|
||||
handler.savePackage(pkgName, pkgExample, (err) => {
|
||||
expect(err).toBeNull();
|
||||
|
||||
handler.updatePackage(
|
||||
pkgName,
|
||||
(json, callback) => {
|
||||
expect(json).toBeDefined();
|
||||
expect(json.name).toBe(pkgExample.name);
|
||||
expect(callback).toBeDefined();
|
||||
callback(getInternalError('some error'));
|
||||
},
|
||||
() => {},
|
||||
// @ts-ignore
|
||||
() => {},
|
||||
onEnd
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('should onWrite update a package', (done) => {
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
const pkgName = 'test';
|
||||
|
||||
const handler = localMemory.getPackageStorage(pkgName);
|
||||
expect(handler).toBeDefined();
|
||||
const onEnd = jest.fn((err) => {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err).toEqual(getInternalError('error on parse the metadata'));
|
||||
done();
|
||||
});
|
||||
|
||||
if (handler) {
|
||||
handler.savePackage(pkgName, pkgExample, (err) => {
|
||||
expect(err).toBeNull();
|
||||
|
||||
handler.updatePackage(
|
||||
pkgName,
|
||||
(json, callback) => {
|
||||
expect(json).toBeDefined();
|
||||
expect(json.name).toBe(pkgExample.name);
|
||||
expect(callback).toBeDefined();
|
||||
callback(null);
|
||||
},
|
||||
(name, data, onEnd) => {
|
||||
expect(name).toBe(pkgName);
|
||||
expect(data.name).toBe(pkgExample.name);
|
||||
onEnd();
|
||||
expect(onEnd).toHaveBeenCalled();
|
||||
done();
|
||||
},
|
||||
() => {
|
||||
throw new Error('dadsads');
|
||||
},
|
||||
onEnd
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('writing/reading files', () => {
|
||||
test('should write a tarball', (done) => {
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
const pkgName = 'test';
|
||||
const dataTarball = '12345';
|
||||
|
||||
const handler = localMemory.getPackageStorage(pkgName);
|
||||
|
||||
if (handler) {
|
||||
const stream = handler.writeTarball(pkgName);
|
||||
stream.on('data', (data) => {
|
||||
expect(data.toString()).toBe(dataTarball);
|
||||
});
|
||||
stream.on('open', () => {
|
||||
stream.done();
|
||||
stream.end();
|
||||
});
|
||||
stream.on('success', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
stream.write(dataTarball);
|
||||
}
|
||||
});
|
||||
|
||||
test('should read a tarball', (done) => {
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
const pkgName = 'test.tar.gz';
|
||||
const dataTarball = '12345';
|
||||
|
||||
const handler = localMemory.getPackageStorage(pkgName);
|
||||
|
||||
if (handler) {
|
||||
const stream = handler.writeTarball(pkgName);
|
||||
stream.on('open', () => {
|
||||
stream.done();
|
||||
stream.end();
|
||||
});
|
||||
stream.on('success', () => {
|
||||
const readStream = handler.readTarball(pkgName);
|
||||
readStream.on('data', (data) => {
|
||||
expect(data.toString()).toBe(dataTarball);
|
||||
done();
|
||||
});
|
||||
});
|
||||
stream.write(dataTarball);
|
||||
}
|
||||
});
|
||||
|
||||
test('should abort read a tarball', (done) => {
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
const pkgName = 'test2.tar.gz';
|
||||
const dataTarball = '12345';
|
||||
|
||||
const handler = localMemory.getPackageStorage(pkgName);
|
||||
|
||||
if (handler) {
|
||||
const stream = handler.writeTarball(pkgName);
|
||||
stream.on('open', () => {
|
||||
stream.done();
|
||||
stream.end();
|
||||
});
|
||||
stream.on('success', () => {
|
||||
const readStream = handler.readTarball(pkgName);
|
||||
readStream.on('data', () => {
|
||||
readStream.abort();
|
||||
});
|
||||
readStream.on('error', (err) => {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.message).toMatch(/read has been aborted/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
stream.write(dataTarball);
|
||||
}
|
||||
});
|
||||
|
||||
test('should fails read a tarball not found', (done) => {
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
const pkgName = 'test2.tar.gz';
|
||||
const handler = localMemory.getPackageStorage(pkgName);
|
||||
|
||||
if (handler) {
|
||||
const readStream = handler.readTarball('not-found');
|
||||
readStream.on('error', (err) => {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.message).toMatch(/no such package/);
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('should abort while write a tarball', (done) => {
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
const pkgName = 'test-abort.tar.gz';
|
||||
const dataTarball = '12345';
|
||||
|
||||
const handler = localMemory.getPackageStorage(pkgName);
|
||||
|
||||
if (handler) {
|
||||
const stream = handler.writeTarball(pkgName);
|
||||
stream.on('error', (err) => {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.message).toMatch(/transmision aborted/);
|
||||
done();
|
||||
});
|
||||
stream.on('open', () => {
|
||||
stream.abort();
|
||||
});
|
||||
|
||||
stream.write(dataTarball);
|
||||
}
|
||||
});
|
||||
|
||||
test('should delete a package', (done) => {
|
||||
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||
const pkgName = 'test2';
|
||||
|
||||
const handler: IPackageStorage = localMemory.getPackageStorage(pkgName);
|
||||
expect(handler).toBeDefined();
|
||||
if (handler) {
|
||||
handler.createPackage(pkgName, pkgExample, (err) => {
|
||||
expect(err).toBeNull();
|
||||
handler.deletePackage(pkgName, (err) => {
|
||||
expect(err).toBeNull();
|
||||
handler.readPackage(pkgName, (err) => {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.message).toMatch(/no such package/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
50
packages/plugins/memory/test/partials/config.ts
Normal file
50
packages/plugins/memory/test/partials/config.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
import { Config } from '@verdaccio/types';
|
||||
|
||||
const config: Config = {
|
||||
user_agent: 'string',
|
||||
server_id: 1234,
|
||||
secret: '12345',
|
||||
self_path: './nowhere',
|
||||
uplinks: {
|
||||
npmjs: {
|
||||
url: 'https://registry.npmjs.org/',
|
||||
},
|
||||
},
|
||||
security: {
|
||||
web: {
|
||||
sign: {},
|
||||
verify: {},
|
||||
},
|
||||
api: {
|
||||
legacy: true,
|
||||
},
|
||||
},
|
||||
packages: {
|
||||
test: {
|
||||
storage: '',
|
||||
publish: [''],
|
||||
proxy: [''],
|
||||
access: [''],
|
||||
},
|
||||
},
|
||||
web: {
|
||||
enable: true,
|
||||
title: 'string',
|
||||
logo: 'string',
|
||||
},
|
||||
logs: [],
|
||||
auth: {},
|
||||
notifications: {
|
||||
method: '',
|
||||
packagePattern: /a/,
|
||||
packagePatternFlags: '',
|
||||
headers: {},
|
||||
endpoint: '',
|
||||
content: '',
|
||||
},
|
||||
checkSecretKey: () => '1234',
|
||||
getMatchedPackagesSpec: jest.fn(),
|
||||
hasProxyTo: () => false,
|
||||
};
|
||||
|
||||
export default config;
|
65
packages/plugins/memory/test/partials/pkg.ts
Normal file
65
packages/plugins/memory/test/partials/pkg.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
import { Package } from '@verdaccio/types';
|
||||
|
||||
const pkg: Package = {
|
||||
name: '@scope/test_npm',
|
||||
versions: {
|
||||
'1.0.1': {
|
||||
name: '@scope/test_npm',
|
||||
version: '1.0.1',
|
||||
description: '',
|
||||
main: 'index.js',
|
||||
readme: 'test readme',
|
||||
scripts: {
|
||||
test: "echo 'Error: no test specified' && exit 1",
|
||||
},
|
||||
author: {
|
||||
name: 'Juan Picado',
|
||||
email: 'me@jotadeveloper.com',
|
||||
url: 'http://jotadeveloper.com/',
|
||||
},
|
||||
license: 'ISC',
|
||||
dependencies: {
|
||||
'create-react-app': '^1.4.1',
|
||||
'fast-static-site': '^1.0.2',
|
||||
watchdom: '^1.0.2',
|
||||
},
|
||||
_id: '@scope/test_npm@1.0.1',
|
||||
_npmUser: {
|
||||
name: 'jpicado',
|
||||
email: 'dsa@dasd.com',
|
||||
},
|
||||
maintainers: [
|
||||
{
|
||||
name: 'jpicado',
|
||||
email: 'dsa@dasd.com',
|
||||
},
|
||||
],
|
||||
dist: {
|
||||
integrity:
|
||||
'sha512-0ThGF2zZiOGmLoHl/n5cMwAS6swbAz7rdzDjgkyDh+C2rADzNfPIfo7KBTRHbY6uJ9akBCvWDFBuR0fgaxYnjQ==',
|
||||
shasum: '1df0c3dfd289b2ac6ef00b0129cab9737eeaa62d',
|
||||
tarball: 'http://localhost:4873/@scope/test_npm/-/@scope/test_npm-1.0.1.tgz',
|
||||
},
|
||||
},
|
||||
},
|
||||
'dist-tags': {
|
||||
latest: '1.0.1',
|
||||
},
|
||||
time: {
|
||||
modified: '2018-02-20T17:50:47.944Z',
|
||||
created: '2018-02-20T17:50:47.944Z',
|
||||
'1.0.1': '2018-02-20T17:50:47.944Z',
|
||||
},
|
||||
_distfiles: {},
|
||||
_attachments: {
|
||||
'test_npm-1.0.1.tgz': {
|
||||
shasum: '1df0c3dfd289b2ac6ef00b0129cab9737eeaa62d',
|
||||
version: '1.0.1',
|
||||
},
|
||||
},
|
||||
_uplinks: {},
|
||||
_rev: '5-ea87644a96a129cf',
|
||||
readme: 'ERROR: No README data found!',
|
||||
};
|
||||
|
||||
export default pkg;
|
9
packages/plugins/memory/tsconfig.build.json
Normal file
9
packages/plugins/memory/tsconfig.build.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.base",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./build"
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["src/**/*.test.ts"]
|
||||
}
|
20
packages/plugins/memory/tsconfig.json
Normal file
20
packages/plugins/memory/tsconfig.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.reference.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./build"
|
||||
},
|
||||
"include": ["src/**/*", "types/*.d.ts"],
|
||||
"exclude": ["src/**/*.test.ts"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../core/commons-api"
|
||||
},
|
||||
{
|
||||
"path": "../../core/streams"
|
||||
},
|
||||
{
|
||||
"path": "../../core/types"
|
||||
}
|
||||
]
|
||||
}
|
853
pnpm-lock.yaml
853
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,6 @@
|
|||
packages:
|
||||
- packages/*
|
||||
- packages/core/*
|
||||
- packages/plugins/*
|
||||
- website
|
||||
- '!**/test/**'
|
||||
|
|
Loading…
Reference in a new issue