diff --git a/lib/datasource/cdnjs/__snapshots__/index.spec.ts.snap b/lib/datasource/cdnjs/__snapshots__/index.spec.ts.snap index a40f46a413c87f..5d9e5c2e0faeff 100644 --- a/lib/datasource/cdnjs/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/cdnjs/__snapshots__/index.spec.ts.snap @@ -13,6 +13,21 @@ Object { } `; +exports[`datasource/cdnjs getReleases filters releases by asset presence 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.cdnjs.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.cdnjs.com/libraries/bulma?fields=homepage,repository,assets", + }, +] +`; + exports[`datasource/cdnjs getReleases processes real data 1`] = ` Object { "homepage": "https://d3js.org/d3-force/", @@ -125,3 +140,138 @@ Object { "sourceUrl": "https://github.com/d3/d3-force.git", } `; + +exports[`datasource/cdnjs getReleases processes real data 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.cdnjs.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.cdnjs.com/libraries/d3-force?fields=homepage,repository,assets", + }, +] +`; + +exports[`datasource/cdnjs getReleases returns null for 404 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.cdnjs.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.cdnjs.com/libraries/foo?fields=homepage,repository,assets", + }, +] +`; + +exports[`datasource/cdnjs getReleases returns null for empty 200 OK 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.cdnjs.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.cdnjs.com/libraries/doesnotexist?fields=homepage,repository,assets", + }, +] +`; + +exports[`datasource/cdnjs getReleases returns null for unknown error 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.cdnjs.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.cdnjs.com/libraries/foo?fields=homepage,repository,assets", + }, +] +`; + +exports[`datasource/cdnjs getReleases throws for 5xx 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.cdnjs.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.cdnjs.com/libraries/foo?fields=homepage,repository,assets", + }, +] +`; + +exports[`datasource/cdnjs getReleases throws for 401 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.cdnjs.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.cdnjs.com/libraries/foo?fields=homepage,repository,assets", + }, +] +`; + +exports[`datasource/cdnjs getReleases throws for 429 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.cdnjs.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.cdnjs.com/libraries/foo?fields=homepage,repository,assets", + }, +] +`; + +exports[`datasource/cdnjs getReleases throws for empty result 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.cdnjs.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.cdnjs.com/libraries/foo?fields=homepage,repository,assets", + }, +] +`; + +exports[`datasource/cdnjs getReleases throws for error 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.cdnjs.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.cdnjs.com/libraries/foo?fields=homepage,repository,assets", + }, +] +`; diff --git a/lib/datasource/cdnjs/index.spec.ts b/lib/datasource/cdnjs/index.spec.ts index 0450d748e14963..966013b1b7ea3c 100644 --- a/lib/datasource/cdnjs/index.spec.ts +++ b/lib/datasource/cdnjs/index.spec.ts @@ -1,11 +1,8 @@ import fs from 'fs'; +import * as httpMock from '../../../test/httpMock'; import { DATASOURCE_FAILURE } from '../../constants/error-messages'; -import _got from '../../util/got'; import { getReleases } from '.'; -const got: jest.Mock = _got as any; -jest.mock('../../util/got'); - let res1 = fs.readFileSync( 'lib/datasource/cdnjs/__fixtures__/d3-force.json', 'utf8' @@ -18,70 +15,98 @@ let res2 = fs.readFileSync( ); res2 = JSON.parse(res2); +const baseUrl = 'https://api.cdnjs.com/'; + +const pathFor = (s: string): string => + `/libraries/${s.split('/').shift()}?fields=homepage,repository,assets`; + describe('datasource/cdnjs', () => { describe('getReleases', () => { beforeEach(() => { jest.clearAllMocks(); + httpMock.setup(); }); + + afterEach(() => { + httpMock.reset(); + }); + it('throws for empty result', async () => { - got.mockResolvedValueOnce(null); + httpMock.scope(baseUrl).get(pathFor('foo/bar')).reply(200, null); await expect(getReleases({ lookupName: 'foo/bar' })).rejects.toThrow( DATASOURCE_FAILURE ); + expect(httpMock.getTrace()).toMatchSnapshot(); }); - it('throws for missing fields', async () => { - got.mockResolvedValueOnce({}); + it('throws for error', async () => { + httpMock.scope(baseUrl).get(pathFor('foo/bar')).replyWithError('error'); await expect(getReleases({ lookupName: 'foo/bar' })).rejects.toThrow( DATASOURCE_FAILURE ); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null for 404', async () => { - got.mockRejectedValueOnce({ statusCode: 404 }); + httpMock.scope(baseUrl).get(pathFor('foo/bar')).reply(404); expect(await getReleases({ lookupName: 'foo/bar' })).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null for empty 200 OK', async () => { - got.mockResolvedValueOnce({ body: {} }); + httpMock + .scope(baseUrl) + .get(pathFor('doesnotexist/doesnotexist')) + .reply(200, {}); expect( await getReleases({ lookupName: 'doesnotexist/doesnotexist' }) ).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('throws for 401', async () => { - got.mockRejectedValueOnce({ statusCode: 401 }); + httpMock.scope(baseUrl).get(pathFor('foo/bar')).reply(401); await expect(getReleases({ lookupName: 'foo/bar' })).rejects.toThrow( DATASOURCE_FAILURE ); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('throws for 429', async () => { - got.mockRejectedValueOnce({ statusCode: 429 }); + httpMock.scope(baseUrl).get(pathFor('foo/bar')).reply(429); await expect(getReleases({ lookupName: 'foo/bar' })).rejects.toThrow( DATASOURCE_FAILURE ); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('throws for 5xx', async () => { - got.mockRejectedValueOnce({ statusCode: 502 }); + httpMock.scope(baseUrl).get(pathFor('foo/bar')).reply(502); await expect(getReleases({ lookupName: 'foo/bar' })).rejects.toThrow( DATASOURCE_FAILURE ); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null for unknown error', async () => { - got.mockImplementationOnce(() => { - throw new Error(); - }); + httpMock.scope(baseUrl).get(pathFor('foo/bar')).replyWithError('error'); await expect(getReleases({ lookupName: 'foo/bar' })).rejects.toThrow( DATASOURCE_FAILURE ); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('processes real data', async () => { - got.mockResolvedValueOnce({ body: res1 }); + httpMock + .scope(baseUrl) + .get(pathFor('d3-force/d3-force.js')) + .reply(200, res1); const res = await getReleases({ lookupName: 'd3-force/d3-force.js' }); expect(res).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('filters releases by asset presence', async () => { - got.mockResolvedValueOnce({ body: res2 }); + httpMock + .scope(baseUrl) + .get(pathFor('bulma/only/0.7.5/style.css')) + .reply(200, res2); const res = await getReleases({ lookupName: 'bulma/only/0.7.5/style.css', }); expect(res).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); }); }); diff --git a/lib/datasource/docker/__snapshots__/index.spec.ts.snap b/lib/datasource/docker/__snapshots__/index.spec.ts.snap index 100f3323ec9e7b..53ad6b0a139bd5 100644 --- a/lib/datasource/docker/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/docker/__snapshots__/index.spec.ts.snap @@ -1,5 +1,370 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`api/docker getDigest continues without token if ECR authentication could not be extracted 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", + }, +] +`; + +exports[`api/docker getDigest continues without token if ECR authentication fails 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", + }, +] +`; + +exports[`api/docker getDigest continues without token, when no header is present 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/vnd.docker.distribution.manifest.v2+json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/library/some-dep/manifests/some-new-value", + }, +] +`; + +exports[`api/docker getDigest falls back to body for digest 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "auth.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/some-dep:pull", + }, + Object { + "headers": Object { + "accept": "application/vnd.docker.distribution.manifest.v2+json", + "accept-encoding": "gzip, deflate", + "authorization": "Bearer some-token", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/library/some-dep/manifests/some-new-value", + }, +] +`; + +exports[`api/docker getDigest returns digest 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "auth.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/some-dep:pull", + }, + Object { + "headers": Object { + "accept": "application/vnd.docker.distribution.manifest.v2+json", + "accept-encoding": "gzip, deflate", + "authorization": "Bearer some-token", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/library/some-dep/manifests/latest", + }, +] +`; + +exports[`api/docker getDigest returns null for 403 with basic authentication 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, +] +`; + +exports[`api/docker getDigest returns null if errored 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/vnd.docker.distribution.manifest.v2+json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/library/some-dep/manifests/some-new-value", + }, +] +`; + +exports[`api/docker getDigest returns null if no token 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/vnd.docker.distribution.manifest.v2+json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/library/some-dep/manifests/some-new-value", + }, +] +`; + +exports[`api/docker getDigest supports ECR authentication 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic abcdef", + "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", + }, + Object { + "headers": Object { + "accept": "application/vnd.docker.distribution.manifest.v2+json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic abcdef", + "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/node/manifests/some-tag", + }, +] +`; + +exports[`api/docker getDigest supports basic authentication 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/vnd.docker.distribution.manifest.v2+json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/library/some-dep/manifests/some-tag", + }, +] +`; + +exports[`api/docker getDigest supports docker insecure registry 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "http://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/vnd.docker.distribution.manifest.v2+json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "http://index.docker.io/v2/library/some-dep/manifests/latest", + }, +] +`; + +exports[`api/docker getDigest supports scoped names 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "auth.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/some-other-dep:pull", + }, + Object { + "headers": Object { + "accept": "application/vnd.docker.distribution.manifest.v2+json", + "accept-encoding": "gzip, deflate", + "authorization": "Bearer some-token", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/library/some-other-dep/manifests/8.0.0-alpine", + }, +] +`; + exports[`api/docker getRegistryRepository handles local registries 1`] = ` Object { "registry": "https://registry:5000", @@ -15,646 +380,373 @@ Object { `; exports[`api/docker getReleases adds library/ prefix for Docker Hub (explicit) 1`] = ` -[MockFunction] { - "calls": Array [ - Array [ - "https://index.docker.io/v2/", - Object { - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - "throwHttpErrors": false, - }, - ], - Array [ - "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/node:pull", - Object { - "headers": Object { - "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "json": true, - "method": "get", - }, - ], - Array [ - "https://index.docker.io/v2/library/node/tags/list?n=10000", - Object { - "headers": Object { - "authorization": "Bearer some-token ", - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "json": true, - "method": "get", - }, - ], - Array [ - "https://index.docker.io/v2/", - Object { - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - "throwHttpErrors": false, - }, - ], - Array [ - "https://index.docker.io/v2/library/node/manifests/1.0.0", - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.v2+json", - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Object { - "headers": Object { - "www-authenticate": "Bearer realm=\\"https://auth.docker.io/token\\",service=\\"registry.docker.io\\",scope=\\"repository:library/node:pull \\"", - }, - }, - }, - Object { - "type": "return", - "value": Object { - "body": Object { - "token": "some-token ", - }, - "headers": Object {}, - }, - }, - Object { - "type": "return", - "value": Object { - "body": Object { - "tags": Array [ - "1.0.0", - ], - }, - "headers": Object {}, - }, - }, - Object { - "type": "return", - "value": Object { - "headers": Object {}, - }, - }, - Object { - "type": "return", - "value": Object { - "body": Object {}, - "headers": Object {}, - }, - }, - ], -} +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "auth.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/node:pull", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Bearer some-token ", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/library/node/tags/list?n=10000", + }, + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/vnd.docker.distribution.manifest.v2+json", + "accept-encoding": "gzip, deflate", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/library/node/manifests/1.0.0", + }, +] `; exports[`api/docker getReleases adds library/ prefix for Docker Hub (implicit) 1`] = ` -[MockFunction] { - "calls": Array [ - Array [ - "https://index.docker.io/v2/", - Object { - "auth": "some-username:some-password", - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - "throwHttpErrors": false, - }, - ], - Array [ - "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/node:pull", - Object { - "headers": Object { - "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "json": true, - "method": "get", - }, - ], - Array [ - "https://index.docker.io/v2/library/node/tags/list?n=10000", - Object { - "headers": Object { - "authorization": "Bearer some-token ", - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "json": true, - "method": "get", - }, - ], - Array [ - "https://index.docker.io/v2/", - Object { - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - "throwHttpErrors": false, - }, - ], - Array [ - "https://index.docker.io/v2/library/node/manifests/1.0.0", - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.v2+json", - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Object { - "headers": Object { - "www-authenticate": "Bearer realm=\\"https://auth.docker.io/token\\",service=\\"registry.docker.io\\",scope=\\"repository:library/node:pull \\"", - }, - }, - }, - Object { - "type": "return", - "value": Object { - "body": Object { - "token": "some-token ", - }, - "headers": Object {}, - }, - }, - Object { - "type": "return", - "value": Object { - "body": Object { - "tags": Array [ - "1.0.0", - ], - }, - "headers": Object {}, - }, - }, - Object { - "type": "return", - "value": Object { - "headers": Object {}, - }, - }, - Object { - "type": "return", - "value": Object { - "body": Object {}, - "headers": Object {}, - }, - }, - ], -} +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "auth.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/node:pull", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Bearer some-token ", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/library/node/tags/list?n=10000", + }, + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/vnd.docker.distribution.manifest.v2+json", + "accept-encoding": "gzip, deflate", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/library/node/manifests/1.0.0", + }, +] `; exports[`api/docker getReleases adds no library/ prefix for other registries 1`] = ` -[MockFunction] { - "calls": Array [ - Array [ - "https://k8s.gcr.io/v2/", - Object { - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - "throwHttpErrors": false, - }, - ], - Array [ - "https://k8s.gcr.io/v2/token?service=k8s.gcr.io&scope=repository:kubernetes-dashboard-amd64:pull", - Object { - "headers": Object { - "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "json": true, - "method": "get", - }, - ], - Array [ - "https://k8s.gcr.io/v2/kubernetes-dashboard-amd64/tags/list?n=10000", - Object { - "headers": Object { - "authorization": "Bearer some-token ", - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "json": true, - "method": "get", - }, - ], - Array [ - "https://k8s.gcr.io/v2/", - Object { - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - "throwHttpErrors": false, - }, - ], - Array [ - "https://k8s.gcr.io/v2/kubernetes-dashboard-amd64/manifests/1.0.0", - Object { - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.v2+json", - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Object { - "headers": Object { - "www-authenticate": "Bearer realm=\\"https://k8s.gcr.io/v2/token\\",service=\\"k8s.gcr.io\\"", - }, - }, - }, - Object { - "type": "return", - "value": Object { - "body": Object { - "token": "some-token ", - }, - "headers": Object {}, - }, - }, - Object { - "type": "return", - "value": Object { - "body": Object { - "tags": Array [ - "1.0.0", - ], - }, - "headers": Object {}, - }, - }, - Object { - "type": "return", - "value": Object { - "headers": Object {}, - }, - }, - Object { - "type": "return", - "value": Object { - "body": Object {}, - "headers": Object {}, - }, - }, - ], -} +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "k8s.gcr.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://k8s.gcr.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "k8s.gcr.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://k8s.gcr.io/v2/token?service=k8s.gcr.io&scope=repository:kubernetes-dashboard-amd64:pull", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Bearer some-token ", + "host": "k8s.gcr.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://k8s.gcr.io/v2/kubernetes-dashboard-amd64/tags/list?n=10000", + }, + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "k8s.gcr.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://k8s.gcr.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/vnd.docker.distribution.manifest.v2+json", + "accept-encoding": "gzip, deflate", + "host": "k8s.gcr.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://k8s.gcr.io/v2/kubernetes-dashboard-amd64/manifests/1.0.0", + }, +] +`; + +exports[`api/docker getReleases returns null if no token 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/library/node/tags/list?n=10000", + }, +] +`; + +exports[`api/docker getReleases returns null on error 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "index.docker.io", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://index.docker.io/v2/my/node/tags/list?n=10000", + }, +] `; exports[`api/docker getReleases uses custom registry in depName 1`] = ` -[MockFunction] { - "calls": Array [ - Array [ - "https://registry.company.com/v2/", - Object { - "auth": "some-username:some-password", - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - "throwHttpErrors": false, - }, - ], - Array [ - "https://registry.company.com/v2/node/tags/list?n=10000", - Object { - "auth": "some-username:some-password", - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "json": true, - "method": "get", - }, - ], - Array [ - "https://registry.company.com/v2/", - Object { - "auth": "some-username:some-password", - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - "throwHttpErrors": false, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Object { - "headers": Object {}, - }, - }, - Object { - "type": "return", - "value": Object { - "body": Object { - "tags": Array [ - "1.0.0", - ], - }, - "headers": Object {}, - }, - }, - Object { - "type": "return", - "value": undefined, - }, - ], -} +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "registry.company.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.company.com/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "registry.company.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.company.com/v2/node/tags/list?n=10000", + }, + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "registry.company.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.company.com/v2/", + }, + Object { + "headers": Object { + "accept": "application/vnd.docker.distribution.manifest.v2+json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "registry.company.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.company.com/v2/node/manifests/1.0.0", + }, +] `; exports[`api/docker getReleases uses custom registry with registryUrls 1`] = ` Array [ - Array [ - "https://registry.company.com/v2/", - Object { - "auth": "some-username:some-password", - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - "throwHttpErrors": false, - }, - ], - Array [ - "https://registry.company.com/v2/node/tags/list?n=10000", - Object { - "auth": "some-username:some-password", - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "json": true, - "method": "get", - }, - ], - Array [ - "https://api.github.com/user/9287/repos?page=3&per_page=100", - Object { - "auth": "some-username:some-password", - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "json": true, - "method": "get", - }, - ], - Array [ - "https://registry.company.com/v2/", - Object { - "auth": "some-username:some-password", - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - "throwHttpErrors": false, - }, - ], - Array [ - "https://registry.company.com/v2/node/manifests/latest", - Object { - "auth": "some-username:some-password", - "headers": Object { - "accept": "application/vnd.docker.distribution.manifest.v2+json", - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - }, - ], + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "registry.company.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.company.com/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "registry.company.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.company.com/v2/node/tags/list?n=10000", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/user/9287/repos?page=3&per_page=100", + }, + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "registry.company.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.company.com/v2/", + }, + Object { + "headers": Object { + "accept": "application/vnd.docker.distribution.manifest.v2+json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "registry.company.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://registry.company.com/v2/node/manifests/latest", + }, ] `; exports[`api/docker getReleases uses lower tag limit for ECR deps 1`] = ` -[MockFunction] { - "calls": Array [ - Array [ - "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", - Object { - "auth": "some-username:some-password", - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - "throwHttpErrors": false, - }, - ], - Array [ - "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/node/tags/list?n=1000", - Object { - "auth": "some-username:some-password", - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "json": true, - "method": "get", - }, - ], - Array [ - "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", - Object { - "auth": "some-username:some-password", - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "docker", - "method": "get", - "throwHttpErrors": false, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Object { - "headers": Object {}, - }, - }, - Object { - "type": "return", - "value": Object { - "body": Object {}, - "headers": Object {}, - }, - }, - Object { - "type": "return", - "value": undefined, - }, - ], -} +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/node/tags/list?n=1000", + }, + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/", + }, + Object { + "headers": Object { + "accept": "application/vnd.docker.distribution.manifest.v2+json", + "accept-encoding": "gzip, deflate", + "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk", + "host": "123456789.dkr.ecr.us-east-1.amazonaws.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/node/manifests/undefined", + }, +] `; diff --git a/lib/datasource/docker/index.spec.ts b/lib/datasource/docker/index.spec.ts index f1803bf0c30ddb..6ca136f7d126d8 100644 --- a/lib/datasource/docker/index.spec.ts +++ b/lib/datasource/docker/index.spec.ts @@ -1,18 +1,34 @@ import AWS from 'aws-sdk'; import AWSMock from 'aws-sdk-mock'; import { getPkgReleases } from '..'; +import * as httpMock from '../../../test/httpMock'; import { DATASOURCE_FAILURE } from '../../constants/error-messages'; -import _got from '../../util/got'; import * as _hostRules from '../../util/host-rules'; import * as docker from '.'; -const got: any = _got; const hostRules: any = _hostRules; -jest.mock('../../util/got'); jest.mock('../../util/host-rules'); +const baseUrl = 'https://index.docker.io/v2'; +const authUrl = 'https://auth.docker.io'; +const amazonUrl = 'https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2'; + describe('api/docker', () => { + beforeEach(() => { + httpMock.setup(); + hostRules.find.mockReturnValue({ + username: 'some-username', + password: 'some-password', + }); + hostRules.hosts = jest.fn(() => []); + }); + + afterEach(() => { + jest.resetAllMocks(); + httpMock.reset(); + }); + describe('getRegistryRepository', () => { it('handles local registries', () => { const res = docker.getRegistryRepository('registry:5000/org/package', []); @@ -27,57 +43,66 @@ describe('api/docker', () => { }); }); describe('getDigest', () => { - beforeEach(() => { - jest.resetAllMocks(); - hostRules.find.mockReturnValue({ - username: 'some-username', - password: 'some-password', - }); - hostRules.hosts = jest.fn(() => []); - }); it('returns null if no token', async () => { - got.mockReturnValueOnce({ body: {} }); + httpMock + .scope(baseUrl) + .get('/') + .reply(200, '', {}) + .get('/library/some-dep/manifests/some-new-value') + .reply(401); const res = await docker.getDigest( { lookupName: 'some-dep' }, 'some-new-value' ); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null if errored', async () => { - got.mockReturnValueOnce({ body: { token: 'some-token' } }); + httpMock + .scope(baseUrl) + .get('/') + .reply(200, { token: 'some-token' }) + .get('/library/some-dep/manifests/some-new-value') + .replyWithError('error'); const res = await docker.getDigest( { lookupName: 'some-dep' }, 'some-new-value' ); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns digest', async () => { - got.mockReturnValueOnce({ - headers: { + httpMock + .scope(baseUrl) + .get('/') + .reply(200, '', { 'www-authenticate': 'Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull "', - }, - }); - got.mockReturnValueOnce({ body: { token: 'some-token' } }); - got.mockReturnValueOnce({ - headers: { 'docker-content-digest': 'some-digest' }, - }); + }) + .get('/library/some-dep/manifests/latest') + .reply(200, {}, { 'docker-content-digest': 'some-digest' }); + httpMock + .scope(authUrl) + .get( + '/token?service=registry.docker.io&scope=repository:library/some-dep:pull' + ) + .reply(200, { token: 'some-token' }); const res = await docker.getDigest({ lookupName: 'some-dep' }); expect(res).toBe('some-digest'); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('falls back to body for digest', async () => { - got.mockReturnValueOnce({ - headers: { + httpMock + .scope(baseUrl) + .get('/') + .reply(200, '', { 'www-authenticate': 'Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull "', - }, - }); - got.mockReturnValueOnce({ body: { token: 'some-token' } }); - got.mockReturnValueOnce({ - headers: { - 'content-type': 'text/plain', - }, - body: `{ + }) + .get('/library/some-dep/manifests/some-new-value') + .reply( + 200, + `{ "signatures": [ { "header": { @@ -95,7 +120,16 @@ describe('api/docker', () => { } ] }`, - }); + { + 'content-type': 'text/plain', + } + ); + httpMock + .scope(authUrl) + .get( + '/token?service=registry.docker.io&scope=repository:library/some-dep:pull' + ) + .reply(200, { token: 'some-token' }); const res = await docker.getDigest( { lookupName: 'some-dep' }, 'some-new-value' @@ -103,60 +137,69 @@ describe('api/docker', () => { expect(res).toBe( 'sha256:b3d6068234f3a18ebeedd2dab81e67b6a192e81192a099df4112ecfc7c3be84f' ); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('supports docker insecure registry', async () => { - got.mockReturnValueOnce({ - headers: {}, - }); - got.mockReturnValueOnce({ - headers: { 'docker-content-digest': 'some-digest' }, - }); + httpMock + .scope(baseUrl.replace('https', 'http')) + .get('/') + .reply(200, '', {}) + .get('/library/some-dep/manifests/latest') + .reply(200, '', { 'docker-content-digest': 'some-digest' }); hostRules.find.mockReturnValueOnce({ insecureRegistry: true }); const res = await docker.getDigest({ lookupName: 'some-dep' }); expect(res).toBe('some-digest'); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('supports basic authentication', async () => { - got.mockReturnValueOnce({ - headers: { + httpMock + .scope(baseUrl) + .get('/') + .reply(200, '', { 'www-authenticate': 'Basic realm="My Private Docker Registry Server"', - }, - }); - got.mockReturnValueOnce({ - statusCode: 200, - }); - got.mockReturnValueOnce({ - headers: { 'docker-content-digest': 'some-digest' }, - }); + }) + .get('/') + .reply(200) + .get('/library/some-dep/manifests/some-tag') + .reply(200, '', { 'docker-content-digest': 'some-digest' }); const res = await docker.getDigest( { lookupName: 'some-dep' }, 'some-tag' ); - expect(got.mock.calls[1][1].headers.authorization).toBe( + const trace = httpMock.getTrace(); + expect(res).toBe('some-digest'); + expect(trace[1].headers.authorization).toBe( 'Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk' ); - expect(res).toBe('some-digest'); + expect(trace).toMatchSnapshot(); }); it('returns null for 403 with basic authentication', async () => { - got.mockReturnValueOnce({ - headers: { + httpMock + .scope(baseUrl) + .get('/') + .reply(200, '', { 'www-authenticate': 'Basic realm="My Private Docker Registry Server"', - }, - }); - got.mockReturnValueOnce({ - statusCode: 403, - }); + }) + .get('/') + .reply(403); const res = await docker.getDigest( { lookupName: 'some-dep' }, 'some-tag' ); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('supports ECR authentication', async () => { - got.mockReturnValueOnce({ - headers: { + httpMock + .scope(amazonUrl) + .get('/') + .reply(200, '', { 'www-authenticate': 'Basic realm="My Private Docker Registry Server"', - }, - }); + }) + .get('/') + .reply(200) + .get('/node/manifests/some-tag') + .reply(200, '', { 'docker-content-digest': 'some-digest' }); AWSMock.setSDKInstance(AWS); AWSMock.mock( 'ECR', @@ -167,26 +210,25 @@ describe('api/docker', () => { }); } ); - got.mockReturnValueOnce({ - statusCode: 200, - }); - got.mockReturnValueOnce({ - headers: { 'docker-content-digest': 'some-digest' }, - }); const res = await docker.getDigest( { lookupName: '123456789.dkr.ecr.us-east-1.amazonaws.com/node' }, 'some-tag' ); - expect(got.mock.calls[1][1].headers.authorization).toBe('Basic abcdef'); + const trace = httpMock.getTrace(); expect(res).toBe('some-digest'); + expect(trace[1].headers.authorization).toBe('Basic abcdef'); + expect(trace).toMatchSnapshot(); AWSMock.restore('ECR'); }); it('continues without token if ECR authentication could not be extracted', async () => { - got.mockReturnValueOnce({ - headers: { + httpMock + .scope(amazonUrl) + .get('/') + .reply(200, '', { 'www-authenticate': 'Basic realm="My Private Docker Registry Server"', - }, - }); + }) + .get('/') + .reply(403); AWSMock.setSDKInstance(AWS); AWSMock.mock( 'ECR', @@ -195,22 +237,23 @@ describe('api/docker', () => { callback(null, {}); } ); - got.mockReturnValueOnce({ - statusCode: 403, - }); const res = await docker.getDigest( { lookupName: '123456789.dkr.ecr.us-east-1.amazonaws.com/node' }, 'some-tag' ); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); AWSMock.restore('ECR'); }); it('continues without token if ECR authentication fails', async () => { - got.mockReturnValueOnce({ - headers: { + httpMock + .scope(amazonUrl) + .get('/') + .reply(200, '', { 'www-authenticate': 'Basic realm="My Private Docker Registry Server"', - }, - }); + }) + .get('/') + .reply(403); AWSMock.setSDKInstance(AWS); AWSMock.mock( 'ECR', @@ -219,90 +262,104 @@ describe('api/docker', () => { callback(Error('some error'), null); } ); - got.mockReturnValueOnce({ - statusCode: 403, - }); const res = await docker.getDigest( { lookupName: '123456789.dkr.ecr.us-east-1.amazonaws.com/node' }, 'some-tag' ); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); AWSMock.restore('ECR'); }); it('continues without token, when no header is present', async () => { - got.mockReturnValueOnce({ - headers: { + httpMock + .scope(baseUrl) + .get('/') + .reply(200, '', { 'content-type': 'text/plain', - }, - }); - got.mockReturnValueOnce({ - headers: { 'docker-content-digest': 'some-digest' }, - }); + }) + .get('/library/some-dep/manifests/some-new-value') + .reply(200, {}, { 'docker-content-digest': 'some-digest' }); const res = await docker.getDigest( { lookupName: 'some-dep' }, 'some-new-value' ); expect(res).toBe('some-digest'); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('supports scoped names', async () => { - got.mockReturnValueOnce({ - headers: { + httpMock + .scope(baseUrl) + .get('/') + .reply(200, '', { 'www-authenticate': 'Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull "', - }, - }); - got.mockReturnValueOnce({ body: { token: 'some-token' } }); - got.mockReturnValueOnce({ - headers: { 'docker-content-digest': 'some-digest' }, - }); + }) + .get('/library/some-other-dep/manifests/8.0.0-alpine') + .reply(200, {}, { 'docker-content-digest': 'some-digest' }); + httpMock + .scope(authUrl) + .get( + '/token?service=registry.docker.io&scope=repository:library/some-other-dep:pull' + ) + .reply(200, { token: 'some-token' }); const res = await docker.getDigest( { lookupName: 'some-other-dep' }, '8.0.0-alpine' ); expect(res).toBe('some-digest'); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('should throw error for 429', async () => { - got.mockRejectedValueOnce({ statusCode: 429 }); + httpMock.scope(baseUrl).get('/').replyWithError({ statusCode: 429 }); await expect( docker.getDigest({ lookupName: 'some-dep' }, 'latest') ).rejects.toThrow(Error(DATASOURCE_FAILURE)); }); it('should throw error for 5xx', async () => { - got.mockRejectedValueOnce({ statusCode: 503 }); + httpMock.scope(baseUrl).get('/').replyWithError({ statusCode: 504 }); await expect( docker.getDigest({ lookupName: 'some-dep' }, 'latest') ).rejects.toThrow(Error(DATASOURCE_FAILURE)); }); }); describe('getReleases', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); it('returns null if no token', async () => { - got.mockReturnValueOnce({ body: {} }); + httpMock + .scope(baseUrl) + .get('/') + .reply(200, '', {}) + .get('/library/node/tags/list?n=10000') + .reply(401); const res = await getPkgReleases({ datasource: docker.id, depName: 'node', }); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('uses custom registry with registryUrls', async () => { const tags = ['1.0.0']; - got.mockReturnValueOnce({ - headers: {}, - }); - got.mockReturnValueOnce({ - headers: { - link: - '; rel="next", ', - }, - body: { tags }, - }); - got.mockReturnValueOnce({ headers: {}, body: { tags: ['latest'] } }); - got.mockReturnValueOnce({ - headers: {}, - }); - got.mockReturnValueOnce({ headers: {}, body: {} }); + httpMock + .scope('https://registry.company.com/v2') + .get('/') + .reply(200, '', {}) + .get('/node/tags/list?n=10000') + .reply( + 200, + { tags }, + { + link: + '; rel="next", ', + } + ) + .get('/') + .reply(200) + .get('/node/manifests/latest') + .reply(200); + httpMock + .scope('https://api.github.com') + .get('/user/9287/repos?page=3&per_page=100') + .reply(200, { tags: ['latest'] }, {}); const config = { datasource: docker.id, depName: 'node', @@ -310,107 +367,141 @@ describe('api/docker', () => { }; const res = await getPkgReleases(config); expect(res.releases).toHaveLength(1); - expect(got.mock.calls).toMatchSnapshot(); - expect(got.mock.calls[0][0].startsWith(config.registryUrls[0])).toBe( - true - ); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('uses custom registry in depName', async () => { const tags = ['1.0.0']; - got.mockReturnValueOnce({ - headers: {}, - }); - got.mockReturnValueOnce({ headers: {}, body: { tags } }); + httpMock + .scope('https://registry.company.com/v2') + .get('/') + .reply(200, '', {}) + .get('/node/tags/list?n=10000') + .reply(200, { tags }, {}) + .get('/') + .reply(200, '', {}) + .get('/node/manifests/1.0.0') + .reply(200, '', {}); const res = await getPkgReleases({ datasource: docker.id, depName: 'registry.company.com/node', }); expect(res.releases).toHaveLength(1); - expect(got).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('uses lower tag limit for ECR deps', async () => { - got.mockReturnValueOnce({ headers: {} }); - got.mockReturnValueOnce({ headers: {}, body: {} }); + httpMock + .scope(amazonUrl) + .get('/') + .reply(200, '', {}) + // The tag limit parameter `n` needs to be limited to 1000 for ECR + // See https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_DescribeRepositories.html#ECR-DescribeRepositories-request-maxResults + .get('/node/tags/list?n=1000') + .reply(200, {}, {}) + .get('/') + .reply(200, '', {}) + .get('/node/manifests/undefined') + .reply(200); await getPkgReleases({ datasource: docker.id, depName: '123456789.dkr.ecr.us-east-1.amazonaws.com/node', }); - // The tag limit parameter `n` needs to be limited to 1000 for ECR - // See https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_DescribeRepositories.html#ECR-DescribeRepositories-request-maxResults - expect(got.mock.calls[1][0]).toEqual( - 'https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2/node/tags/list?n=1000' - ); - expect(got).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('adds library/ prefix for Docker Hub (implicit)', async () => { const tags = ['1.0.0']; - got.mockReturnValueOnce({ - headers: { + httpMock + .scope(baseUrl) + .get('/') + .reply(200, '', { 'www-authenticate': 'Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:library/node:pull "', - }, - }); - got.mockReturnValueOnce({ headers: {}, body: { token: 'some-token ' } }); - got.mockReturnValueOnce({ headers: {}, body: { tags } }); - got.mockReturnValueOnce({ - headers: {}, - }); - got.mockReturnValueOnce({ headers: {}, body: {} }); + }) + .get('/library/node/tags/list?n=10000') + .reply(200, { tags }, {}) + .get('/') + .reply(200) + .get('/library/node/manifests/1.0.0') + .reply(200); + httpMock + .scope(authUrl) + .get( + '/token?service=registry.docker.io&scope=repository:library/node:pull' + ) + .reply(200, { token: 'some-token ' }); const res = await getPkgReleases({ datasource: docker.id, depName: 'node', }); expect(res.releases).toHaveLength(1); - expect(got).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('adds library/ prefix for Docker Hub (explicit)', async () => { const tags = ['1.0.0']; - got.mockReturnValueOnce({ - headers: { + httpMock + .scope(baseUrl) + .get('/') + .reply(200, '', { 'www-authenticate': 'Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:library/node:pull "', - }, - }); - got.mockReturnValueOnce({ headers: {}, body: { token: 'some-token ' } }); - got.mockReturnValueOnce({ headers: {}, body: { tags } }); - got.mockReturnValueOnce({ - headers: {}, - }); - got.mockReturnValueOnce({ headers: {}, body: {} }); + }) + .get('/library/node/tags/list?n=10000') + .reply(200, { tags }, {}) + .get('/') + .reply(200) + .get('/library/node/manifests/1.0.0') + .reply(200); + httpMock + .scope(authUrl) + .get( + '/token?service=registry.docker.io&scope=repository:library/node:pull' + ) + .reply(200, { token: 'some-token ' }); const res = await getPkgReleases({ datasource: docker.id, depName: 'docker.io/node', }); expect(res.releases).toHaveLength(1); - expect(got).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('adds no library/ prefix for other registries', async () => { const tags = ['1.0.0']; - got.mockReturnValueOnce({ - headers: { + httpMock + .scope('https://k8s.gcr.io/v2/') + .get('/') + .reply(200, '', { 'www-authenticate': 'Bearer realm="https://k8s.gcr.io/v2/token",service="k8s.gcr.io"', - }, - }); - got.mockReturnValueOnce({ headers: {}, body: { token: 'some-token ' } }); - got.mockReturnValueOnce({ headers: {}, body: { tags } }); - got.mockReturnValueOnce({ - headers: {}, - }); - got.mockReturnValueOnce({ headers: {}, body: {} }); + }) + .get( + '/token?service=k8s.gcr.io&scope=repository:kubernetes-dashboard-amd64:pull' + ) + .reply(200, { token: 'some-token ' }) + .get('/kubernetes-dashboard-amd64/tags/list?n=10000') + .reply(200, { tags }, {}) + .get('/') + .reply(200) + .get('/kubernetes-dashboard-amd64/manifests/1.0.0') + .reply(200); const res = await getPkgReleases({ datasource: docker.id, depName: 'k8s.gcr.io/kubernetes-dashboard-amd64', }); + httpMock.getTrace(); expect(res.releases).toHaveLength(1); - expect(got).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null on error', async () => { - got.mockReturnValueOnce({}); + httpMock + .scope(baseUrl) + .get('/') + .reply(200, null) + .get('/my/node/tags/list?n=10000') + .replyWithError('error'); const res = await docker.getReleases({ lookupName: 'my/node', }); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); }); }); diff --git a/lib/datasource/galaxy/__snapshots__/index.spec.ts.snap b/lib/datasource/galaxy/__snapshots__/index.spec.ts.snap index 21b827a2d9f9b3..1ecdb5c221d5cd 100644 --- a/lib/datasource/galaxy/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/galaxy/__snapshots__/index.spec.ts.snap @@ -21,4 +21,130 @@ Object { } `; +exports[`datasource/galaxy getReleases processes real data 2`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "galaxy.ansible.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://galaxy.ansible.com/api/v1/roles/?owner__username=yatesr&name=timezone", + }, +] +`; + +exports[`datasource/galaxy getReleases return null if searching random username and project name 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "galaxy.ansible.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://galaxy.ansible.com/api/v1/roles/?owner__username=foo&name=bar", + }, +] +`; + +exports[`datasource/galaxy getReleases returns null for 404 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "galaxy.ansible.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://galaxy.ansible.com/api/v1/roles/?owner__username=some_crate&name=undefined", + }, +] +`; + +exports[`datasource/galaxy getReleases returns null for empty list 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "galaxy.ansible.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://galaxy.ansible.com/api/v1/roles/?owner__username=non_existent_crate&name=undefined", + }, +] +`; + +exports[`datasource/galaxy getReleases returns null for empty result 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "galaxy.ansible.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://galaxy.ansible.com/api/v1/roles/?owner__username=non_existent_crate&name=undefined", + }, +] +`; + +exports[`datasource/galaxy getReleases returns null for missing fields 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "galaxy.ansible.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://galaxy.ansible.com/api/v1/roles/?owner__username=non_existent_crate&name=undefined", + }, +] +`; + +exports[`datasource/galaxy getReleases returns null for unknown error 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "galaxy.ansible.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://galaxy.ansible.com/api/v1/roles/?owner__username=some_crate&name=undefined", + }, +] +`; + exports[`datasource/galaxy getReleases throws for 5xx 1`] = `[Error: registry-failure]`; + +exports[`datasource/galaxy getReleases throws for 5xx 2`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "galaxy.ansible.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://galaxy.ansible.com/api/v1/roles/?owner__username=some_crate&name=undefined", + }, +] +`; + +exports[`datasource/galaxy getReleases throws for 404 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "galaxy.ansible.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://galaxy.ansible.com/api/v1/roles/?owner__username=foo&name=bar", + }, +] +`; diff --git a/lib/datasource/galaxy/index.spec.ts b/lib/datasource/galaxy/index.spec.ts index 535f956a9dacf5..046fe2a05df8f2 100644 --- a/lib/datasource/galaxy/index.spec.ts +++ b/lib/datasource/galaxy/index.spec.ts @@ -1,10 +1,8 @@ import fs from 'fs'; +import * as httpMock from '../../../test/httpMock'; -import _got from '../../util/got'; import { getReleases } from './index'; -const got: any = _got; - const res1 = fs.readFileSync( 'lib/datasource/galaxy/__fixtures__/timezone', 'utf8' @@ -14,68 +12,90 @@ const empty = fs.readFileSync( 'utf8' ); -jest.mock('../../util/got'); +const baseUrl = 'https://galaxy.ansible.com/'; describe('datasource/galaxy', () => { describe('getReleases', () => { + beforeEach(() => { + httpMock.setup(); + }); + + afterEach(() => { + httpMock.reset(); + }); + it('returns null for empty result', async () => { - got.mockReturnValueOnce(null); + httpMock + .scope(baseUrl) + .get('/api/v1/roles/?owner__username=non_existent_crate&name=undefined') + .reply(200); expect( await getReleases({ lookupName: 'non_existent_crate' }) ).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null for missing fields', async () => { - got.mockReturnValueOnce({ - body: undefined, - }); + httpMock + .scope(baseUrl) + .get('/api/v1/roles/?owner__username=non_existent_crate&name=undefined') + .reply(200, undefined); expect( await getReleases({ lookupName: 'non_existent_crate' }) ).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null for empty list', async () => { - got.mockReturnValueOnce({ - body: '\n', - }); + httpMock + .scope(baseUrl) + .get('/api/v1/roles/?owner__username=non_existent_crate&name=undefined') + .reply(200, '\n'); expect( await getReleases({ lookupName: 'non_existent_crate' }) ).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null for 404', async () => { - got.mockImplementationOnce(() => - Promise.reject({ - statusCode: 404, - }) - ); + httpMock + .scope(baseUrl) + .get('/api/v1/roles/?owner__username=some_crate&name=undefined') + .reply(404); expect(await getReleases({ lookupName: 'some_crate' })).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null for unknown error', async () => { - got.mockImplementationOnce(() => { - throw new Error(); - }); + httpMock + .scope(baseUrl) + .get('/api/v1/roles/?owner__username=some_crate&name=undefined') + .replyWithError('some unknown error'); expect(await getReleases({ lookupName: 'some_crate' })).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('processes real data', async () => { - got.mockReturnValueOnce({ - body: res1, - }); + httpMock + .scope(baseUrl) + .get('/api/v1/roles/?owner__username=yatesr&name=timezone') + .reply(200, res1); const res = await getReleases({ lookupName: 'yatesr.timezone' }); expect(res).toMatchSnapshot(); expect(res).not.toBeNull(); expect(res).toBeDefined(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('return null if searching random username and project name', async () => { - got.mockReturnValueOnce({ - body: empty, - }); + httpMock + .scope(baseUrl) + .get('/api/v1/roles/?owner__username=foo&name=bar') + .reply(200, empty); const res = await getReleases({ lookupName: 'foo.bar' }); + httpMock.getTrace(); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('throws for 5xx', async () => { - got.mockImplementationOnce(() => - Promise.reject({ - statusCode: 502, - }) - ); + httpMock + .scope(baseUrl) + .get('/api/v1/roles/?owner__username=some_crate&name=undefined') + .reply(502); let e; try { await getReleases({ lookupName: 'some_crate' }); @@ -84,14 +104,16 @@ describe('datasource/galaxy', () => { } expect(e).toBeDefined(); expect(e).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('throws for 404', async () => { - const err = new Error(); - err.statusCode = 404; - got.mockImplementationOnce(() => { - throw err; - }); - expect(await getReleases({ lookupName: 'foo.bar' })).toBeNull(); + httpMock + .scope(baseUrl) + .get('/api/v1/roles/?owner__username=foo&name=bar') + .reply(404); + const res = await getReleases({ lookupName: 'foo.bar' }); + expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); }); }); diff --git a/lib/datasource/go/__snapshots__/index.spec.ts.snap b/lib/datasource/go/__snapshots__/index.spec.ts.snap index 1e688a0e2ee5cb..0e266db3864f57 100644 --- a/lib/datasource/go/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/go/__snapshots__/index.spec.ts.snap @@ -1,5 +1,33 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`datasource/go getDigest returns digest 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "golang.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://golang.org/x/text?go-get=1", + }, +] +`; + +exports[`datasource/go getDigest returns null for wrong name 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "golang.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://golang.org/y/text?go-get=1", + }, +] +`; + exports[`datasource/go getReleases processes real data 1`] = ` Object { "releases": Array [ @@ -13,6 +41,90 @@ Object { } `; +exports[`datasource/go getReleases processes real data 2`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "golang.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://golang.org/x/text?go-get=1", + }, +] +`; + +exports[`datasource/go getReleases returns null for 404 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "golang.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://golang.org/foo/something?go-get=1", + }, +] +`; + +exports[`datasource/go getReleases returns null for empty result 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "golang.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://golang.org/foo/something?go-get=1", + }, +] +`; + +exports[`datasource/go getReleases returns null for unknown error 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "golang.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://golang.org/foo/something?go-get=1", + }, +] +`; + +exports[`datasource/go getReleases skips unsupported platform 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "golang.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://golang.org/x/text?go-get=1", + }, +] +`; + +exports[`datasource/go getReleases skips wrong package 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "golang.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://golang.org/x/sys?go-get=1", + }, +] +`; + exports[`datasource/go getReleases works for known servers 1`] = ` Array [ Array [ diff --git a/lib/datasource/go/index.spec.ts b/lib/datasource/go/index.spec.ts index 3ebb1a522e8f10..0dc24554ef7e4c 100644 --- a/lib/datasource/go/index.spec.ts +++ b/lib/datasource/go/index.spec.ts @@ -1,13 +1,11 @@ import { ReleaseResult } from '..'; +import * as httpMock from '../../../test/httpMock'; import { mocked, partial } from '../../../test/util'; -import _got from '../../util/got'; import * as _github from '../github-tags'; import * as go from '.'; -jest.mock('../../util/got'); jest.mock('../github-tags'); -const got: any = mocked(_got); const github = mocked(_github); const res1 = ` @@ -24,59 +22,78 @@ Nothing to see here; move along`; describe('datasource/go', () => { + beforeEach(() => { + httpMock.setup(); + }); + + afterEach(() => { + httpMock.reset(); + }); + describe('getDigest', () => { it('returns null for wrong name', async () => { - got.mockReturnValueOnce({ - body: res1, - }); + httpMock + .scope('https://golang.org/') + .get('/y/text?go-get=1') + .reply(200, res1); github.getDigest.mockResolvedValueOnce('abcdefabcdefabcdefabcdef'); const res = await go.getDigest({ lookupName: 'golang.org/y/text' }, null); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns digest', async () => { - got.mockReturnValueOnce({ - body: res1, - }); + httpMock + .scope('https://golang.org/') + .get('/x/text?go-get=1') + .reply(200, res1); github.getDigest.mockResolvedValueOnce('abcdefabcdefabcdefabcdef'); const res = await go.getDigest({ lookupName: 'golang.org/x/text' }, null); expect(res).toBe('abcdefabcdefabcdefabcdef'); + expect(httpMock.getTrace()).toMatchSnapshot(); }); }); describe('getReleases', () => { it('returns null for empty result', async () => { - got.mockReturnValueOnce(null); + httpMock + .scope('https://golang.org/') + .get('/foo/something?go-get=1') + .reply(200, res1); expect( await go.getReleases({ lookupName: 'golang.org/foo/something', }) ).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null for 404', async () => { - got.mockImplementationOnce(() => - Promise.reject({ - statusCode: 404, - }) - ); + httpMock + .scope('https://golang.org/') + .get('/foo/something?go-get=1') + .reply(404); expect( await go.getReleases({ lookupName: 'golang.org/foo/something', }) ).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null for unknown error', async () => { - got.mockImplementationOnce(() => { - throw new Error(); - }); + httpMock + .scope('https://golang.org/') + .get('/foo/something?go-get=1') + .replyWithError('error'); expect( await go.getReleases({ lookupName: 'golang.org/foo/something', }) ).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('processes real data', async () => { - got.mockReturnValueOnce({ - body: res1, - }); + httpMock + .scope('https://golang.org/') + .get('/x/text?go-get=1') + .reply(200, res1); github.getReleases.mockResolvedValueOnce({ releases: [{ version: 'v1.0.0' }, { version: 'v2.0.0' }], }); @@ -86,30 +103,37 @@ describe('datasource/go', () => { expect(res).toMatchSnapshot(); expect(res).not.toBeNull(); expect(res).toBeDefined(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('skips wrong package', async () => { - got.mockReturnValueOnce({ - body: res1, - }); + httpMock + .scope('https://golang.org/') + .get('/x/sys?go-get=1') + .reply(200, res1); const res = await go.getReleases({ lookupName: 'golang.org/x/sys', }); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('skips unsupported platform', async () => { - got.mockReturnValueOnce({ - body: res1.replace( - 'https://github.com/golang/text/', - 'https://google.com/golang/text/' - ), - }); + httpMock + .scope('https://golang.org/') + .get('/x/text?go-get=1') + .reply( + 200, + res1.replace( + 'https://github.com/golang/text/', + 'https://google.com/golang/text/' + ) + ); const res = await go.getReleases({ lookupName: 'golang.org/x/text', }); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('works for known servers', async () => { - got.mockClear(); github.getReleases.mockClear(); const packages = [ { lookupName: 'github.com/x/text' }, @@ -123,11 +147,9 @@ describe('datasource/go', () => { ); expect(await go.getReleases(pkg)).toEqual(githubRes); } - expect(got).toHaveBeenCalledTimes(0); expect(github.getReleases.mock.calls).toMatchSnapshot(); }); it('works for nested modules on github', async () => { - got.mockClear(); github.getReleases.mockClear(); const packages = [ { lookupName: 'github.com/x/text/a' }, @@ -143,11 +165,9 @@ describe('datasource/go', () => { expect(result.releases).toHaveLength(1); expect(result.releases[0].version.startsWith(prefix)).toBeFalse(); } - expect(got).toHaveBeenCalledTimes(0); expect(github.getReleases.mock.calls).toMatchSnapshot(); }); it('falls back to old behaviour', async () => { - got.mockClear(); github.getReleases.mockClear(); const packages = [ { lookupName: 'github.com/x/text/a' }, diff --git a/lib/datasource/gradle-version/__snapshots__/index.spec.ts.snap b/lib/datasource/gradle-version/__snapshots__/index.spec.ts.snap index d24fa476a09379..2b98af0ca7e1c4 100644 --- a/lib/datasource/gradle-version/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/gradle-version/__snapshots__/index.spec.ts.snap @@ -537,6 +537,31 @@ Object { } `; +exports[`datasource/gradle-version getReleases calls configured registryUrls 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "foo.bar", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://foo.bar/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "baz.qux", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "http://baz.qux/", + }, +] +`; + exports[`datasource/gradle-version getReleases processes real data 1`] = ` Object { "homepage": "https://gradle.org", @@ -809,3 +834,18 @@ Object { "sourceUrl": "https://github.com/gradle/gradle", } `; + +exports[`datasource/gradle-version getReleases processes real data 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "services.gradle.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://services.gradle.org/versions/all", + }, +] +`; diff --git a/lib/datasource/gradle-version/index.spec.ts b/lib/datasource/gradle-version/index.spec.ts index a839bd5b1e7b66..6f60d98f646c18 100644 --- a/lib/datasource/gradle-version/index.spec.ts +++ b/lib/datasource/gradle-version/index.spec.ts @@ -1,11 +1,7 @@ import fs from 'fs'; -import _got from '../../util/got'; +import * as httpMock from '../../../test/httpMock'; import * as gradleVersion from '.'; -jest.mock('../../util/got'); - -const got: any = _got; - const allResponse: any = fs.readFileSync( 'lib/datasource/gradle-version/__fixtures__/all.json' ); @@ -19,36 +15,44 @@ describe('datasource/gradle-version', () => { lookupName: 'abc', }; jest.clearAllMocks(); + httpMock.setup(); + }); + + afterEach(() => { + httpMock.reset(); }); it('processes real data', async () => { - got.mockReturnValueOnce({ - body: JSON.parse(allResponse), - }); + httpMock + .scope('https://services.gradle.org/') + .get('/versions/all') + .reply(200, JSON.parse(allResponse)); const res = await gradleVersion.getReleases(config); - expect(got).toHaveBeenCalledTimes(1); - expect(got.mock.calls[0][0]).toEqual( - 'https://services.gradle.org/versions/all' - ); expect(res).toMatchSnapshot(); expect(res).not.toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('calls configured registryUrls', async () => { - got.mockReturnValue({ - body: JSON.parse(allResponse), - }); + httpMock + .scope('https://foo.bar') + .get('/') + .reply(200, JSON.parse(allResponse)); + + httpMock + .scope('http://baz.qux') + .get('/') + .reply(200, JSON.parse(allResponse)); + const res = await gradleVersion.getReleases({ ...config, registryUrls: ['https://foo.bar', 'http://baz.qux'], }); - expect(got).toHaveBeenCalledTimes(2); - expect(got.mock.calls[0][0]).toEqual('https://foo.bar'); - expect(got.mock.calls[1][0]).toEqual('http://baz.qux'); // This will have every release duplicated, because we used the same // mocked data for both responses. expect(res).toMatchSnapshot(); expect(res).not.toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); }); }); diff --git a/lib/datasource/pypi/__snapshots__/index.spec.ts.snap b/lib/datasource/pypi/__snapshots__/index.spec.ts.snap index 0acd2fbda85a2f..70ac7523c9dba6 100644 --- a/lib/datasource/pypi/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/pypi/__snapshots__/index.spec.ts.snap @@ -9,6 +9,21 @@ Object { } `; +exports[`datasource/pypi getReleases find url from project_urls 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "pypi.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://pypi.org/pypi/flexget/json", + }, +] +`; + exports[`datasource/pypi getReleases process data from +simple endpoint 1`] = ` Object { "releases": Array [ @@ -49,6 +64,20 @@ Object { } `; +exports[`datasource/pypi getReleases process data from +simple endpoint 2`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "some.registry.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://some.registry.org/+simple/dj-database-url", + }, +] +`; + exports[`datasource/pypi getReleases process data from simple endpoint 1`] = ` Object { "releases": Array [ @@ -89,6 +118,20 @@ Object { } `; +exports[`datasource/pypi getReleases process data from simple endpoint 2`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "pypi.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://pypi.org/simple/dj-database-url", + }, +] +`; + exports[`datasource/pypi getReleases process data from simple endpoint with hyphens replaced with underscores 1`] = ` Object { "releases": Array [ @@ -99,6 +142,20 @@ Object { } `; +exports[`datasource/pypi getReleases process data from simple endpoint with hyphens replaced with underscores 2`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "pypi.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://pypi.org/simple/image-collector", + }, +] +`; + exports[`datasource/pypi getReleases processes real data 1`] = ` Object { "homepage": "https://github.com/Azure/azure-cli", @@ -196,6 +253,21 @@ Object { } `; +exports[`datasource/pypi getReleases processes real data 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "pypi.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://pypi.org/pypi/azure-cli-monitor/json", + }, +] +`; + exports[`datasource/pypi getReleases respects compatibility 1`] = ` Object { "releases": Array [ @@ -215,6 +287,21 @@ Object { } `; +exports[`datasource/pypi getReleases respects compatibility 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "pypi.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://pypi.org/pypi/doit/json", + }, +] +`; + exports[`datasource/pypi getReleases returns non-github home_page 1`] = ` Object { "homepage": "https://microsoft.com", @@ -222,81 +309,145 @@ Object { } `; +exports[`datasource/pypi getReleases returns non-github home_page 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "pypi.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://pypi.org/pypi/something/json", + }, +] +`; + +exports[`datasource/pypi getReleases returns null for 404 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "pypi.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://pypi.org/pypi/something/json", + }, +] +`; + +exports[`datasource/pypi getReleases returns null for 404 response from simple endpoint 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "pypi.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://pypi.org/simple/dj-database-url", + }, +] +`; + +exports[`datasource/pypi getReleases returns null for empty response 1`] = ` +Array [ + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "pypi.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://pypi.org/simple/dj-database-url", + }, +] +`; + +exports[`datasource/pypi getReleases returns null for empty result 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "pypi.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://pypi.org/pypi/something/json", + }, +] +`; + +exports[`datasource/pypi getReleases returns null if mismatched name 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "pypi.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://pypi.org/pypi/something/json", + }, +] +`; + exports[`datasource/pypi getReleases supports custom datasource url 1`] = ` Array [ - Array [ - "https://custom.pypi.net/foo/azure-cli-monitor/json", - Object { - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "pypi", - "json": true, - "method": "get", - }, - ], + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "custom.pypi.net", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://custom.pypi.net/foo/azure-cli-monitor/json", + }, ] `; exports[`datasource/pypi getReleases supports custom datasource url from environmental variable 1`] = ` Array [ - Array [ - "https://my.pypi.python/pypi/azure-cli-monitor/json", - Object { - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "pypi", - "json": true, - "method": "get", - }, - ], + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "my.pypi.python", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://my.pypi.python/pypi/azure-cli-monitor/json", + }, ] `; exports[`datasource/pypi getReleases supports multiple custom datasource urls 1`] = ` Array [ - Array [ - "https://custom.pypi.net/foo/azure-cli-monitor/json", - Object { - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "pypi", - "json": true, - "method": "get", - }, - ], - Array [ - "https://second-index/foo/azure-cli-monitor/json", - Object { - "headers": Object { - "user-agent": "https://github.com/renovatebot/renovate", - }, - "hooks": Object { - "beforeRedirect": Array [ - [Function], - ], - }, - "hostType": "pypi", - "json": true, - "method": "get", - }, - ], + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "custom.pypi.net", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://custom.pypi.net/foo/azure-cli-monitor/json", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "second-index", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://second-index/foo/azure-cli-monitor/json", + }, ] `; diff --git a/lib/datasource/pypi/index.spec.ts b/lib/datasource/pypi/index.spec.ts index 7268ca4b9a7870..87638f5f872a8a 100644 --- a/lib/datasource/pypi/index.spec.ts +++ b/lib/datasource/pypi/index.spec.ts @@ -1,11 +1,7 @@ import fs from 'fs'; -import _got from '../../util/got'; +import * as httpMock from '../../../test/httpMock'; import * as pypi from '.'; -jest.mock('../../util/got'); - -const got: any = _got; - const res1: any = fs.readFileSync( 'lib/datasource/pypi/__fixtures__/azure-cli-monitor.json' ); @@ -19,6 +15,8 @@ const mixedHyphensResponse = fs.readFileSync( 'lib/datasource/pypi/__fixtures__/versions-html-mixed-hyphens.html' ); +const baseUrl = 'https://pypi.org/pypi'; + describe('datasource/pypi', () => { describe('getReleases', () => { const OLD_ENV = process.env; @@ -26,47 +24,50 @@ describe('datasource/pypi', () => { beforeEach(() => { process.env = { ...OLD_ENV }; delete process.env.PIP_INDEX_URL; + httpMock.setup(); + jest.resetAllMocks(); }); afterEach(() => { process.env = OLD_ENV; + httpMock.reset(); }); - beforeEach(() => { - jest.resetAllMocks(); - }); it('returns null for empty result', async () => { - got.mockReturnValueOnce({}); + httpMock.scope(baseUrl).get('/something/json').reply(200); expect( await pypi.getReleases({ lookupName: 'something', }) ).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null for 404', async () => { - got.mockImplementationOnce(() => { - throw new Error(); - }); + httpMock.scope(baseUrl).get('/something/json').reply(404); expect( await pypi.getReleases({ lookupName: 'something', }) ).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('processes real data', async () => { - got.mockReturnValueOnce({ - body: JSON.parse(res1), - }); + httpMock + .scope(baseUrl) + .get('/azure-cli-monitor/json') + .reply(200, JSON.parse(res1)); expect( await pypi.getReleases({ lookupName: 'azure-cli-monitor', }) ).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('supports custom datasource url', async () => { - got.mockReturnValueOnce({ - body: JSON.parse(res1), - }); + httpMock + .scope('https://custom.pypi.net/foo') + .get('/azure-cli-monitor/json') + .reply(200, JSON.parse(res1)); const config = { registryUrls: ['https://custom.pypi.net/foo'], }; @@ -74,26 +75,30 @@ describe('datasource/pypi', () => { ...config, lookupName: 'azure-cli-monitor', }); - expect(got.mock.calls).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('supports custom datasource url from environmental variable', async () => { - got.mockReturnValueOnce({ - body: JSON.parse(res1), - }); + httpMock + .scope('https://my.pypi.python/pypi/') + .get('/azure-cli-monitor/json') + .reply(200, JSON.parse(res1)); const pipIndexUrl = process.env.PIP_INDEX_URL; process.env.PIP_INDEX_URL = 'https://my.pypi.python/pypi/'; await pypi.getReleases({ lookupName: 'azure-cli-monitor', }); - expect(got.mock.calls).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); process.env.PIP_INDEX_URL = pipIndexUrl; }); it('supports multiple custom datasource urls', async () => { - got - .mockImplementationOnce(() => { - throw new Error(); - }) - .mockImplementationOnce(() => ({ body: JSON.parse(res1) })); + httpMock + .scope('https://custom.pypi.net/foo') + .get('/azure-cli-monitor/json') + .replyWithError('error'); + httpMock + .scope('https://second-index/foo') + .get('/azure-cli-monitor/json') + .reply(200, JSON.parse(res1)); const config = { registryUrls: [ 'https://custom.pypi.net/foo', @@ -105,22 +110,24 @@ describe('datasource/pypi', () => { ...config, lookupName: 'azure-cli-monitor', }); - expect(got.mock.calls).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns non-github home_page', async () => { - got.mockReturnValueOnce({ - body: { + httpMock + .scope(baseUrl) + .get('/something/json') + .reply(200, { info: { name: 'something', home_page: 'https://microsoft.com', }, - }, - }); + }); expect( await pypi.getReleases({ lookupName: 'something', }) ).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('find url from project_urls', async () => { const info = { @@ -134,37 +141,38 @@ describe('datasource/pypi', () => { Repository: 'https://github.com/Flexget/Flexget', }, }; - got.mockReturnValueOnce({ - body: { - info, - }, - }); + httpMock.scope(baseUrl).get('/flexget/json').reply(200, { info }); const result = await pypi.getReleases({ lookupName: 'flexget', }); expect(result.sourceUrl).toBe(info.project_urls.Repository); expect(result.changelogUrl).toBe(info.project_urls.changelog); expect(result).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null if mismatched name', async () => { - got.mockReturnValueOnce({ - body: { + httpMock + .scope(baseUrl) + .get('/something/json') + .reply(200, { info: { name: 'something-else', home_page: 'https://microsoft.com', }, - }, - }); + }); expect( await pypi.getReleases({ lookupName: 'something', }) ).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('respects compatibility', async () => { - got.mockReturnValueOnce({ - body: { + httpMock + .scope(baseUrl) + .get('/doit/json') + .reply(200, { info: { name: 'doit', }, @@ -177,19 +185,20 @@ describe('datasource/pypi', () => { '0.31.1': [{ requires_python: '>=3.4' }], '0.4.0': [{ requires_python: '>=3.4' }, { requires_python: null }], }, - }, - }); + }); expect( await pypi.getReleases({ compatibility: { python: '2.7' }, lookupName: 'doit', }) ).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('process data from simple endpoint', async () => { - got.mockReturnValueOnce({ - body: htmlResponse + '', - }); + httpMock + .scope('https://pypi.org/simple/') + .get('/dj-database-url') + .reply(200, htmlResponse + ''); const config = { registryUrls: ['https://pypi.org/simple/'], }; @@ -200,11 +209,13 @@ describe('datasource/pypi', () => { lookupName: 'dj-database-url', }) ).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('process data from +simple endpoint', async () => { - got.mockReturnValueOnce({ - body: htmlResponse + '', - }); + httpMock + .scope('https://some.registry.org/+simple/') + .get('/dj-database-url') + .reply(200, htmlResponse + ''); const config = { registryUrls: ['https://some.registry.org/+simple/'], }; @@ -215,11 +226,13 @@ describe('datasource/pypi', () => { lookupName: 'dj-database-url', }) ).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('process data from simple endpoint with hyphens replaced with underscores', async () => { - got.mockReturnValueOnce({ - body: mixedHyphensResponse + '', - }); + httpMock + .scope('https://pypi.org/simple/') + .get('/image-collector') + .reply(200, mixedHyphensResponse + ''); const config = { registryUrls: ['https://pypi.org/simple/'], }; @@ -230,9 +243,13 @@ describe('datasource/pypi', () => { lookupName: 'image-collector', }) ).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null for empty response', async () => { - got.mockReturnValueOnce({}); + httpMock + .scope('https://pypi.org/simple/') + .get('/dj-database-url') + .reply(200); const config = { registryUrls: ['https://pypi.org/simple/'], }; @@ -243,11 +260,13 @@ describe('datasource/pypi', () => { lookupName: 'dj-database-url', }) ).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null for 404 response from simple endpoint', async () => { - got.mockImplementationOnce(() => { - throw new Error(); - }); + httpMock + .scope('https://pypi.org/simple/') + .get('/dj-database-url') + .replyWithError('error'); const config = { registryUrls: ['https://pypi.org/simple/'], }; @@ -258,11 +277,13 @@ describe('datasource/pypi', () => { lookupName: 'dj-database-url', }) ).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('returns null for response with no versions', async () => { - got.mockReturnValueOnce({ - body: badResponse + '', - }); + httpMock + .scope('https://pypi.org/simple/') + .get('/dj-database-url') + .reply(200, badResponse + ''); const config = { registryUrls: ['https://pypi.org/simple/'], }; diff --git a/lib/workers/pr/changelog/__snapshots__/release-notes.spec.ts.snap b/lib/workers/pr/changelog/__snapshots__/release-notes.spec.ts.snap index 1a39da0a4e4e30..b1d3afabd870af 100644 --- a/lib/workers/pr/changelog/__snapshots__/release-notes.spec.ts.snap +++ b/lib/workers/pr/changelog/__snapshots__/release-notes.spec.ts.snap @@ -57,6 +57,21 @@ Array [ ] `; +exports[`workers/pr/release-notes getReleaseList() should return release list for github repo 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/some/yet-other-repository/releases?per_page=100", + }, +] +`; + exports[`workers/pr/release-notes getReleaseList() should return release list for gitlab.com project 1`] = ` Array [ Object { @@ -74,6 +89,21 @@ Array [ ] `; +exports[`workers/pr/release-notes getReleaseList() should return release list for gitlab.com project 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "gitlab.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://gitlab.com/api/v4/projects/some%2fyet-other-repository/releases?per_page=100", + }, +] +`; + exports[`workers/pr/release-notes getReleaseNotes() gets release notes with body 1`] = ` Object { "body": "some body [#123](https://github.com/some/other-repository/issues/123), [#124](https://github.com/some/yet-other-repository/issues/124) @@ -86,6 +116,21 @@ Object { `; exports[`workers/pr/release-notes getReleaseNotes() gets release notes with body 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/some/other-repository/releases?per_page=100", + }, +] +`; + +exports[`workers/pr/release-notes getReleaseNotes() gets release notes with body 3`] = ` Object { "body": "some body [#123](https://github.com/some/other-repository/issues/123), [#124](https://github.com/some/yet-other-repository/issues/124) ", @@ -96,7 +141,22 @@ Object { } `; -exports[`workers/pr/release-notes getReleaseNotes() gets release notes with body 3`] = ` +exports[`workers/pr/release-notes getReleaseNotes() gets release notes with body 4`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/some/other-repository/releases?per_page=100", + }, +] +`; + +exports[`workers/pr/release-notes getReleaseNotes() gets release notes with body 5`] = ` Object { "body": "some body [#123](https://github.com/some/other-repository/issues/123), [#124](https://github.com/some/yet-other-repository/issues/124) ", @@ -107,12 +167,87 @@ Object { } `; +exports[`workers/pr/release-notes getReleaseNotes() gets release notes with body 6`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/some/other-repository/releases?per_page=100", + }, +] +`; + exports[`workers/pr/release-notes getReleaseNotes() gets release notes with body from gitlab repo 1`] = `null`; +exports[`workers/pr/release-notes getReleaseNotes() gets release notes with body from gitlab repo 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.gitlab.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.gitlab.com/projects/some%2fother-repository/releases?per_page=100", + }, +] +`; + exports[`workers/pr/release-notes getReleaseNotes() gets release notes with body from gitlab repo other- 1`] = `null`; +exports[`workers/pr/release-notes getReleaseNotes() gets release notes with body from gitlab repo other- 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.gitlab.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.gitlab.com/projects/some%2fother-repository/releases?per_page=100", + }, +] +`; + exports[`workers/pr/release-notes getReleaseNotes() gets release notes with body from gitlab repo v 1`] = `null`; +exports[`workers/pr/release-notes getReleaseNotes() gets release notes with body from gitlab repo v 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.gitlab.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.gitlab.com/projects/some%2fother-repository/releases?per_page=100", + }, +] +`; + +exports[`workers/pr/release-notes getReleaseNotes() should return null for release notes without body 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/some/repository/releases?per_page=100", + }, +] +`; + exports[`workers/pr/release-notes getReleaseNotesMd() ReleaseNotes Correctness parses adapter-utils 4.33.0 1`] = ` Object { "body": "- add new auth, fix accept header and base path in mock @@ -127,6 +262,31 @@ See merge request itentialopensource/adapter-utils!177 } `; +exports[`workers/pr/release-notes getReleaseNotesMd() ReleaseNotes Correctness parses adapter-utils 4.33.0 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "gitlab.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://gitlab.com/api/v4/projects/itentialopensource/adapter-utils/repository/tree/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "gitlab.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://gitlab.com/api/v4/projects/itentialopensource/adapter-utils/repository/files/CHANGELOG.md?ref=master", + }, +] +`; + exports[`workers/pr/release-notes getReleaseNotesMd() ReleaseNotes Correctness parses yargs 15.2.0 1`] = ` Object { "body": "##### ⚠ BREAKING CHANGES @@ -157,6 +317,31 @@ Object { } `; +exports[`workers/pr/release-notes getReleaseNotesMd() ReleaseNotes Correctness parses yargs 15.2.0 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/yargs/yargs/contents/", + }, + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/yargs/yargs/contents/CHANGELOG.md", + }, +] +`; + exports[`workers/pr/release-notes getReleaseNotesMd() ReleaseNotes Correctness parses yargs 15.3.0 1`] = ` Object { "body": "##### Features @@ -172,6 +357,96 @@ Object { } `; +exports[`workers/pr/release-notes getReleaseNotesMd() ReleaseNotes Correctness parses yargs 15.3.0 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/yargs/yargs/contents/", + }, + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/yargs/yargs/contents/CHANGELOG.md", + }, +] +`; + +exports[`workers/pr/release-notes getReleaseNotesMd() handles bad markdown 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/some/repository2/contents/", + }, + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/some/repository2/contents/CHANGELOG.md", + }, +] +`; + +exports[`workers/pr/release-notes getReleaseNotesMd() handles files mismatch 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/chalk/contents/", + }, +] +`; + +exports[`workers/pr/release-notes getReleaseNotesMd() handles wrong format 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/some/repository1/contents/", + }, + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/some/repository1/contents/CHANGELOG.md", + }, +] +`; + exports[`workers/pr/release-notes getReleaseNotesMd() parses angular.js 1`] = ` Object { "body": "#### Bug Fixes @@ -202,6 +477,31 @@ Object { } `; +exports[`workers/pr/release-notes getReleaseNotesMd() parses angular.js 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/angular/angular.js/contents/", + }, + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/angular/angular.js/contents/CHANGELOG.md", + }, +] +`; + exports[`workers/pr/release-notes getReleaseNotesMd() parses gitlab.com/gitlab-org/gitter/webapp 1`] = ` Object { "body": "- Removing markup from a part of the French translation, @@ -218,6 +518,31 @@ Object { } `; +exports[`workers/pr/release-notes getReleaseNotesMd() parses gitlab.com/gitlab-org/gitter/webapp 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.gitlab.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.gitlab.com/projects/gitlab-org/gitter/webapp/repository/tree/", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.gitlab.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.gitlab.com/projects/gitlab-org/gitter/webapp/repository/files/CHANGELOG.md?ref=master", + }, +] +`; + exports[`workers/pr/release-notes getReleaseNotesMd() parses jest 1`] = ` Object { "body": "##### Fixes @@ -410,6 +735,31 @@ Object { } `; +exports[`workers/pr/release-notes getReleaseNotesMd() parses jest 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/facebook/jest/contents/", + }, + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/facebook/jest/contents/CHANGELOG.md", + }, +] +`; + exports[`workers/pr/release-notes getReleaseNotesMd() parses js-yaml 1`] = ` Object { "body": "- Fix \`condenseFlow\` output (quote keys for sure, instead of spaces), [#371](https://github.com/nodeca/js-yaml/issues/371), [#370](https://github.com/nodeca/js-yaml/issues/370). @@ -418,3 +768,28 @@ Object { "url": "https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md#3100--2017-09-10", } `; + +exports[`workers/pr/release-notes getReleaseNotesMd() parses js-yaml 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/nodeca/js-yaml/contents/", + }, + Object { + "headers": Object { + "accept": "application/vnd.github.v3+json", + "accept-encoding": "gzip, deflate", + "host": "api.github.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.github.com/repos/nodeca/js-yaml/contents/CHANGELOG.md", + }, +] +`; diff --git a/lib/workers/pr/changelog/release-notes.spec.ts b/lib/workers/pr/changelog/release-notes.spec.ts index f966acef488444..6221246ebd5837 100644 --- a/lib/workers/pr/changelog/release-notes.spec.ts +++ b/lib/workers/pr/changelog/release-notes.spec.ts @@ -1,5 +1,5 @@ import fs from 'fs-extra'; -import got from '../../../util/got'; +import * as httpMock from '../../../../test/httpMock'; import { ChangeLogNotes } from './common'; import { addReleaseNotes, @@ -8,8 +8,6 @@ import { getReleaseNotesMd, } from './release-notes'; -const ghGot: jest.Mock> = got as never; - const angularJsChangelogMd = fs.readFileSync( 'lib/workers/pr/__fixtures__/angular.js.md', 'utf8' @@ -45,9 +43,15 @@ const contentsResponse = [ { name: 'README.md' }, ]; -jest.mock('../../../util/got'); - describe('workers/pr/release-notes', () => { + beforeEach(() => { + httpMock.setup(); + }); + + afterEach(() => { + httpMock.reset(); + }); + describe('addReleaseNotes()', () => { it('returns input if invalid', async () => { const input = { a: 1 }; @@ -76,45 +80,53 @@ describe('workers/pr/release-notes', () => { expect(res).toEqual([]); }); it('should return release list for github repo', async () => { - ghGot.mockResolvedValueOnce({ - body: [ + httpMock + .scope('https://api.github.com/') + .get('/repos/some/yet-other-repository/releases?per_page=100') + .reply(200, [ { tag_name: `v1.0.0` }, { tag_name: `v1.0.1`, body: 'some body #123, [#124](https://github.com/some/yet-other-repository/issues/124)', }, - ], - }); + ]); + const res = await getReleaseList( 'https://api.github.com/', 'some/yet-other-repository' ); expect(res).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('should return release list for gitlab.com project', async () => { - ghGot.mockResolvedValueOnce({ - body: [ + httpMock + .scope('https://gitlab.com/') + .get( + '/api/v4/projects/some%2fyet-other-repository/releases?per_page=100' + ) + .reply(200, [ { tag_name: `v1.0.0` }, { tag_name: `v1.0.1`, body: 'some body #123, [#124](https://gitlab.com/some/yet-other-repository/issues/124)', }, - ], - }); + ]); const res = await getReleaseList( 'https://gitlab.com/api/v4/', 'some/yet-other-repository' ); expect(res).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); }); describe('getReleaseNotes()', () => { it('should return null for release notes without body', async () => { - ghGot.mockResolvedValueOnce({ - body: [{ tag_name: 'v1.0.0' }, { tag_name: 'v1.0.1' }], - }); + httpMock + .scope('https://api.github.com/') + .get('/repos/some/repository/releases?per_page=100') + .reply(200, [{ tag_name: 'v1.0.0' }, { tag_name: 'v1.0.1' }]); const res = await getReleaseNotes( 'some/repository', '1.0.0', @@ -123,20 +135,22 @@ describe('workers/pr/release-notes', () => { 'https://api.github.com/' ); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it.each([[''], ['v'], ['other-']])( 'gets release notes with body', async (prefix) => { - ghGot.mockResolvedValueOnce({ - body: [ + httpMock + .scope('https://api.github.com/') + .get('/repos/some/other-repository/releases?per_page=100') + .reply(200, [ { tag_name: `${prefix}1.0.0` }, { tag_name: `${prefix}1.0.1`, body: 'some body #123, [#124](https://github.com/some/yet-other-repository/issues/124)', }, - ], - }); + ]); const res = await getReleaseNotes( 'some/other-repository', '1.0.1', @@ -145,21 +159,24 @@ describe('workers/pr/release-notes', () => { 'https://api.github.com/' ); expect(res).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); } ); it.each([[''], ['v'], ['other-']])( 'gets release notes with body from gitlab repo %s', async (prefix) => { - ghGot.mockResolvedValueOnce({ - body: [ + httpMock + .scope('https://api.gitlab.com/') + .get('/projects/some%2fother-repository/releases?per_page=100') + .reply(200, [ { tag_name: `${prefix}1.0.0` }, { tag_name: `${prefix}1.0.1`, body: 'some body #123, [#124](https://gitlab.com/some/yet-other-repository/issues/124)', }, - ], - }); + ]); + const res = await getReleaseNotes( 'some/other-repository', '1.0.1', @@ -168,6 +185,7 @@ describe('workers/pr/release-notes', () => { 'https://api.gitlab.com/' ); expect(res).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); } ); it.each([[''], ['v'], ['other-']])( @@ -195,9 +213,10 @@ describe('workers/pr/release-notes', () => { expect(res).toBeNull(); }); it('handles files mismatch', async () => { - ghGot.mockResolvedValueOnce({ - body: [{ name: 'lib' }, { name: 'README.md' }], - }); + httpMock + .scope('https://api.github.com') + .get('/repos/chalk/contents/') + .reply(200, [{ name: 'lib' }, { name: 'README.md' }]); const res = await getReleaseNotesMd( 'chalk', '2.0.0', @@ -205,14 +224,16 @@ describe('workers/pr/release-notes', () => { 'https://api.github.com/' ); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('handles wrong format', async () => { - ghGot - .mockResolvedValueOnce({ body: contentsResponse }) - .mockResolvedValueOnce({ - body: { - content: Buffer.from('not really markdown').toString('base64'), - }, + httpMock + .scope('https://api.github.com') + .get('/repos/some/repository1/contents/') + .reply(200, contentsResponse) + .get('/repos/some/repository1/contents/CHANGELOG.md') + .reply(200, { + content: Buffer.from('not really markdown').toString('base64'), }); const res = await getReleaseNotesMd( 'some/repository1', @@ -221,14 +242,16 @@ describe('workers/pr/release-notes', () => { 'https://api.github.com/' ); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('handles bad markdown', async () => { - ghGot - .mockResolvedValueOnce({ body: contentsResponse }) - .mockResolvedValueOnce({ - body: { - content: Buffer.from(`#\nha\nha\n#\nha\nha`).toString('base64'), - }, + httpMock + .scope('https://api.github.com') + .get('/repos/some/repository2/contents/') + .reply(200, contentsResponse) + .get('/repos/some/repository2/contents/CHANGELOG.md') + .reply(200, { + content: Buffer.from(`#\nha\nha\n#\nha\nha`).toString('base64'), }); const res = await getReleaseNotesMd( 'some/repository2', @@ -237,14 +260,16 @@ describe('workers/pr/release-notes', () => { 'https://api.github.com/' ); expect(res).toBeNull(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('parses angular.js', async () => { - ghGot - .mockResolvedValueOnce({ body: contentsResponse }) - .mockResolvedValueOnce({ - body: { - content: Buffer.from(angularJsChangelogMd).toString('base64'), - }, + httpMock + .scope('https://api.github.com') + .get('/repos/angular/angular.js/contents/') + .reply(200, contentsResponse) + .get('/repos/angular/angular.js/contents/CHANGELOG.md') + .reply(200, { + content: Buffer.from(angularJsChangelogMd).toString('base64'), }); const res = await getReleaseNotesMd( 'angular/angular.js', @@ -254,14 +279,18 @@ describe('workers/pr/release-notes', () => { ); expect(res).not.toBeNull(); expect(res).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('parses gitlab.com/gitlab-org/gitter/webapp', async () => { - ghGot - .mockResolvedValueOnce({ body: contentsResponse }) - .mockResolvedValueOnce({ - body: { - content: Buffer.from(gitterWebappChangelogMd).toString('base64'), - }, + httpMock + .scope('https://api.gitlab.com/') + .get('/projects/gitlab-org/gitter/webapp/repository/tree/') + .reply(200, contentsResponse) + .get( + '/projects/gitlab-org/gitter/webapp/repository/files/CHANGELOG.md?ref=master' + ) + .reply(200, { + content: Buffer.from(gitterWebappChangelogMd).toString('base64'), }); const res = await getReleaseNotesMd( 'gitlab-org/gitter/webapp', @@ -269,16 +298,19 @@ describe('workers/pr/release-notes', () => { 'https://gitlab.com/', 'https://api.gitlab.com/' ); + httpMock.getTrace(); expect(res).not.toBeNull(); expect(res).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('parses jest', async () => { - ghGot - .mockResolvedValueOnce({ body: contentsResponse }) - .mockResolvedValueOnce({ - body: { - content: Buffer.from(jestChangelogMd).toString('base64'), - }, + httpMock + .scope('https://api.github.com') + .get('/repos/facebook/jest/contents/') + .reply(200, contentsResponse) + .get('/repos/facebook/jest/contents/CHANGELOG.md') + .reply(200, { + content: Buffer.from(jestChangelogMd).toString('base64'), }); const res = await getReleaseNotesMd( 'facebook/jest', @@ -288,14 +320,16 @@ describe('workers/pr/release-notes', () => { ); expect(res).not.toBeNull(); expect(res).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('parses js-yaml', async () => { - ghGot - .mockResolvedValueOnce({ body: contentsResponse }) - .mockResolvedValueOnce({ - body: { - content: Buffer.from(jsYamlChangelogMd).toString('base64'), - }, + httpMock + .scope('https://api.github.com') + .get('/repos/nodeca/js-yaml/contents/') + .reply(200, contentsResponse) + .get('/repos/nodeca/js-yaml/contents/CHANGELOG.md') + .reply(200, { + content: Buffer.from(jsYamlChangelogMd).toString('base64'), }); const res = await getReleaseNotesMd( 'nodeca/js-yaml', @@ -305,17 +339,19 @@ describe('workers/pr/release-notes', () => { ); expect(res).not.toBeNull(); expect(res).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); describe('ReleaseNotes Correctness', () => { let versionOneNotes: ChangeLogNotes; let versionTwoNotes: ChangeLogNotes; it('parses yargs 15.3.0', async () => { - ghGot - .mockResolvedValueOnce({ body: contentsResponse }) - .mockResolvedValueOnce({ - body: { - content: Buffer.from(yargsChangelogMd).toString('base64'), - }, + httpMock + .scope('https://api.github.com') + .get('/repos/yargs/yargs/contents/') + .reply(200, contentsResponse) + .get('/repos/yargs/yargs/contents/CHANGELOG.md') + .reply(200, { + content: Buffer.from(yargsChangelogMd).toString('base64'), }); const res = await getReleaseNotesMd( 'yargs/yargs', @@ -326,14 +362,16 @@ describe('workers/pr/release-notes', () => { versionOneNotes = res; expect(res).not.toBeNull(); expect(res).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('parses yargs 15.2.0', async () => { - ghGot - .mockResolvedValueOnce({ body: contentsResponse }) - .mockResolvedValueOnce({ - body: { - content: Buffer.from(yargsChangelogMd).toString('base64'), - }, + httpMock + .scope('https://api.github.com') + .get('/repos/yargs/yargs/contents/') + .reply(200, contentsResponse) + .get('/repos/yargs/yargs/contents/CHANGELOG.md') + .reply(200, { + content: Buffer.from(yargsChangelogMd).toString('base64'), }); const res = await getReleaseNotesMd( 'yargs/yargs', @@ -344,14 +382,20 @@ describe('workers/pr/release-notes', () => { versionTwoNotes = res; expect(res).not.toBeNull(); expect(res).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('parses adapter-utils 4.33.0', async () => { - ghGot - .mockResolvedValueOnce({ body: contentsResponse }) - .mockResolvedValueOnce({ - body: { - content: Buffer.from(adapterutilsChangelogMd).toString('base64'), - }, + httpMock + .scope('https://gitlab.com/') + .get( + '/api/v4/projects/itentialopensource/adapter-utils/repository/tree/' + ) + .reply(200, contentsResponse) + .get( + '/api/v4/projects/itentialopensource/adapter-utils/repository/files/CHANGELOG.md?ref=master' + ) + .reply(200, { + content: Buffer.from(adapterutilsChangelogMd).toString('base64'), }); const res = await getReleaseNotesMd( 'itentialopensource/adapter-utils', @@ -362,6 +406,7 @@ describe('workers/pr/release-notes', () => { versionTwoNotes = res; expect(res).not.toBeNull(); expect(res).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); }); it('isUrl', () => { expect(versionOneNotes).not.toMatchObject(versionTwoNotes);