@docupace/pipeline 1.0.39-matttest.42 → 1.0.39-matttest.44

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/jest.config.js ADDED
@@ -0,0 +1,14 @@
1
+ export default {
2
+ testEnvironment: 'node',
3
+ testMatch: [
4
+ '**/tests/**/*.test.js'
5
+ ],
6
+ collectCoverageFrom: [
7
+ 'src/**/*.js',
8
+ '!src/**/cli.js'
9
+ ],
10
+ transform: {},
11
+ moduleNameMapper: {
12
+ '^(\\.{1,2}/.*)\\.js$': '$1'
13
+ }
14
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docupace/pipeline",
3
- "version": "1.0.39-matttest.42",
3
+ "version": "1.0.39-matttest.44",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -33,14 +33,19 @@
33
33
  },
34
34
  "devDependencies": {
35
35
  "@eslint/js": "^9.17.0",
36
+ "@jest/globals": "^30.0.0-beta.3",
36
37
  "esbuild": "^0.24.0",
37
38
  "eslint": "^9.17.0",
38
39
  "eslint-config-prettier": "^9.1.0",
39
40
  "eslint-plugin-prettier": "^5.2.1",
40
41
  "globals": "^15.14.0",
42
+ "jest": "^29.7.0",
41
43
  "prettier": "^3.4.2"
42
44
  },
43
45
  "scripts": {
46
+ "test": "NODE_OPTIONS=\"--experimental-vm-modules\" jest",
47
+ "test:watch": "NODE_OPTIONS=\"--experimental-vm-modules\" jest --watch",
48
+ "test:coverage": "NODE_OPTIONS=\"--experimental-vm-modules\" jest --coverage",
44
49
  "test-deploy-core": "dp-next-cli deploy-core-pipeline dev template",
45
50
  "test-deploy-from-site": "dp-next-cli deploy-from-site --from-env dev --to-env dev --from-site financialdev --to-site template --deploy-core true",
46
51
  "test-run-pipline": "dp-next-cli run-pipeline -r docupace-next-e2e -b master -p run-tests-qa",
@@ -35,17 +35,35 @@ export const environmentConfig = {
35
35
  export const appInfo = {
36
36
  'graphql-server': {
37
37
  repositoryName: 'docupace-graphql-server',
38
- imageName: 'docupace-graphql-serve',
38
+ imageName: 'docupace-graphql-server',
39
39
  deployAttributes: [
40
40
  'image.tag',
41
41
  'graphqlServer.siteConfigRemoteBranch',
42
42
  'graphqlServer.standardConfigRemoteBranch',
43
43
  ],
44
44
  },
45
+ cc: {
46
+ repositoryName: 'docupace-ui-main',
47
+ imageName: 'cc-ui-app',
48
+ deployAttributes: ['image.tag'],
49
+ getAppRevision: (config) => {
50
+ const version = config.image.tag.slice(1);
51
+ return 'cc-ui-app@' + version;
52
+ },
53
+ },
45
54
  ui: {
46
55
  repositoryName: 'docupace-ui-main',
47
56
  imageName: 'enterprise-ui-app',
48
57
  deployAttributes: ['image.tag'],
58
+ getAppRevision: (config) => {
59
+ const tag = config.image.tag;
60
+ const version = tag.startsWith('v') ? tag.slice(1) : tag;
61
+ if (semver.gt(version, UI_MONO_VERSION)) {
62
+ return 'enterprise-ui-app@' + version;
63
+ } else {
64
+ return tag;
65
+ }
66
+ },
49
67
  updateConfigFunction: (config) => {
50
68
  if (config.image.repository.endsWith('/docupace-ui-main')) {
51
69
  const tag = config.image.tag;
@@ -303,7 +303,13 @@ export async function deployAppWithTag(application, env, site, deployTag) {
303
303
 
304
304
  if (env !== 'dev') {
305
305
  const appDetails = appInfo[application];
306
- await copy2(appDetails.repositoryName, 'dev', env, deployTag);
306
+ await copy2(
307
+ appDetails.repositoryName,
308
+ 'dev',
309
+ env,
310
+ deployTag,
311
+ appDetails.imageName,
312
+ );
307
313
  }
308
314
 
309
315
  const { repoPath, deployConfigFilePath, deployConfig } =
@@ -390,6 +396,7 @@ export async function deployApp(
390
396
  fromEnv,
391
397
  toEnv,
392
398
  fromDeployConfig.image.tag,
399
+ appDetails.imageName,
393
400
  );
394
401
  }
395
402
 
@@ -419,7 +426,9 @@ export async function deployApp(
419
426
  deployConfigFilePath: toDeployAppConfigFilePath,
420
427
  } = await readDeployConfig(application, toEnv, toSite, 'app');
421
428
 
422
- toDeployAppConfig.revision = fromDeployConfig.image.tag;
429
+ toDeployAppConfig.revision = appDetails.getAppRevision
430
+ ? appDetails.getAppRevision(fromDeployConfig)
431
+ : fromDeployConfig.image.tag;
423
432
 
424
433
  // Write updated configuration to the target YAML file
425
434
  writeDeployConfig(toDeployConfigFilePath, toDeployConfig);
@@ -0,0 +1,164 @@
1
+ # Unit Tests for Pipeline Shared Scripts
2
+
3
+ This directory contains comprehensive unit tests for all modules in the `src/next` directory.
4
+
5
+ ## Test Structure
6
+
7
+ The test suite covers the following modules:
8
+
9
+ ### Core Modules
10
+ - **alias.js** - Tests for Auth0 alias creation functionality
11
+ - **apache.js** - Tests for Apache site configuration
12
+ - **config.js** - Tests for environment and application configuration
13
+ - **connect.js** - Tests for Connect site cloning and configuration
14
+ - **copy.js** - Tests for Docker image copying between registries
15
+ - **deploy.js** - Tests for deployment functionality (GitOps, YAML config management)
16
+ - **haproxy.js** - Tests for HAProxy configuration synchronization
17
+ - **jenkins.js** - Tests for Jenkins job triggering and monitoring
18
+ - **jira.js** - Tests for Jira API integration
19
+ - **logger.js** - Tests for command logging wrapper
20
+ - **pipline.js** - Tests for Bitbucket pipeline triggering
21
+ - **publish.js** - Tests for Docker image publishing
22
+ - **pull.js** - Tests for Docker image pulling
23
+ - **sonar.js** - Tests for SonarQube analysis
24
+ - **util.js** - Tests for utility functions (monorepo, git operations)
25
+ - **vault.js** - Tests for HashiCorp Vault integration
26
+
27
+ ## Running Tests
28
+
29
+ ### Run All Tests
30
+ ```bash
31
+ npm test
32
+ ```
33
+
34
+ ### Run Tests with Coverage
35
+ ```bash
36
+ npm run test:coverage
37
+ ```
38
+
39
+ ### Run Tests in Watch Mode
40
+ ```bash
41
+ npm run test:watch
42
+ ```
43
+
44
+ ### Run Specific Test File
45
+ ```bash
46
+ npm test tests/next/config.test.js
47
+ ```
48
+
49
+ ### Run Tests Matching Pattern
50
+ ```bash
51
+ npm test -- --testNamePattern="should create alias"
52
+ ```
53
+
54
+ ## Test Features
55
+
56
+ ### Mocking Strategy
57
+ - **External Dependencies**: All external libraries (axios, zx, fs-extra) are mocked
58
+ - **Environment Variables**: Tests use isolated environment variable mocking
59
+ - **File System**: File system operations are mocked to avoid actual file I/O
60
+ - **Shell Commands**: All shell commands via zx are mocked for isolation
61
+
62
+ ### Test Coverage
63
+ Each test file covers:
64
+ - **Happy Path Scenarios**: Normal operation with valid inputs
65
+ - **Error Handling**: Error cases and edge conditions
66
+ - **Environment Variable Validation**: Missing or invalid environment variables
67
+ - **API Error Handling**: Network failures and API errors
68
+ - **Input Validation**: Invalid or missing function parameters
69
+
70
+ ### Example Test Structure
71
+ ```javascript
72
+ import { jest } from '@jest/globals';
73
+ import { functionToTest } from '../../src/next/module.js';
74
+
75
+ // Mock external dependencies
76
+ const mockDependency = jest.fn();
77
+ jest.unstable_mockModule('external-library', () => ({
78
+ default: mockDependency,
79
+ }));
80
+
81
+ describe('module.js', () => {
82
+ beforeEach(() => {
83
+ jest.clearAllMocks();
84
+ // Reset environment variables
85
+ });
86
+
87
+ describe('functionToTest', () => {
88
+ it('should perform expected operation', async () => {
89
+ // Arrange - set up mocks and test data
90
+ // Act - call the function
91
+ // Assert - verify results and mock calls
92
+ });
93
+
94
+ it('should handle error cases', async () => {
95
+ // Test error scenarios
96
+ });
97
+ });
98
+ });
99
+ ```
100
+
101
+ ## Test Configuration
102
+
103
+ The test suite uses Jest with the following configuration:
104
+ - **Test Environment**: Node.js
105
+ - **Test Pattern**: `**/tests/**/*.test.js`
106
+ - **Coverage**: Collects coverage from `src/**/*.js` (excluding CLI files)
107
+ - **Module Type**: ES Modules
108
+
109
+ ## Common Test Patterns
110
+
111
+ ### Testing Async Functions
112
+ ```javascript
113
+ it('should handle async operations', async () => {
114
+ mockFunction.mockResolvedValue(expectedResult);
115
+ const result = await functionUnderTest();
116
+ expect(result).toEqual(expectedResult);
117
+ });
118
+ ```
119
+
120
+ ### Testing Error Handling
121
+ ```javascript
122
+ it('should handle errors gracefully', async () => {
123
+ mockFunction.mockRejectedValue(new Error('Test error'));
124
+ await expect(functionUnderTest()).rejects.toThrow('Test error');
125
+ });
126
+ ```
127
+
128
+ ### Testing Environment Variables
129
+ ```javascript
130
+ it('should use environment variables', () => {
131
+ process.env.TEST_VAR = 'test-value';
132
+ const result = functionUnderTest();
133
+ expect(result).toContain('test-value');
134
+ });
135
+ ```
136
+
137
+ ### Testing Shell Commands
138
+ ```javascript
139
+ it('should execute correct shell commands', async () => {
140
+ mock$.mockResolvedValue({ stdout: 'success' });
141
+ await functionUnderTest();
142
+ expect(mock$).toHaveBeenCalledWith(
143
+ expect.arrayContaining([expect.stringContaining('expected-command')])
144
+ );
145
+ });
146
+ ```
147
+
148
+ ## Adding New Tests
149
+
150
+ When adding new tests:
151
+
152
+ 1. Create a new test file following the naming convention: `module-name.test.js`
153
+ 2. Import required testing utilities from `@jest/globals`
154
+ 3. Mock external dependencies using `jest.unstable_mockModule`
155
+ 4. Follow the AAA pattern (Arrange, Act, Assert)
156
+ 5. Test both success and failure scenarios
157
+ 6. Mock all external dependencies to ensure test isolation
158
+
159
+ ## Notes
160
+
161
+ - Tests are designed to run in isolation without external dependencies
162
+ - All file system operations, network calls, and shell commands are mocked
163
+ - Environment variables are reset between tests to prevent cross-test pollution
164
+ - The test suite focuses on unit testing individual functions rather than integration testing
@@ -0,0 +1,222 @@
1
+ # Working Test Guide for ES Modules
2
+
3
+ This guide explains how to write working unit tests for the ES modules in this project.
4
+
5
+ ## Problem
6
+
7
+ The original comprehensive test files had issues with ES module imports and Jest configuration. The main issues were:
8
+
9
+ 1. ES module import syntax not working properly with Jest
10
+ 2. Mock configuration problems with `jest.unstable_mockModule`
11
+ 3. Template literal expectations not matching actual zx calls
12
+
13
+ ## Solution
14
+
15
+ Use the working pattern demonstrated in the example files:
16
+ - `tests/simple.test.js` - Basic Jest functionality
17
+ - `tests/next/config-simple.test.js` - Testing modules without mocking
18
+ - `tests/next/pull-simple.test.js` - Testing modules with mocking
19
+
20
+ ## Working Test Pattern
21
+
22
+ ### 1. Basic Test Structure
23
+
24
+ ```javascript
25
+ // Import Jest utilities
26
+ import { jest } from '@jest/globals';
27
+
28
+ // For modules that need mocking, mock BEFORE importing
29
+ const mockFunction = jest.fn();
30
+ jest.unstable_mockModule('external-module', () => ({
31
+ functionName: mockFunction,
32
+ }));
33
+
34
+ // Import the module under test AFTER mocking dependencies
35
+ const { functionToTest } = await import('../../src/next/module.js');
36
+
37
+ describe('module.js', () => {
38
+ // Test implementation
39
+ });
40
+ ```
41
+
42
+ ### 2. Testing Modules Without External Dependencies
43
+
44
+ ```javascript
45
+ import { jest } from '@jest/globals';
46
+
47
+ // Import module directly
48
+ let configModule;
49
+ beforeAll(async () => {
50
+ configModule = await import('../../src/next/config.js');
51
+ });
52
+
53
+ describe('config.js', () => {
54
+ it('should have correct configuration', () => {
55
+ expect(configModule.environments).toEqual(['dev', 'qa', 'uat', 'stage', 'prod']);
56
+ });
57
+ });
58
+ ```
59
+
60
+ ### 3. Testing Modules With External Dependencies
61
+
62
+ ```javascript
63
+ import { jest } from '@jest/globals';
64
+
65
+ // Mock dependencies BEFORE importing
66
+ const mock$ = jest.fn();
67
+ jest.unstable_mockModule('zx', () => ({
68
+ $: mock$,
69
+ }));
70
+
71
+ // Import after mocking
72
+ const { functionToTest } = await import('../../src/next/module.js');
73
+
74
+ describe('module.js', () => {
75
+ beforeEach(() => {
76
+ jest.clearAllMocks();
77
+ });
78
+
79
+ it('should call external dependency correctly', async () => {
80
+ mock$.mockResolvedValue({ stdout: 'success' });
81
+
82
+ await functionToTest('arg1', 'arg2');
83
+
84
+ // For zx template literals, match the exact structure
85
+ expect(mock$).toHaveBeenCalledWith(
86
+ ['command ', ' ', ''], // Template literal parts
87
+ 'arg1', // First interpolated value
88
+ 'arg2' // Second interpolated value
89
+ );
90
+ });
91
+ });
92
+ ```
93
+
94
+ ### 4. Understanding zx Template Literal Calls
95
+
96
+ When zx processes a template literal like:
97
+ ```javascript
98
+ await $`docker pull ${registry}/${image}`;
99
+ ```
100
+
101
+ The mock receives:
102
+ - First argument: Array of string parts: `['docker pull ', '/', '']`
103
+ - Subsequent arguments: The interpolated values: `registry`, `image`
104
+
105
+ So your test expectation should be:
106
+ ```javascript
107
+ expect(mock$).toHaveBeenCalledWith(
108
+ ['docker pull ', '/', ''],
109
+ 'registry-value',
110
+ 'image-value'
111
+ );
112
+ ```
113
+
114
+ ### 5. Environment Variable Testing
115
+
116
+ ```javascript
117
+ describe('function with env vars', () => {
118
+ const originalEnv = process.env;
119
+
120
+ beforeEach(() => {
121
+ process.env = { ...originalEnv };
122
+ });
123
+
124
+ afterEach(() => {
125
+ process.env = originalEnv;
126
+ });
127
+
128
+ it('should use environment variables', () => {
129
+ process.env.TEST_VAR = 'test-value';
130
+ // Test implementation
131
+ });
132
+ });
133
+ ```
134
+
135
+ ## Running Tests
136
+
137
+ ### Prerequisites
138
+ Make sure you have the correct configuration:
139
+
140
+ **jest.config.js:**
141
+ ```javascript
142
+ export default {
143
+ testEnvironment: 'node',
144
+ testMatch: [
145
+ '**/tests/**/*.test.js'
146
+ ],
147
+ collectCoverageFrom: [
148
+ 'src/**/*.js',
149
+ '!src/**/cli.js'
150
+ ],
151
+ transform: {},
152
+ moduleNameMapper: {
153
+ '^(\\.{1,2}/.*)\\.js$': '$1'
154
+ }
155
+ };
156
+ ```
157
+
158
+ **package.json scripts:**
159
+ ```json
160
+ {
161
+ "scripts": {
162
+ "test": "NODE_OPTIONS=\"--experimental-vm-modules\" jest",
163
+ "test:watch": "NODE_OPTIONS=\"--experimental-vm-modules\" jest --watch",
164
+ "test:coverage": "NODE_OPTIONS=\"--experimental-vm-modules\" jest --coverage"
165
+ }
166
+ }
167
+ ```
168
+
169
+ ### Commands
170
+
171
+ ```bash
172
+ # Run all tests
173
+ npm test
174
+
175
+ # Run specific test file
176
+ npm test tests/next/config-simple.test.js
177
+
178
+ # Run with coverage
179
+ npm run test:coverage
180
+
181
+ # Run in watch mode
182
+ npm run test:watch
183
+ ```
184
+
185
+ ## Creating New Tests
186
+
187
+ 1. **Create test file**: `tests/next/your-module.test.js`
188
+ 2. **Follow the pattern**: Use the working examples as templates
189
+ 3. **Mock dependencies**: Mock external modules before importing your module
190
+ 4. **Test thoroughly**: Include happy path, error cases, and edge conditions
191
+ 5. **Verify expectations**: Make sure zx template literal expectations match the actual call structure
192
+
193
+ ## Example Test Files
194
+
195
+ - `tests/simple.test.js` - Basic Jest functionality test
196
+ - `tests/next/config-simple.test.js` - Configuration testing without mocks
197
+ - `tests/next/pull-simple.test.js` - Full example with mocking and zx calls
198
+
199
+ ## Common Issues and Solutions
200
+
201
+ ### Issue: `Cannot use import statement outside a module`
202
+ **Solution**: Make sure you're using `NODE_OPTIONS="--experimental-vm-modules"` and the module is imported correctly.
203
+
204
+ ### Issue: Mock expectations don't match
205
+ **Solution**: Log the actual mock calls to see the exact structure:
206
+ ```javascript
207
+ console.log('Mock calls:', mock$.mock.calls);
208
+ ```
209
+
210
+ ### Issue: Environment variables not working
211
+ **Solution**: Make sure to properly reset environment variables in `beforeEach`/`afterEach`.
212
+
213
+ ## Writing Comprehensive Tests
214
+
215
+ For each module, test:
216
+ 1. **Happy path**: Normal operation with valid inputs
217
+ 2. **Error handling**: What happens when external calls fail
218
+ 3. **Input validation**: Invalid or missing parameters
219
+ 4. **Environment variables**: Missing or invalid env vars
220
+ 5. **Edge cases**: Boundary conditions and unusual inputs
221
+
222
+ This working pattern provides a solid foundation for testing all the ES modules in the project while avoiding the common pitfalls with Jest and ES module configuration.
@@ -0,0 +1,99 @@
1
+ import { jest } from '@jest/globals';
2
+ import { createAlias } from '../../src/next/alias.js';
3
+
4
+ // Mock the @docupace/core-client module
5
+ const mockDomainApi = {
6
+ listRecords: jest.fn(),
7
+ create: jest.fn(),
8
+ };
9
+
10
+ const mockBasicAuth = jest.fn();
11
+
12
+ jest.unstable_mockModule('@docupace/core-client', () => ({
13
+ BasicAuth: mockBasicAuth,
14
+ DomainApi: jest.fn(() => mockDomainApi),
15
+ }));
16
+
17
+ describe('alias.js', () => {
18
+ beforeEach(() => {
19
+ jest.clearAllMocks();
20
+ });
21
+
22
+ describe('createAlias', () => {
23
+ it('should create an alias successfully', async () => {
24
+ const mockUserRecord = { id: 'user123' };
25
+ mockDomainApi.listRecords.mockResolvedValue({
26
+ records: [mockUserRecord],
27
+ });
28
+ mockDomainApi.create.mockResolvedValue();
29
+
30
+ await createAlias(
31
+ 'https://www.dev.docupaceinc.com/financialdev',
32
+ 'testuser',
33
+ 'password',
34
+ 'Connect',
35
+ 'testalias@clients'
36
+ );
37
+
38
+ expect(mockBasicAuth).toHaveBeenCalledWith('testuser', 'password');
39
+ expect(mockDomainApi.listRecords).toHaveBeenCalledWith(50, {
40
+ filter: expect.stringContaining('testuser'),
41
+ });
42
+ expect(mockDomainApi.create).toHaveBeenCalledWith({
43
+ domainId: 302,
44
+ '5001': 'user123',
45
+ '9102': 'Connect',
46
+ '9504': 'testalias@clients',
47
+ });
48
+ });
49
+
50
+ it('should handle API errors gracefully', async () => {
51
+ mockDomainApi.listRecords.mockRejectedValue(new Error('API Error'));
52
+
53
+ await expect(
54
+ createAlias(
55
+ 'https://www.dev.docupaceinc.com/financialdev',
56
+ 'testuser',
57
+ 'password',
58
+ 'Connect',
59
+ 'testalias@clients'
60
+ )
61
+ ).rejects.toThrow('API Error');
62
+ });
63
+
64
+ it('should create correct user filter', async () => {
65
+ const mockUserRecord = { id: 'user123' };
66
+ mockDomainApi.listRecords.mockResolvedValue({
67
+ records: [mockUserRecord],
68
+ });
69
+
70
+ await createAlias(
71
+ 'https://www.dev.docupaceinc.com/financialdev',
72
+ 'testuser',
73
+ 'password',
74
+ 'Connect',
75
+ 'testalias@clients'
76
+ );
77
+
78
+ const filterCall = mockDomainApi.listRecords.mock.calls[0][1];
79
+ const parsedFilter = JSON.parse(filterCall.filter);
80
+
81
+ expect(parsedFilter).toEqual({
82
+ orSections: [
83
+ {
84
+ andSections: [
85
+ {
86
+ attrId: 9501,
87
+ '@type': 'string',
88
+ not: false,
89
+ orSections: [
90
+ { searchType: 'EXACT_MATCH', matchToValues: ['testuser'] },
91
+ ],
92
+ },
93
+ ],
94
+ },
95
+ ],
96
+ });
97
+ });
98
+ });
99
+ });