@docupace/pipeline 1.0.66 → 1.0.68
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/bitbucket-pipelines.yml +39 -7
- package/eslint.config.js +14 -3
- package/jest.config.js +15 -0
- package/package.json +4 -1
- package/src/auth0/alias.js +14 -7
- package/src/auth0/alias.test.js +172 -0
- package/src/auth0/cli.js +11 -9
- package/src/auth0/configure.js +2 -1
- package/src/auth0/configure.test.js +167 -0
- package/src/auth0/export.js +7 -2
- package/src/auth0/export.test.js +103 -0
- package/src/auth0/metadata.js +15 -9
- package/src/auth0/metadata.test.js +186 -0
- package/src/auth0/vault.js +34 -81
- package/src/auth0/vault.test.js +134 -0
- package/src/next/deploy.js +22 -17
- package/src/next/git-to-semver.js +79 -0
- package/src/next/jenkins.js +2 -2
- package/src/next/test.js +5 -0
package/bitbucket-pipelines.yml
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# bitbucket-pipelines.yml
|
|
1
2
|
clone:
|
|
2
3
|
depth: 2
|
|
3
4
|
|
|
@@ -6,23 +7,54 @@ pipelines:
|
|
|
6
7
|
'**':
|
|
7
8
|
- step:
|
|
8
9
|
runs-on:
|
|
10
|
+
- self.hosted
|
|
9
11
|
- linux.shell
|
|
10
|
-
- onprem
|
|
11
12
|
name: On Pull Request
|
|
12
13
|
script:
|
|
14
|
+
- npm install -g pnpm
|
|
13
15
|
- pnpm config set //registry.npmjs.org/:_authToken $DOCUPACE_NPM_TOKEN
|
|
14
16
|
- pnpm install --frozen-lockfile
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
+
- 'npx eslint . --rule "no-unused-vars: warn" --rule "no-useless-escape: warn" --color'
|
|
18
|
+
- pnpm test
|
|
19
|
+
artifacts:
|
|
20
|
+
- coverage/**
|
|
21
|
+
after-script:
|
|
22
|
+
- echo "Pipeline completed with status $BITBUCKET_EXIT_CODE"
|
|
23
|
+
|
|
17
24
|
custom:
|
|
18
25
|
scheduled-tests:
|
|
19
26
|
- step:
|
|
20
27
|
runs-on:
|
|
28
|
+
- self.hosted
|
|
21
29
|
- linux.shell
|
|
22
|
-
|
|
23
|
-
name: Manual
|
|
30
|
+
name: Scheduled Tests
|
|
24
31
|
script:
|
|
32
|
+
- npm install -g pnpm
|
|
25
33
|
- pnpm config set //registry.npmjs.org/:_authToken $DOCUPACE_NPM_TOKEN
|
|
26
34
|
- pnpm install --frozen-lockfile
|
|
27
|
-
-
|
|
28
|
-
|
|
35
|
+
- 'npx eslint . --rule "no-unused-vars: warn" --rule "no-useless-escape: warn" --color'
|
|
36
|
+
- pnpm test
|
|
37
|
+
artifacts:
|
|
38
|
+
- coverage/**
|
|
39
|
+
after-script:
|
|
40
|
+
- echo "Pipeline completed with status $BITBUCKET_EXIT_CODE"
|
|
41
|
+
|
|
42
|
+
branches:
|
|
43
|
+
main:
|
|
44
|
+
- step:
|
|
45
|
+
runs-on:
|
|
46
|
+
- self.hosted
|
|
47
|
+
- linux.shell
|
|
48
|
+
name: Main Branch Build
|
|
49
|
+
script:
|
|
50
|
+
- npm install -g pnpm
|
|
51
|
+
- pnpm config set //registry.npmjs.org/:_authToken $DOCUPACE_NPM_TOKEN
|
|
52
|
+
- pnpm install --frozen-lockfile
|
|
53
|
+
- 'npx eslint . --rule "no-unused-vars: warn" --rule "no-useless-escape: warn" --color'
|
|
54
|
+
- pnpm test
|
|
55
|
+
artifacts:
|
|
56
|
+
- coverage/**
|
|
57
|
+
|
|
58
|
+
definitions:
|
|
59
|
+
caches:
|
|
60
|
+
pnpm: ~/.pnpm-store
|
package/eslint.config.js
CHANGED
|
@@ -4,17 +4,29 @@ import prettierConfig from 'eslint-config-prettier';
|
|
|
4
4
|
import prettierPlugin from 'eslint-plugin-prettier';
|
|
5
5
|
import importPlugin from 'eslint-plugin-import';
|
|
6
6
|
|
|
7
|
-
/** @type {import('eslint').Linter.Config[]} */
|
|
8
7
|
export default [
|
|
9
8
|
{
|
|
10
9
|
languageOptions: {
|
|
11
10
|
globals: {
|
|
12
11
|
...globals.browser,
|
|
13
|
-
...globals.node,
|
|
12
|
+
...globals.node,
|
|
14
13
|
},
|
|
15
14
|
},
|
|
16
15
|
},
|
|
16
|
+
|
|
17
|
+
// Core JS rules
|
|
17
18
|
pluginJs.configs.recommended,
|
|
19
|
+
|
|
20
|
+
// Jest globals for test files only
|
|
21
|
+
{
|
|
22
|
+
files: ['**/*.test.js', '**/__tests__/**/*.js'],
|
|
23
|
+
languageOptions: {
|
|
24
|
+
globals: {
|
|
25
|
+
...globals.jest,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
|
|
18
30
|
{
|
|
19
31
|
plugins: {
|
|
20
32
|
prettier: prettierPlugin,
|
|
@@ -29,7 +41,6 @@ export default [
|
|
|
29
41
|
rules: {
|
|
30
42
|
...prettierConfig.rules,
|
|
31
43
|
'prettier/prettier': 'error',
|
|
32
|
-
// Import-related rules
|
|
33
44
|
'import/no-unresolved': 'error',
|
|
34
45
|
'import/named': 'error',
|
|
35
46
|
'import/no-extraneous-dependencies': 'error',
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// jest.config.js
|
|
2
|
+
export default {
|
|
3
|
+
testEnvironment: 'node',
|
|
4
|
+
transform: {},
|
|
5
|
+
verbose: true,
|
|
6
|
+
collectCoverage: true,
|
|
7
|
+
coverageDirectory: 'coverage',
|
|
8
|
+
coverageReporters: ['text', 'lcov', 'json', 'html'],
|
|
9
|
+
collectCoverageFrom: [
|
|
10
|
+
'src/**/*.{js,ts}',
|
|
11
|
+
'!src/**/cli.js',
|
|
12
|
+
'!src/**/*.test.{js,ts}',
|
|
13
|
+
'!**/node_modules/**',
|
|
14
|
+
],
|
|
15
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docupace/pipeline",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.68",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"@changesets/cli": "^2.27.10",
|
|
19
19
|
"@docupace/core-client": "^0.1.94",
|
|
20
20
|
"@docupace/pipeline": "^1.0.33-alpha-2",
|
|
21
|
+
"@jest/globals": "^30.1.2",
|
|
21
22
|
"auth0-deploy-cli": "^8.8.0",
|
|
22
23
|
"axios": "^1.7.9",
|
|
23
24
|
"commander": "^12.1.0",
|
|
@@ -41,9 +42,11 @@
|
|
|
41
42
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
42
43
|
"eslint-plugin-prettier": "^5.2.1",
|
|
43
44
|
"globals": "^15.14.0",
|
|
45
|
+
"jest": "^30.1.3",
|
|
44
46
|
"prettier": "^3.4.2"
|
|
45
47
|
},
|
|
46
48
|
"scripts": {
|
|
49
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage --testPathIgnorePatterns='src/next/test\\.js'",
|
|
47
50
|
"test-deploy-core": "dp-next-cli deploy-core-pipeline dev template",
|
|
48
51
|
"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",
|
|
49
52
|
"test-run-pipline": "dp-next-cli run-pipeline -r docupace-next-e2e -b master -p run-tests-qa",
|
package/src/auth0/alias.js
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
|
|
3
2
|
import { createAlias } from '../next/alias.js';
|
|
4
|
-
|
|
5
3
|
import { getAuth0AuthToken } from './configure.js';
|
|
6
|
-
|
|
7
4
|
import { getVaultSecrets } from './vault.js';
|
|
8
|
-
|
|
9
5
|
import { getVaultSecretsConfigs } from '../next/config.js';
|
|
10
|
-
|
|
11
6
|
import { cloneEnvRepository, readDeployConfig } from '../next/deploy.js';
|
|
12
7
|
|
|
13
8
|
export async function getCCAlias({ application, auth0Domain, auth0AuthToken }) {
|
|
@@ -35,7 +30,14 @@ export async function getCCAlias({ application, auth0Domain, auth0AuthToken }) {
|
|
|
35
30
|
}
|
|
36
31
|
}
|
|
37
32
|
|
|
38
|
-
export async function configureAlias(
|
|
33
|
+
export async function configureAlias(
|
|
34
|
+
site,
|
|
35
|
+
env,
|
|
36
|
+
username,
|
|
37
|
+
password,
|
|
38
|
+
realm,
|
|
39
|
+
auth0Tenant,
|
|
40
|
+
) {
|
|
39
41
|
console.log(`Creating alias for site: ${site} in env: ${env}`);
|
|
40
42
|
// Site Common Secrets
|
|
41
43
|
const siteCommonSecretsPath =
|
|
@@ -43,7 +45,12 @@ export async function configureAlias(site, env, username, password, realm) {
|
|
|
43
45
|
console.log(`siteCommonSecretsPath => ${siteCommonSecretsPath}`);
|
|
44
46
|
const siteCommonSecrets = await getVaultSecrets(siteCommonSecretsPath);
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
let auth0Domain;
|
|
49
|
+
if (auth0Tenant === '') {
|
|
50
|
+
auth0Domain = `docupace-${env}-${site}.us.auth0.com`;
|
|
51
|
+
} else {
|
|
52
|
+
auth0Domain = `${auth0Tenant}.us.auth0.com`;
|
|
53
|
+
}
|
|
47
54
|
const auth0ClientId = siteCommonSecrets.AUTH0_CLIENT_ID;
|
|
48
55
|
const auth0ClientSecret = siteCommonSecrets.AUTH0_CLIENT_SECRET;
|
|
49
56
|
const auth0Token = await getAuth0AuthToken(
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// alias.test.js
|
|
2
|
+
import { jest } from '@jest/globals';
|
|
3
|
+
|
|
4
|
+
// --------- ESM-Compatible Mocks ---------
|
|
5
|
+
jest.unstable_mockModule('axios', () => ({
|
|
6
|
+
default: {
|
|
7
|
+
get: jest.fn(),
|
|
8
|
+
},
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
jest.unstable_mockModule('../next/alias.js', () => ({
|
|
12
|
+
createAlias: jest.fn(),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
jest.unstable_mockModule('./configure.js', () => ({
|
|
16
|
+
getAuth0AuthToken: jest.fn(),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
jest.unstable_mockModule('./vault.js', () => ({
|
|
20
|
+
getVaultSecrets: jest.fn(),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
jest.unstable_mockModule('../next/config.js', () => ({
|
|
24
|
+
getVaultSecretsConfigs: jest.fn(),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
jest.unstable_mockModule('../next/deploy.js', () => ({
|
|
28
|
+
cloneEnvRepository: jest.fn(),
|
|
29
|
+
readDeployConfig: jest.fn(),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
// --------- Import AFTER mocks ---------
|
|
33
|
+
const { configureAlias, getCCAlias } = await import('./alias.js');
|
|
34
|
+
const axios = (await import('axios')).default;
|
|
35
|
+
const { createAlias } = await import('../next/alias.js');
|
|
36
|
+
const { getAuth0AuthToken } = await import('./configure.js');
|
|
37
|
+
const { getVaultSecrets } = await import('./vault.js');
|
|
38
|
+
const { getVaultSecretsConfigs } = await import('../next/config.js');
|
|
39
|
+
const { cloneEnvRepository, readDeployConfig } = await import(
|
|
40
|
+
'../next/deploy.js'
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// --------- Shared Test Setup ---------
|
|
44
|
+
beforeAll(() => {
|
|
45
|
+
jest.spyOn(console, 'log').mockImplementation(() => {}); // silence logs
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
afterAll(() => {
|
|
49
|
+
console.log.mockRestore();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('configureAlias', () => {
|
|
53
|
+
const site = 'example';
|
|
54
|
+
const env = 'dev';
|
|
55
|
+
const username = 'admin';
|
|
56
|
+
const password = 'pass';
|
|
57
|
+
const realm = 'Users';
|
|
58
|
+
const auth0Tenant = '';
|
|
59
|
+
|
|
60
|
+
const mockSecrets = {
|
|
61
|
+
AUTH0_CLIENT_ID: 'client-id',
|
|
62
|
+
AUTH0_CLIENT_SECRET: 'client-secret',
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const mockToken = 'mocked-auth0-token';
|
|
66
|
+
|
|
67
|
+
const mockClients = [
|
|
68
|
+
{ name: 'docupace-graphql-client-credentials', client_id: 'client-123' },
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
const mockConfig = {
|
|
72
|
+
deployConfig: {
|
|
73
|
+
graphqlServer: {
|
|
74
|
+
coreUrl: 'https://core.example.com',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
beforeEach(() => {
|
|
80
|
+
jest.clearAllMocks();
|
|
81
|
+
|
|
82
|
+
getVaultSecretsConfigs.mockReturnValue({
|
|
83
|
+
siteCommonSecretsPath: 'vault/path/to/secrets',
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
getVaultSecrets.mockResolvedValue(mockSecrets);
|
|
87
|
+
getAuth0AuthToken.mockResolvedValue(mockToken);
|
|
88
|
+
axios.get.mockResolvedValue({ data: mockClients });
|
|
89
|
+
cloneEnvRepository.mockResolvedValue(undefined);
|
|
90
|
+
readDeployConfig.mockResolvedValue(mockConfig);
|
|
91
|
+
createAlias.mockResolvedValue(undefined);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should configure alias with default tenant and call all required steps', async () => {
|
|
95
|
+
await configureAlias(site, env, username, password, realm, auth0Tenant);
|
|
96
|
+
|
|
97
|
+
expect(getVaultSecretsConfigs).toHaveBeenCalledWith(site);
|
|
98
|
+
expect(getVaultSecrets).toHaveBeenCalledWith('vault/path/to/secrets');
|
|
99
|
+
|
|
100
|
+
expect(getAuth0AuthToken).toHaveBeenCalledWith(
|
|
101
|
+
'docupace-dev-example.us.auth0.com',
|
|
102
|
+
'client-id',
|
|
103
|
+
'client-secret',
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
expect(cloneEnvRepository).toHaveBeenCalledWith(env);
|
|
107
|
+
|
|
108
|
+
expect(readDeployConfig).toHaveBeenCalledWith(
|
|
109
|
+
'graphql-server',
|
|
110
|
+
env,
|
|
111
|
+
site,
|
|
112
|
+
'values',
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
expect(axios.get).toHaveBeenCalledWith(
|
|
116
|
+
'https://docupace-dev-example.us.auth0.com/api/v2/clients',
|
|
117
|
+
{ headers: { authorization: `Bearer ${mockToken}` } },
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
expect(createAlias).toHaveBeenCalledWith(
|
|
121
|
+
'https://core.example.com',
|
|
122
|
+
username,
|
|
123
|
+
password,
|
|
124
|
+
realm,
|
|
125
|
+
'client-123@clients',
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('getCCAlias', () => {
|
|
131
|
+
const application = 'docupace-graphql-client-credentials';
|
|
132
|
+
const auth0Domain = 'example.auth0.com';
|
|
133
|
+
const auth0AuthToken = 'token';
|
|
134
|
+
|
|
135
|
+
it('should return correct alias string for client', async () => {
|
|
136
|
+
axios.get.mockResolvedValue({
|
|
137
|
+
data: [{ name: application, client_id: 'abc123' }],
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const alias = await getCCAlias({
|
|
141
|
+
application,
|
|
142
|
+
auth0Domain,
|
|
143
|
+
auth0AuthToken,
|
|
144
|
+
});
|
|
145
|
+
expect(alias).toBe('abc123@clients');
|
|
146
|
+
|
|
147
|
+
expect(axios.get).toHaveBeenCalledWith(
|
|
148
|
+
'https://example.auth0.com/api/v2/clients',
|
|
149
|
+
{ headers: { authorization: 'Bearer token' } },
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should log error and return undefined if axios fails', async () => {
|
|
154
|
+
axios.get.mockRejectedValue(new Error('Network error'));
|
|
155
|
+
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
156
|
+
|
|
157
|
+
const result = await getCCAlias({
|
|
158
|
+
application,
|
|
159
|
+
auth0Domain,
|
|
160
|
+
auth0AuthToken,
|
|
161
|
+
});
|
|
162
|
+
expect(result).toBeUndefined();
|
|
163
|
+
|
|
164
|
+
expect(errorSpy).toHaveBeenCalledWith(
|
|
165
|
+
expect.stringContaining(
|
|
166
|
+
'Failed to form alias name for application: docupace-graphql-client-credentials: Network error',
|
|
167
|
+
),
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
errorSpy.mockRestore();
|
|
171
|
+
});
|
|
172
|
+
});
|
package/src/auth0/cli.js
CHANGED
|
@@ -57,24 +57,26 @@ program
|
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
program
|
|
60
|
-
.command(
|
|
60
|
+
.command(
|
|
61
|
+
'configureAlias <site> <env> <username> <password> <realm> [auth0Tenant]',
|
|
62
|
+
)
|
|
61
63
|
.description('Configure Core Alias for the given site')
|
|
62
|
-
.action(async (site, env, username, password, realm) => {
|
|
63
|
-
await configureAlias(site, env, username, password, realm);
|
|
64
|
+
.action(async (site, env, username, password, realm, auth0Tenant = '') => {
|
|
65
|
+
await configureAlias(site, env, username, password, realm, auth0Tenant);
|
|
64
66
|
});
|
|
65
67
|
|
|
66
68
|
program
|
|
67
|
-
.command('exportConf <site> <env>')
|
|
69
|
+
.command('exportConf <site> <env> [auth0Tenant]')
|
|
68
70
|
.description('Export Auth0 configuration for the given site')
|
|
69
|
-
.action(async (site, env) => {
|
|
70
|
-
await exportConf(site, env);
|
|
71
|
+
.action(async (site, env, auth0Tenant = '') => {
|
|
72
|
+
await exportConf(site, env, auth0Tenant);
|
|
71
73
|
});
|
|
72
74
|
|
|
73
75
|
program
|
|
74
|
-
.command('getAuth0Metadata <site> <env>')
|
|
76
|
+
.command('getAuth0Metadata <site> <env> [auth0Tenant]')
|
|
75
77
|
.description('Get Auth0 metadata for the given site')
|
|
76
|
-
.action(async (site, env) => {
|
|
77
|
-
await getAuth0Metadata(site, env);
|
|
78
|
+
.action(async (site, env, auth0Tenant = '') => {
|
|
79
|
+
await getAuth0Metadata(site, env, auth0Tenant);
|
|
78
80
|
});
|
|
79
81
|
|
|
80
82
|
program.parse(process.argv);
|
package/src/auth0/configure.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import axios from 'axios';
|
|
4
|
-
import crypto from 'crypto';
|
|
4
|
+
import * as crypto from 'crypto';
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
cloneEnvRepository,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from '../next/deploy.js';
|
|
12
12
|
|
|
13
13
|
import { getVaultSecrets, updateVaultSecrets } from './vault.js';
|
|
14
|
+
|
|
14
15
|
import {
|
|
15
16
|
coreSiteEnvMap,
|
|
16
17
|
environmentConfig,
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// @jest-environment node
|
|
2
|
+
import { jest } from '@jest/globals';
|
|
3
|
+
|
|
4
|
+
// ─── Mock All Required Modules ───────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
jest.unstable_mockModule('fs-extra', () => ({
|
|
7
|
+
default: {
|
|
8
|
+
promises: {
|
|
9
|
+
mkdir: jest.fn(),
|
|
10
|
+
},
|
|
11
|
+
writeFile: jest.fn(),
|
|
12
|
+
ensureDir: jest.fn(),
|
|
13
|
+
},
|
|
14
|
+
writeFile: jest.fn(),
|
|
15
|
+
ensureDir: jest.fn(),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
jest.unstable_mockModule('axios', () => ({
|
|
19
|
+
default: {
|
|
20
|
+
post: jest.fn(),
|
|
21
|
+
get: jest.fn(),
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
jest.unstable_mockModule('crypto', () => ({
|
|
26
|
+
randomBytes: () => ({
|
|
27
|
+
toString: () => 'mockBase64==',
|
|
28
|
+
}),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
jest.unstable_mockModule('../next/deploy.js', () => ({
|
|
32
|
+
cloneRepository: jest.fn(),
|
|
33
|
+
cloneEnvRepository: jest.fn(),
|
|
34
|
+
pushDeployConfig: jest.fn(),
|
|
35
|
+
readDeployConfig: jest.fn(),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
jest.unstable_mockModule('./vault.js', () => ({
|
|
39
|
+
getVaultSecrets: jest.fn(),
|
|
40
|
+
updateVaultSecrets: jest.fn(),
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
jest.unstable_mockModule('../next/config.js', () => ({
|
|
44
|
+
coreSiteEnvMap: { dev: 'dev-env' },
|
|
45
|
+
environmentConfig: {
|
|
46
|
+
dev: {
|
|
47
|
+
newUrl: (site) => `dev.${site}.com`,
|
|
48
|
+
oldUrl: (site) => `legacy.${site}.com`,
|
|
49
|
+
ccUrl: (site) => `cc.${site}.com`,
|
|
50
|
+
docupaceHost: () => 'docupace.com',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
getVaultSecretsConfigs: jest.fn(),
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
// ─── Global References ───────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
let fs, axios, deploy, vault, config;
|
|
59
|
+
let getAuth0AuthToken, cleanAndWritePropertiesFile, updateCoreSiteConfig;
|
|
60
|
+
|
|
61
|
+
const originalProcessExit = process.exit;
|
|
62
|
+
|
|
63
|
+
beforeAll(async () => {
|
|
64
|
+
// Mock process.exit to prevent killing the test runner
|
|
65
|
+
process.exit = jest.fn();
|
|
66
|
+
|
|
67
|
+
// Suppress noisy error output
|
|
68
|
+
jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
69
|
+
jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
70
|
+
|
|
71
|
+
// Import after mocks
|
|
72
|
+
({ default: fs } = await import('fs-extra'));
|
|
73
|
+
({ default: axios } = await import('axios'));
|
|
74
|
+
deploy = await import('../next/deploy.js');
|
|
75
|
+
vault = await import('./vault.js');
|
|
76
|
+
config = await import('../next/config.js');
|
|
77
|
+
|
|
78
|
+
const mod = await import('./configure.js');
|
|
79
|
+
({ getAuth0AuthToken, cleanAndWritePropertiesFile, updateCoreSiteConfig } =
|
|
80
|
+
mod);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
afterAll(() => {
|
|
84
|
+
// Restore
|
|
85
|
+
process.exit = originalProcessExit;
|
|
86
|
+
console.error.mockRestore();
|
|
87
|
+
console.log.mockRestore();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
describe('Deployment Automation Module (ESM)', () => {
|
|
93
|
+
describe('getAuth0AuthToken', () => {
|
|
94
|
+
it('should return access token on success', async () => {
|
|
95
|
+
axios.post.mockResolvedValue({ data: { access_token: 'mock-token' } });
|
|
96
|
+
const token = await getAuth0AuthToken('auth.domain', 'cid', 'secret');
|
|
97
|
+
expect(token).toBe('mock-token');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should return undefined on failure', async () => {
|
|
101
|
+
axios.post.mockRejectedValue({ message: 'fail' });
|
|
102
|
+
const token = await getAuth0AuthToken('auth.domain', 'cid', 'secret');
|
|
103
|
+
expect(token).toBeUndefined();
|
|
104
|
+
expect(console.error).toHaveBeenCalledWith(
|
|
105
|
+
'Error fetching Auth0 token:',
|
|
106
|
+
'fail',
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('cleanAndWritePropertiesFile', () => {
|
|
112
|
+
it('writes cleaned file content', async () => {
|
|
113
|
+
fs.writeFile.mockResolvedValue();
|
|
114
|
+
await cleanAndWritePropertiesFile('/tmp/test.properties', 'a=1\n\nb=2\n');
|
|
115
|
+
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
116
|
+
'/tmp/test.properties',
|
|
117
|
+
'a=1\nb=2',
|
|
118
|
+
'utf8',
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('calls process.exit on write error', async () => {
|
|
123
|
+
fs.writeFile.mockRejectedValue(new Error('disk full'));
|
|
124
|
+
await cleanAndWritePropertiesFile('/bad/path', 'x=y');
|
|
125
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('updateCoreSiteConfig', () => {
|
|
130
|
+
it('updates config when S3 is enabled', async () => {
|
|
131
|
+
config.getVaultSecretsConfigs.mockReturnValue({
|
|
132
|
+
awsSecretsPath: 'aws/path',
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
deploy.cloneRepository.mockResolvedValue('/repo/path');
|
|
136
|
+
|
|
137
|
+
vault.getVaultSecrets.mockResolvedValue({
|
|
138
|
+
AWS_ACCESS_KEY_ID: 'abc',
|
|
139
|
+
AWS_SECRET_ACCESS_KEY: 'def',
|
|
140
|
+
AWS_REGION: 'us-west-2',
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
fs.ensureDir.mockResolvedValue();
|
|
144
|
+
fs.writeFile.mockResolvedValue();
|
|
145
|
+
deploy.pushDeployConfig.mockResolvedValue();
|
|
146
|
+
|
|
147
|
+
await updateCoreSiteConfig('mysite', 'dev', true, false);
|
|
148
|
+
|
|
149
|
+
expect(fs.ensureDir).toHaveBeenCalledWith('/repo/path/MYSITE');
|
|
150
|
+
expect(fs.writeFile).toHaveBeenCalled();
|
|
151
|
+
expect(deploy.pushDeployConfig).toHaveBeenCalled();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('calls process.exit if cloneRepository fails', async () => {
|
|
155
|
+
config.getVaultSecretsConfigs.mockReturnValue({
|
|
156
|
+
awsSecretsPath: 'aws/path',
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
deploy.cloneRepository.mockRejectedValue(new Error('bad clone'));
|
|
160
|
+
|
|
161
|
+
await updateCoreSiteConfig('mysite', 'dev', true, false);
|
|
162
|
+
|
|
163
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
164
|
+
expect(console.error).toHaveBeenCalledWith('Error:', 'bad clone');
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
});
|
package/src/auth0/export.js
CHANGED
|
@@ -2,7 +2,7 @@ import { getVaultSecrets } from './vault.js';
|
|
|
2
2
|
import { getVaultSecretsConfigs } from '../next/config.js';
|
|
3
3
|
import { dump } from 'auth0-deploy-cli';
|
|
4
4
|
|
|
5
|
-
export async function exportConf(site, env) {
|
|
5
|
+
export async function exportConf(site, env, auth0Tenant) {
|
|
6
6
|
try {
|
|
7
7
|
console.log(
|
|
8
8
|
`Exporting Auth0 configuration for site: ${site} and env: ${env}`,
|
|
@@ -13,7 +13,12 @@ export async function exportConf(site, env) {
|
|
|
13
13
|
console.log(`siteCommonSecretsPath => ${siteCommonSecretsPath}`);
|
|
14
14
|
const siteCommonSecrets = await getVaultSecrets(siteCommonSecretsPath);
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
let auth0Domain;
|
|
17
|
+
if (auth0Tenant === '') {
|
|
18
|
+
auth0Domain = `docupace-${env}-${site}.us.auth0.com`;
|
|
19
|
+
} else {
|
|
20
|
+
auth0Domain = `${auth0Tenant}.us.auth0.com`;
|
|
21
|
+
}
|
|
17
22
|
const auth0ClientId = siteCommonSecrets.AUTH0_CLIENT_ID;
|
|
18
23
|
const auth0ClientSecret = siteCommonSecrets.AUTH0_CLIENT_SECRET;
|
|
19
24
|
|