From d1ea2b84a40283d340ceaef8b377e79b60291af9 Mon Sep 17 00:00:00 2001 From: Branden Rodgers Date: Wed, 1 Nov 2023 11:37:14 -0400 Subject: [PATCH 1/2] getAxiosConfig tests --- http/__tests__/getAxiosConfig.ts | 40 +++++++++++ http/__tests__/index.ts | 113 ++++++++++++++++++++++++++++++- 2 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 http/__tests__/getAxiosConfig.ts diff --git a/http/__tests__/getAxiosConfig.ts b/http/__tests__/getAxiosConfig.ts new file mode 100644 index 00000000..1e8cd50d --- /dev/null +++ b/http/__tests__/getAxiosConfig.ts @@ -0,0 +1,40 @@ +import { getAndLoadConfigIfNeeded as __getAndLoadConfigIfNeeded } from '../../config'; +import { ENVIRONMENTS } from '../../constants/environments'; +import { getAxiosConfig } from '../getAxiosConfig'; + +jest.mock('../../config'); + +const getAndLoadConfigIfNeeded = + __getAndLoadConfigIfNeeded as jest.MockedFunction< + typeof __getAndLoadConfigIfNeeded + >; + +const url = 'https://app.hubspot.com'; + +describe('getAxiosConfig', () => { + it('constructs baseURL as expected based on environment', () => { + getAndLoadConfigIfNeeded.mockReturnValue({ + accounts: [], + }); + + expect(getAxiosConfig({ url })).toMatchObject({ + baseURL: 'https://api.hubapi.com', + }); + expect(getAxiosConfig({ url, env: ENVIRONMENTS.QA })).toMatchObject({ + baseURL: 'https://api.hubapiqa.com', + }); + }); + it('supports httpUseLocalhost config option to construct baseURL for local HTTP services', () => { + getAndLoadConfigIfNeeded.mockReturnValue({ + httpUseLocalhost: true, + accounts: [], + }); + + expect(getAxiosConfig({ url })).toMatchObject({ + baseURL: 'https://local.hubapi.com', + }); + expect(getAxiosConfig({ url, env: ENVIRONMENTS.QA })).toMatchObject({ + baseURL: 'https://local.hubapiqa.com', + }); + }); +}); diff --git a/http/__tests__/index.ts b/http/__tests__/index.ts index b8012ec9..fc4fa878 100644 --- a/http/__tests__/index.ts +++ b/http/__tests__/index.ts @@ -1,11 +1,13 @@ import axios from 'axios'; import fs from 'fs-extra'; +//import moment from 'moment'; import { getAndLoadConfigIfNeeded as __getAndLoadConfigIfNeeded, getAccountConfig as __getAccountConfig, } from '../../config'; import { ENVIRONMENTS } from '../../constants/environments'; import http from '../'; +//import { version } from '../../package.json'; import { StatusCodeError } from '../../types/Error'; jest.mock('fs-extra'); @@ -109,5 +111,114 @@ describe('http', () => { } }); }); - // NOTE: there are more tests to add, but I'm stopping here to keep the PR smaller + // describe('get()', () => { + // it('adds authorization header when using OAuth2 with valid access token', async () => { + // const accessToken = 'let-me-in'; + // const account = { + // portalId: 123, + // authType: 'oauth2', + // clientId: 'd996372f-2b53-30d3-9c3b-4fdde4bce3a2', + // clientSecret: 'f90a6248-fbc0-3b03-b0db-ec58c95e791', + // scopes: ['content'], + // tokenInfo: { + // expiresAt: moment().add(2, 'hours').toISOString(), + // refreshToken: '84d22710-4cb7-5581-ba05-35f9945e5e8e', + // accessToken, + // }, + // }; + // getAndLoadConfigIfNeeded.mockReturnValue({ + // accounts: [account], + // }); + // getAccountConfig.mockReturnValue(account); + // await http.get(123, { + // uri: 'some/endpoint/path', + // }); + + // expect(requestPN.get).toBeCalledWith({ + // baseUrl: `https://api.hubapi.com`, + // uri: 'some/endpoint/path', + // headers: { + // 'User-Agent': `HubSpot CLI/${version}`, + // Authorization: `Bearer ${accessToken}`, + // }, + // json: true, + // simple: true, + // timeout: 15000, + // qs: { + // portalId: 123, + // }, + // }); + // }); + // it('adds authorization header when using a user token', async () => { + // const accessToken = 'let-me-in'; + // const account = { + // portalId: 123, + // authType: 'personalaccesskey', + // personalAccessKey: 'some-secret', + // auth: { + // tokenInfo: { + // expiresAt: moment().add(2, 'hours').toISOString(), + // accessToken, + // }, + // }, + // }; + // getAndLoadConfigIfNeeded.mockReturnValue({ + // accounts: [account], + // }); + // getAccountConfig.mockReturnValue(account); + // await http.get(123, { + // uri: 'some/endpoint/path', + // }); + + // expect(requestPN.get).toBeCalledWith({ + // baseUrl: `https://api.hubapi.com`, + // uri: 'some/endpoint/path', + // headers: { + // 'User-Agent': `HubSpot CLI/${version}`, + // Authorization: `Bearer ${accessToken}`, + // }, + // json: true, + // simple: true, + // timeout: 15000, + // qs: { + // portalId: 123, + // }, + // }); + // }); + + // it('supports setting a custom timeout', async () => { + // getAndLoadConfigIfNeeded.mockReturnValue({ + // httpTimeout: 1000, + // accounts: [ + // { + // portalId: 123, + // apiKey: 'abc', + // }, + // ], + // }); + // getAccountConfig.mockReturnValue({ + // portalId: 123, + // apiKey: 'abc', + // }); + + // await http.get(123, { + // uri: 'some/endpoint/path', + // }); + + // expect(requestPN.get).toBeCalledWith({ + // baseUrl: `https://api.hubapi.com`, + // uri: 'some/endpoint/path', + // headers: { + // 'User-Agent': `HubSpot CLI/${version}`, + // }, + // json: true, + // simple: true, + // timeout: 1000, + // qs: { + // portalId: 123, + // hapikey: 'abc', + // }, + // }); + // }); + //}); }); From 7b7f22d629d1c5c2eb537118d28162ef52281045 Mon Sep 17 00:00:00 2001 From: Branden Rodgers Date: Thu, 2 Nov 2023 10:43:22 -0400 Subject: [PATCH 2/2] HTTP unit tests part 2 --- config/__tests__/config.ts | 24 +++- config/config_DEPRECATED.ts | 5 +- http/__tests__/index.ts | 232 +++++++++++++++++----------------- lib/__tests__/uploadFolder.ts | 2 +- models/OAuth2Manager.ts | 6 +- 5 files changed, 145 insertions(+), 124 deletions(-) diff --git a/config/__tests__/config.ts b/config/__tests__/config.ts index 57020757..1e8e4aee 100644 --- a/config/__tests__/config.ts +++ b/config/__tests__/config.ts @@ -241,7 +241,8 @@ describe('lib/config', () => { ).toEqual(env); }); - it('overwrites the existing env in the config if specified', () => { + it('overwrites the existing env in the config if specified as environment', () => { + // NOTE: the config now uses "env", but this is to support legacy behavior const previousEnv = ENVIRONMENTS.PROD; const newEnv = ENVIRONMENTS.QA; setConfig({ @@ -262,6 +263,27 @@ describe('lib/config', () => { ).toEqual(newEnv); }); + it('overwrites the existing env in the config if specified as env', () => { + const previousEnv = ENVIRONMENTS.PROD; + const newEnv = ENVIRONMENTS.QA; + setConfig({ + defaultPortal: PERSONAL_ACCESS_KEY_CONFIG.name, + portals: [{ ...PERSONAL_ACCESS_KEY_CONFIG, env: previousEnv }], + }); + const modifiedPersonalAccessKeyConfig = { + ...PERSONAL_ACCESS_KEY_CONFIG, + env: newEnv, + }; + updateAccountConfig(modifiedPersonalAccessKeyConfig); + + expect( + getAccountByAuthType( + getConfig(), + modifiedPersonalAccessKeyConfig.authType + ).env + ).toEqual(newEnv); + }); + it('sets the name in the config if specified', () => { const name = 'MYNAME'; const modifiedPersonalAccessKeyConfig = { diff --git a/config/config_DEPRECATED.ts b/config/config_DEPRECATED.ts index d3c2d913..3a72922b 100644 --- a/config/config_DEPRECATED.ts +++ b/config/config_DEPRECATED.ts @@ -479,8 +479,9 @@ export function updateAccountConfig( } const env = getValidEnv( - environment || (accountConfig && accountConfig.env), - undefined + environment || + (configOptions && configOptions.env) || + (accountConfig && accountConfig.env) ); const mode: Mode | undefined = defaultMode && (defaultMode.toLowerCase() as Mode); diff --git a/http/__tests__/index.ts b/http/__tests__/index.ts index fc4fa878..5e41bfb6 100644 --- a/http/__tests__/index.ts +++ b/http/__tests__/index.ts @@ -1,14 +1,15 @@ import axios from 'axios'; import fs from 'fs-extra'; -//import moment from 'moment'; +import moment from 'moment'; import { getAndLoadConfigIfNeeded as __getAndLoadConfigIfNeeded, getAccountConfig as __getAccountConfig, } from '../../config'; import { ENVIRONMENTS } from '../../constants/environments'; import http from '../'; -//import { version } from '../../package.json'; +import { version } from '../../package.json'; import { StatusCodeError } from '../../types/Error'; +import { AuthType } from '../../types/Accounts'; jest.mock('fs-extra'); jest.mock('axios'); @@ -39,20 +40,20 @@ describe('http', () => { getAccountConfig.mockReset(); }); - describe('getOctetStream', () => { + describe('http.getOctetStream()', () => { beforeEach(() => { getAndLoadConfigIfNeeded.mockReturnValue({ httpTimeout: 1000, - portals: [ + accounts: [ { - portalId: 123, + accountId: 123, apiKey: 'abc', env: ENVIRONMENTS.QA, }, ], }); getAccountConfig.mockReturnValue({ - portalId: 123, + accountId: 123, apiKey: 'abc', env: ENVIRONMENTS.QA, }); @@ -79,7 +80,7 @@ describe('http', () => { url: 'some/endpoint/path', }) ); - expect(fs.createWriteStream).toBeCalledWith('path/to/local/file', { + expect(fs.createWriteStream).toHaveBeenCalledWith('path/to/local/file', { encoding: 'binary', }); }); @@ -111,114 +112,111 @@ describe('http', () => { } }); }); - // describe('get()', () => { - // it('adds authorization header when using OAuth2 with valid access token', async () => { - // const accessToken = 'let-me-in'; - // const account = { - // portalId: 123, - // authType: 'oauth2', - // clientId: 'd996372f-2b53-30d3-9c3b-4fdde4bce3a2', - // clientSecret: 'f90a6248-fbc0-3b03-b0db-ec58c95e791', - // scopes: ['content'], - // tokenInfo: { - // expiresAt: moment().add(2, 'hours').toISOString(), - // refreshToken: '84d22710-4cb7-5581-ba05-35f9945e5e8e', - // accessToken, - // }, - // }; - // getAndLoadConfigIfNeeded.mockReturnValue({ - // accounts: [account], - // }); - // getAccountConfig.mockReturnValue(account); - // await http.get(123, { - // uri: 'some/endpoint/path', - // }); - - // expect(requestPN.get).toBeCalledWith({ - // baseUrl: `https://api.hubapi.com`, - // uri: 'some/endpoint/path', - // headers: { - // 'User-Agent': `HubSpot CLI/${version}`, - // Authorization: `Bearer ${accessToken}`, - // }, - // json: true, - // simple: true, - // timeout: 15000, - // qs: { - // portalId: 123, - // }, - // }); - // }); - // it('adds authorization header when using a user token', async () => { - // const accessToken = 'let-me-in'; - // const account = { - // portalId: 123, - // authType: 'personalaccesskey', - // personalAccessKey: 'some-secret', - // auth: { - // tokenInfo: { - // expiresAt: moment().add(2, 'hours').toISOString(), - // accessToken, - // }, - // }, - // }; - // getAndLoadConfigIfNeeded.mockReturnValue({ - // accounts: [account], - // }); - // getAccountConfig.mockReturnValue(account); - // await http.get(123, { - // uri: 'some/endpoint/path', - // }); - - // expect(requestPN.get).toBeCalledWith({ - // baseUrl: `https://api.hubapi.com`, - // uri: 'some/endpoint/path', - // headers: { - // 'User-Agent': `HubSpot CLI/${version}`, - // Authorization: `Bearer ${accessToken}`, - // }, - // json: true, - // simple: true, - // timeout: 15000, - // qs: { - // portalId: 123, - // }, - // }); - // }); - - // it('supports setting a custom timeout', async () => { - // getAndLoadConfigIfNeeded.mockReturnValue({ - // httpTimeout: 1000, - // accounts: [ - // { - // portalId: 123, - // apiKey: 'abc', - // }, - // ], - // }); - // getAccountConfig.mockReturnValue({ - // portalId: 123, - // apiKey: 'abc', - // }); - - // await http.get(123, { - // uri: 'some/endpoint/path', - // }); - - // expect(requestPN.get).toBeCalledWith({ - // baseUrl: `https://api.hubapi.com`, - // uri: 'some/endpoint/path', - // headers: { - // 'User-Agent': `HubSpot CLI/${version}`, - // }, - // json: true, - // simple: true, - // timeout: 1000, - // qs: { - // portalId: 123, - // hapikey: 'abc', - // }, - // }); - // }); - //}); + + describe('http.get()', () => { + it('adds authorization header when using OAuth2 with valid access token', async () => { + const accessToken = 'let-me-in'; + const account = { + accountId: 123, + env: ENVIRONMENTS.PROD, + authType: 'oauth2' as AuthType, + auth: { + clientId: 'd996372f-2b53-30d3-9c3b-4fdde4bce3a2', + clientSecret: 'f90a6248-fbc0-3b03-b0db-ec58c95e791', + scopes: ['content'], + tokenInfo: { + expiresAt: moment().add(2, 'hours').toISOString(), + refreshToken: '84d22710-4cb7-5581-ba05-35f9945e5e8e', + accessToken, + }, + }, + }; + getAndLoadConfigIfNeeded.mockReturnValue({ + accounts: [account], + }); + getAccountConfig.mockReturnValue(account); + + await http.get(123, { url: 'some/endpoint/path' }); + + expect(mockedAxios).toHaveBeenCalledWith({ + baseURL: `https://api.hubapi.com`, + url: 'some/endpoint/path', + headers: { + 'User-Agent': `HubSpot Local Dev Lib/${version}`, + Authorization: `Bearer ${accessToken}`, + }, + timeout: 15000, + params: { + portalId: 123, + }, + }); + }); + it('adds authorization header when using a user token', async () => { + const accessToken = 'let-me-in'; + const account = { + accountId: 123, + env: ENVIRONMENTS.PROD, + authType: 'personalaccesskey' as AuthType, + personalAccessKey: 'some-secret', + auth: { + tokenInfo: { + expiresAt: moment().add(2, 'hours').toISOString(), + accessToken, + }, + }, + }; + getAndLoadConfigIfNeeded.mockReturnValue({ + accounts: [account], + }); + getAccountConfig.mockReturnValue(account); + + await http.get(123, { url: 'some/endpoint/path' }); + + expect(mockedAxios).toHaveBeenCalledWith({ + baseURL: `https://api.hubapi.com`, + url: 'some/endpoint/path', + headers: { + 'User-Agent': `HubSpot Local Dev Lib/${version}`, + Authorization: `Bearer ${accessToken}`, + }, + timeout: 15000, + params: { + portalId: 123, + }, + }); + }); + + it('supports setting a custom timeout', async () => { + getAndLoadConfigIfNeeded.mockReturnValue({ + httpTimeout: 1000, + accounts: [ + { + accountId: 123, + apiKey: 'abc', + env: ENVIRONMENTS.PROD, + }, + ], + }); + getAccountConfig.mockReturnValue({ + accountId: 123, + apiKey: 'abc', + env: ENVIRONMENTS.PROD, + }); + + await http.get(123, { url: 'some/endpoint/path' }); + + expect(mockedAxios).toHaveBeenCalledWith({ + baseURL: `https://api.hubapi.com`, + url: 'some/endpoint/path', + headers: { + 'User-Agent': `HubSpot Local Dev Lib/${version}`, + }, + timeout: 1000, + params: { + portalId: 123, + hapikey: 'abc', + }, + }); + }); + }); }); diff --git a/lib/__tests__/uploadFolder.ts b/lib/__tests__/uploadFolder.ts index 196616a5..297e1490 100644 --- a/lib/__tests__/uploadFolder.ts +++ b/lib/__tests__/uploadFolder.ts @@ -80,7 +80,7 @@ const filesProto = [ 'folder/sample.module/module.html', 'folder/templates/page.html', ]; -describe('uploadFolder', () => { +describe('cms/uploadFolder', () => { beforeAll(() => { createIgnoreFilter.mockImplementation(() => () => true); }); diff --git a/models/OAuth2Manager.ts b/models/OAuth2Manager.ts index ab14fb87..b4142111 100644 --- a/models/OAuth2Manager.ts +++ b/models/OAuth2Manager.ts @@ -1,7 +1,6 @@ import axios from 'axios'; import moment from 'moment'; -import { ENVIRONMENTS } from '../constants/environments'; import { getHubSpotApiOrigin } from '../lib/urls'; import { getValidEnv } from '../lib/environment'; import { FlatAccountFields, OAuthAccount, TokenInfo } from '../types/Accounts'; @@ -38,9 +37,10 @@ class OAuth2Manager { refreshTokenRequest: Promise | null; constructor(account: OAuthAccount, writeTokenInfo: WriteTokenInfoFunction) { - this.account = account; this.writeTokenInfo = writeTokenInfo; this.refreshTokenRequest = null; + this.account = account; + // NOTE: Potential issues by not using maskProductionValue = '' for env like in cli-lib } async accessToken(): Promise { @@ -142,7 +142,7 @@ class OAuth2Manager { toObj() { return { - environment: this.account.env ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD, + env: this.account.env, clientSecret: this.account.auth.clientSecret, clientId: this.account.auth.clientId, scopes: this.account.auth.scopes,