@hubspot/cli 7.9.0-experimental.0 → 7.9.1-experimental.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/bin/cli.js +5 -4
  2. package/commands/__tests__/getStarted.test.js +10 -0
  3. package/commands/account/__tests__/rename.test.js +42 -0
  4. package/commands/account/auth.js +10 -14
  5. package/commands/account/clean.js +11 -19
  6. package/commands/account/createOverride.js +15 -11
  7. package/commands/account/info.js +8 -5
  8. package/commands/account/list.js +13 -18
  9. package/commands/account/remove.js +23 -22
  10. package/commands/account/removeOverride.js +6 -6
  11. package/commands/account/rename.d.ts +1 -1
  12. package/commands/account/rename.js +6 -3
  13. package/commands/account/use.js +19 -8
  14. package/commands/app/__tests__/migrate.test.js +8 -4
  15. package/commands/app/migrate.js +2 -2
  16. package/commands/auth.js +18 -14
  17. package/commands/config/migrate.js +5 -5
  18. package/commands/config/set.js +1 -2
  19. package/commands/customObject/createSchema.js +2 -3
  20. package/commands/customObject/updateSchema.js +2 -3
  21. package/commands/getStarted.js +10 -5
  22. package/commands/hubdb/__tests__/list.test.js +1 -0
  23. package/commands/hubdb/list.js +2 -2
  24. package/commands/hubdb.d.ts +1 -1
  25. package/commands/init.js +36 -32
  26. package/commands/project/__tests__/deploy.test.js +10 -5
  27. package/commands/project/__tests__/devUnifiedFlow.test.js +6 -4
  28. package/commands/project/__tests__/logs.test.js +4 -0
  29. package/commands/project/__tests__/validate.test.js +2 -2
  30. package/commands/project/cloneApp.js +2 -2
  31. package/commands/project/deploy.js +2 -2
  32. package/commands/project/dev/deprecatedFlow.js +4 -5
  33. package/commands/project/dev/index.js +14 -4
  34. package/commands/project/dev/unifiedFlow.js +4 -5
  35. package/commands/project/listBuilds.js +7 -1
  36. package/commands/project/logs.js +2 -3
  37. package/commands/project/profile/add.js +6 -7
  38. package/commands/project/profile/delete.js +2 -2
  39. package/commands/project/upload.js +9 -3
  40. package/commands/project/validate.js +9 -3
  41. package/commands/project/watch.js +7 -2
  42. package/commands/sandbox/__tests__/create.test.js +14 -5
  43. package/commands/sandbox/create.js +4 -5
  44. package/commands/sandbox/delete.js +23 -20
  45. package/commands/testAccount/__tests__/create.test.js +68 -0
  46. package/commands/testAccount/create.d.ts +8 -0
  47. package/commands/testAccount/create.js +134 -44
  48. package/commands/testAccount/delete.js +9 -8
  49. package/commands/testAccount/importData.d.ts +1 -1
  50. package/lang/en.d.ts +3204 -3205
  51. package/lang/en.js +33 -9
  52. package/lib/__tests__/buildAccount.test.js +22 -30
  53. package/lib/__tests__/commonOpts.test.js +9 -13
  54. package/lib/__tests__/developerTestAccounts.test.js +29 -17
  55. package/lib/__tests__/importData.test.js +20 -10
  56. package/lib/__tests__/oauth.test.js +19 -8
  57. package/lib/__tests__/sandboxSync.test.js +33 -11
  58. package/lib/__tests__/sandboxes.test.js +30 -19
  59. package/lib/__tests__/usageTracking.test.js +10 -10
  60. package/lib/__tests__/validation.test.js +32 -32
  61. package/lib/accountTypes.d.ts +9 -9
  62. package/lib/accountTypes.js +2 -4
  63. package/lib/app/__tests__/migrate.test.js +15 -0
  64. package/lib/app/__tests__/migrate_legacy.test.js +9 -0
  65. package/lib/app/migrate_legacy.d.ts +2 -2
  66. package/lib/buildAccount.d.ts +4 -4
  67. package/lib/buildAccount.js +7 -14
  68. package/lib/commonOpts.js +3 -3
  69. package/lib/configMigrate.d.ts +2 -2
  70. package/lib/configMigrate.js +42 -18
  71. package/lib/configOptions.js +3 -2
  72. package/lib/constants.d.ts +1 -0
  73. package/lib/constants.js +6 -0
  74. package/lib/developerTestAccounts.d.ts +3 -3
  75. package/lib/developerTestAccounts.js +4 -7
  76. package/lib/doctor/DiagnosticInfoBuilder.d.ts +1 -1
  77. package/lib/doctor/DiagnosticInfoBuilder.js +9 -6
  78. package/lib/doctor/Doctor.js +4 -3
  79. package/lib/doctor/__tests__/Diagnosis.test.js +4 -3
  80. package/lib/doctor/__tests__/DiagnosticInfoBuilder.test.js +17 -9
  81. package/lib/doctor/__tests__/Doctor.test.js +14 -0
  82. package/lib/importData.js +8 -7
  83. package/lib/links.js +5 -5
  84. package/lib/mcp/__tests__/setup.test.js +127 -0
  85. package/lib/mcp/setup.d.ts +4 -12
  86. package/lib/mcp/setup.js +34 -1
  87. package/lib/middleware/__test__/commandTargetingUtils.test.js +3 -3
  88. package/lib/middleware/__test__/configMiddleware.test.js +23 -22
  89. package/lib/middleware/__test__/gitMiddleware.test.js +9 -7
  90. package/lib/middleware/autoUpdateMiddleware.d.ts +3 -1
  91. package/lib/middleware/autoUpdateMiddleware.js +10 -2
  92. package/lib/middleware/commandTargetingUtils.js +2 -2
  93. package/lib/middleware/configMiddleware.d.ts +6 -1
  94. package/lib/middleware/configMiddleware.js +36 -15
  95. package/lib/middleware/gitMiddleware.js +8 -4
  96. package/lib/oauth.d.ts +2 -2
  97. package/lib/oauth.js +8 -10
  98. package/lib/projects/__tests__/AppDevModeInterface.test.js +17 -6
  99. package/lib/projects/__tests__/DevServerManager.test.js +1 -0
  100. package/lib/projects/__tests__/LocalDevProcess.test.js +1 -0
  101. package/lib/projects/__tests__/components.test.js +148 -24
  102. package/lib/projects/__tests__/deploy.test.js +1 -0
  103. package/lib/projects/__tests__/projects.test.js +13 -42
  104. package/lib/projects/components.js +76 -20
  105. package/lib/projects/config.js +5 -9
  106. package/lib/projects/create/__tests__/v2.test.js +11 -0
  107. package/lib/projects/localDev/AppDevModeInterface.js +2 -2
  108. package/lib/projects/localDev/DevServerManager_DEPRECATED.js +2 -2
  109. package/lib/projects/localDev/LocalDevLogger.js +4 -4
  110. package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +3 -3
  111. package/lib/projects/localDev/helpers/account.d.ts +10 -10
  112. package/lib/projects/localDev/helpers/account.js +6 -11
  113. package/lib/projects/urls.js +5 -6
  114. package/lib/prompts/__tests__/createDeveloperTestAccountConfigPrompt.test.d.ts +1 -0
  115. package/lib/prompts/__tests__/createDeveloperTestAccountConfigPrompt.test.js +153 -0
  116. package/lib/prompts/__tests__/downloadProjectPrompt.test.js +7 -5
  117. package/lib/prompts/accountNamePrompt.js +3 -3
  118. package/lib/prompts/accountsPrompt.d.ts +1 -1
  119. package/lib/prompts/accountsPrompt.js +6 -7
  120. package/lib/prompts/confirmImportDataPrompt.js +2 -2
  121. package/lib/prompts/createDeveloperTestAccountConfigPrompt.d.ts +5 -0
  122. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +76 -66
  123. package/lib/prompts/downloadProjectPrompt.d.ts +1 -0
  124. package/lib/prompts/downloadProjectPrompt.js +5 -2
  125. package/lib/prompts/importDataTestAccountSelectPrompt.js +4 -5
  126. package/lib/prompts/personalAccessKeyPrompt.js +2 -2
  127. package/lib/prompts/projectDevTargetAccountPrompt.d.ts +3 -3
  128. package/lib/prompts/projectDevTargetAccountPrompt.js +5 -7
  129. package/lib/prompts/sandboxesPrompt.js +7 -8
  130. package/lib/prompts/setAsDefaultAccountPrompt.js +7 -6
  131. package/lib/sandboxSync.d.ts +2 -2
  132. package/lib/sandboxSync.js +3 -9
  133. package/lib/sandboxes.d.ts +4 -4
  134. package/lib/sandboxes.js +6 -11
  135. package/lib/serverlessLogs.js +2 -2
  136. package/lib/theme/__tests__/migrate.test.js +15 -0
  137. package/lib/ui/index.js +6 -3
  138. package/lib/usageTracking.js +15 -8
  139. package/lib/validation.js +13 -11
  140. package/mcp-server/tools/cms/HsCreateFunctionTool.js +8 -2
  141. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +4 -4
  142. package/mcp-server/tools/cms/HsCreateModuleTool.js +8 -2
  143. package/mcp-server/tools/cms/HsCreateTemplateTool.js +8 -2
  144. package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +4 -4
  145. package/mcp-server/tools/cms/HsFunctionLogsTool.js +6 -2
  146. package/mcp-server/tools/cms/HsListFunctionsTool.js +5 -1
  147. package/mcp-server/tools/cms/HsListTool.js +5 -1
  148. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -0
  149. package/mcp-server/tools/index.js +4 -0
  150. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +1 -1
  151. package/mcp-server/tools/project/AddFeatureToProjectTool.js +9 -3
  152. package/mcp-server/tools/project/CreateProjectTool.js +8 -2
  153. package/mcp-server/tools/project/CreateTestAccountTool.d.ts +41 -0
  154. package/mcp-server/tools/project/CreateTestAccountTool.js +150 -0
  155. package/mcp-server/tools/project/DeployProjectTool.d.ts +1 -1
  156. package/mcp-server/tools/project/DeployProjectTool.js +8 -2
  157. package/mcp-server/tools/project/DocFetchTool.d.ts +1 -1
  158. package/mcp-server/tools/project/DocFetchTool.js +9 -5
  159. package/mcp-server/tools/project/DocsSearchTool.d.ts +1 -1
  160. package/mcp-server/tools/project/DocsSearchTool.js +12 -8
  161. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.d.ts +1 -1
  162. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +11 -7
  163. package/mcp-server/tools/project/GetApplicationInfoTool.d.ts +1 -1
  164. package/mcp-server/tools/project/GetApplicationInfoTool.js +11 -7
  165. package/mcp-server/tools/project/GetBuildStatusTool.d.ts +26 -0
  166. package/mcp-server/tools/project/GetBuildStatusTool.js +164 -0
  167. package/mcp-server/tools/project/GetConfigValuesTool.d.ts +1 -1
  168. package/mcp-server/tools/project/GetConfigValuesTool.js +11 -7
  169. package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +1 -1
  170. package/mcp-server/tools/project/GuidedWalkthroughTool.js +7 -3
  171. package/mcp-server/tools/project/UploadProjectTools.d.ts +9 -3
  172. package/mcp-server/tools/project/UploadProjectTools.js +51 -5
  173. package/mcp-server/tools/project/ValidateProjectTool.d.ts +1 -1
  174. package/mcp-server/tools/project/ValidateProjectTool.js +7 -3
  175. package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.d.ts +1 -0
  176. package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.js +454 -0
  177. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +5 -1
  178. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +25 -13
  179. package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.js +7 -5
  180. package/mcp-server/tools/project/__tests__/GetApplicationInfoTool.test.js +7 -5
  181. package/mcp-server/tools/project/__tests__/GetBuildStatusTool.test.d.ts +1 -0
  182. package/mcp-server/tools/project/__tests__/GetBuildStatusTool.test.js +240 -0
  183. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +8 -6
  184. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +56 -4
  185. package/mcp-server/utils/__tests__/content.test.js +21 -20
  186. package/mcp-server/utils/__tests__/feedbackTracking.test.js +33 -28
  187. package/mcp-server/utils/content.d.ts +1 -1
  188. package/mcp-server/utils/content.js +2 -2
  189. package/mcp-server/utils/feedbackTracking.d.ts +1 -1
  190. package/mcp-server/utils/feedbackTracking.js +3 -3
  191. package/mcp-server/utils/toolUsageTracking.js +4 -3
  192. package/package.json +8 -7
  193. package/mcp-server/utils/__tests__/cliConfig.test.js +0 -110
  194. package/mcp-server/utils/cliConfig.d.ts +0 -1
  195. package/mcp-server/utils/cliConfig.js +0 -12
  196. /package/{mcp-server/utils/__tests__/cliConfig.test.d.ts → lib/mcp/__tests__/setup.test.d.ts} +0 -0
package/lang/en.js CHANGED
@@ -7,15 +7,15 @@ import { uiAccountDescription, uiBetaTag, uiCommandReference, uiLink, UI_COLORS,
7
7
  import { getProjectDetailUrl, getProjectSettingsUrl, getLocalDevUiUrl, } from '../lib/projects/urls.js';
8
8
  import { getProductUpdatesUrl } from '../lib/links.js';
9
9
  import { APP_DISTRIBUTION_TYPES, APP_AUTH_TYPES, PROJECT_CONFIG_FILE, PROJECT_WITH_APP, LEGACY_PUBLIC_APP_FILE, } from '../lib/constants.js';
10
- import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
11
10
  export const commands = {
12
11
  generalErrors: {
13
12
  srcIsProject: (src, command) => `"${src}" is in a project folder. Did you mean "hs project ${command}"?`,
14
13
  handleDeprecatedEnvVariables: {
15
14
  portalEnvVarDeprecated: 'The HUBSPOT_PORTAL_ID environment variable is deprecated. Please use HUBSPOT_ACCOUNT_ID instead.',
16
15
  },
17
- loadConfigMiddleware: {
18
- configFileExists: (configPath) => `A configuration file already exists at ${configPath}. To specify a new configuration file, delete the existing one and try again.`,
16
+ validateConfigMiddleware: {
17
+ missingConfigFile: `A HubSpot config file is required to run this command, but none was found. To create a new config file and authenticate a HubSpot account, run ${uiCommandReference('hs account auth')}`,
18
+ configValidationFailed: (errors) => `Your HubSpot config file is invalid. Please fix the following errors:\n - ${errors.join('\n- ')}`,
19
19
  },
20
20
  },
21
21
  getStarted: {
@@ -118,7 +118,7 @@ export const commands = {
118
118
  },
119
119
  },
120
120
  success: {
121
- renamed: (name, newName) => `Account "${name}" renamed to "${newName}"`,
121
+ renamed: (name, newName, nameWasSanitized) => `Account "${chalk.bold(name)}" successfully renamed to "${chalk.bold(newName)}"${nameWasSanitized ? ' (Sanitized to remove invalid characters)' : ''}.`,
122
122
  },
123
123
  },
124
124
  use: {
@@ -1196,6 +1196,7 @@ export const commands = {
1196
1196
  setup: {
1197
1197
  describe: 'Setup the HubSpot development MCP servers.',
1198
1198
  installingDocSearch: 'Adding the docs-search mcp server',
1199
+ codex: 'Codex CLI',
1199
1200
  claudeCode: 'Claude Code',
1200
1201
  cursor: 'Cursor',
1201
1202
  windsurf: 'Windsurf',
@@ -1217,7 +1218,11 @@ export const commands = {
1217
1218
  configuredClaudeCode: 'Configured Claude Code',
1218
1219
  claudeCodeNotFound: 'Claude Code not found - skipping configuration',
1219
1220
  claudeCodeInstallFailed: 'Claude Code CLI not working - skipping configuration',
1220
- failedToConfigureClaudeDesktop: 'Failed to configure Claude Desktop',
1221
+ // Codex
1222
+ configuringCodex: 'Configuring Codex...',
1223
+ configuredCodex: 'Configured Codex',
1224
+ codexNotFound: 'Codex command not found - skipping configuration',
1225
+ codexInstallFailed: 'Failed to configure Codex',
1221
1226
  // Cursor
1222
1227
  configuringCursor: 'Configuring Cursor...',
1223
1228
  failedToConfigureCursor: 'Failed to configure Cursor',
@@ -2113,9 +2118,21 @@ export const commands = {
2113
2118
  createFailure: 'Failed to create test account.',
2114
2119
  },
2115
2120
  options: {
2116
- configPath: 'The path to the test account config',
2121
+ configPath: 'Path to config file (mutually exclusive with other flags)',
2122
+ accountName: 'Name for the test account',
2123
+ description: 'Description for the test account',
2124
+ marketingLevel: 'Marketing Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
2125
+ opsLevel: 'Operations Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
2126
+ serviceLevel: 'Service Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
2127
+ salesLevel: 'Sales Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
2128
+ contentLevel: 'CMS Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
2117
2129
  },
2118
2130
  example: (configPath) => `Create a test account from the config file at ${configPath}`,
2131
+ examples: {
2132
+ withAllHubsEnterprise: 'Create a test account with all hubs at ENTERPRISE level',
2133
+ withSpecificHubLevels: 'Create a test account with specific hub levels',
2134
+ },
2135
+ savedAccountNameDiffers: (originalName, savedName) => `Account name "${chalk.bold(originalName)}" was saved as "${chalk.bold(savedName)}" in config.`,
2119
2136
  },
2120
2137
  createConfig: {
2121
2138
  describe: 'Create a test account config file.',
@@ -2984,6 +3001,10 @@ export const lib = {
2984
3001
  header: 'Created the following components and features:',
2985
3002
  applicationLog: (componentType, uid, name) => ` - Created ${chalk.bold(componentType)} with uid ${chalk.bold(uid)} and name ${chalk.bold(name)}`,
2986
3003
  componentLog: (componentType, uid) => ` - Created ${chalk.bold(componentType)} feature with uid ${chalk.bold(uid)}`,
3004
+ failedToUpdate: (hsMetaFile) => `Failed to update the uid in ${chalk.bold(hsMetaFile)}`,
3005
+ },
3006
+ generateSafeFilenameDifferentiator: {
3007
+ failedToCheckFiles: 'Failed to check files for filename differentiator. Falling back to timestamp.',
2987
3008
  },
2988
3009
  validateProjectConfig: {
2989
3010
  configNotFound: `Unable to locate a project configuration file. Try running again from a project directory, or run ${uiCommandReference('hs project create')} to create a new project.`,
@@ -3232,6 +3253,9 @@ export const lib = {
3232
3253
  skippedExistingAccounts: (accountIds) => `The following accounts were not merged because they already exist in the global config:${accountIds.map(id => `\n- ${uiAccountDescription(Number(id))}`).join('')}`,
3233
3254
  success: 'Your deprecated config file has been successfully merged with the global config file.',
3234
3255
  },
3256
+ errors: {
3257
+ archive: (deprecatedConfigPath) => `The config migration was successful, but an error occurred when archiving config file at ${deprecatedConfigPath}. You may need to manually delete or rename this config file.`,
3258
+ },
3235
3259
  },
3236
3260
  prompts: {
3237
3261
  promptUtils: {
@@ -3244,7 +3268,7 @@ export const lib = {
3244
3268
  promptMessage: '[--file-path] Select the JSON file that will be used to import your data.',
3245
3269
  },
3246
3270
  confirmImportDataPrompt: {
3247
- message: (dataFileNames, cliAccount) => `You are importing [${dataFileNames.join(', ')}] into ${uiAccountDescription(getAccountIdentifier(cliAccount))}. Continue?`,
3271
+ message: (dataFileNames, cliAccount) => `You are importing [${dataFileNames.join(', ')}] into ${uiAccountDescription(cliAccount?.accountId)}. Continue?`,
3248
3272
  },
3249
3273
  importDataTestAccountSelectPrompt: {
3250
3274
  errors: {
@@ -3596,7 +3620,7 @@ export const lib = {
3596
3620
  },
3597
3621
  failure: {
3598
3622
  syncTypeFetch: 'Unable to fetch available sandbox sync types. Please try again.',
3599
- invalidUser: (accountName, parentAccountName) => `Couldn't sync ${chalk.bold(accountName)} because your account has been removed from ${chalk.bold(parentAccountName)} or your permission set doesn't allow you to sync the sandbox. To update your permissions, contact a super admin in ${chalk.bold(parentAccountName)}.`,
3623
+ invalidUser: (accountName, parentAccountName) => `Couldn't sync sandbox ${chalk.bold(accountName)} because your account has been removed from parent account${chalk.bold(parentAccountName)} or your permission set doesn't allow you to sync the sandbox. To update your permissions, contact a super admin in ${chalk.bold(parentAccountName)}.`,
3600
3624
  syncInProgress: (url) => `Couldn't run the sync because there's another sync in progress. Wait for the current sync to finish and then try again. To check the sync status, visit the sync activity log: ${url}.`,
3601
3625
  notSuperAdmin: (accountId) => `Couldn't run the sync because you are not a super admin in ${uiAccountDescription(accountId)}. Ask the account owner for super admin access to the sandbox.`,
3602
3626
  objectNotFound: (accountId) => `Couldn't sync the sandbox because ${uiAccountDescription(accountId)} may have been deleted through the UI. Run ${uiCommandReference('hs sandbox delete')} to remove this account from the config.`,
@@ -3724,7 +3748,7 @@ export const lib = {
3724
3748
  },
3725
3749
  validation: {
3726
3750
  accountNotFoundInConfig: (userProvidedAccount) => `The account "${userProvidedAccount}" could not be found in the config`,
3727
- accountRequired: 'An account needs to be supplied either via "--account" or through setting a "defaultPortal"',
3751
+ accountRequired: 'An account needs to be supplied either via "--account" or through setting a "defaultAccount"',
3728
3752
  userProvidedAccount: 'Cannot specify an account when environment variables are supplied. Please unset the environment variables or do not use the "--account" flag.',
3729
3753
  accountNotConfigured: (accountId) => `The account ${uiAccountDescription(accountId)} has not been configured`,
3730
3754
  invalidAuthType: (authType, accountId, configPath, validValues) => `Invalid "authType" value "${authType}" for account "${uiAccountDescription(accountId)}" in config file: ${configPath}. Valid values are ${validValues}.`,
@@ -1,11 +1,12 @@
1
1
  import { getAccessToken, updateConfigWithAccessToken, } from '@hubspot/local-dev-lib/personalAccessKey';
2
- import { accountNameExistsInConfig, updateAccountConfig, writeConfig, getAccountId, } from '@hubspot/local-dev-lib/config';
2
+ import { getConfigAccountIfExists, updateConfigAccount, } from '@hubspot/local-dev-lib/config';
3
3
  import { createDeveloperTestAccount, fetchDeveloperTestAccountGateSyncStatus, generateDeveloperTestAccountPersonalAccessKey, } from '@hubspot/local-dev-lib/api/developerTestAccounts';
4
4
  import { createSandbox, createV2Sandbox, getSandboxPersonalAccessKey, } from '@hubspot/local-dev-lib/api/sandboxHubs';
5
5
  import { HUBSPOT_ACCOUNT_TYPES } from '@hubspot/local-dev-lib/constants/config';
6
6
  import { personalAccessKeyPrompt } from '../prompts/personalAccessKeyPrompt.js';
7
7
  import { cliAccountNamePrompt } from '../prompts/accountNamePrompt.js';
8
8
  import * as buildAccount from '../buildAccount.js';
9
+ import { poll } from '../polling.js';
9
10
  vi.mock('@hubspot/local-dev-lib/personalAccessKey');
10
11
  vi.mock('@hubspot/local-dev-lib/config');
11
12
  vi.mock('@hubspot/local-dev-lib/api/developerTestAccounts');
@@ -14,6 +15,7 @@ vi.mock('../ui/logger.js');
14
15
  vi.mock('../errorHandlers/index.js');
15
16
  vi.mock('../prompts/personalAccessKeyPrompt');
16
17
  vi.mock('../prompts/accountNamePrompt');
18
+ vi.mock('../polling.js');
17
19
  vi.mock('../ui/SpinniesManager', () => ({
18
20
  default: {
19
21
  init: vi.fn(),
@@ -25,17 +27,16 @@ vi.mock('../ui/SpinniesManager', () => ({
25
27
  const mockedPersonalAccessKeyPrompt = personalAccessKeyPrompt;
26
28
  const mockedGetAccessToken = getAccessToken;
27
29
  const mockedUpdateConfigWithAccessToken = updateConfigWithAccessToken;
28
- const mockedAccountNameExistsInConfig = accountNameExistsInConfig;
29
- const mockedUpdateAccountConfig = updateAccountConfig;
30
- const mockedWriteConfig = writeConfig;
30
+ const mockedGetConfigAccountIfExists = getConfigAccountIfExists;
31
+ const mockedUpdateConfigAccount = updateConfigAccount;
31
32
  const mockedCliAccountNamePrompt = cliAccountNamePrompt;
32
- const mockedGetAccountId = getAccountId;
33
33
  const mockedCreateDeveloperTestAccount = createDeveloperTestAccount;
34
34
  const mockedFetchDeveloperTestAccountGateSyncStatus = fetchDeveloperTestAccountGateSyncStatus;
35
35
  const mockedGenerateDeveloperTestAccountPersonalAccessKey = generateDeveloperTestAccountPersonalAccessKey;
36
36
  const mockedCreateSandbox = createSandbox;
37
37
  const mockedCreateV2Sandbox = createV2Sandbox;
38
38
  const mockedGetPersonalAccessKey = getSandboxPersonalAccessKey;
39
+ const mockedPoll = poll;
39
40
  describe('lib/buildAccount', () => {
40
41
  describe('saveAccountToConfig()', () => {
41
42
  const mockAccountConfig = {
@@ -61,9 +62,8 @@ describe('lib/buildAccount', () => {
61
62
  });
62
63
  mockedGetAccessToken.mockResolvedValue(accessToken);
63
64
  mockedUpdateConfigWithAccessToken.mockResolvedValue(mockAccountConfig);
64
- mockedAccountNameExistsInConfig.mockResolvedValue(false);
65
- mockedUpdateAccountConfig.mockReturnValue(undefined);
66
- mockedWriteConfig.mockReturnValue(undefined);
65
+ mockedGetConfigAccountIfExists.mockResolvedValue(undefined);
66
+ mockedUpdateConfigAccount.mockReturnValue(undefined);
67
67
  });
68
68
  afterEach(() => {
69
69
  vi.clearAllMocks();
@@ -72,8 +72,7 @@ describe('lib/buildAccount', () => {
72
72
  const result = await buildAccount.saveAccountToConfig(mockAccountConfig.accountId, mockAccountConfig.name, mockAccountConfig.env, 'test-key');
73
73
  expect(mockedPersonalAccessKeyPrompt).not.toHaveBeenCalled();
74
74
  expect(mockedUpdateConfigWithAccessToken).toHaveBeenCalledWith(accessToken, 'test-key', mockAccountConfig.env);
75
- expect(mockedUpdateAccountConfig).toHaveBeenCalledWith(expect.objectContaining(mockAccountConfig));
76
- expect(mockedWriteConfig).toHaveBeenCalled();
75
+ expect(mockedUpdateConfigAccount).toHaveBeenCalledWith(expect.objectContaining(mockAccountConfig));
77
76
  expect(result).toBe(mockAccountConfig.name);
78
77
  });
79
78
  it('should prompt for personal access key if not provided', async () => {
@@ -83,8 +82,7 @@ describe('lib/buildAccount', () => {
83
82
  account: mockAccountConfig.accountId,
84
83
  });
85
84
  expect(mockedUpdateConfigWithAccessToken).toHaveBeenCalledWith(accessToken, 'test-key', mockAccountConfig.env);
86
- expect(mockedUpdateAccountConfig).toHaveBeenCalledWith(expect.objectContaining(mockAccountConfig));
87
- expect(mockedWriteConfig).toHaveBeenCalled();
85
+ expect(mockedUpdateConfigAccount).toHaveBeenCalledWith(expect.objectContaining(mockAccountConfig));
88
86
  expect(result).toBe(mockAccountConfig.name);
89
87
  });
90
88
  it('should handle duplicate account names', async () => {
@@ -93,7 +91,7 @@ describe('lib/buildAccount', () => {
93
91
  name: undefined,
94
92
  };
95
93
  mockedUpdateConfigWithAccessToken.mockResolvedValue(mockAccountConfigWithNoName);
96
- mockedAccountNameExistsInConfig.mockResolvedValue(true);
94
+ mockedGetConfigAccountIfExists.mockResolvedValue({ accountId: 123 });
97
95
  mockedCliAccountNamePrompt.mockResolvedValue({
98
96
  name: 'test-account-with-new-name',
99
97
  });
@@ -109,6 +107,7 @@ describe('lib/buildAccount', () => {
109
107
  description: 'Test Account created by the HubSpot CLI',
110
108
  };
111
109
  beforeEach(() => {
110
+ vi.useFakeTimers();
112
111
  mockedCreateDeveloperTestAccount.mockResolvedValue({
113
112
  data: { id: 123456 },
114
113
  });
@@ -118,15 +117,23 @@ describe('lib/buildAccount', () => {
118
117
  mockedGenerateDeveloperTestAccountPersonalAccessKey.mockResolvedValue({
119
118
  data: { personalAccessKey: 'test-key' },
120
119
  });
120
+ // Mock poll to resolve immediately with the success status
121
+ mockedPoll.mockResolvedValue({ status: 'SUCCESS' });
122
+ });
123
+ afterEach(() => {
124
+ vi.useRealTimers();
121
125
  });
122
126
  it('should create a developer test account successfully', async () => {
123
- const result = await buildAccount.createDeveloperTestAccountV2(parentAccountId, mockDeveoperTestAccountConfig);
127
+ const resultPromise = buildAccount.createDeveloperTestAccountV2(parentAccountId, mockDeveoperTestAccountConfig);
128
+ // Fast-forward the 5 second setTimeout
129
+ await vi.advanceTimersByTimeAsync(5000);
130
+ const result = await resultPromise;
124
131
  expect(result).toEqual({
125
132
  accountName: mockDeveoperTestAccountConfig.accountName,
126
133
  accountId: 123456,
127
134
  personalAccessKey: 'test-key',
128
135
  });
129
- }, 10000);
136
+ });
130
137
  });
131
138
  describe('buildDeveloperTestAccount()', () => {
132
139
  const mockParentAccountConfig = {
@@ -146,7 +153,6 @@ describe('lib/buildAccount', () => {
146
153
  };
147
154
  beforeEach(() => {
148
155
  vi.spyOn(buildAccount, 'saveAccountToConfig').mockResolvedValue(mockParentAccountConfig.name);
149
- mockedGetAccountId.mockReturnValue(mockParentAccountConfig.accountId);
150
156
  mockedCreateDeveloperTestAccount.mockResolvedValue({
151
157
  data: mockDeveloperTestAccount,
152
158
  });
@@ -158,10 +164,6 @@ describe('lib/buildAccount', () => {
158
164
  const result = await buildAccount.buildDeveloperTestAccount(mockDeveloperTestAccount.accountName, mockParentAccountConfig, mockParentAccountConfig.env, 10);
159
165
  expect(result).toEqual(mockDeveloperTestAccount.id);
160
166
  });
161
- it('should throw error if account ID is not found', async () => {
162
- mockedGetAccountId.mockReturnValue(null);
163
- await expect(buildAccount.buildDeveloperTestAccount(mockDeveloperTestAccount.accountName, mockParentAccountConfig, mockParentAccountConfig.env, 10)).rejects.toThrow();
164
- });
165
167
  it('should handle API errors when creating developer test account', async () => {
166
168
  mockedCreateDeveloperTestAccount.mockRejectedValue(new Error('test-error'));
167
169
  await expect(buildAccount.buildDeveloperTestAccount(mockDeveloperTestAccount.accountName, mockParentAccountConfig, mockParentAccountConfig.env, 10)).rejects.toThrow();
@@ -192,7 +194,6 @@ describe('lib/buildAccount', () => {
192
194
  };
193
195
  beforeEach(() => {
194
196
  vi.spyOn(buildAccount, 'saveAccountToConfig').mockResolvedValue(mockParentAccountConfig.name);
195
- mockedGetAccountId.mockReturnValue(mockParentAccountConfig.accountId);
196
197
  mockedCreateSandbox.mockResolvedValue({
197
198
  data: { sandbox: mockSandbox, personalAccessKey: 'test-key' },
198
199
  });
@@ -216,10 +217,6 @@ describe('lib/buildAccount', () => {
216
217
  sandbox: mockSandbox,
217
218
  });
218
219
  });
219
- it('should throw error if account ID is not found', async () => {
220
- mockedGetAccountId.mockReturnValue(null);
221
- await expect(buildAccount.buildSandbox(mockSandbox.name, mockParentAccountConfig, HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX, mockParentAccountConfig.env)).rejects.toThrow();
222
- });
223
220
  it('should handle API errors when creating sandbox', async () => {
224
221
  mockedCreateSandbox.mockRejectedValue(new Error('test-error'));
225
222
  await expect(buildAccount.buildSandbox(mockSandbox.name, mockParentAccountConfig, HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX, mockParentAccountConfig.env, false)).rejects.toThrow();
@@ -250,7 +247,6 @@ describe('lib/buildAccount', () => {
250
247
  };
251
248
  beforeEach(() => {
252
249
  vi.spyOn(buildAccount, 'saveAccountToConfig').mockResolvedValue(mockParentAccountConfig.name);
253
- mockedGetAccountId.mockReturnValue(mockParentAccountConfig.accountId);
254
250
  mockedCreateV2Sandbox.mockResolvedValue({
255
251
  data: mockSandbox,
256
252
  });
@@ -271,10 +267,6 @@ describe('lib/buildAccount', () => {
271
267
  expect(result).toEqual({ sandbox: mockSandbox });
272
268
  expect(mockedGetPersonalAccessKey).toHaveBeenCalledWith(mockParentAccountConfig.accountId, mockSandbox.sandboxHubId);
273
269
  });
274
- it('should throw error if account ID is not found', async () => {
275
- mockedGetAccountId.mockReturnValue(null);
276
- await expect(buildAccount.buildV2Sandbox(mockSandbox.name, mockParentAccountConfig, HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX, false, mockParentAccountConfig.env, false)).rejects.toThrow();
277
- });
278
270
  it('should handle API errors when creating sandbox', async () => {
279
271
  mockedCreateV2Sandbox.mockRejectedValue(new Error('test-error'));
280
272
  await expect(buildAccount.buildV2Sandbox(mockSandbox.name, mockParentAccountConfig, HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX, false, mockParentAccountConfig.env, false)).rejects.toThrow();
@@ -1,9 +1,8 @@
1
1
  import { CMS_PUBLISH_MODE, DEFAULT_CMS_PUBLISH_MODE, } from '@hubspot/local-dev-lib/constants/files';
2
- import { getAndLoadConfigIfNeeded, getAccountConfig, loadConfigFromEnvironment, } from '@hubspot/local-dev-lib/config';
2
+ import { getConfig, getConfigAccountById } from '@hubspot/local-dev-lib/config';
3
3
  import { getCmsPublishMode } from '../commonOpts.js';
4
- const mockedGetAccountConfig = getAccountConfig;
5
- const mockedGetAndLoadConfigIfNeeded = getAndLoadConfigIfNeeded;
6
- const mockedLoadConfigFromEnvironment = loadConfigFromEnvironment;
4
+ const mockedGetConfigAccountById = getConfigAccountById;
5
+ const mockedGetConfig = getConfig;
7
6
  function buildArguments(args) {
8
7
  return {
9
8
  _: [],
@@ -42,8 +41,8 @@ describe('lib/commonOpts', () => {
42
41
  describe('cms publish mode option precedence', () => {
43
42
  describe('1. --cmsPublishMode', () => {
44
43
  it('should return the cms publish mode specified by the command option if present.', () => {
45
- mockedGetAndLoadConfigIfNeeded.mockReturnValue(configWithDefaultCmsPublishMode);
46
- mockedGetAccountConfig.mockReturnValue(devAccountConfig);
44
+ mockedGetConfig.mockReturnValue(configWithDefaultCmsPublishMode);
45
+ mockedGetConfigAccountById.mockReturnValue(devAccountConfig);
47
46
  expect(getCmsPublishMode(buildArguments({
48
47
  cmsPublishMode: CMS_PUBLISH_MODE.draft,
49
48
  }))).toBe(CMS_PUBLISH_MODE.draft);
@@ -54,9 +53,8 @@ describe('lib/commonOpts', () => {
54
53
  });
55
54
  describe('2. hubspot.config.yml -> config.accounts[x].defaultCmsPublishMode', () => {
56
55
  it('should return the defaultCmsPublishMode specified by the account specific config if present.', () => {
57
- mockedGetAndLoadConfigIfNeeded.mockReturnValue(configWithDefaultCmsPublishMode);
58
- mockedGetAccountConfig.mockReturnValue(devAccountConfig);
59
- mockedLoadConfigFromEnvironment.mockReturnValue(undefined);
56
+ mockedGetConfig.mockReturnValue(configWithDefaultCmsPublishMode);
57
+ mockedGetConfigAccountById.mockReturnValue(devAccountConfig);
60
58
  expect(getCmsPublishMode(buildArguments({
61
59
  account: accounts.DEV,
62
60
  }))).toBe(CMS_PUBLISH_MODE.draft);
@@ -64,9 +62,8 @@ describe('lib/commonOpts', () => {
64
62
  });
65
63
  describe('3. hubspot.config.yml -> config.defaultCmsPublishMode', () => {
66
64
  it('should return the defaultCmsPublishMode specified by the config if present.', () => {
67
- mockedGetAndLoadConfigIfNeeded.mockReturnValue(configWithDefaultCmsPublishMode);
68
- mockedGetAccountConfig.mockReturnValue(prodAccountConfig);
69
- mockedLoadConfigFromEnvironment.mockReturnValue(undefined);
65
+ mockedGetConfig.mockReturnValue(configWithDefaultCmsPublishMode);
66
+ mockedGetConfigAccountById.mockReturnValue(prodAccountConfig);
70
67
  expect(getCmsPublishMode(buildArguments({
71
68
  account: accounts.PROD,
72
69
  }))).toBe(CMS_PUBLISH_MODE.draft);
@@ -74,7 +71,6 @@ describe('lib/commonOpts', () => {
74
71
  });
75
72
  describe('4. DEFAULT_CMS_PUBLISH_MODE', () => {
76
73
  it('should return the defaultCmsPubishMode specified by the config if present.', () => {
77
- mockedLoadConfigFromEnvironment.mockReturnValue(undefined);
78
74
  expect(getCmsPublishMode(buildArguments({
79
75
  account: 'xxxxx',
80
76
  }))).toBe(DEFAULT_CMS_PUBLISH_MODE);
@@ -1,16 +1,18 @@
1
- import { getAccountId, getConfigAccounts } from '@hubspot/local-dev-lib/config';
1
+ import { getAllConfigAccounts, getConfigAccountIfExists, getConfigAccountById, } from '@hubspot/local-dev-lib/config';
2
2
  import { uiLogger } from '../ui/logger.js';
3
3
  import { HUBSPOT_ACCOUNT_TYPES } from '@hubspot/local-dev-lib/constants/config';
4
4
  import { fetchDeveloperTestAccounts } from '@hubspot/local-dev-lib/api/developerTestAccounts';
5
5
  import { mockHubSpotHttpError } from '../testUtils.js';
6
6
  import * as errorHandlers from '../errorHandlers/index.js';
7
7
  import { getHasDevTestAccounts, handleDeveloperTestAccountCreateError, validateDevTestAccountUsageLimits, } from '../developerTestAccounts.js';
8
+ import { logError } from '../errorHandlers/index.js';
8
9
  vi.mock('@hubspot/local-dev-lib/config');
9
10
  vi.mock('../ui/logger.js');
10
11
  vi.mock('@hubspot/local-dev-lib/api/developerTestAccounts');
11
12
  vi.mock('../errorHandlers');
12
- const mockedGetAccountId = getAccountId;
13
- const mockedGetConfigAccounts = getConfigAccounts;
13
+ const mockedGetConfigAccountIfExists = getConfigAccountIfExists;
14
+ const mockedGetAllConfigAccounts = getAllConfigAccounts;
15
+ const mockedGetConfigAccountById = getConfigAccountById;
14
16
  const mockedFetchDeveloperTestAccounts = fetchDeveloperTestAccounts;
15
17
  const APP_DEVELOPER_ACCOUNT_1 = {
16
18
  name: 'app-developer-1',
@@ -33,41 +35,43 @@ const accounts = [
33
35
  parentAccountId: APP_DEVELOPER_ACCOUNT_1.accountId,
34
36
  accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST,
35
37
  env: 'prod',
38
+ authType: 'personalaccesskey',
36
39
  },
37
40
  ];
38
41
  describe('lib/developerTestAccounts', () => {
39
42
  describe('getHasDevTestAccounts()', () => {
40
43
  it('should return true if there are developer test accounts associated with the account', () => {
41
- mockedGetAccountId.mockReturnValueOnce(APP_DEVELOPER_ACCOUNT_1.accountId);
42
- mockedGetConfigAccounts.mockReturnValueOnce(accounts);
44
+ mockedGetAllConfigAccounts.mockReturnValueOnce(accounts);
43
45
  const result = getHasDevTestAccounts(APP_DEVELOPER_ACCOUNT_1);
44
46
  expect(result).toBe(true);
45
47
  });
46
48
  it('should return false if there are no developer test accounts associated with the account', () => {
47
- mockedGetAccountId.mockReturnValueOnce(APP_DEVELOPER_ACCOUNT_2.accountId);
48
- mockedGetConfigAccounts.mockReturnValueOnce(accounts);
49
+ mockedGetConfigAccountIfExists.mockReturnValueOnce(APP_DEVELOPER_ACCOUNT_2);
50
+ mockedGetAllConfigAccounts.mockReturnValueOnce(accounts);
49
51
  const result = getHasDevTestAccounts(APP_DEVELOPER_ACCOUNT_2);
50
52
  expect(result).toBe(false);
51
53
  });
52
54
  it('should return false if there are no accounts configured', () => {
53
- mockedGetAccountId.mockReturnValueOnce(APP_DEVELOPER_ACCOUNT_1.accountId);
54
- mockedGetConfigAccounts.mockReturnValueOnce(undefined);
55
+ mockedGetConfigAccountIfExists.mockReturnValueOnce(APP_DEVELOPER_ACCOUNT_1);
56
+ mockedGetAllConfigAccounts.mockReturnValueOnce(undefined);
55
57
  const result = getHasDevTestAccounts(APP_DEVELOPER_ACCOUNT_1);
56
58
  expect(result).toBe(false);
57
59
  });
58
60
  });
59
61
  describe('validateDevTestAccountUsageLimits()', () => {
60
62
  afterEach(() => {
61
- mockedGetAccountId.mockRestore();
63
+ mockedGetConfigAccountIfExists.mockRestore();
62
64
  mockedFetchDeveloperTestAccounts.mockRestore();
63
65
  });
64
66
  it('should return null if the account id is not found', async () => {
65
- mockedGetAccountId.mockReturnValueOnce(undefined);
67
+ mockedFetchDeveloperTestAccounts.mockResolvedValueOnce({
68
+ data: null,
69
+ });
66
70
  const result = await validateDevTestAccountUsageLimits(APP_DEVELOPER_ACCOUNT_1);
67
71
  expect(result).toBe(null);
68
72
  });
69
73
  it('should return null if there is no developer test account data', async () => {
70
- mockedGetAccountId.mockReturnValueOnce(APP_DEVELOPER_ACCOUNT_1.accountId);
74
+ mockedGetConfigAccountIfExists.mockReturnValueOnce(APP_DEVELOPER_ACCOUNT_1);
71
75
  mockedFetchDeveloperTestAccounts.mockResolvedValueOnce({
72
76
  data: null,
73
77
  });
@@ -75,7 +79,7 @@ describe('lib/developerTestAccounts', () => {
75
79
  expect(result).toBe(null);
76
80
  });
77
81
  it('should return the test account data if the account has not reached the limit', async () => {
78
- mockedGetAccountId.mockReturnValueOnce(APP_DEVELOPER_ACCOUNT_1.accountId);
82
+ mockedGetConfigAccountIfExists.mockReturnValueOnce(APP_DEVELOPER_ACCOUNT_1);
79
83
  const testAccountData = {
80
84
  maxTestPortals: 10,
81
85
  results: [],
@@ -87,7 +91,7 @@ describe('lib/developerTestAccounts', () => {
87
91
  expect(result).toEqual(expect.objectContaining(testAccountData));
88
92
  });
89
93
  it('should throw an error if the account has reached the limit', async () => {
90
- mockedGetAccountId.mockReturnValueOnce(APP_DEVELOPER_ACCOUNT_1.accountId);
94
+ mockedGetConfigAccountIfExists.mockReturnValueOnce(APP_DEVELOPER_ACCOUNT_1);
91
95
  mockedFetchDeveloperTestAccounts.mockResolvedValueOnce({
92
96
  data: {
93
97
  maxTestPortals: 0,
@@ -103,6 +107,14 @@ describe('lib/developerTestAccounts', () => {
103
107
  beforeEach(() => {
104
108
  loggerErrorSpy = vi.spyOn(uiLogger, 'error');
105
109
  logErrorSpy = vi.spyOn(errorHandlers, 'logError');
110
+ // Mock account config for uiAccountDescription calls
111
+ mockedGetConfigAccountById.mockReturnValue({
112
+ accountId: APP_DEVELOPER_ACCOUNT_1.accountId,
113
+ name: 'Test Account',
114
+ authType: 'personalaccesskey',
115
+ personalAccessKey: 'test-key',
116
+ env: 'prod',
117
+ });
106
118
  });
107
119
  afterEach(() => {
108
120
  loggerErrorSpy.mockRestore();
@@ -117,7 +129,7 @@ describe('lib/developerTestAccounts', () => {
117
129
  },
118
130
  });
119
131
  expect(() => handleDeveloperTestAccountCreateError(missingScopesError, APP_DEVELOPER_ACCOUNT_1.accountId, 'prod', 10)).toThrow('Missing scopes error');
120
- expect(loggerErrorSpy).toHaveBeenCalled();
132
+ expect(uiLogger.error).toHaveBeenCalled();
121
133
  });
122
134
  it('should log and throw an error if the account is missing the required scopes', () => {
123
135
  const portalLimitReachedError = mockHubSpotHttpError('Portal limit reached error', {
@@ -128,7 +140,7 @@ describe('lib/developerTestAccounts', () => {
128
140
  },
129
141
  });
130
142
  expect(() => handleDeveloperTestAccountCreateError(portalLimitReachedError, APP_DEVELOPER_ACCOUNT_1.accountId, 'prod', 10)).toThrow('Portal limit reached error');
131
- expect(loggerErrorSpy).toHaveBeenCalled();
143
+ expect(uiLogger.error).toHaveBeenCalled();
132
144
  });
133
145
  it('should log a generic error message for an unknown error type', () => {
134
146
  const someUnknownError = mockHubSpotHttpError('Some unknown error', {
@@ -139,7 +151,7 @@ describe('lib/developerTestAccounts', () => {
139
151
  },
140
152
  });
141
153
  expect(() => handleDeveloperTestAccountCreateError(someUnknownError, APP_DEVELOPER_ACCOUNT_1.accountId, 'prod', 10)).toThrow('Some unknown error');
142
- expect(logErrorSpy).toHaveBeenCalled();
154
+ expect(logError).toHaveBeenCalled();
143
155
  });
144
156
  });
145
157
  });
@@ -1,6 +1,6 @@
1
1
  import { uiLogger } from '../ui/logger.js';
2
2
  import { createImport } from '@hubspot/local-dev-lib/api/crm';
3
- import { getAccountConfig, getAccountId } from '@hubspot/local-dev-lib/config';
3
+ import { getConfigAccountById, getConfigAccountIfExists, } from '@hubspot/local-dev-lib/config';
4
4
  import { handleImportData, handleTargetTestAccountSelectionFlow, } from '../importData.js';
5
5
  import { lib } from '../../lang/en.js';
6
6
  import { isDeveloperTestAccount, isStandardAccount, isAppDeveloperAccount, } from '../accountTypes.js';
@@ -13,8 +13,8 @@ vi.mock('../prompts/importDataTestAccountSelectPrompt');
13
13
  describe('lib/importData', () => {
14
14
  const mockUiLogger = vi.mocked(uiLogger);
15
15
  const mockCreateImport = vi.mocked(createImport);
16
- const mockGetAccountConfig = vi.mocked(getAccountConfig);
17
- const mockGetAccountId = vi.mocked(getAccountId);
16
+ const mockGetConfigAccountById = vi.mocked(getConfigAccountById);
17
+ const mockGetConfigAccountIfExists = vi.mocked(getConfigAccountIfExists);
18
18
  const mockIsDeveloperTestAccount = vi.mocked(isDeveloperTestAccount);
19
19
  const mockIsStandardAccount = vi.mocked(isStandardAccount);
20
20
  const mockIsAppDeveloperAccount = vi.mocked(isAppDeveloperAccount);
@@ -24,8 +24,8 @@ describe('lib/importData', () => {
24
24
  mockUiLogger.success.mockReset();
25
25
  mockUiLogger.error.mockReset();
26
26
  mockCreateImport.mockReset();
27
- mockGetAccountConfig.mockReset();
28
- mockGetAccountId.mockReset();
27
+ mockGetConfigAccountById.mockReset();
28
+ mockGetConfigAccountIfExists.mockReset();
29
29
  mockIsDeveloperTestAccount.mockReset();
30
30
  mockIsStandardAccount.mockReset();
31
31
  mockIsAppDeveloperAccount.mockReset();
@@ -56,32 +56,42 @@ describe('lib/importData', () => {
56
56
  const userProvidedAccountId = '1234';
57
57
  const derivedAccountId = 123456789;
58
58
  it('should error if the userProvidedAccountId is not the right account type', async () => {
59
- mockGetAccountConfig.mockReturnValue({});
60
- mockGetAccountId.mockReturnValue(1234);
59
+ mockGetConfigAccountIfExists.mockReturnValue({
60
+ accountId: 1234,
61
+ });
61
62
  mockIsDeveloperTestAccount.mockReturnValue(false);
62
63
  await expect(handleTargetTestAccountSelectionFlow(derivedAccountId, userProvidedAccountId)).rejects.toThrow(lib.importData.errors.notDeveloperTestAccount);
63
64
  });
64
65
  it('should error if the derivedAccountId belongs to the wrong account type', async () => {
65
- mockGetAccountConfig.mockReturnValue({});
66
+ mockGetConfigAccountIfExists.mockReturnValue({
67
+ accountId: derivedAccountId,
68
+ });
69
+ mockGetConfigAccountById.mockReturnValue({
70
+ accountId: derivedAccountId,
71
+ });
66
72
  mockIsDeveloperTestAccount.mockReturnValue(false);
67
73
  mockIsStandardAccount.mockReturnValue(false);
68
74
  mockIsAppDeveloperAccount.mockReturnValue(false);
69
75
  await expect(handleTargetTestAccountSelectionFlow(derivedAccountId, undefined)).rejects.toThrow(lib.importData.errors.incorrectAccountType(derivedAccountId));
70
76
  });
71
77
  it('should return the derivedAccountId if it is a developer test account', async () => {
72
- mockGetAccountConfig.mockReturnValue({});
73
78
  mockIsDeveloperTestAccount.mockReturnValue(true);
79
+ mockGetConfigAccountById.mockReturnValue({
80
+ accountId: derivedAccountId,
81
+ });
74
82
  const result = await handleTargetTestAccountSelectionFlow(derivedAccountId, undefined);
75
83
  expect(result).toBe(derivedAccountId);
76
84
  });
77
85
  it('should return the result of the importDataTestAccountSelectPrompt if the derivedAccountId is a standard or app developer account', async () => {
78
- mockGetAccountConfig.mockReturnValue({});
79
86
  mockIsDeveloperTestAccount.mockReturnValue(false);
80
87
  mockIsStandardAccount.mockReturnValue(true);
81
88
  mockIsAppDeveloperAccount.mockReturnValue(true);
82
89
  mockImportDataTestAccountSelectPrompt.mockResolvedValue({
83
90
  selectedAccountId: 890223,
84
91
  });
92
+ mockGetConfigAccountById.mockReturnValue({
93
+ accountId: derivedAccountId,
94
+ });
85
95
  const result = await handleTargetTestAccountSelectionFlow(derivedAccountId, undefined);
86
96
  expect(result).toBe(890223);
87
97
  });
@@ -1,7 +1,7 @@
1
1
  import express from 'express';
2
2
  import open from 'open';
3
3
  import { OAuth2Manager } from '@hubspot/local-dev-lib/models/OAuth2Manager';
4
- import { getAccountConfig } from '@hubspot/local-dev-lib/config';
4
+ import { getConfigAccountById } from '@hubspot/local-dev-lib/config';
5
5
  import { addOauthToAccountConfig } from '@hubspot/local-dev-lib/oauth';
6
6
  import { uiLogger } from '../ui/logger.js';
7
7
  import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
@@ -15,18 +15,22 @@ vi.mock('@hubspot/local-dev-lib/oauth');
15
15
  vi.mock('../ui/logger.js');
16
16
  const mockedExpress = express;
17
17
  const mockedOAuth2Manager = OAuth2Manager;
18
- const mockedGetAccountConfig = getAccountConfig;
18
+ const mockedGetConfigAccountById = getConfigAccountById;
19
19
  describe('lib/oauth', () => {
20
20
  const mockExpressReq = {
21
21
  query: { code: 'test-auth-code' },
22
22
  };
23
23
  const mockExpressResp = { send: vi.fn() };
24
24
  const mockAccountConfig = {
25
+ name: 'test-account',
25
26
  accountId: 123,
26
- clientId: 'test-client-id',
27
- clientSecret: 'test-client-secret',
28
- scopes: ['test-scope'],
27
+ authType: 'oauth2',
29
28
  env: ENVIRONMENTS.PROD,
29
+ auth: {
30
+ clientId: 'test-client-id',
31
+ clientSecret: 'test-client-secret',
32
+ scopes: ['test-scope'],
33
+ },
30
34
  };
31
35
  beforeEach(() => {
32
36
  mockedExpress.mockReturnValue({
@@ -49,7 +53,7 @@ describe('lib/oauth', () => {
49
53
  exchangeForTokens: vi.fn().mockResolvedValue({}),
50
54
  };
51
55
  mockedOAuth2Manager.mockImplementation(() => mockOAuth2Manager);
52
- mockedGetAccountConfig.mockReturnValue({
56
+ mockedGetConfigAccountById.mockReturnValue({
53
57
  env: ENVIRONMENTS.PROD,
54
58
  });
55
59
  await authenticateWithOauth(mockAccountConfig);
@@ -66,7 +70,10 @@ describe('lib/oauth', () => {
66
70
  it('should handle missing clientId', async () => {
67
71
  const invalidConfig = {
68
72
  ...mockAccountConfig,
69
- clientId: undefined,
73
+ auth: {
74
+ ...mockAccountConfig.auth,
75
+ clientId: undefined,
76
+ },
70
77
  };
71
78
  mockedOAuth2Manager.mockImplementation(() => ({
72
79
  account: invalidConfig,
@@ -74,6 +81,7 @@ describe('lib/oauth', () => {
74
81
  const exitSpy = vi.spyOn(process, 'exit').mockImplementation(() => {
75
82
  throw new Error('exit');
76
83
  });
84
+ // @ts-expect-error Testing invalid config
77
85
  await expect(authenticateWithOauth(invalidConfig)).rejects.toThrow('exit');
78
86
  expect(uiLogger.error).toHaveBeenCalled();
79
87
  expect(exitSpy).toHaveBeenCalled();
@@ -82,7 +90,10 @@ describe('lib/oauth', () => {
82
90
  it('should use default scopes when none provided', async () => {
83
91
  const configWithoutScopes = {
84
92
  ...mockAccountConfig,
85
- scopes: undefined,
93
+ auth: {
94
+ ...mockAccountConfig.auth,
95
+ scopes: [],
96
+ },
86
97
  };
87
98
  const mockOAuth2Manager = {
88
99
  account: configWithoutScopes,