@ibm-cloud/cd-tools 1.2.2 → 1.2.4

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.
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Licensed Materials - Property of IBM
3
+ * (c) Copyright IBM Corporation 2025. All Rights Reserved.
4
+ *
5
+ * Note to U.S. Government Users Restricted Rights:
6
+ * Use, duplication or disclosure restricted by GSA ADP Schedule
7
+ * Contract with IBM Corp.
8
+ */
9
+
10
+ import path from 'node:path';
11
+ import nconf from 'nconf';
12
+ import fs from 'node:fs';
13
+
14
+ import * as chai from 'chai';
15
+ chai.config.truncateThreshold = 0;
16
+ import { expect, assert } from 'chai';
17
+
18
+ import { assertPtyOutput, assertExecError, areFilesInDir, deleteCreatedToolchains, parseTcIdAndRegion } from '../utils/testUtils.js';
19
+ import { getBearerToken, getToolchain } from '../../cmd/utils/requests.js';
20
+ import { TEST_TOOLCHAINS, DEFAULT_RG_ID, R2R_CLI_RG_ID } from '../data/test-toolchains.js';
21
+ import { TARGET_REGIONS } from '../../config.js';
22
+
23
+ nconf.env('__');
24
+ nconf.file('local', 'test/config/local.json');
25
+
26
+ const TEMP_DIR = nconf.get('TEST_TEMP_DIR');
27
+ const VERBOSE_MODE = nconf.get('VERBOSE_MODE');
28
+ const IBMCLOUD_API_KEY = nconf.get('IBMCLOUD_API_KEY');
29
+
30
+ const CLI_PATH = path.resolve('index.js');
31
+ const COMMAND = 'copy-toolchain';
32
+
33
+ const toolchainsToDelete = new Map();
34
+ after(async () => await deleteCreatedToolchains(toolchainsToDelete));
35
+
36
+ describe('copy-toolchain: Test functionalities', function () {
37
+ this.timeout('300s');
38
+ this.command = COMMAND;
39
+ const testCases = [
40
+ {
41
+ name: 'Terraform Version Verification',
42
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TARGET_REGIONS[0]],
43
+ expected: /✔ Terraform Version:/,
44
+ options: {
45
+ exitCondition: '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):',
46
+ timeout: 10000
47
+ }
48
+ },
49
+ {
50
+ name: 'Check if CD instance exists in target region',
51
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TARGET_REGIONS[0]],
52
+ expected: new RegExp(`Could not find a Continuous Delivery instance in the target region '${TARGET_REGIONS[0]}', please create one before proceeding.`),
53
+ options: {
54
+ exitCondition: `Could not find a Continuous Delivery instance in the target region '${TARGET_REGIONS[0]}', please create one before proceeding.`,
55
+ timeout: 10000,
56
+ env: { ...process.env, MOCK_ALL_REQUESTS: 'true', MOCK_GET_CD_INSTANCE_BY_REGION_SCENARIO: 'NOT_FOUND' }
57
+ }
58
+ },
59
+ {
60
+ name: 'Log file is created successfully',
61
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TARGET_REGIONS[0]],
62
+ expected: null,
63
+ options: {
64
+ exitCondition: '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):',
65
+ timeout: 10000,
66
+ cwd: TEMP_DIR + '/' + 'log-file-is-created-successfully'
67
+ },
68
+ assertionFunc: () => areFilesInDir(TEMP_DIR + '/' + 'log-file-is-created-successfully', ['.log'])
69
+ },
70
+ {
71
+ name: 'Force Flag bypasses all user prompts',
72
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TEST_TOOLCHAINS['empty'].region, '-f'],
73
+ expected: null,
74
+ options: {
75
+ timeout: 120000,
76
+ },
77
+ assertionFunc: async (output) => {
78
+ // Should bypass everything and clone the toolchain
79
+ output.match(/See cloned toolchain:/);
80
+ const { toolchainId, region } = parseTcIdAndRegion(output);
81
+ const token = await getBearerToken(IBMCLOUD_API_KEY);
82
+ const toolchainData = await getToolchain(token, toolchainId, region);
83
+ assert.isTrue(toolchainData.id === toolchainId, 'Was toolchain created successfully without any confirmations?');
84
+ }
85
+ },
86
+ {
87
+ name: 'Prompt User when toolchain name already exists in resource group',
88
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TARGET_REGIONS[0]],
89
+ expected: new RegExp(`Warning! A toolchain named \'${TEST_TOOLCHAINS['empty'].name}\' already exists in:[\\s\\S]*?Resource Group:[\\s\\S]*?${R2R_CLI_RG_ID}`),
90
+ options: {
91
+ exitCondition: '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):',
92
+ timeout: 10000
93
+ }
94
+ },
95
+ {
96
+ name: 'Prompt User when toolchain name already exists in region',
97
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TEST_TOOLCHAINS['empty'].region, '-g', DEFAULT_RG_ID],
98
+ expected: new RegExp(`Warning! A toolchain named \'${TEST_TOOLCHAINS['empty'].name}\' already exists in:[\\s\\S]*?Region: ${TEST_TOOLCHAINS['empty'].region}`),
99
+ options: {
100
+ exitCondition: '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):',
101
+ timeout: 10000
102
+ }
103
+ },
104
+ {
105
+ name: 'Dry Run Flag does not clone a toolchain',
106
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TEST_TOOLCHAINS['empty'].region, '-D'],
107
+ expected: null,
108
+ options: {
109
+ timeout: 100000,
110
+ questionAnswerMap: {
111
+ '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):': '',
112
+ [`(Recommended) Edit the cloned toolchain's name [default: ${TEST_TOOLCHAINS['empty'].name}] (Ctrl-C to abort):`]: '',
113
+ },
114
+ cwd: TEMP_DIR + '/' + 'dry-run-flag-does-not-clone-a-toolchain'
115
+ },
116
+ assertionFunc: (output) => {
117
+ expect(output).to.match(/DRY_RUN: true, skipping terraform apply/);
118
+ assert.isTrue(areFilesInDir(TEMP_DIR + '/' + 'dry-run-flag-does-not-clone-a-toolchain', ['cd_toolchain.tf', 'output.tf']));
119
+ }
120
+ },
121
+ {
122
+ name: 'Silent flag suppresses info, debug and log messages',
123
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TEST_TOOLCHAINS['empty'].region, '-s'],
124
+ expected: null,
125
+ options: {
126
+ timeout: 100000,
127
+ questionAnswerMap: {
128
+ '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):': '',
129
+ [`(Recommended) Edit the cloned toolchain's name [default: ${TEST_TOOLCHAINS['empty'].name}] (Ctrl-C to abort):`]: '',
130
+ },
131
+ },
132
+ assertionFunc: (output) => {
133
+ // finds any [INFO] level logs that matches '[INFO] ...' but not '[INFO] See cloned toolchain...'
134
+ expect(output).to.not.match(/^(?!.*\[INFO\]\s+See cloned toolchain).*\[INFO\].*$/m);
135
+
136
+ expect(output).to.not.match(/\[DEBUG\]/);
137
+ expect(output).to.not.match(/\[LOG\]/);
138
+ }
139
+ },
140
+ {
141
+ name: 'Compact flag only generates one terraform file',
142
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['single-pl'].crn, '-r', TEST_TOOLCHAINS['single-pl'].region, '-D', '-f', '-C'],
143
+ expected: null,
144
+ options: {
145
+ timeout: 100000,
146
+ cwd: TEMP_DIR + '/' + 'compact-flag-only-generates-one-tf-file'
147
+ },
148
+ assertionFunc: () => {
149
+ // check only resources.tf is created
150
+ assert.isFalse(
151
+ areFilesInDir(TEMP_DIR + '/' + 'compact-flag-only-generates-one-tf-file', [
152
+ 'cd_toolchain.tf',
153
+ 'cd_toolchain_tool_pipeline.tf',
154
+ 'cd_tekton_pipeline.tf',
155
+ ])
156
+ );
157
+ assert.isTrue(areFilesInDir(TEMP_DIR + '/' + 'compact-flag-only-generates-one-tf-file', ['resources.tf']));
158
+ }
159
+ },
160
+ {
161
+ name: 'Prompt user when OAuth does not exist for Git tool in target region',
162
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['single-pl'].crn, '-r', TEST_TOOLCHAINS['single-pl'].region, '-D'],
163
+ expected: /Warning! The following git tool integration\(s\) are not authorized in the target region/,
164
+ options: {
165
+ timeout: 60000,
166
+ questionAnswerMap: {
167
+ '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):': '',
168
+ [`(Recommended) Edit the cloned toolchain's name [default: ${TEST_TOOLCHAINS['single-pl'].name}] (Ctrl-C to abort):`]: '',
169
+ 'Only \'yes\' will be accepted to proceed. (Ctrl-C to abort)': 'yes'
170
+ },
171
+ }
172
+ }
173
+ ];
174
+
175
+ for (const { name, cmd, expected, options, assertionFunc } of testCases) {
176
+ if (VERBOSE_MODE) cmd.push('-v');
177
+ it(`${name}`, async () => {
178
+ const res = await assertPtyOutput(cmd, expected, options, assertionFunc);
179
+ if (res) toolchainsToDelete.set(res.toolchainId, res.region);
180
+ });
181
+ }
182
+
183
+ it('Check for existing .tf files in output directory', async () => {
184
+ const testDir = path.resolve(TEMP_DIR, 'check-for-existing-tf-files-in-out-dir');
185
+ const tfFilePath = path.resolve(testDir, 'empty.tf');
186
+ if (!fs.existsSync(testDir)) fs.mkdirSync(testDir, { recursive: true });
187
+ fs.writeFileSync(tfFilePath, '');
188
+
189
+ const cmd = [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TARGET_REGIONS[0], '-d', testDir];
190
+ if (VERBOSE_MODE) cmd.push('-v');
191
+
192
+ await assertExecError(
193
+ cmd,
194
+ /Output directory already has 1 '.tf' files, please specify a different output directory/,
195
+ { cwd: testDir }
196
+ );
197
+ });
198
+ });
@@ -9,31 +9,32 @@
9
9
 
10
10
  import path from 'node:path';
11
11
  import nconf from 'nconf';
12
+ import fs from 'node:fs';
12
13
 
13
14
  import * as chai from 'chai';
15
+ import { expect } from 'chai';
14
16
  chai.config.truncateThreshold = 0;
15
17
 
16
18
  import mocks from '../data/mocks.js';
17
- import { testSuiteCleanup, expectExecError, expectPtyOutputToMatch } from '../utils/testUtils.js';
19
+ import { assertExecError, assertPtyOutput } from '../utils/testUtils.js';
18
20
  import { TEST_TOOLCHAINS } from '../data/test-toolchains.js';
19
21
  import { TARGET_REGIONS } from '../../config.js';
20
22
 
21
23
  nconf.env('__');
22
24
  nconf.file('local', 'test/config/local.json');
23
25
 
26
+ const VERBOSE_MODE = nconf.get('VERBOSE_MODE');
27
+ const TEMP_DIR = nconf.get('TEST_TEMP_DIR');
28
+
24
29
  const CLI_PATH = path.resolve('index.js');
25
30
  const COMMAND = 'copy-toolchain';
26
31
 
27
- const toolchainsToDelete = new Map();
28
-
29
- after(async () => await testSuiteCleanup(toolchainsToDelete));
30
32
 
31
33
  describe('copy-toolchain: Test user input handling', function () {
32
- this.timeout('60s');
33
- this.command = 'copy-toolchain';
34
+ this.timeout('120s');
35
+ this.command = COMMAND;
34
36
 
35
37
  const validCrn = TEST_TOOLCHAINS['empty'].crn;
36
-
37
38
  const invalidArgsCases = [
38
39
  {
39
40
  name: 'Toolchain CRN not specified',
@@ -87,23 +88,51 @@ describe('copy-toolchain: Test user input handling', function () {
87
88
  cmd: [CLI_PATH, COMMAND, '-c', validCrn, '-r', TARGET_REGIONS[0], '-g', mocks.invalidRgId],
88
89
  expected: /The resource group with provided ID or name was not found or is not accessible/,
89
90
  },
91
+ {
92
+ name: 'Non-existent GRIT mapping file provided',
93
+ cmd: [CLI_PATH, COMMAND, '-c', validCrn, '-r', TARGET_REGIONS[0], '-G', 'non-existent.json'],
94
+ expected: /ENOENT: no such file or directory/
95
+ }
90
96
  ];
91
97
 
92
- for (const { name, cmd, expected, options } of invalidArgsCases) {
98
+ for (const { name, cmd, expected, options, assertionFn } of invalidArgsCases) {
99
+ if (VERBOSE_MODE) cmd.push('-v');
93
100
  it(`Invalid args: ${name}`, async () => {
94
- await expectExecError(cmd, expected, options);
101
+ await assertExecError(cmd, expected, options, assertionFn);
95
102
  });
96
103
  }
97
104
 
105
+ it('Invalid GRIT URL mapping provided in file', async () => {
106
+
107
+ const gritTestDir = path.resolve(TEMP_DIR, 'invalid-grit-mapping-provided-in-file');
108
+ const gritMappingFilePath = path.resolve(gritTestDir, mocks.invalidGritFileName);
109
+
110
+ if (!fs.existsSync(gritTestDir)) fs.mkdirSync(gritTestDir, { recursive: true });
111
+ fs.writeFileSync(gritMappingFilePath, JSON.stringify(mocks.invalidGritMapping, null, 2));
112
+
113
+ const cmd = [CLI_PATH, COMMAND, '-c', validCrn, '-r', TARGET_REGIONS[0], '-G', mocks.invalidGritFileName];
114
+ if (VERBOSE_MODE) cmd.push('-v');
115
+
116
+ await assertExecError(
117
+ cmd,
118
+ null,
119
+ { cwd: gritTestDir },
120
+ (err) => {
121
+ expect(err).to.match(/Error: Provided full GRIT url is not valid/);
122
+ expect(err).to.match(/One or more invalid entries in GRIT mapping file, error count: 2/);
123
+ }
124
+ );
125
+ });
126
+
98
127
  const invalidUserInputCases = [
99
128
  {
100
129
  name: 'Invalid Toolchain tag is provided',
101
130
  cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TARGET_REGIONS[0]],
102
131
  expected: /Provided tag is invalid/,
103
132
  options: {
104
- questionAnswerMap: { '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):' : mocks.invalidTag },
133
+ questionAnswerMap: { '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):': mocks.invalidTag },
105
134
  exitCondition: 'Validation failed',
106
- timeout: 5000
135
+ timeout: 10000
107
136
  }
108
137
  },
109
138
  {
@@ -111,16 +140,17 @@ describe('copy-toolchain: Test user input handling', function () {
111
140
  cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['empty'].crn, '-r', TEST_TOOLCHAINS['empty'].region],
112
141
  expected: /Provided toolchain name is invalid/,
113
142
  options: {
114
- questionAnswerMap: { [`(Recommended) Edit the cloned toolchain's name [default: ${TEST_TOOLCHAINS['empty'].name}] (Ctrl-C to abort):`] : mocks.invalidTcName },
143
+ questionAnswerMap: { [`(Recommended) Edit the cloned toolchain's name [default: ${TEST_TOOLCHAINS['empty'].name}] (Ctrl-C to abort):`]: mocks.invalidTcName },
115
144
  exitCondition: 'Validation failed',
116
- timeout: 5000
145
+ timeout: 10000
117
146
  }
118
- },
147
+ }
119
148
  ];
120
149
 
121
150
  for (const { name, cmd, expected, options } of invalidUserInputCases) {
151
+ if (VERBOSE_MODE) cmd.push('-v');
122
152
  it(`Invalid user input in prompts: ${name}`, async () => {
123
- await expectPtyOutputToMatch(cmd, expected, options);
153
+ await assertPtyOutput(cmd, expected, options);
124
154
  });
125
155
  }
126
156
  });
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Licensed Materials - Property of IBM
3
+ * (c) Copyright IBM Corporation 2025. All Rights Reserved.
4
+ *
5
+ * Note to U.S. Government Users Restricted Rights:
6
+ * Use, duplication or disclosure restricted by GSA ADP Schedule
7
+ * Contract with IBM Corp.
8
+ */
9
+
10
+ import path from 'node:path';
11
+ import nconf from 'nconf';
12
+
13
+ import * as chai from 'chai';
14
+ chai.config.truncateThreshold = 0;
15
+
16
+ import { assertTfResourcesInDir, assertPtyOutput } from '../utils/testUtils.js';
17
+ import { TEST_TOOLCHAINS } from '../data/test-toolchains.js';
18
+
19
+ nconf.env('__');
20
+ nconf.file('local', 'test/config/local.json');
21
+
22
+ const TEMP_DIR = nconf.get('TEST_TEMP_DIR');
23
+ const VERBOSE_MODE = nconf.get('VERBOSE_MODE');
24
+
25
+ const CLI_PATH = path.resolve('index.js');
26
+ const COMMAND = 'copy-toolchain';
27
+
28
+ describe('copy-toolchain: Test import-terraform output', function () {
29
+ this.timeout('300s');
30
+ this.command = COMMAND;
31
+
32
+ const testCases = [
33
+ {
34
+ name: 'Import 1PL-GHE-CC Toolchain',
35
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['1pl-ghe-cc'].crn, '-r', TEST_TOOLCHAINS['1pl-ghe-cc'].region, '-D', '-f'],
36
+ expected: null,
37
+ options: {
38
+ timeout: 100000,
39
+ cwd: TEMP_DIR + '/' + 'import-1pl-ghe-cc-toolchain'
40
+ },
41
+ assertionFunc: async () => {
42
+ await assertTfResourcesInDir(TEMP_DIR + '/' + 'import-1pl-ghe-cc-toolchain', {
43
+ ibm_cd_tekton_pipeline: 1,
44
+ ibm_cd_tekton_pipeline_definition: 1,
45
+ ibm_cd_tekton_pipeline_property: 1,
46
+ ibm_cd_tekton_pipeline_trigger: 1,
47
+ ibm_cd_tekton_pipeline_trigger_property: 1,
48
+ ibm_cd_toolchain: 1,
49
+ ibm_cd_toolchain_tool_custom: 1,
50
+ ibm_cd_toolchain_tool_githubconsolidated: 1,
51
+ ibm_cd_toolchain_tool_pipeline: 1,
52
+ ibm_cd_toolchain_tool_secretsmanager: 1,
53
+ ibm_cd_toolchain_tool_slack: 1,
54
+ ibm_iam_authorization_policy: 1
55
+ });
56
+ }
57
+ },
58
+ {
59
+ name: 'Import 1PL-GHE-CD Toolchain',
60
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['1pl-ghe-cd'].crn, '-r', TEST_TOOLCHAINS['1pl-ghe-cd'].region, '-D', '-f'],
61
+ expected: null,
62
+ options: {
63
+ timeout: 100000,
64
+ cwd: TEMP_DIR + '/' + 'import-1pl-ghe-cd-toolchain'
65
+ },
66
+ assertionFunc: async () => {
67
+ await assertTfResourcesInDir(TEMP_DIR + '/' + 'import-1pl-ghe-cd-toolchain', {
68
+ ibm_cd_tekton_pipeline: 1,
69
+ ibm_cd_tekton_pipeline_definition: 1,
70
+ ibm_cd_tekton_pipeline_property: 1,
71
+ ibm_cd_tekton_pipeline_trigger: 1,
72
+ ibm_cd_tekton_pipeline_trigger_property: 1,
73
+ ibm_cd_toolchain: 1,
74
+ ibm_cd_toolchain_tool_custom: 1,
75
+ ibm_cd_toolchain_tool_githubconsolidated: 1,
76
+ ibm_cd_toolchain_tool_pipeline: 1,
77
+ ibm_cd_toolchain_tool_secretsmanager: 1,
78
+ ibm_cd_toolchain_tool_slack: 1,
79
+ ibm_iam_authorization_policy: 1
80
+ });
81
+ }
82
+ },
83
+ {
84
+ name: 'Import DevSecOps-GRIT-CI Toolchain',
85
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['devsecops-grit-ci'].crn, '-r', TEST_TOOLCHAINS['devsecops-grit-ci'].region, '-D', '-f'],
86
+ expected: null,
87
+ options: {
88
+ timeout: 100000,
89
+ cwd: TEMP_DIR + '/' + 'import-devsecops-grit-ci-toolchain'
90
+ },
91
+ assertionFunc: async () => {
92
+ await assertTfResourcesInDir(TEMP_DIR + '/' + 'import-devsecops-grit-ci-toolchain', {
93
+ ibm_cd_tekton_pipeline: 1,
94
+ ibm_cd_tekton_pipeline_definition: 1,
95
+ ibm_cd_tekton_pipeline_property: 1,
96
+ ibm_cd_tekton_pipeline_trigger: 1,
97
+ ibm_cd_tekton_pipeline_trigger_property: 1,
98
+ ibm_cd_toolchain: 1,
99
+ ibm_cd_toolchain_tool_custom: 1,
100
+ ibm_cd_toolchain_tool_devopsinsights: 1,
101
+ ibm_cd_toolchain_tool_hostedgit: 1,
102
+ ibm_cd_toolchain_tool_pipeline: 1,
103
+ ibm_cd_toolchain_tool_secretsmanager: 1,
104
+ ibm_cd_toolchain_tool_slack: 1,
105
+ ibm_iam_authorization_policy: 1
106
+ })
107
+ }
108
+ },
109
+ ];
110
+
111
+ for (const { name, cmd, expected, options, assertionFunc } of testCases) {
112
+ if (VERBOSE_MODE) cmd.push('-v');
113
+ it(`${name}`, async () => {
114
+ await assertPtyOutput(cmd, expected, options, assertionFunc);
115
+ });
116
+ }
117
+ });
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Licensed Materials - Property of IBM
3
+ * (c) Copyright IBM Corporation 2025. All Rights Reserved.
4
+ *
5
+ * Note to U.S. Government Users Restricted Rights:
6
+ * Use, duplication or disclosure restricted by GSA ADP Schedule
7
+ * Contract with IBM Corp.
8
+ */
9
+
10
+ import path from 'node:path';
11
+ import nconf from 'nconf';
12
+
13
+ import * as chai from 'chai';
14
+ chai.config.truncateThreshold = 0;
15
+ import { expect } from 'chai';
16
+
17
+ import { assertPtyOutput, deleteCreatedToolchains } from '../utils/testUtils.js';
18
+ import { TEST_TOOLCHAINS } from '../data/test-toolchains.js';
19
+ import { TARGET_REGIONS } from '../../config.js';
20
+
21
+ nconf.env('__');
22
+ nconf.file('local', 'test/config/local.json');
23
+
24
+ const VERBOSE_MODE = nconf.get('VERBOSE_MODE');
25
+
26
+ const CLI_PATH = path.resolve('index.js');
27
+ const COMMAND = 'copy-toolchain';
28
+
29
+ const toolchainsToDelete = new Map();
30
+ after(async () => await deleteCreatedToolchains(toolchainsToDelete));
31
+
32
+ describe('copy-toolchain: Test tool validation', function () {
33
+ this.timeout('300s');
34
+ this.command = COMMAND;
35
+ const testCases = [
36
+ {
37
+ name: 'Misconfigured tool identified',
38
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['1pl-ghe-cd'].crn, '-r', TARGET_REGIONS[10]],
39
+ expected: /slack[\s\S]*?misconfigured/,
40
+ options: {
41
+ exitCondition: 'Caution: The above tool(s) will not be properly configured post migration. Do you want to proceed?',
42
+ questionAnswerMap: {
43
+ '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):': '',
44
+ },
45
+ timeout: 30000
46
+ }
47
+ },
48
+ {
49
+ name: 'Tools with plain text secrets identified',
50
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['1pl-ghe-cd'].crn, '-r', TARGET_REGIONS[10]],
51
+ expected: null,
52
+ options: {
53
+ exitCondition: 'Caution: The above tool(s) will not be properly configured post migration. Do you want to proceed?',
54
+ questionAnswerMap: {
55
+ '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):': '',
56
+ },
57
+ timeout: 30000
58
+ },
59
+ assertionFunc: (output) => {
60
+ expect(output).to.match(/Warning! The following tools contain secrets that cannot be migrated/);
61
+ expect(output).to.match(/cloudobjectstorage[\s\S]*?cos_api_key/);
62
+ expect(output).to.match(/slack[\s\S]*?api_token/);
63
+ expect(output).to.match(/pipeline[\s\S]*?properties.doi-ibmcloud-api-key/);
64
+ }
65
+ },
66
+ {
67
+ name: 'Classic pipelines are identified',
68
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['misconfigured'].crn, '-r', TARGET_REGIONS[10]],
69
+ expected: /Warning! Classic pipelines are currently not supported in migration/,
70
+ options: {
71
+ exitCondition: 'Caution: The above tool(s) will not be properly configured post migration. Do you want to proceed?',
72
+ questionAnswerMap: {
73
+ '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):': '',
74
+ },
75
+ timeout: 30000
76
+ }
77
+ },
78
+ {
79
+ name: 'Git tools using PAT are identified',
80
+ cmd: [CLI_PATH, COMMAND, '-c', TEST_TOOLCHAINS['misconfigured'].crn, '-r', TARGET_REGIONS[10]],
81
+ expected: null,
82
+ options: {
83
+ exitCondition: 'Caution: The above tool(s) will not be properly configured post migration. Do you want to proceed?',
84
+ questionAnswerMap: {
85
+ '(Recommended) Add a tag to the cloned toolchain (Ctrl-C to abort):': '',
86
+ },
87
+ timeout: 30000
88
+ },
89
+ assertionFunc: (output) => {
90
+ expect(output).to.match(/Warning! The following GRIT integration\(s\) are using auth_type "pat", please switch to auth_type "oauth" before proceeding/);
91
+ expect(output).to.match(/hostedgit/);
92
+ expect(output).to.match(/Warning! The following tools contain secrets that cannot be migrated/);
93
+ expect(output).to.match(/githubconsolidated[\s\S]*?api_token/);
94
+ expect(output).to.match(/github_integrated[\s\S]*?api_token/);
95
+ expect(output).to.match(/gitlab[\s\S]*?api_token/);
96
+ }
97
+ },
98
+ ];
99
+
100
+ for (const { name, cmd, expected, options, assertionFunc } of testCases) {
101
+ if (VERBOSE_MODE) cmd.push('-v');
102
+ it(`${name}`, async () => {
103
+ const res = await assertPtyOutput(cmd, expected, options, assertionFunc);
104
+ if (res) toolchainsToDelete.set(res.toolchainId, res.region);
105
+ });
106
+ }
107
+ });
@@ -19,11 +19,31 @@ const invalidRgId = 'invalid#RgId';
19
19
 
20
20
  const invalidRgName = 'invalid#Rg@Name';
21
21
 
22
+ const invalidGritMapping = {
23
+ 'ca-tor.git.cloud.ibm.com/fake-user/fake-repo': 'eu-gb.git.cloud.ibm.com/fake-user/fake-repo',
24
+ 'ibm.com/fake-user/fake-repo': 'ibm.com/fake-user/fake-repo'
25
+ };
26
+
27
+ const invalidGritFileName = 'invalid-mapping.json';
28
+
29
+ const getCdInstanceByRegionResponses = {
30
+ 'NOT_FOUND': {
31
+ status: 200,
32
+ data: {
33
+ items: [],
34
+ limit: 10
35
+ }
36
+ }
37
+ };
38
+
22
39
  export default {
23
40
  invalidCrn,
24
41
  invalidRegion,
25
42
  invalidTcName,
26
43
  invalidTag,
27
44
  invalidRgId,
28
- invalidRgName
45
+ invalidRgName,
46
+ invalidGritMapping,
47
+ invalidGritFileName,
48
+ getCdInstanceByRegionResponses
29
49
  };
@@ -9,13 +9,51 @@
9
9
 
10
10
  export const TEST_TOOLCHAINS = {
11
11
  'empty': {
12
- 'name': 'empty-toolchain',
13
- 'crn': 'crn:v1:bluemix:public:toolchain:ca-tor:a/9e8559fac61ee9fc74d3e595fa75d147:a3d4a26a-b447-490e-af84-356bbe63dd1c::',
14
- 'region': 'ca-tor'
12
+ name: 'KEEP-EMPTY-TOOLCHAIN',
13
+ crn: 'crn:v1:bluemix:public:toolchain:ca-tor:a/9e8559fac61ee9fc74d3e595fa75d147:0100aa9f-1e57-41d8-b4c7-5d84178d59bb::',
14
+ region: 'ca-tor',
15
15
  },
16
16
  'misconfigured': {
17
- 'name': 'misconfigured-toolchain',
18
- 'crn': 'crn:v1:bluemix:public:toolchain:eu-es:a/9e8559fac61ee9fc74d3e595fa75d147:0ccfaa70-ca90-47db-8246-f4ecfc6ad8f3::',
19
- 'region': 'eu-es'
17
+ name: 'KEEP-MISCONFIGURED-TOOLCHAIN',
18
+ crn: 'crn:v1:bluemix:public:toolchain:eu-es:a/9e8559fac61ee9fc74d3e595fa75d147:0ccfaa70-ca90-47db-8246-f4ecfc6ad8f3::',
19
+ region: 'eu-es'
20
+ },
21
+ '1pl-ghe-cc': {
22
+ name: 'KEEP-1PL-GHE-CC',
23
+ crn: 'crn:v1:bluemix:public:toolchain:eu-es:a/9e8559fac61ee9fc74d3e595fa75d147:9d51bb6b-f659-4ab7-9bc4-2eae1d61f4e7::',
24
+ region: 'eu-es'
25
+ },
26
+ '1pl-ghe-cd': {
27
+ name: 'KEEP-1PL-GHE-CD',
28
+ crn: 'crn:v1:bluemix:public:toolchain:eu-es:a/9e8559fac61ee9fc74d3e595fa75d147:6a70313f-a927-4b0e-8471-70f17330998d::',
29
+ region: 'eu-es'
30
+ },
31
+ '1pl-ghe-ci': {
32
+ name: 'KEEP-1PL-GHE-CI',
33
+ crn: 'crn:v1:bluemix:public:toolchain:eu-es:a/9e8559fac61ee9fc74d3e595fa75d147:6b8e27ae-5924-4a38-8819-f405366cb900::',
34
+ region: 'eu-es'
35
+ },
36
+ 'devsecops-grit-cc': {
37
+ name: 'KEEP-DevSecOps-GRIT-CC',
38
+ crn: 'crn:v1:bluemix:public:toolchain:eu-es:a/9e8559fac61ee9fc74d3e595fa75d147:920f6a94-4c1b-412b-b95c-baf823958744::',
39
+ region: 'eu-es'
40
+ },
41
+ 'devsecops-grit-cd': {
42
+ name: 'KEEP-DevSecOps-GRIT-CD',
43
+ crn: 'crn:v1:bluemix:public:toolchain:eu-es:a/9e8559fac61ee9fc74d3e595fa75d147:8618565f-08fa-4cac-9250-029cac7b41ba::',
44
+ region: 'eu-es'
45
+ },
46
+ 'devsecops-grit-ci': {
47
+ name: 'KEEP-DevSecOps-GRIT-CI',
48
+ crn: 'crn:v1:bluemix:public:toolchain:eu-es:a/9e8559fac61ee9fc74d3e595fa75d147:cdc271bc-cc07-4a85-beb2-895e033319b0::',
49
+ region: 'eu-es'
50
+ },
51
+ 'single-pl': {
52
+ name: 'KEEP-SINGLE-PIPELINE-TOOLCHAIN',
53
+ crn: 'crn:v1:bluemix:public:toolchain:us-east:a/9e8559fac61ee9fc74d3e595fa75d147:5ef88780-1e0f-4cda-94c7-f78909cc1140::',
54
+ region: 'us-east'
20
55
  }
21
56
  };
57
+
58
+ export const DEFAULT_RG_ID = '63b47433992f4295bc490852cbf1cb55';
59
+ export const R2R_CLI_RG_ID = 'f64e5eb8cfee406a983803bd79aa6c93';