@hubspot/cli 7.10.0 → 7.10.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.
- package/bin/cli.js +5 -4
- package/commands/__tests__/getStarted.test.js +10 -0
- package/commands/__tests__/project.test.js +3 -0
- package/commands/account/__tests__/rename.test.js +10 -3
- package/commands/account/auth.js +10 -14
- package/commands/account/clean.js +11 -19
- package/commands/account/createOverride.js +15 -11
- package/commands/account/info.js +8 -5
- package/commands/account/list.js +15 -19
- package/commands/account/remove.js +23 -22
- package/commands/account/removeOverride.js +6 -6
- package/commands/account/rename.js +2 -2
- package/commands/account/use.js +19 -8
- package/commands/app/__tests__/migrate.test.js +8 -4
- package/commands/app/migrate.js +2 -2
- package/commands/auth.js +18 -14
- package/commands/config/migrate.js +5 -5
- package/commands/customObject/createSchema.js +2 -3
- package/commands/customObject/updateSchema.js +2 -3
- package/commands/getStarted.js +2 -3
- package/commands/hubdb/__tests__/list.test.js +1 -0
- package/commands/hubdb/list.js +2 -2
- package/commands/init.js +36 -32
- package/commands/project/__tests__/deploy.test.js +10 -5
- package/commands/project/__tests__/devUnifiedFlow.test.js +6 -4
- package/commands/project/__tests__/lint.test.js +709 -0
- package/commands/project/__tests__/logs.test.js +4 -0
- package/commands/project/__tests__/validate.test.js +286 -28
- package/commands/project/cloneApp.js +2 -2
- package/commands/project/deploy.js +16 -8
- package/commands/project/dev/deprecatedFlow.js +4 -5
- package/commands/project/dev/index.js +19 -7
- package/commands/project/dev/unifiedFlow.js +4 -5
- package/commands/project/lint.d.ts +6 -0
- package/commands/project/lint.js +178 -0
- package/commands/project/logs.js +2 -3
- package/commands/project/migrate.js +4 -13
- package/commands/project/profile/add.js +6 -7
- package/commands/project/profile/delete.js +2 -2
- package/commands/project/upload.js +10 -4
- package/commands/project/validate.js +73 -13
- package/commands/project.js +2 -0
- package/commands/sandbox/__tests__/create.test.js +14 -5
- package/commands/sandbox/create.js +4 -5
- package/commands/sandbox/delete.js +23 -20
- package/commands/testAccount/create.js +2 -2
- package/commands/testAccount/delete.js +9 -8
- package/lang/en.d.ts +48 -11
- package/lang/en.js +58 -15
- package/lib/__tests__/buildAccount.test.js +22 -30
- package/lib/__tests__/commonOpts.test.js +9 -13
- package/lib/__tests__/developerTestAccounts.test.js +29 -17
- package/lib/__tests__/importData.test.js +20 -10
- package/lib/__tests__/oauth.test.js +19 -8
- package/lib/__tests__/projectProfiles.test.js +273 -32
- package/lib/__tests__/sandboxSync.test.js +33 -11
- package/lib/__tests__/sandboxes.test.js +30 -19
- package/lib/__tests__/usageTracking.test.js +10 -10
- package/lib/__tests__/validation.test.js +32 -32
- package/lib/accountTypes.d.ts +9 -9
- package/lib/accountTypes.js +2 -4
- package/lib/app/__tests__/migrate.test.js +15 -0
- package/lib/app/__tests__/migrate_legacy.test.js +9 -0
- package/lib/app/migrate_legacy.d.ts +2 -2
- package/lib/buildAccount.d.ts +4 -4
- package/lib/buildAccount.js +7 -14
- package/lib/commonOpts.js +3 -3
- package/lib/configMigrate.d.ts +2 -2
- package/lib/configMigrate.js +42 -18
- package/lib/configOptions.js +3 -2
- package/lib/developerTestAccounts.d.ts +3 -3
- package/lib/developerTestAccounts.js +4 -7
- package/lib/doctor/DiagnosticInfoBuilder.d.ts +1 -1
- package/lib/doctor/DiagnosticInfoBuilder.js +9 -6
- package/lib/doctor/Doctor.js +4 -3
- package/lib/doctor/__tests__/Diagnosis.test.js +4 -3
- package/lib/doctor/__tests__/DiagnosticInfoBuilder.test.js +17 -9
- package/lib/doctor/__tests__/Doctor.test.js +14 -0
- package/lib/importData.js +8 -7
- package/lib/links.js +5 -5
- package/lib/middleware/{__test__ → __tests__}/commandTargetingUtils.test.js +3 -3
- package/lib/middleware/{__test__ → __tests__}/configMiddleware.test.js +23 -22
- package/lib/middleware/{__test__ → __tests__}/gitMiddleware.test.js +9 -7
- package/lib/middleware/autoUpdateMiddleware.js +34 -23
- package/lib/middleware/commandTargetingUtils.js +3 -2
- package/lib/middleware/configMiddleware.d.ts +6 -1
- package/lib/middleware/configMiddleware.js +36 -15
- package/lib/middleware/fireAlarmMiddleware.js +4 -15
- package/lib/middleware/gitMiddleware.js +8 -4
- package/lib/oauth.d.ts +2 -2
- package/lib/oauth.js +8 -10
- package/lib/projectProfiles.d.ts +4 -3
- package/lib/projectProfiles.js +78 -32
- package/lib/projects/__tests__/AppDevModeInterface.test.js +17 -6
- package/lib/projects/__tests__/DevServerManager.test.js +1 -0
- package/lib/projects/__tests__/LocalDevProcess.test.js +1 -0
- package/lib/projects/__tests__/deploy.test.js +1 -0
- package/lib/projects/__tests__/uieLinting.test.js +640 -0
- package/lib/projects/create/__tests__/v2.test.js +11 -0
- package/lib/projects/localDev/AppDevModeInterface.js +2 -2
- package/lib/projects/localDev/DevServerManager_DEPRECATED.js +2 -2
- package/lib/projects/localDev/LocalDevLogger.js +4 -4
- package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +3 -3
- package/lib/projects/localDev/helpers/account.d.ts +10 -10
- package/lib/projects/localDev/helpers/account.js +6 -11
- package/lib/projects/uieLinting.d.ts +33 -0
- package/lib/projects/uieLinting.js +222 -0
- package/lib/projects/urls.js +5 -6
- package/lib/prompts/__tests__/downloadProjectPrompt.test.js +7 -5
- package/lib/prompts/accountNamePrompt.js +3 -3
- package/lib/prompts/accountsPrompt.d.ts +1 -1
- package/lib/prompts/accountsPrompt.js +6 -7
- package/lib/prompts/confirmImportDataPrompt.js +2 -2
- package/lib/prompts/downloadProjectPrompt.d.ts +1 -0
- package/lib/prompts/downloadProjectPrompt.js +5 -2
- package/lib/prompts/importDataTestAccountSelectPrompt.js +4 -5
- package/lib/prompts/personalAccessKeyPrompt.js +2 -2
- package/lib/prompts/projectDevTargetAccountPrompt.d.ts +3 -3
- package/lib/prompts/projectDevTargetAccountPrompt.js +5 -7
- package/lib/prompts/sandboxesPrompt.js +7 -8
- package/lib/prompts/setAsDefaultAccountPrompt.js +7 -6
- package/lib/sandboxSync.d.ts +2 -2
- package/lib/sandboxSync.js +3 -9
- package/lib/sandboxes.d.ts +4 -4
- package/lib/sandboxes.js +6 -11
- package/lib/serverlessLogs.js +2 -2
- package/lib/theme/__tests__/migrate.test.js +15 -0
- package/lib/ui/index.js +6 -3
- package/lib/usageTracking.js +15 -8
- package/lib/validation.js +13 -11
- package/mcp-server/tools/cms/HsCreateFunctionTool.js +4 -2
- package/mcp-server/tools/cms/HsCreateModuleTool.js +4 -2
- package/mcp-server/tools/cms/HsCreateTemplateTool.js +4 -2
- package/mcp-server/tools/cms/HsFunctionLogsTool.js +4 -2
- package/mcp-server/tools/cms/HsListFunctionsTool.js +3 -1
- package/mcp-server/tools/cms/HsListTool.js +3 -1
- package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -0
- package/mcp-server/tools/index.js +4 -0
- package/mcp-server/tools/project/AddFeatureToProjectTool.js +4 -2
- package/mcp-server/tools/project/CreateProjectTool.js +4 -2
- package/mcp-server/tools/project/CreateTestAccountTool.js +17 -7
- package/mcp-server/tools/project/DeployProjectTool.js +3 -1
- package/mcp-server/tools/project/DocFetchTool.js +6 -4
- package/mcp-server/tools/project/DocsSearchTool.d.ts +1 -1
- package/mcp-server/tools/project/DocsSearchTool.js +10 -8
- package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.d.ts +1 -1
- package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +9 -7
- package/mcp-server/tools/project/GetApplicationInfoTool.js +8 -6
- package/mcp-server/tools/project/GetBuildLogsTool.d.ts +26 -0
- package/mcp-server/tools/project/GetBuildLogsTool.js +125 -0
- package/mcp-server/tools/project/GetBuildStatusTool.d.ts +26 -0
- package/mcp-server/tools/project/GetBuildStatusTool.js +166 -0
- package/mcp-server/tools/project/GetConfigValuesTool.d.ts +1 -1
- package/mcp-server/tools/project/GetConfigValuesTool.js +9 -7
- package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +1 -1
- package/mcp-server/tools/project/GuidedWalkthroughTool.js +5 -3
- package/mcp-server/tools/project/UploadProjectTools.js +3 -1
- package/mcp-server/tools/project/ValidateProjectTool.js +4 -2
- package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.js +12 -2
- package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +5 -1
- package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +23 -11
- package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.js +7 -5
- package/mcp-server/tools/project/__tests__/GetApplicationInfoTool.test.js +7 -5
- package/mcp-server/tools/project/__tests__/GetBuildLogsTool.test.d.ts +1 -0
- package/mcp-server/tools/project/__tests__/GetBuildLogsTool.test.js +305 -0
- package/mcp-server/tools/project/__tests__/GetBuildStatusTool.test.d.ts +1 -0
- package/mcp-server/tools/project/__tests__/GetBuildStatusTool.test.js +240 -0
- package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +8 -6
- package/mcp-server/utils/__tests__/content.test.js +21 -20
- package/mcp-server/utils/__tests__/feedbackTracking.test.js +34 -28
- package/mcp-server/utils/config.d.ts +1 -0
- package/mcp-server/utils/config.js +10 -0
- package/mcp-server/utils/content.d.ts +1 -1
- package/mcp-server/utils/content.js +2 -2
- package/mcp-server/utils/feedbackTracking.d.ts +1 -1
- package/mcp-server/utils/feedbackTracking.js +3 -3
- package/mcp-server/utils/toolUsageTracking.js +4 -3
- package/package.json +9 -9
- package/ui/components/BoxWithTitle.d.ts +2 -1
- package/ui/components/BoxWithTitle.js +2 -2
- package/ui/components/StatusMessageBoxes.d.ts +5 -4
- package/ui/components/StatusMessageBoxes.js +8 -8
- package/lib/middleware/__test__/notificationsMiddleware.test.js +0 -8
- package/lib/middleware/notificationsMiddleware.d.ts +0 -1
- package/lib/middleware/notificationsMiddleware.js +0 -28
- package/lib/ui/boxen.d.ts +0 -5
- package/lib/ui/boxen.js +0 -26
- package/mcp-server/utils/__tests__/cliConfig.test.js +0 -110
- package/mcp-server/utils/cliConfig.d.ts +0 -1
- package/mcp-server/utils/cliConfig.js +0 -12
- /package/{lib/middleware/__test__/commandTargetingUtils.test.d.ts → commands/project/__tests__/lint.test.d.ts} +0 -0
- /package/lib/middleware/{__test__/configMiddleware.test.d.ts → __tests__/commandTargetingUtils.test.d.ts} +0 -0
- /package/lib/middleware/{__test__/gitMiddleware.test.d.ts → __tests__/configMiddleware.test.d.ts} +0 -0
- /package/lib/middleware/{__test__/notificationsMiddleware.test.d.ts → __tests__/gitMiddleware.test.d.ts} +0 -0
- /package/lib/middleware/{__test__ → __tests__}/requestMiddleware.test.d.ts +0 -0
- /package/lib/middleware/{__test__ → __tests__}/requestMiddleware.test.js +0 -0
- /package/lib/middleware/{__test__ → __tests__}/yargsChecksMiddleware.test.d.ts +0 -0
- /package/lib/middleware/{__test__ → __tests__}/yargsChecksMiddleware.test.js +0 -0
- /package/{mcp-server/utils/__tests__/cliConfig.test.d.ts → lib/projects/__tests__/uieLinting.test.d.ts} +0 -0
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import { loadHsProfileFile, getHsProfileFilename, getAllHsProfiles, } from '@hubspot/project-parsing-lib';
|
|
2
|
+
import { loadHsProfileFile, getHsProfileFilename, getAllHsProfiles, validateProfileVariables, } from '@hubspot/project-parsing-lib';
|
|
3
3
|
import { lib } from '../../lang/en.js';
|
|
4
4
|
import { uiBetaTag, uiLine } from '../ui/index.js';
|
|
5
5
|
import { uiLogger } from '../ui/logger.js';
|
|
6
|
-
import {
|
|
7
|
-
import { logProfileHeader, logProfileFooter, loadProfile,
|
|
6
|
+
import { getConfigAccountById } from '@hubspot/local-dev-lib/config';
|
|
7
|
+
import { logProfileHeader, logProfileFooter, loadProfile, enforceProfileUsage, loadAndValidateProfile, validateProjectForProfile, } from '../projectProfiles.js';
|
|
8
|
+
import { handleTranslate } from '../projects/upload.js';
|
|
9
|
+
import SpinniesManager from '../ui/SpinniesManager.js';
|
|
10
|
+
import { commands } from '../../lang/en.js';
|
|
8
11
|
// Mock dependencies
|
|
9
12
|
vi.mock('@hubspot/project-parsing-lib');
|
|
13
|
+
vi.mock('@hubspot/local-dev-lib/config');
|
|
10
14
|
vi.mock('../ui');
|
|
11
15
|
vi.mock('../ui/logger');
|
|
12
16
|
vi.mock('../../lang/en');
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
throw new Error(`Process.exit called with code ${code}`);
|
|
16
|
-
});
|
|
17
|
+
vi.mock('../projects/upload');
|
|
18
|
+
vi.mock('../ui/SpinniesManager');
|
|
17
19
|
const mockedLoadHsProfileFile = loadHsProfileFile;
|
|
18
20
|
const mockedGetHsProfileFilename = getHsProfileFilename;
|
|
19
21
|
const mockedGetAllHsProfiles = getAllHsProfiles;
|
|
22
|
+
const mockedValidateProfileVariables = validateProfileVariables;
|
|
23
|
+
const mockedGetConfigAccountById = getConfigAccountById;
|
|
20
24
|
const mockedUiBetaTag = uiBetaTag;
|
|
21
25
|
const mockedUiLine = uiLine;
|
|
22
26
|
const mockedUiLogger = uiLogger;
|
|
@@ -68,62 +72,299 @@ describe('lib/projectProfiles', () => {
|
|
|
68
72
|
const mockProfile = {
|
|
69
73
|
accountId: 123,
|
|
70
74
|
};
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
vi.clearAllMocks();
|
|
77
|
+
});
|
|
78
|
+
it('should throw error when project config is missing', () => {
|
|
79
|
+
expect(() => loadProfile(null, mockProjectDir, mockProfileName)).toThrow(lib.projectProfiles.loadProfile.errors.noProjectConfig);
|
|
75
80
|
});
|
|
76
|
-
it('should
|
|
81
|
+
it('should throw error when project dir is missing', () => {
|
|
82
|
+
expect(() => loadProfile(mockProjectConfig, null, mockProfileName)).toThrow(lib.projectProfiles.loadProfile.errors.noProjectConfig);
|
|
83
|
+
});
|
|
84
|
+
it('should throw error when profile is not found', () => {
|
|
77
85
|
mockedLoadHsProfileFile.mockReturnValue(null);
|
|
78
86
|
const filename = 'test-profile.hsprofile';
|
|
79
87
|
mockedGetHsProfileFilename.mockReturnValue(filename);
|
|
80
|
-
|
|
81
|
-
expect(result).toBeUndefined();
|
|
82
|
-
expect(mockedUiLogger.error).toHaveBeenCalledWith(lib.projectProfiles.loadProfile.errors.profileNotFound(filename));
|
|
88
|
+
expect(() => loadProfile(mockProjectConfig, mockProjectDir, mockProfileName)).toThrow(lib.projectProfiles.loadProfile.errors.profileNotFound(filename));
|
|
83
89
|
});
|
|
84
|
-
it('should
|
|
90
|
+
it('should throw error when profile has no account ID', () => {
|
|
85
91
|
mockedLoadHsProfileFile.mockReturnValue({});
|
|
86
92
|
const filename = 'test-profile.hsprofile';
|
|
87
93
|
mockedGetHsProfileFilename.mockReturnValue(filename);
|
|
88
|
-
|
|
89
|
-
expect(result).toBeUndefined();
|
|
90
|
-
expect(mockedUiLogger.error).toHaveBeenCalledWith(lib.projectProfiles.loadProfile.errors.missingAccountId(filename));
|
|
94
|
+
expect(() => loadProfile(mockProjectConfig, mockProjectDir, mockProfileName)).toThrow(lib.projectProfiles.loadProfile.errors.missingAccountId(filename));
|
|
91
95
|
});
|
|
92
|
-
it('should
|
|
96
|
+
it('should throw error when profile loading fails', () => {
|
|
93
97
|
mockedLoadHsProfileFile.mockImplementation(() => {
|
|
94
98
|
throw new Error('Load failed');
|
|
95
99
|
});
|
|
96
100
|
const filename = 'test-profile.hsprofile';
|
|
97
101
|
mockedGetHsProfileFilename.mockReturnValue(filename);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
102
|
+
expect(() => loadProfile(mockProjectConfig, mockProjectDir, mockProfileName)).toThrow(lib.projectProfiles.loadProfile.errors.failedToLoadProfile(filename));
|
|
103
|
+
});
|
|
104
|
+
it('should throw error when account is not found in config', () => {
|
|
105
|
+
mockedLoadHsProfileFile.mockReturnValue(mockProfile);
|
|
106
|
+
mockedGetConfigAccountById.mockImplementation(() => {
|
|
107
|
+
throw new Error('Account not found');
|
|
108
|
+
});
|
|
109
|
+
const filename = 'test-profile.hsprofile';
|
|
110
|
+
mockedGetHsProfileFilename.mockReturnValue(filename);
|
|
111
|
+
expect(() => loadProfile(mockProjectConfig, mockProjectDir, mockProfileName)).toThrow(lib.projectProfiles.loadProfile.errors.listedAccountNotFound(mockProfile.accountId, filename));
|
|
101
112
|
});
|
|
102
113
|
it('should return profile when loading succeeds', () => {
|
|
103
114
|
mockedLoadHsProfileFile.mockReturnValue(mockProfile);
|
|
115
|
+
mockedGetConfigAccountById.mockReturnValue({
|
|
116
|
+
accountId: mockProfile.accountId,
|
|
117
|
+
});
|
|
104
118
|
const result = loadProfile(mockProjectConfig, mockProjectDir, mockProfileName);
|
|
105
119
|
expect(result).toEqual(mockProfile);
|
|
106
120
|
expect(mockedLoadHsProfileFile).toHaveBeenCalledWith(path.join(mockProjectDir, mockProjectConfig.srcDir), mockProfileName);
|
|
121
|
+
expect(mockedGetConfigAccountById).toHaveBeenCalledWith(mockProfile.accountId);
|
|
107
122
|
});
|
|
108
123
|
});
|
|
109
|
-
describe('
|
|
124
|
+
describe('enforceProfileUsage()', () => {
|
|
110
125
|
const mockProjectConfig = {
|
|
111
126
|
srcDir: 'src',
|
|
112
127
|
name: 'test-project',
|
|
113
128
|
platformVersion: '1.0.0',
|
|
114
129
|
};
|
|
115
130
|
const mockProjectDir = '/test/project';
|
|
116
|
-
|
|
131
|
+
beforeEach(() => {
|
|
132
|
+
vi.clearAllMocks();
|
|
133
|
+
});
|
|
134
|
+
it('should not throw when no profiles exist', async () => {
|
|
117
135
|
mockedGetAllHsProfiles.mockResolvedValue([]);
|
|
118
|
-
await
|
|
119
|
-
expect(mockedUiLogger.error).not.toHaveBeenCalled();
|
|
120
|
-
expect(mockExit).not.toHaveBeenCalled();
|
|
136
|
+
await expect(enforceProfileUsage(mockProjectConfig, mockProjectDir)).resolves.toBeUndefined();
|
|
121
137
|
});
|
|
122
|
-
it('should
|
|
138
|
+
it('should throw error when profiles exist', async () => {
|
|
123
139
|
mockedGetAllHsProfiles.mockResolvedValue(['profile1', 'profile2']);
|
|
124
|
-
await expect(
|
|
125
|
-
|
|
126
|
-
|
|
140
|
+
await expect(enforceProfileUsage(mockProjectConfig, mockProjectDir)).rejects.toThrow(lib.projectProfiles.exitIfUsingProfiles.errors.noProfileSpecified);
|
|
141
|
+
});
|
|
142
|
+
it('should not throw when project config is null', async () => {
|
|
143
|
+
await expect(enforceProfileUsage(null, mockProjectDir)).resolves.toBeUndefined();
|
|
144
|
+
});
|
|
145
|
+
it('should not throw when project dir is null', async () => {
|
|
146
|
+
await expect(enforceProfileUsage(mockProjectConfig, null)).resolves.toBeUndefined();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('loadAndValidateProfile()', () => {
|
|
150
|
+
const mockProjectConfig = {
|
|
151
|
+
srcDir: 'src',
|
|
152
|
+
name: 'test-project',
|
|
153
|
+
platformVersion: '1.0.0',
|
|
154
|
+
};
|
|
155
|
+
const mockProjectDir = '/test/project';
|
|
156
|
+
const mockProfileName = 'test-profile';
|
|
157
|
+
const mockProfile = {
|
|
158
|
+
accountId: 123,
|
|
159
|
+
variables: {
|
|
160
|
+
key1: 'value1',
|
|
161
|
+
key2: 'value2',
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
beforeEach(() => {
|
|
165
|
+
vi.clearAllMocks();
|
|
166
|
+
});
|
|
167
|
+
it('should enforce profile usage when no profile name provided', async () => {
|
|
168
|
+
mockedGetAllHsProfiles.mockResolvedValue([]);
|
|
169
|
+
const result = await loadAndValidateProfile(mockProjectConfig, mockProjectDir, undefined);
|
|
170
|
+
expect(result).toBeUndefined();
|
|
171
|
+
expect(mockedGetAllHsProfiles).toHaveBeenCalledWith(path.join(mockProjectDir, mockProjectConfig.srcDir));
|
|
172
|
+
});
|
|
173
|
+
it('should throw when profiles exist but no profile name provided', async () => {
|
|
174
|
+
mockedGetAllHsProfiles.mockResolvedValue(['profile1']);
|
|
175
|
+
await expect(loadAndValidateProfile(mockProjectConfig, mockProjectDir, undefined)).rejects.toThrow(lib.projectProfiles.exitIfUsingProfiles.errors.noProfileSpecified);
|
|
176
|
+
});
|
|
177
|
+
it('should load and return account ID when profile is valid', async () => {
|
|
178
|
+
mockedLoadHsProfileFile.mockReturnValue(mockProfile);
|
|
179
|
+
mockedGetConfigAccountById.mockReturnValue({
|
|
180
|
+
accountId: mockProfile.accountId,
|
|
181
|
+
});
|
|
182
|
+
mockedGetHsProfileFilename.mockReturnValue('test-profile.hsprofile');
|
|
183
|
+
mockedValidateProfileVariables.mockReturnValue({ success: true });
|
|
184
|
+
const result = await loadAndValidateProfile(mockProjectConfig, mockProjectDir, mockProfileName);
|
|
185
|
+
expect(result).toBe(mockProfile.accountId);
|
|
186
|
+
expect(mockedLoadHsProfileFile).toHaveBeenCalledWith(path.join(mockProjectDir, mockProjectConfig.srcDir), mockProfileName);
|
|
187
|
+
expect(mockedValidateProfileVariables).toHaveBeenCalledWith(mockProfile.variables, mockProfileName);
|
|
188
|
+
});
|
|
189
|
+
it('should log profile header and footer when not silent', async () => {
|
|
190
|
+
mockedLoadHsProfileFile.mockReturnValue(mockProfile);
|
|
191
|
+
mockedGetConfigAccountById.mockReturnValue({
|
|
192
|
+
accountId: mockProfile.accountId,
|
|
193
|
+
});
|
|
194
|
+
mockedGetHsProfileFilename.mockReturnValue('test-profile.hsprofile');
|
|
195
|
+
mockedValidateProfileVariables.mockReturnValue({ success: true });
|
|
196
|
+
await loadAndValidateProfile(mockProjectConfig, mockProjectDir, mockProfileName, false);
|
|
197
|
+
expect(mockedUiBetaTag).toHaveBeenCalled();
|
|
198
|
+
expect(mockedUiLine).toHaveBeenCalled();
|
|
199
|
+
expect(mockedUiLogger.log).toHaveBeenCalled();
|
|
200
|
+
});
|
|
201
|
+
it('should not log when silent is true', async () => {
|
|
202
|
+
mockedLoadHsProfileFile.mockReturnValue(mockProfile);
|
|
203
|
+
mockedGetConfigAccountById.mockReturnValue({
|
|
204
|
+
accountId: mockProfile.accountId,
|
|
205
|
+
});
|
|
206
|
+
mockedValidateProfileVariables.mockReturnValue({ success: true });
|
|
207
|
+
await loadAndValidateProfile(mockProjectConfig, mockProjectDir, mockProfileName, true);
|
|
208
|
+
expect(mockedUiBetaTag).not.toHaveBeenCalled();
|
|
209
|
+
expect(mockedUiLine).not.toHaveBeenCalled();
|
|
210
|
+
});
|
|
211
|
+
it('should throw error when profile variables are invalid', async () => {
|
|
212
|
+
const invalidProfile = {
|
|
213
|
+
accountId: 123,
|
|
214
|
+
variables: {
|
|
215
|
+
invalid: 'value',
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
const validationErrors = ['Variable "invalid" is not allowed'];
|
|
219
|
+
mockedLoadHsProfileFile.mockReturnValue(invalidProfile);
|
|
220
|
+
mockedGetConfigAccountById.mockReturnValue({
|
|
221
|
+
accountId: invalidProfile.accountId,
|
|
222
|
+
});
|
|
223
|
+
mockedGetHsProfileFilename.mockReturnValue('test-profile.hsprofile');
|
|
224
|
+
mockedValidateProfileVariables.mockReturnValue({
|
|
225
|
+
success: false,
|
|
226
|
+
errors: validationErrors,
|
|
227
|
+
});
|
|
228
|
+
await expect(loadAndValidateProfile(mockProjectConfig, mockProjectDir, mockProfileName)).rejects.toThrow(lib.projectProfiles.loadProfile.errors.profileNotValid('test-profile.hsprofile', validationErrors));
|
|
229
|
+
});
|
|
230
|
+
it('should not validate when profile has no variables', async () => {
|
|
231
|
+
const profileWithoutVars = {
|
|
232
|
+
accountId: 123,
|
|
233
|
+
};
|
|
234
|
+
mockedLoadHsProfileFile.mockReturnValue(profileWithoutVars);
|
|
235
|
+
mockedGetConfigAccountById.mockReturnValue({
|
|
236
|
+
accountId: profileWithoutVars.accountId,
|
|
237
|
+
});
|
|
238
|
+
mockedGetHsProfileFilename.mockReturnValue('test-profile.hsprofile');
|
|
239
|
+
const result = await loadAndValidateProfile(mockProjectConfig, mockProjectDir, mockProfileName);
|
|
240
|
+
expect(result).toBe(profileWithoutVars.accountId);
|
|
241
|
+
expect(mockedValidateProfileVariables).not.toHaveBeenCalled();
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
describe('validateProjectForProfile()', () => {
|
|
245
|
+
const mockProjectConfig = {
|
|
246
|
+
srcDir: 'src',
|
|
247
|
+
name: 'test-project',
|
|
248
|
+
platformVersion: '2025.2',
|
|
249
|
+
};
|
|
250
|
+
const mockProjectDir = '/test/project';
|
|
251
|
+
const mockProfileName = 'test-profile';
|
|
252
|
+
const mockDerivedAccountId = 123;
|
|
253
|
+
const mockProfileFilename = 'test-profile.hsprofile';
|
|
254
|
+
const mockProfile = {
|
|
255
|
+
accountId: mockDerivedAccountId,
|
|
256
|
+
};
|
|
257
|
+
beforeEach(() => {
|
|
258
|
+
vi.clearAllMocks();
|
|
259
|
+
mockedGetHsProfileFilename.mockReturnValue(mockProfileFilename);
|
|
260
|
+
vi.mocked(SpinniesManager.init);
|
|
261
|
+
vi.mocked(SpinniesManager.add);
|
|
262
|
+
vi.mocked(SpinniesManager.succeed);
|
|
263
|
+
vi.mocked(SpinniesManager.fail);
|
|
264
|
+
// Mock dependencies for loadAndValidateProfile
|
|
265
|
+
mockedGetAllHsProfiles.mockResolvedValue([]);
|
|
266
|
+
mockedLoadHsProfileFile.mockReturnValue(mockProfile);
|
|
267
|
+
mockedGetConfigAccountById.mockReturnValue({
|
|
268
|
+
accountId: mockDerivedAccountId,
|
|
269
|
+
});
|
|
270
|
+
mockedValidateProfileVariables.mockReturnValue({ success: true });
|
|
271
|
+
vi.mocked(handleTranslate).mockResolvedValue(undefined);
|
|
272
|
+
});
|
|
273
|
+
it('should return empty array when validation succeeds', async () => {
|
|
274
|
+
const result = await validateProjectForProfile(mockProjectConfig, mockProjectDir, mockProfileName, mockDerivedAccountId);
|
|
275
|
+
expect(result).toEqual([]);
|
|
276
|
+
expect(SpinniesManager.init).toHaveBeenCalled();
|
|
277
|
+
expect(SpinniesManager.add).toHaveBeenCalledWith(`validatingProfile-${mockProfileName}`, {
|
|
278
|
+
text: commands.project.validate.spinners.validatingProfile(mockProfileFilename),
|
|
279
|
+
indent: 0,
|
|
280
|
+
});
|
|
281
|
+
expect(SpinniesManager.succeed).toHaveBeenCalledWith(`validatingProfile-${mockProfileName}`, {
|
|
282
|
+
text: commands.project.validate.spinners.profileValidationSucceeded(mockProfileFilename),
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
it('should call handleTranslate with profile account ID from profile', async () => {
|
|
286
|
+
await validateProjectForProfile(mockProjectConfig, mockProjectDir, mockProfileName, mockDerivedAccountId);
|
|
287
|
+
expect(handleTranslate).toHaveBeenCalledWith(mockProjectDir, mockProjectConfig, mockDerivedAccountId, false, mockProfileName);
|
|
288
|
+
});
|
|
289
|
+
it('should call handleTranslate with different profile account ID when profile has different ID', async () => {
|
|
290
|
+
const profileAccountId = 456;
|
|
291
|
+
const profileWithDifferentId = {
|
|
292
|
+
accountId: profileAccountId,
|
|
293
|
+
};
|
|
294
|
+
mockedLoadHsProfileFile.mockReturnValue(profileWithDifferentId);
|
|
295
|
+
mockedGetConfigAccountById.mockReturnValue({
|
|
296
|
+
accountId: profileAccountId,
|
|
297
|
+
});
|
|
298
|
+
await validateProjectForProfile(mockProjectConfig, mockProjectDir, mockProfileName, mockDerivedAccountId);
|
|
299
|
+
expect(handleTranslate).toHaveBeenCalledWith(mockProjectDir, mockProjectConfig, profileAccountId, false, mockProfileName);
|
|
300
|
+
});
|
|
301
|
+
it('should return error when profile has no accountId', async () => {
|
|
302
|
+
// @ts-expect-error causing an error on purpose
|
|
303
|
+
const profileWithoutId = {};
|
|
304
|
+
mockedLoadHsProfileFile.mockReturnValue(profileWithoutId);
|
|
305
|
+
const result = await validateProjectForProfile(mockProjectConfig, mockProjectDir, mockProfileName, mockDerivedAccountId);
|
|
306
|
+
expect(result.length).toBeGreaterThan(0);
|
|
307
|
+
expect(SpinniesManager.fail).toHaveBeenCalled();
|
|
308
|
+
expect(handleTranslate).not.toHaveBeenCalled();
|
|
309
|
+
});
|
|
310
|
+
it('should indent spinners when indentSpinners is true', async () => {
|
|
311
|
+
await validateProjectForProfile(mockProjectConfig, mockProjectDir, mockProfileName, mockDerivedAccountId, true);
|
|
312
|
+
expect(SpinniesManager.add).toHaveBeenCalledWith(`validatingProfile-${mockProfileName}`, {
|
|
313
|
+
text: commands.project.validate.spinners.validatingProfile(mockProfileFilename),
|
|
314
|
+
indent: 4,
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
it('should not indent spinners when indentSpinners is false', async () => {
|
|
318
|
+
await validateProjectForProfile(mockProjectConfig, mockProjectDir, mockProfileName, mockDerivedAccountId, false);
|
|
319
|
+
expect(SpinniesManager.add).toHaveBeenCalledWith(`validatingProfile-${mockProfileName}`, {
|
|
320
|
+
text: commands.project.validate.spinners.validatingProfile(mockProfileFilename),
|
|
321
|
+
indent: 0,
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
it('should return error array when profile loading fails', async () => {
|
|
325
|
+
mockedLoadHsProfileFile.mockReturnValue(null);
|
|
326
|
+
const result = await validateProjectForProfile(mockProjectConfig, mockProjectDir, mockProfileName, mockDerivedAccountId);
|
|
327
|
+
expect(result.length).toBeGreaterThan(0);
|
|
328
|
+
expect(SpinniesManager.fail).toHaveBeenCalledWith(`validatingProfile-${mockProfileName}`, {
|
|
329
|
+
text: commands.project.validate.spinners.profileValidationFailed(mockProfileFilename),
|
|
330
|
+
});
|
|
331
|
+
expect(handleTranslate).not.toHaveBeenCalled();
|
|
332
|
+
});
|
|
333
|
+
it('should return error when profile file loading throws', async () => {
|
|
334
|
+
mockedLoadHsProfileFile.mockImplementation(() => {
|
|
335
|
+
throw new Error('Failed to load profile file');
|
|
336
|
+
});
|
|
337
|
+
const result = await validateProjectForProfile(mockProjectConfig, mockProjectDir, mockProfileName, mockDerivedAccountId);
|
|
338
|
+
expect(result.length).toBeGreaterThan(0);
|
|
339
|
+
expect(SpinniesManager.fail).toHaveBeenCalledWith(`validatingProfile-${mockProfileName}`, {
|
|
340
|
+
text: commands.project.validate.spinners.profileValidationFailed(mockProfileFilename),
|
|
341
|
+
});
|
|
342
|
+
expect(handleTranslate).not.toHaveBeenCalled();
|
|
343
|
+
});
|
|
344
|
+
it('should return error array when translation fails', async () => {
|
|
345
|
+
const error = new Error('Translation failed');
|
|
346
|
+
vi.mocked(handleTranslate).mockRejectedValue(error);
|
|
347
|
+
const result = await validateProjectForProfile(mockProjectConfig, mockProjectDir, mockProfileName, mockDerivedAccountId);
|
|
348
|
+
expect(result).toHaveLength(2);
|
|
349
|
+
expect(result[0]).toBe(commands.project.validate.failure(mockProjectConfig.name));
|
|
350
|
+
expect(result[1]).toBe(error);
|
|
351
|
+
expect(SpinniesManager.fail).toHaveBeenCalledWith(`validatingProfile-${mockProfileName}`, {
|
|
352
|
+
text: commands.project.validate.spinners.invalidWithProfile(mockProfileFilename, mockProjectConfig.name),
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
it('should return string error when translation fails with non-Error', async () => {
|
|
356
|
+
const error = 'Translation error';
|
|
357
|
+
vi.mocked(handleTranslate).mockRejectedValue(error);
|
|
358
|
+
const result = await validateProjectForProfile(mockProjectConfig, mockProjectDir, mockProfileName, mockDerivedAccountId);
|
|
359
|
+
expect(result).toHaveLength(2);
|
|
360
|
+
expect(result[0]).toBe(commands.project.validate.failure(mockProjectConfig.name));
|
|
361
|
+
expect(result[1]).toBe(error);
|
|
362
|
+
});
|
|
363
|
+
it('should use correct spinner name based on profile name', async () => {
|
|
364
|
+
const customProfileName = 'custom-profile';
|
|
365
|
+
await validateProjectForProfile(mockProjectConfig, mockProjectDir, customProfileName, mockDerivedAccountId);
|
|
366
|
+
expect(SpinniesManager.add).toHaveBeenCalledWith(`validatingProfile-${customProfileName}`, expect.any(Object));
|
|
367
|
+
expect(SpinniesManager.succeed).toHaveBeenCalledWith(`validatingProfile-${customProfileName}`, expect.any(Object));
|
|
127
368
|
});
|
|
128
369
|
});
|
|
129
370
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { uiLogger } from '../ui/logger.js';
|
|
2
2
|
import { initiateSync } from '@hubspot/local-dev-lib/api/sandboxSync';
|
|
3
|
-
import {
|
|
3
|
+
import { getConfigAccountIfExists, getConfigAccountById, } from '@hubspot/local-dev-lib/config';
|
|
4
4
|
import { HUBSPOT_ACCOUNT_TYPES } from '@hubspot/local-dev-lib/constants/config';
|
|
5
5
|
import { mockHubSpotHttpError } from '../testUtils.js';
|
|
6
6
|
import { getAvailableSyncTypes } from '../sandboxes.js';
|
|
@@ -13,7 +13,8 @@ vi.mock('../sandboxes');
|
|
|
13
13
|
vi.mock('../ui/SpinniesManager');
|
|
14
14
|
const mockedUiLogger = uiLogger;
|
|
15
15
|
const mockedInitiateSync = initiateSync;
|
|
16
|
-
const
|
|
16
|
+
const mockedGetConfigAccountIfExists = getConfigAccountIfExists;
|
|
17
|
+
const mockedGetConfigAccountById = getConfigAccountById;
|
|
17
18
|
const mockedGetAvailableSyncTypes = getAvailableSyncTypes;
|
|
18
19
|
const mockedSpinniesInit = SpinniesManager.init;
|
|
19
20
|
const mockedSpinniesAdd = SpinniesManager.add;
|
|
@@ -23,22 +24,45 @@ describe('lib/sandboxSync', () => {
|
|
|
23
24
|
const mockEnv = 'qa';
|
|
24
25
|
const mockParentAccount = {
|
|
25
26
|
name: 'Parent Account',
|
|
26
|
-
|
|
27
|
+
accountId: 123,
|
|
27
28
|
accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX,
|
|
28
29
|
env: mockEnv,
|
|
30
|
+
authType: 'personalaccesskey',
|
|
29
31
|
};
|
|
30
32
|
const mockChildAccount = {
|
|
31
33
|
name: 'Child Account',
|
|
32
|
-
|
|
34
|
+
accountId: 456,
|
|
33
35
|
accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
|
|
34
36
|
env: mockEnv,
|
|
37
|
+
authType: 'personalaccesskey',
|
|
38
|
+
};
|
|
39
|
+
const mockChildAccountWithMissingId = {
|
|
40
|
+
name: 'Child Account',
|
|
41
|
+
accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
|
|
42
|
+
env: mockEnv,
|
|
43
|
+
authType: 'personalaccesskey',
|
|
35
44
|
};
|
|
36
45
|
const mockSyncTasks = [{ type: 'mock-sync-type' }];
|
|
37
46
|
beforeEach(() => {
|
|
38
|
-
|
|
39
|
-
.mockReturnValueOnce(mockChildAccount
|
|
40
|
-
.mockReturnValueOnce(mockParentAccount
|
|
47
|
+
mockedGetConfigAccountIfExists
|
|
48
|
+
.mockReturnValueOnce(mockChildAccount)
|
|
49
|
+
.mockReturnValueOnce(mockParentAccount);
|
|
41
50
|
mockedGetAvailableSyncTypes.mockResolvedValue(mockSyncTasks);
|
|
51
|
+
// Mock SpinniesManager methods to prevent spinner errors
|
|
52
|
+
mockedSpinniesInit.mockImplementation(() => { });
|
|
53
|
+
mockedSpinniesAdd.mockImplementation(() => { });
|
|
54
|
+
mockedSpinniesSucceed.mockImplementation(() => { });
|
|
55
|
+
mockedSpinniesFail.mockImplementation(() => { });
|
|
56
|
+
// Mock account config for uiAccountDescription calls
|
|
57
|
+
mockedGetConfigAccountById.mockImplementation(accountId => {
|
|
58
|
+
if (accountId === mockChildAccount.accountId) {
|
|
59
|
+
return mockChildAccount;
|
|
60
|
+
}
|
|
61
|
+
if (accountId === mockParentAccount.accountId) {
|
|
62
|
+
return mockParentAccount;
|
|
63
|
+
}
|
|
64
|
+
return undefined; // Don't throw, just return undefined for unknown accounts
|
|
65
|
+
});
|
|
42
66
|
});
|
|
43
67
|
describe('syncSandbox()', () => {
|
|
44
68
|
it('successfully syncs a sandbox with provided sync tasks', async () => {
|
|
@@ -46,7 +70,7 @@ describe('lib/sandboxSync', () => {
|
|
|
46
70
|
await syncSandbox(mockChildAccount, mockParentAccount, mockEnv, mockSyncTasks);
|
|
47
71
|
expect(mockedSpinniesInit).toHaveBeenCalled();
|
|
48
72
|
expect(mockedSpinniesAdd).toHaveBeenCalled();
|
|
49
|
-
expect(mockedInitiateSync).toHaveBeenCalledWith(mockParentAccount.
|
|
73
|
+
expect(mockedInitiateSync).toHaveBeenCalledWith(mockParentAccount.accountId, mockChildAccount.accountId, mockSyncTasks, mockChildAccount.accountId);
|
|
50
74
|
expect(mockedSpinniesSucceed).toHaveBeenCalled();
|
|
51
75
|
});
|
|
52
76
|
it('fetches sync types when no tasks are provided', async () => {
|
|
@@ -57,10 +81,8 @@ describe('lib/sandboxSync', () => {
|
|
|
57
81
|
expect(mockedInitiateSync).toHaveBeenCalled();
|
|
58
82
|
});
|
|
59
83
|
it('throws error when account IDs are missing', async () => {
|
|
60
|
-
mockedGetAccountId.mockReset();
|
|
61
|
-
mockedGetAccountId.mockReturnValue(null);
|
|
62
84
|
const errorRegex = new RegExp(`because your account has been removed from`);
|
|
63
|
-
await expect(syncSandbox(
|
|
85
|
+
await expect(syncSandbox(mockChildAccountWithMissingId, mockParentAccount, mockEnv, mockSyncTasks)).rejects.toThrow(errorRegex);
|
|
64
86
|
});
|
|
65
87
|
it('handles sync in progress error', async () => {
|
|
66
88
|
const error = mockHubSpotHttpError('', {
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { uiLogger } from '../ui/logger.js';
|
|
2
2
|
import { getSandboxUsageLimits } from '@hubspot/local-dev-lib/api/sandboxHubs';
|
|
3
3
|
import { fetchTypes } from '@hubspot/local-dev-lib/api/sandboxSync';
|
|
4
|
-
import {
|
|
4
|
+
import { getAllConfigAccounts, getConfigAccountIfExists, } from '@hubspot/local-dev-lib/config';
|
|
5
5
|
import { HUBSPOT_ACCOUNT_TYPES } from '@hubspot/local-dev-lib/constants/config';
|
|
6
6
|
import { mockHubSpotHttpError } from '../testUtils.js';
|
|
7
7
|
import { getSandboxTypeAsString, getHasSandboxesByType, getAvailableSyncTypes, validateSandboxUsageLimits, handleSandboxCreateError, } from '../sandboxes.js';
|
|
8
|
+
import { isMissingScopeError, isSpecifiedError, } from '@hubspot/local-dev-lib/errors/index';
|
|
8
9
|
vi.mock('../ui/logger.js');
|
|
9
10
|
vi.mock('@hubspot/local-dev-lib/api/sandboxHubs');
|
|
10
11
|
vi.mock('@hubspot/local-dev-lib/api/sandboxSync');
|
|
11
12
|
vi.mock('@hubspot/local-dev-lib/config');
|
|
12
|
-
|
|
13
|
+
vi.mock('@hubspot/local-dev-lib/errors/index');
|
|
14
|
+
const mockedGetConfigAccountIfExists = getConfigAccountIfExists;
|
|
13
15
|
const mockedGetSandboxUsageLimits = getSandboxUsageLimits;
|
|
14
16
|
const mockedFetchTypes = fetchTypes;
|
|
15
|
-
const
|
|
17
|
+
const mockedGetAllConfigAccounts = getAllConfigAccounts;
|
|
16
18
|
const mockedUiLogger = uiLogger;
|
|
19
|
+
const mockedIsMissingScopeError = isMissingScopeError;
|
|
20
|
+
const mockedIsSpecifiedError = isSpecifiedError;
|
|
17
21
|
describe('lib/sandboxes', () => {
|
|
18
22
|
describe('getSandboxTypeAsString()', () => {
|
|
19
23
|
it('returns "development" for development sandbox type', () => {
|
|
@@ -29,14 +33,13 @@ describe('lib/sandboxes', () => {
|
|
|
29
33
|
describe('getHasSandboxesByType()', () => {
|
|
30
34
|
const mockParentAccount = {
|
|
31
35
|
name: 'Parent Account',
|
|
32
|
-
|
|
33
|
-
authType: undefined,
|
|
36
|
+
accountId: 123,
|
|
34
37
|
accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX,
|
|
35
38
|
env: 'qa',
|
|
36
39
|
};
|
|
37
40
|
it('returns true when sandbox of specified type exists', () => {
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
mockedGetConfigAccountIfExists.mockReturnValue(mockParentAccount);
|
|
42
|
+
mockedGetAllConfigAccounts.mockReturnValue([
|
|
40
43
|
mockParentAccount,
|
|
41
44
|
{
|
|
42
45
|
...mockParentAccount,
|
|
@@ -47,25 +50,25 @@ describe('lib/sandboxes', () => {
|
|
|
47
50
|
expect(getHasSandboxesByType(mockParentAccount, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX)).toBe(true);
|
|
48
51
|
});
|
|
49
52
|
it('returns false when no sandbox of specified type exists', () => {
|
|
50
|
-
|
|
53
|
+
mockedGetAllConfigAccounts.mockReturnValue([mockParentAccount]);
|
|
51
54
|
expect(getHasSandboxesByType(mockParentAccount, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX)).toBe(false);
|
|
52
55
|
});
|
|
53
56
|
});
|
|
54
57
|
describe('getAvailableSyncTypes()', () => {
|
|
55
58
|
const mockParentAccount = {
|
|
56
59
|
name: 'Parent Account',
|
|
57
|
-
|
|
60
|
+
accountId: 123,
|
|
58
61
|
env: 'qa',
|
|
59
62
|
};
|
|
60
63
|
const mockChildAccount = {
|
|
61
64
|
...mockParentAccount,
|
|
62
|
-
|
|
65
|
+
accountId: 456,
|
|
63
66
|
};
|
|
64
67
|
it('returns available sync types when fetch is successful', async () => {
|
|
65
68
|
const mockSyncTypes = [{ name: 'type1' }, { name: 'type2' }];
|
|
66
|
-
|
|
67
|
-
.mockReturnValue(mockParentAccount.
|
|
68
|
-
.mockReturnValue(mockChildAccount.
|
|
69
|
+
mockedGetConfigAccountIfExists
|
|
70
|
+
.mockReturnValue(mockParentAccount.accountId)
|
|
71
|
+
.mockReturnValue(mockChildAccount.accountId);
|
|
69
72
|
mockedFetchTypes.mockResolvedValue({
|
|
70
73
|
data: { results: mockSyncTypes },
|
|
71
74
|
});
|
|
@@ -80,12 +83,11 @@ describe('lib/sandboxes', () => {
|
|
|
80
83
|
describe('validateSandboxUsageLimits()', () => {
|
|
81
84
|
const mockAccount = {
|
|
82
85
|
name: 'Test Account',
|
|
83
|
-
|
|
84
|
-
authType: undefined,
|
|
86
|
+
accountId: 123,
|
|
85
87
|
env: 'qa',
|
|
86
88
|
};
|
|
87
89
|
it('validates successfully when limits are not reached', async () => {
|
|
88
|
-
|
|
90
|
+
mockedGetConfigAccountIfExists.mockReturnValue(mockAccount.accountId);
|
|
89
91
|
mockedGetSandboxUsageLimits.mockResolvedValue({
|
|
90
92
|
data: {
|
|
91
93
|
usage: { DEVELOPER: { available: 1, limit: 3 } },
|
|
@@ -94,8 +96,8 @@ describe('lib/sandboxes', () => {
|
|
|
94
96
|
await expect(validateSandboxUsageLimits(mockAccount, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX, 'qa')).resolves.not.toThrow();
|
|
95
97
|
});
|
|
96
98
|
it('throws error when development sandbox limit is reached', async () => {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
mockedGetConfigAccountIfExists.mockReturnValue(mockAccount.accountId);
|
|
100
|
+
mockedGetAllConfigAccounts.mockReturnValue([]);
|
|
99
101
|
mockedGetSandboxUsageLimits.mockResolvedValue({
|
|
100
102
|
data: {
|
|
101
103
|
usage: { DEVELOPER: { available: 0, limit: 1 } },
|
|
@@ -116,6 +118,9 @@ describe('lib/sandboxes', () => {
|
|
|
116
118
|
category: 'MISSING_SCOPES',
|
|
117
119
|
},
|
|
118
120
|
});
|
|
121
|
+
// Mock the error checking function to return true for missing scope error
|
|
122
|
+
mockedIsMissingScopeError.mockReturnValue(true);
|
|
123
|
+
mockedIsSpecifiedError.mockReturnValue(false);
|
|
119
124
|
expect(() => handleSandboxCreateError(error, mockEnv, mockName, mockAccountId)).toThrow(error);
|
|
120
125
|
expect(mockedUiLogger.error).toHaveBeenCalledWith(expect.stringMatching(/The personal access key you provided doesn't include sandbox permissions/));
|
|
121
126
|
expect(mockedUiLogger.info).toHaveBeenCalledWith(expect.stringMatching(/To update CLI permissions for/));
|
|
@@ -128,6 +133,9 @@ describe('lib/sandboxes', () => {
|
|
|
128
133
|
subCategory: 'SandboxErrors.USER_ACCESS_NOT_ALLOWED',
|
|
129
134
|
},
|
|
130
135
|
});
|
|
136
|
+
// Mock the error checking function to return true for this error type
|
|
137
|
+
mockedIsMissingScopeError.mockReturnValue(false);
|
|
138
|
+
mockedIsSpecifiedError.mockReturnValue(true);
|
|
131
139
|
expect(() => handleSandboxCreateError(error, mockEnv, mockName, mockAccountId)).toThrow(error);
|
|
132
140
|
expect(mockedUiLogger.error).toHaveBeenCalledWith(expect.stringMatching(/your permission set doesn't allow you to create the sandbox/));
|
|
133
141
|
});
|
|
@@ -139,8 +147,11 @@ describe('lib/sandboxes', () => {
|
|
|
139
147
|
subCategory: 'SandboxErrors.DEVELOPMENT_SANDBOX_ACCESS_NOT_ALLOWED',
|
|
140
148
|
},
|
|
141
149
|
});
|
|
150
|
+
// Mock the error checking function to return true for this error type
|
|
151
|
+
mockedIsMissingScopeError.mockReturnValue(false);
|
|
152
|
+
mockedIsSpecifiedError.mockReturnValue(true);
|
|
142
153
|
expect(() => handleSandboxCreateError(error, mockEnv, mockName, mockAccountId)).toThrow(error);
|
|
143
|
-
expect(mockedUiLogger.error).toHaveBeenCalledWith(expect.stringMatching(/
|
|
154
|
+
expect(mockedUiLogger.error).toHaveBeenCalledWith(expect.stringMatching(/Couldn't create.*because your account has been removed from.*or your permission set doesn't allow you to create/));
|
|
144
155
|
});
|
|
145
156
|
});
|
|
146
157
|
});
|