From 02eafc70919d8b149457cc91aa1905d4fd319839 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 3 Oct 2020 15:10:34 +0200 Subject: [PATCH] backend-common: refactor BitbucketUrlReader test and exported symbols --- .../src/reading/BitbucketUrlReader.test.ts | 257 +++++++++--------- .../src/reading/BitbucketUrlReader.ts | 37 ++- 2 files changed, 140 insertions(+), 154 deletions(-) diff --git a/packages/backend-common/src/reading/BitbucketUrlReader.test.ts b/packages/backend-common/src/reading/BitbucketUrlReader.test.ts index 43f797edd4..c3e61fb821 100644 --- a/packages/backend-common/src/reading/BitbucketUrlReader.test.ts +++ b/packages/backend-common/src/reading/BitbucketUrlReader.test.ts @@ -14,151 +14,140 @@ * limitations under the License. */ -import { BitbucketUrlReader, readConfig } from './BitbucketUrlReader'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; import { ConfigReader } from '@backstage/config'; +import { getVoidLogger } from '../logging'; +import { BitbucketUrlReader } from './BitbucketUrlReader'; -const host = 'bitbucket.org'; +const logger = getVoidLogger(); describe('BitbucketUrlReader', () => { - const createConfig = ( - username: string | undefined, - appPassword: string | undefined, - ) => - ConfigReader.fromConfigs([ + const worker = setupServer(); + + beforeAll(() => worker.listen({ onUnhandledRequest: 'error' })); + afterAll(() => worker.close()); + + beforeEach(() => { + worker.use( + rest.get('*', (req, res, ctx) => + res( + ctx.status(200), + ctx.json({ + url: req.url.toString(), + headers: req.headers.getAllHeaders(), + }), + ), + ), + ); + }); + afterEach(() => worker.resetHandlers()); + + const createConfig = (username?: string, appPassword?: string) => + new ConfigReader( { - context: '', - data: { - integrations: { - bitbucket: [ - { - host, - username: username, - appPassword: appPassword, - }, - ], - }, + integrations: { + bitbucket: [ + { + host: 'bitbucket.org', + username: username, + appPassword: appPassword, + }, + ], }, }, - ]); + 'test-config', + ); - it('should build raw api', () => { - const processor = new BitbucketUrlReader({ host }); - - const tests = [ - { - target: - 'https://bitbucket.org/org-name/repo-name/src/master/templates/my-template.yaml', - url: new URL( + it.each([ + { + url: + 'https://bitbucket.org/org-name/repo-name/src/master/templates/my-template.yaml', + config: createConfig(), + response: expect.objectContaining({ + url: 'https://api.bitbucket.org/2.0/repositories/org-name/repo-name/src/master/templates/my-template.yaml', - ), - err: undefined, - }, - { - target: 'https://api.com/a/b/blob/master/path/to/c.yaml', - url: null, - err: - 'Incorrect url: https://api.com/a/b/blob/master/path/to/c.yaml, Error: Wrong Bitbucket URL or Invalid file path', - }, - { - target: 'com/a/b/blob/master/path/to/c.yaml', - url: null, - err: - 'Incorrect url: com/a/b/blob/master/path/to/c.yaml, TypeError: Invalid URL: com/a/b/blob/master/path/to/c.yaml', - }, - ]; + }), + }, + { + url: + 'https://bitbucket.org/org-name/repo-name/src/master/templates/my-template.yaml', + config: createConfig('some-user', 'my-secret'), + response: expect.objectContaining({ + headers: expect.objectContaining({ + authorization: 'Basic c29tZS11c2VyOm15LXNlY3JldA==', + }), + }), + }, + { + url: + 'https://bitbucket.org/org-name/repo-name/src/master/templates/my-template.yaml', + config: createConfig(), + response: expect.objectContaining({ + headers: expect.not.objectContaining({ + authorization: expect.anything(), + }), + }), + }, + { + url: + 'https://bitbucket.org/org-name/repo-name/src/master/templates/my-template.yaml', + config: createConfig(undefined, 'only-password-provided'), + response: expect.objectContaining({ + headers: expect.not.objectContaining({ + authorization: expect.anything(), + }), + }), + }, + ])('should handle happy path %#', async ({ url, config, response }) => { + const [{ reader }] = BitbucketUrlReader.factory({ config, logger }); - for (const test of tests) { - if (test.err) { - expect(() => processor.buildRawUrl(test.target)).toThrowError(test.err); - } else if (test.url) { - expect(processor.buildRawUrl(test.target).toString()).toEqual( - test.url.toString(), - ); - } else { - throw new Error( - 'This should not have happened. Either err or url should have matched.', - ); - } - } + const data = await reader.read(url); + const res = await JSON.parse(data.toString('utf-8')); + expect(res).toEqual(response); }); - it('should return request options', () => { - const tests = [ - { - username: '', - password: '', - expect: { - headers: {}, - }, - err: - "Invalid type in config for key 'integrations.bitbucket[0].username' in '', got empty-string, wanted string", - }, - { - username: 'only-user-provided', - password: '', - expect: { - headers: {}, - }, - err: - "Invalid type in config for key 'integrations.bitbucket[0].appPassword' in '', got empty-string, wanted string", - }, - { - username: '', - password: 'only-password-provided', - expect: { - headers: {}, - }, - err: - "Invalid type in config for key 'integrations.bitbucket[0].username' in '', got empty-string, wanted string", - }, - { - username: 'some-user', - password: 'my-secret', - expect: { - headers: { - Authorization: 'Basic c29tZS11c2VyOm15LXNlY3JldA==', - }, - }, - }, - { - username: undefined, - password: undefined, - expect: { - headers: {}, - }, - }, - { - username: 'only-user-provided', - password: undefined, - expect: { - headers: {}, - }, - err: - "Missing required config value at 'integrations.bitbucket[0].appPassword'", - }, - { - username: undefined, - password: 'only-password-provided', - expect: { - headers: {}, - }, - }, - ]; - - for (const test of tests) { - if (test.err) { - expect( - () => - new BitbucketUrlReader( - readConfig(createConfig(test.username, test.password))[0], - ), - ).toThrowError(test.err); - } else { - const processor = new BitbucketUrlReader( - readConfig(createConfig(test.username, test.password))[0], - ); - expect(processor.getRequestOptions()).toEqual(test.expect); - } - } + it.each([ + { + url: 'https://api.com/a/b/blob/master/path/to/c.yaml', + config: createConfig(), + error: + 'Incorrect url: https://api.com/a/b/blob/master/path/to/c.yaml, Error: Wrong Bitbucket URL or Invalid file path', + }, + { + url: 'com/a/b/blob/master/path/to/c.yaml', + config: createConfig(), + error: + 'Incorrect url: com/a/b/blob/master/path/to/c.yaml, TypeError: Invalid URL: com/a/b/blob/master/path/to/c.yaml', + }, + { + url: '', + config: createConfig('', ''), + error: + "Invalid type in config for key 'integrations.bitbucket[0].username' in 'test-config', got empty-string, wanted string", + }, + { + url: '', + config: createConfig('only-user-provided', ''), + error: + "Invalid type in config for key 'integrations.bitbucket[0].appPassword' in 'test-config', got empty-string, wanted string", + }, + { + url: '', + config: createConfig('', 'only-password-provided'), + error: + "Invalid type in config for key 'integrations.bitbucket[0].username' in 'test-config', got empty-string, wanted string", + }, + { + url: '', + config: createConfig('only-user-provided', undefined), + error: + "Missing required config value at 'integrations.bitbucket[0].appPassword'", + }, + ])('should handle error path %#', async ({ url, config, error }) => { + await expect(async () => { + const [{ reader }] = BitbucketUrlReader.factory({ config, logger }); + await reader.read(url); + }).rejects.toThrow(error); }); }); diff --git a/packages/backend-common/src/reading/BitbucketUrlReader.ts b/packages/backend-common/src/reading/BitbucketUrlReader.ts index 53966471af..3b5301af59 100644 --- a/packages/backend-common/src/reading/BitbucketUrlReader.ts +++ b/packages/backend-common/src/reading/BitbucketUrlReader.ts @@ -29,7 +29,7 @@ type Options = { }; }; -export function readConfig(config: Config): Options[] { +function readConfig(config: Config): Options[] { const optionsArr = Array(); const providerConfigs = @@ -74,23 +74,6 @@ export class BitbucketUrlReader implements UrlReader { } } - getRequestOptions(): RequestInit { - const headers: HeadersInit = {}; - - if (this.options.auth) { - headers.Authorization = `Basic ${Buffer.from( - `${this.options.auth.username}:${this.options.auth.appPassword}`, - 'utf8', - ).toString('base64')}`; - } - - const requestOptions: RequestInit = { - headers, - }; - - return requestOptions; - } - async read(url: string): Promise { const builtUrl = this.buildRawUrl(url); @@ -115,8 +98,7 @@ export class BitbucketUrlReader implements UrlReader { // Converts // from: https://bitbucket.org/orgname/reponame/src/master/file.yaml // to: https://api.bitbucket.org/2.0/repositories/orgname/reponame/src/master/file.yaml - - buildRawUrl(target: string): URL { + private buildRawUrl(target: string): URL { try { const url = new URL(target); @@ -159,6 +141,21 @@ export class BitbucketUrlReader implements UrlReader { } } + private getRequestOptions(): RequestInit { + const headers: HeadersInit = {}; + + if (this.options.auth) { + headers.Authorization = `Basic ${Buffer.from( + `${this.options.auth.username}:${this.options.auth.appPassword}`, + 'utf8', + ).toString('base64')}`; + } + + return { + headers, + }; + } + toString() { const { host, auth } = this.options; return `bitbucket{host=${host},authed=${Boolean(auth)}}`;