diff --git a/packages/proxy/package.json b/packages/proxy/package.json index 84ee10ada..5ed42a8e3 100644 --- a/packages/proxy/package.json +++ b/packages/proxy/package.json @@ -50,7 +50,8 @@ "request": "2.87.0" }, "devDependencies": { - "@verdaccio/types": "workspace:11.0.0-6-next.7" + "@verdaccio/types": "workspace:11.0.0-6-next.7", + "nock": "13.0.11" }, "funding": { "type": "opencollective", diff --git a/packages/proxy/test/conf/proxy1.yaml b/packages/proxy/test/conf/proxy1.yaml new file mode 100644 index 000000000..cbdee5ad1 --- /dev/null +++ b/packages/proxy/test/conf/proxy1.yaml @@ -0,0 +1,31 @@ +storage: ./storage +plugins: ./plugins +web: + title: Verdaccio +auth: + htpasswd: + file: ./htpasswd +uplinks: + npmjs: + url: https://registry.npmjs.org/ + +packages: + '@*/*': + access: $all + publish: $authenticated + unpublish: $authenticated + proxy: npmjs + + '**': + access: $all + publish: $authenticated + unpublish: $authenticated + proxy: npmjs + +server: + keepAliveTimeout: 60 + +middlewares: + audit: + enabled: true +logs: { type: stdout, format: pretty, level: http } diff --git a/packages/proxy/test/headers.auth.spec.ts b/packages/proxy/test/headers.auth.spec.ts index 871f87a6e..a4cff0d51 100644 --- a/packages/proxy/test/headers.auth.spec.ts +++ b/packages/proxy/test/headers.auth.spec.ts @@ -19,6 +19,7 @@ function createUplink(config) { function setHeaders(config: unknown = {}, headers: unknown = {}) { const uplink = createUplink(config); + // @ts-expect-error return uplink._setHeaders({ headers, }); diff --git a/packages/proxy/test/proxy.spec.ts b/packages/proxy/test/proxy.spec.ts new file mode 100644 index 000000000..472e64be7 --- /dev/null +++ b/packages/proxy/test/proxy.spec.ts @@ -0,0 +1,149 @@ +import path from 'path'; +import nock from 'nock'; +import { Config, parseConfigFile } from '@verdaccio/config'; +import { ErrorCode } from '@verdaccio/utils'; +import { API_ERROR } from '@verdaccio/commons-api'; +import { ProxyStorage } from '../src/up-storage'; + +const getConf = (name) => path.join(__dirname, '/conf', name); + +const mockDebug = jest.fn(); +const mockInfo = jest.fn(); +const mockHttp = jest.fn(); +const mockError = jest.fn(); +const mockWarn = jest.fn(); +jest.mock('@verdaccio/logger', () => { + const originalLogger = jest.requireActual('@verdaccio/logger'); + return { + ...originalLogger, + logger: { + child: () => ({ + debug: (arg) => mockDebug(arg), + info: (arg) => mockInfo(arg), + http: (arg) => mockHttp(arg), + error: (arg) => mockError(arg), + warn: (arg) => mockWarn(arg), + }), + }, + }; +}); + +const domain = 'https://registry.npmjs.org'; + +describe('proxy', () => { + beforeEach(() => { + nock.cleanAll(); + }); + const defaultRequestOptions = { + url: 'https://registry.npmjs.org', + }; + const proxyPath = getConf('proxy1.yaml'); + const conf = new Config(parseConfigFile(proxyPath)); + describe('basic requests', () => { + test('proxy call with etag', (done) => { + nock(domain) + .get('/jquery') + .reply( + 200, + { body: 'test' }, + { + etag: () => `_ref_4444`, + } + ); + const prox1 = new ProxyStorage(defaultRequestOptions, conf); + prox1.getRemoteMetadata('jquery', {}, (_error, body, etag) => { + expect(etag).toEqual('_ref_4444'); + expect(body).toEqual({ body: 'test' }); + done(); + }); + }); + + test('proxy call with etag as option', (done) => { + nock(domain) + .get('/jquery') + .reply( + 200, + { body: 'test' }, + { + etag: () => `_ref_4444`, + } + ); + const prox1 = new ProxyStorage(defaultRequestOptions, conf); + prox1.getRemoteMetadata('jquery', { etag: 'rev_3333' }, (_error, body, etag) => { + expect(etag).toEqual('_ref_4444'); + expect(body).toEqual({ body: 'test' }); + done(); + }); + }); + + test('proxy not found', (done) => { + nock(domain).get('/jquery').reply(404); + const prox1 = new ProxyStorage(defaultRequestOptions, conf); + prox1.getRemoteMetadata('jquery', { etag: 'rev_3333' }, (error) => { + expect(error).toEqual(ErrorCode.getNotFound(API_ERROR.NOT_PACKAGE_UPLINK)); + done(); + }); + }); + }); + + describe('tarballs', () => { + test('tarball call', (done) => { + nock(domain) + .get('/jquery') + .reply( + 200, + { body: 'test' }, + { + etag: () => `_ref_4444`, + } + ); + const prox1 = new ProxyStorage(defaultRequestOptions, conf); + prox1.getRemoteMetadata('jquery', {}, (_error, body, etag) => { + expect(etag).toEqual('_ref_4444'); + expect(body).toEqual({ body: 'test' }); + done(); + }); + }); + }); + + describe('error handling', () => { + test('reply with error', (done) => { + nock(domain).get('/jquery').replyWithError('something awful happened'); + const prox1 = new ProxyStorage(defaultRequestOptions, conf); + prox1.getRemoteMetadata('jquery', {}, (error) => { + expect(error).toEqual(new Error('something awful happened')); + done(); + }); + }); + + test('reply with bad body json format', (done) => { + nock(domain).get('/jquery').reply(200, 'some-text'); + const prox1 = new ProxyStorage(defaultRequestOptions, conf); + prox1.getRemoteMetadata('jquery', {}, (error) => { + expect(error).toEqual(new SyntaxError('Unexpected token s in JSON at position 0')); + done(); + }); + }); + + test('400 error proxy call', (done) => { + nock(domain).get('/jquery').reply(409); + const prox1 = new ProxyStorage(defaultRequestOptions, conf); + prox1.getRemoteMetadata('jquery', {}, (error) => { + expect(error.statusCode).toEqual(500); + expect(mockInfo).toHaveBeenCalled(); + expect(mockHttp).toHaveBeenCalledWith({ + request: { method: 'GET', url: 'https://registry.npmjs.org/jquery' }, + status: 409, + }); + expect(mockHttp).toHaveBeenCalledWith({ + bytes: { in: 0, out: 0 }, + err: undefined, + error: undefined, + request: { method: 'GET', url: 'https://registry.npmjs.org/jquery' }, + status: 409, + }); + done(); + }); + }); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68c28a55e..fe481dff6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -886,6 +886,7 @@ importers: '@verdaccio/utils': workspace:6.0.0-6-next.4 JSONStream: 1.3.5 lodash: ^4.17.20 + nock: 13.0.11 request: 2.87.0 dependencies: '@verdaccio/commons-api': link:../core/commons-api @@ -899,6 +900,7 @@ importers: request: 2.87.0 devDependencies: '@verdaccio/types': link:../core/types + nock: 13.0.11 packages/server: specifiers: @@ -19935,6 +19937,18 @@ packages: propagate: 2.0.1 dev: true + /nock/13.0.11: + resolution: {integrity: sha512-sKZltNkkWblkqqPAsjYW0bm3s9DcHRPiMOyKO/PkfJ+ANHZ2+LA2PLe22r4lLrKgXaiSaDQwW3qGsJFtIpQIeQ==} + engines: {node: '>= 10.13'} + dependencies: + debug: 4.3.1 + json-stringify-safe: 5.0.1 + lodash.set: 4.3.2 + propagate: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: true + /nock/13.0.4: resolution: {integrity: sha512-alqTV8Qt7TUbc74x1pKRLSENzfjp4nywovcJgi/1aXDiUxXdt7TkruSTF5MDWPP7UoPVgea4F9ghVdmX0xxnSA==} engines: {node: '>= 10.13'}