@ibm-cloud/cd-tools 1.1.2 → 1.2.1

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,29 @@
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
+ const invalidCrn = 'crn:v1:bluemix:public:not-a-toolchain:ca-tor:a/955ce52f7b4f4aad8020fbee3e7a8sje:dacff581-8a40sdsdf3kfsd-12n3s::';
11
+
12
+ const invalidRegion = 'not-br-sao';
13
+
14
+ const invalidTcName = 'invalidToolchainName@';
15
+
16
+ const invalidTag = 'invalid@Tag';
17
+
18
+ const invalidRgId = 'invalid#RgId';
19
+
20
+ const invalidRgName = 'invalid#Rg@Name';
21
+
22
+ export default {
23
+ invalidCrn,
24
+ invalidRegion,
25
+ invalidTcName,
26
+ invalidTag,
27
+ invalidRgId,
28
+ invalidRgName
29
+ };
@@ -0,0 +1,21 @@
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
+ export const TEST_TOOLCHAINS = {
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'
15
+ },
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'
20
+ }
21
+ };
package/test/setup.js ADDED
@@ -0,0 +1,46 @@
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 fs from 'node:fs';
11
+ import { resolve } from 'node:path'
12
+ import nconf from 'nconf';
13
+
14
+ import { logger } from '../cmd/utils/logger.js';
15
+
16
+ nconf.env('__');
17
+ nconf.file('local', 'test/config/local.json');
18
+ process.env.IBMCLOUD_API_KEY = nconf.get('IBMCLOUD_API_KEY');
19
+ process.env.DISABLE_SPINNER = nconf.get('DISABLE_SPINNER');
20
+ process.env.LOG_DUMP = nconf.get('LOG_DUMP') || false; // Disable each individual test case's process's log file generation by default
21
+
22
+ const TEMP_DIR = resolve(nconf.get('TEST_TEMP_DIR'));
23
+ const LOG_DIR = resolve(nconf.get('TEST_LOG_DIR'));
24
+ const DEBUG_MODE = nconf.get('TEST_DEBUG_MODE');
25
+
26
+ export const mochaHooks = {
27
+ beforeAll() {
28
+ if (!fs.existsSync(TEMP_DIR)) fs.mkdirSync(TEMP_DIR, { recursive: true });
29
+ if (fs.existsSync(LOG_DIR)) fs.rmSync(LOG_DIR, { recursive: true });
30
+ },
31
+ beforeEach() {
32
+ if (DEBUG_MODE === true && LOG_DIR) {
33
+ const testTitle = this.currentTest.title.toLowerCase().replaceAll(':', '').replaceAll(' ', '-');
34
+ const logFile = this.currentTest.parent.command ?
35
+ resolve(LOG_DIR, this.currentTest.parent.command, testTitle + '.log') :
36
+ resolve(LOG_DIR, testTitle + '.log');
37
+ logger.createLogStream(logFile);
38
+ }
39
+ },
40
+ afterEach() {
41
+ logger.close();
42
+ },
43
+ afterAll() {
44
+ if (fs.existsSync(TEMP_DIR) && DEBUG_MODE === false) fs.rmSync(TEMP_DIR, { recursive: true });
45
+ },
46
+ };
@@ -0,0 +1,137 @@
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 { promisify } from 'util';
11
+ import child_process from 'child_process';
12
+ import stripAnsi from 'strip-ansi';
13
+ import pty from 'node-pty';
14
+ import nconf from 'nconf';
15
+ import { expect } from 'chai';
16
+
17
+ import { getBearerToken, deleteToolchain } from '../../cmd/utils/requests.js';
18
+ import { logger } from '../../cmd/utils/logger.js';
19
+
20
+ nconf.env('__');
21
+ nconf.file('local', 'test/config/local.json');
22
+
23
+ const IBMCLOUD_API_KEY = nconf.get('IBMCLOUD_API_KEY');
24
+
25
+ function cleanOutput(data) {
26
+ if (typeof data === 'string') return stripAnsi(data).replace(/\r/g, '').trim();
27
+ }
28
+
29
+ export async function execCommand(fullCommand, options) {
30
+ const commandStr = `node ${fullCommand.join(' ')}`;
31
+ const execPromise = promisify(child_process.exec);
32
+ try {
33
+ const { stdout, stderr } = await execPromise(commandStr, options);
34
+ if (stderr) {
35
+ const err = new Error(cleanOutput(stderr));
36
+ err.stdout = cleanOutput(stdout);
37
+ err.stderr = cleanOutput(stderr);
38
+ throw err;
39
+ }
40
+ return cleanOutput(stdout);
41
+ } catch (e) {
42
+ const err = new Error(cleanOutput(e.message));
43
+ err.stdout = cleanOutput(e.stdout);
44
+ err.stderr = cleanOutput(e.stderr);
45
+ err.code = e.code;
46
+ err.signal = e.signal;
47
+ throw err;
48
+ }
49
+ }
50
+
51
+ export function runPtyProcess(fullCommand, options) {
52
+ const {
53
+ timeout = 0,
54
+ cwd = process.cwd(),
55
+ env = process.env,
56
+ questionAnswerMap = {},
57
+ exitCondition = '',
58
+ } = options;
59
+
60
+ return new Promise((resolve, reject) => {
61
+ try {
62
+ const ptyProcess = pty.spawn('node', fullCommand, {
63
+ name: 'xterm-color',
64
+ cols: 80,
65
+ rows: 30,
66
+ cwd: cwd,
67
+ env: env
68
+ });
69
+
70
+ let output = '';
71
+ let timedOut = false;
72
+
73
+ const timer = timeout > 0 ? setTimeout(() => {
74
+ timedOut = true;
75
+ ptyProcess.kill();
76
+ }, timeout) : null;
77
+
78
+ ptyProcess.onData((data) => {
79
+ output += data;
80
+ for (const [question, answer] of Object.entries(questionAnswerMap)) {
81
+ if (data.includes(question)) {
82
+ ptyProcess.write(answer + '\r');
83
+ }
84
+ }
85
+ if (exitCondition.length > 0 && data.includes(exitCondition)) {
86
+ ptyProcess.kill();
87
+ resolve(cleanOutput(output));
88
+ }
89
+ });
90
+
91
+ ptyProcess.onExit(({ exitCode }) => {
92
+ if (timer) clearTimeout(timer);
93
+ if (timedOut) {
94
+ reject(new Error(`ERROR: Process timed out after ${timeout}ms\n\nCommand: ${'node ' + fullCommand.join(' ')}\n\nOutput:\n${cleanOutput(output)}`));
95
+ }
96
+ if (exitCode !== 0) {
97
+ reject(new Error(`ERROR: Process exited with code ${exitCode}\n\nCommand: ${'node ' + fullCommand.join(' ')}\n\nOutput:\n${cleanOutput(output)}`));
98
+ } else {
99
+ resolve(cleanOutput(output));
100
+ }
101
+ });
102
+
103
+ } catch (err) {
104
+ reject(err);
105
+ }
106
+ });
107
+ }
108
+
109
+ export async function testSuiteCleanup(toolchainsToDelete) {
110
+ if (toolchainsToDelete && typeof toolchainsToDelete === 'object' && toolchainsToDelete.size > 0) {
111
+ const token = await getBearerToken(IBMCLOUD_API_KEY);
112
+ const deletePromises = [...toolchainsToDelete.entries()].map(([id, region]) => deleteToolchain(token, id, region));
113
+ await Promise.all(deletePromises);
114
+ }
115
+ }
116
+
117
+ export async function expectExecError(fullCommand, expectedMessage, options) {
118
+ try {
119
+ const output = await execCommand(fullCommand, options);
120
+ logger.dump(output);
121
+ throw new Error('Expected command to fail but it succeeded');
122
+ } catch (e) {
123
+ logger.dump(e.message);
124
+ expect(e.message).to.match(expectedMessage);
125
+ }
126
+ }
127
+
128
+ export async function expectPtyOutputToMatch(fullCommand, expectedMessage, options) {
129
+ try {
130
+ const output = await runPtyProcess(fullCommand, options);
131
+ logger.dump(output);
132
+ expect(output).to.match(expectedMessage);
133
+ } catch (e) {
134
+ logger.dump(e.message);
135
+ throw (e);
136
+ }
137
+ }