@docupace/pipeline 1.0.77 → 1.0.79
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/.prettierrc.cjs +12 -12
- package/README.md +135 -135
- package/bin/azuread_update_callbacks_for_app.sh +58 -58
- package/bin/next-changeset-validate.sh +8 -8
- package/bitbucket-pipelines.yml +59 -59
- package/eslint.config.js +49 -49
- package/jest.config.js +15 -15
- package/next/changeset.sh +75 -75
- package/next/copy.sh +34 -34
- package/next/deploy.sh +40 -40
- package/next/execute.sh +123 -123
- package/next/sonar.sh +23 -23
- package/package.json +1 -1
- package/src/auth0/alias.js +79 -79
- package/src/auth0/alias.test.js +172 -172
- package/src/auth0/cli.js +82 -82
- package/src/auth0/configure.js +902 -902
- package/src/auth0/configure.test.js +167 -167
- package/src/auth0/export.js +48 -48
- package/src/auth0/export.test.js +103 -103
- package/src/auth0/metadata.js +141 -141
- package/src/auth0/metadata.test.js +186 -186
- package/src/auth0/vault.js +60 -60
- package/src/auth0/vault.test.js +134 -134
- package/src/changeset/changelog-wiki.js +328 -328
- package/src/changeset/changeset-config.js +34 -34
- package/src/changeset/cli.js +237 -237
- package/src/changeset/create.js +215 -215
- package/src/changeset/package-util.js +87 -87
- package/src/changeset/util.js +43 -43
- package/src/changeset/validate.js +91 -91
- package/src/changeset/version.js +102 -102
- package/src/jenkins/setParams.groovy +58 -58
- package/src/lib/merge-driver.js +204 -204
- package/src/merge/cli-merge-all.js +507 -507
- package/src/merge/cli-merge.js +492 -492
- package/src/merge/cli-resolve.js +399 -399
- package/src/merge/cli.js +38 -38
- package/src/merge/merge-changelog.js +87 -87
- package/src/merge/merge-package.js +106 -106
- package/src/merge/merge-types.js +82 -82
- package/src/merge/pr.js +231 -231
- package/src/micro/deploy-microservice.js +153 -0
- package/src/micro/{ms-publish.js → publish-ms.js} +8 -6
- package/src/next/alias.js +53 -53
- package/src/next/apache.js +81 -81
- package/src/next/cli.js +10 -1
- package/src/next/config.js +137 -137
- package/src/next/connect.js +226 -226
- package/src/next/copy.js +61 -61
- package/src/next/deploy.js +666 -633
- package/src/next/git-to-semver.js +79 -79
- package/src/next/haproxy.js +76 -76
- package/src/next/jenkins.js +123 -123
- package/src/next/jira.js +84 -84
- package/src/next/logger.js +17 -17
- package/src/next/pipline.js +114 -114
- package/src/next/publish.js +71 -71
- package/src/next/pull.js +7 -7
- package/src/next/sonar.js +23 -23
- package/src/next/test.js +5 -5
- package/src/next/util.js +95 -93
- package/src/next/vault.js +45 -45
- package/terraform/apply.sh +37 -37
- package/terraform/init.sh +72 -72
- package/terraform/plan.sh +76 -76
- package/terraform/validate.sh +17 -17
- package/src/micro/cli.js +0 -71
- package/src/micro/ms-deploy.js +0 -271
package/src/auth0/metadata.js
CHANGED
|
@@ -1,141 +1,141 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import axios from 'axios';
|
|
3
|
-
import * as path from 'node:path'; // ESM-compatible import
|
|
4
|
-
import { cloneRepository, pushDeployConfig } from '../next/deploy.js';
|
|
5
|
-
import { getAuth0AuthToken } from './configure.js';
|
|
6
|
-
import { getVaultSecrets } from './vault.js';
|
|
7
|
-
import { getVaultSecretsConfigs } from '../next/config.js';
|
|
8
|
-
|
|
9
|
-
export async function getAuth0Metadata(site, env, auth0Tenant, jiraTicket) {
|
|
10
|
-
try {
|
|
11
|
-
const connectDeployRepoPath = await cloneRepository('connect-deploy');
|
|
12
|
-
|
|
13
|
-
const envMap = {
|
|
14
|
-
dev: 'dev',
|
|
15
|
-
preprod: 'qa',
|
|
16
|
-
stage: 'stage',
|
|
17
|
-
uat: 'uat',
|
|
18
|
-
'prod-www3': 'prod',
|
|
19
|
-
'prod-lax': 'prod',
|
|
20
|
-
'prod-las': 'prod',
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
process.chdir(`${connectDeployRepoPath}/envs/${env}/${site}`);
|
|
24
|
-
|
|
25
|
-
const configPath = path.join('conf', 'application.conf');
|
|
26
|
-
const appConf = await fs.readFile(configPath, 'utf-8');
|
|
27
|
-
|
|
28
|
-
const connectContext = appConf.match(
|
|
29
|
-
/application\.context\s*=\s*"(.*?)"/,
|
|
30
|
-
)?.[1];
|
|
31
|
-
if (!connectContext) {
|
|
32
|
-
console.error('connectContext not found in application.conf. Exiting...');
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const siteHost = appConf.match(
|
|
37
|
-
/docupace\.baseUrlClient\s*=\s*"?https?:\/\/([^/]+)"?/,
|
|
38
|
-
)?.[1];
|
|
39
|
-
if (!siteHost) {
|
|
40
|
-
console.error('siteHost not found in application.conf. Exiting...');
|
|
41
|
-
process.exit(1);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const connectEndpoint = `https://${siteHost}${connectContext}`;
|
|
45
|
-
console.log('CONNECT_ENDPOINT:', connectEndpoint);
|
|
46
|
-
|
|
47
|
-
console.log('Requesting token from Auth0...');
|
|
48
|
-
const siteCommonSecretsPath =
|
|
49
|
-
getVaultSecretsConfigs(site).siteCommonSecretsPath;
|
|
50
|
-
console.log(`siteCommonSecretsPath => ${siteCommonSecretsPath}`);
|
|
51
|
-
|
|
52
|
-
const siteCommonSecrets = await getVaultSecrets(siteCommonSecretsPath);
|
|
53
|
-
|
|
54
|
-
const auth0Domain =
|
|
55
|
-
auth0Tenant === ''
|
|
56
|
-
? `docupace-${envMap[env]}-${site}.us.auth0.com`
|
|
57
|
-
: `${auth0Tenant}.us.auth0.com`;
|
|
58
|
-
|
|
59
|
-
const auth0ClientId = siteCommonSecrets.AUTH0_CLIENT_ID;
|
|
60
|
-
const auth0ClientSecret = siteCommonSecrets.AUTH0_CLIENT_SECRET;
|
|
61
|
-
|
|
62
|
-
const auth0Token = await getAuth0AuthToken(
|
|
63
|
-
auth0Domain,
|
|
64
|
-
auth0ClientId,
|
|
65
|
-
auth0ClientSecret,
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
const auth0Response = await axios.get(
|
|
69
|
-
`https://${auth0Domain}/api/v2/clients`,
|
|
70
|
-
{
|
|
71
|
-
headers: { authorization: `Bearer ${auth0Token}` },
|
|
72
|
-
},
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
const connectClient = auth0Response.data.find(
|
|
76
|
-
(client) => client.name === 'docupace-connect',
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
if (!connectClient) {
|
|
80
|
-
console.error('Client "docupace-connect" not found.');
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const clientId = connectClient.client_id;
|
|
85
|
-
console.log('Found client_id:', clientId);
|
|
86
|
-
|
|
87
|
-
const metadataRes = await axios.get(
|
|
88
|
-
`https://${auth0Domain}/samlp/metadata/${clientId}`,
|
|
89
|
-
{ responseType: 'text' },
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
const metadata = metadataRes.data;
|
|
93
|
-
|
|
94
|
-
if (metadata.length < 2000) {
|
|
95
|
-
console.error(
|
|
96
|
-
'Downloaded metadata looks incorrect (too small). Exiting...',
|
|
97
|
-
);
|
|
98
|
-
process.exit(1);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
await fs.ensureDir('conf/saml');
|
|
102
|
-
await fs.writeFile('conf/saml/auth0.xml', metadata);
|
|
103
|
-
console.log('SAML metadata written to conf/saml/auth0.xml');
|
|
104
|
-
|
|
105
|
-
let newConfig = appConf;
|
|
106
|
-
|
|
107
|
-
if (/useSAML\s*=/.test(appConf)) {
|
|
108
|
-
newConfig = newConfig.replace(/useSAML\s*=.*/, 'useSAML=true');
|
|
109
|
-
} else {
|
|
110
|
-
newConfig += '\nuseSAML=true';
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const replacements = {
|
|
114
|
-
'saml.clients.auth0.idpMetadataPath': '${samlEnvPath}"/auth0.xml"',
|
|
115
|
-
'saml.clients.auth0.spEntityId': `"${connectEndpoint}"`,
|
|
116
|
-
'saml.clients.auth0.redirectFormInput': '"RelayState"',
|
|
117
|
-
'saml.clients.auth0.idpLoginUrl': `"https://${auth0Domain}/v2/logout?client_id=${clientId}&returnTo=${connectEndpoint}"`,
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
for (const [key, value] of Object.entries(replacements)) {
|
|
121
|
-
const pattern = new RegExp(`${key}\\s*=.*`, 'g');
|
|
122
|
-
if (pattern.test(newConfig)) {
|
|
123
|
-
newConfig = newConfig.replace(pattern, `${key}=${value}`);
|
|
124
|
-
} else {
|
|
125
|
-
newConfig += `\n${key}=${value}`;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
await fs.writeFile(configPath, newConfig);
|
|
130
|
-
console.log('The application.conf has been updated.');
|
|
131
|
-
|
|
132
|
-
const commitMessage = jiraTicket
|
|
133
|
-
? `${jiraTicket} Auth0 SAML metadata update for site: (${site}) in env: (${env})`
|
|
134
|
-
: `Auth0 SAML metadata update for site: (${site}) in env: (${env})`;
|
|
135
|
-
console.log('Commit message:', commitMessage);
|
|
136
|
-
await pushDeployConfig(connectDeployRepoPath, commitMessage);
|
|
137
|
-
} catch (error) {
|
|
138
|
-
console.error('Error:', error.message);
|
|
139
|
-
process.exit(1);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import * as path from 'node:path'; // ESM-compatible import
|
|
4
|
+
import { cloneRepository, pushDeployConfig } from '../next/deploy.js';
|
|
5
|
+
import { getAuth0AuthToken } from './configure.js';
|
|
6
|
+
import { getVaultSecrets } from './vault.js';
|
|
7
|
+
import { getVaultSecretsConfigs } from '../next/config.js';
|
|
8
|
+
|
|
9
|
+
export async function getAuth0Metadata(site, env, auth0Tenant, jiraTicket) {
|
|
10
|
+
try {
|
|
11
|
+
const connectDeployRepoPath = await cloneRepository('connect-deploy');
|
|
12
|
+
|
|
13
|
+
const envMap = {
|
|
14
|
+
dev: 'dev',
|
|
15
|
+
preprod: 'qa',
|
|
16
|
+
stage: 'stage',
|
|
17
|
+
uat: 'uat',
|
|
18
|
+
'prod-www3': 'prod',
|
|
19
|
+
'prod-lax': 'prod',
|
|
20
|
+
'prod-las': 'prod',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
process.chdir(`${connectDeployRepoPath}/envs/${env}/${site}`);
|
|
24
|
+
|
|
25
|
+
const configPath = path.join('conf', 'application.conf');
|
|
26
|
+
const appConf = await fs.readFile(configPath, 'utf-8');
|
|
27
|
+
|
|
28
|
+
const connectContext = appConf.match(
|
|
29
|
+
/application\.context\s*=\s*"(.*?)"/,
|
|
30
|
+
)?.[1];
|
|
31
|
+
if (!connectContext) {
|
|
32
|
+
console.error('connectContext not found in application.conf. Exiting...');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const siteHost = appConf.match(
|
|
37
|
+
/docupace\.baseUrlClient\s*=\s*"?https?:\/\/([^/]+)"?/,
|
|
38
|
+
)?.[1];
|
|
39
|
+
if (!siteHost) {
|
|
40
|
+
console.error('siteHost not found in application.conf. Exiting...');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const connectEndpoint = `https://${siteHost}${connectContext}`;
|
|
45
|
+
console.log('CONNECT_ENDPOINT:', connectEndpoint);
|
|
46
|
+
|
|
47
|
+
console.log('Requesting token from Auth0...');
|
|
48
|
+
const siteCommonSecretsPath =
|
|
49
|
+
getVaultSecretsConfigs(site).siteCommonSecretsPath;
|
|
50
|
+
console.log(`siteCommonSecretsPath => ${siteCommonSecretsPath}`);
|
|
51
|
+
|
|
52
|
+
const siteCommonSecrets = await getVaultSecrets(siteCommonSecretsPath);
|
|
53
|
+
|
|
54
|
+
const auth0Domain =
|
|
55
|
+
auth0Tenant === ''
|
|
56
|
+
? `docupace-${envMap[env]}-${site}.us.auth0.com`
|
|
57
|
+
: `${auth0Tenant}.us.auth0.com`;
|
|
58
|
+
|
|
59
|
+
const auth0ClientId = siteCommonSecrets.AUTH0_CLIENT_ID;
|
|
60
|
+
const auth0ClientSecret = siteCommonSecrets.AUTH0_CLIENT_SECRET;
|
|
61
|
+
|
|
62
|
+
const auth0Token = await getAuth0AuthToken(
|
|
63
|
+
auth0Domain,
|
|
64
|
+
auth0ClientId,
|
|
65
|
+
auth0ClientSecret,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const auth0Response = await axios.get(
|
|
69
|
+
`https://${auth0Domain}/api/v2/clients`,
|
|
70
|
+
{
|
|
71
|
+
headers: { authorization: `Bearer ${auth0Token}` },
|
|
72
|
+
},
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const connectClient = auth0Response.data.find(
|
|
76
|
+
(client) => client.name === 'docupace-connect',
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
if (!connectClient) {
|
|
80
|
+
console.error('Client "docupace-connect" not found.');
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const clientId = connectClient.client_id;
|
|
85
|
+
console.log('Found client_id:', clientId);
|
|
86
|
+
|
|
87
|
+
const metadataRes = await axios.get(
|
|
88
|
+
`https://${auth0Domain}/samlp/metadata/${clientId}`,
|
|
89
|
+
{ responseType: 'text' },
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const metadata = metadataRes.data;
|
|
93
|
+
|
|
94
|
+
if (metadata.length < 2000) {
|
|
95
|
+
console.error(
|
|
96
|
+
'Downloaded metadata looks incorrect (too small). Exiting...',
|
|
97
|
+
);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
await fs.ensureDir('conf/saml');
|
|
102
|
+
await fs.writeFile('conf/saml/auth0.xml', metadata);
|
|
103
|
+
console.log('SAML metadata written to conf/saml/auth0.xml');
|
|
104
|
+
|
|
105
|
+
let newConfig = appConf;
|
|
106
|
+
|
|
107
|
+
if (/useSAML\s*=/.test(appConf)) {
|
|
108
|
+
newConfig = newConfig.replace(/useSAML\s*=.*/, 'useSAML=true');
|
|
109
|
+
} else {
|
|
110
|
+
newConfig += '\nuseSAML=true';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const replacements = {
|
|
114
|
+
'saml.clients.auth0.idpMetadataPath': '${samlEnvPath}"/auth0.xml"',
|
|
115
|
+
'saml.clients.auth0.spEntityId': `"${connectEndpoint}"`,
|
|
116
|
+
'saml.clients.auth0.redirectFormInput': '"RelayState"',
|
|
117
|
+
'saml.clients.auth0.idpLoginUrl': `"https://${auth0Domain}/v2/logout?client_id=${clientId}&returnTo=${connectEndpoint}"`,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
121
|
+
const pattern = new RegExp(`${key}\\s*=.*`, 'g');
|
|
122
|
+
if (pattern.test(newConfig)) {
|
|
123
|
+
newConfig = newConfig.replace(pattern, `${key}=${value}`);
|
|
124
|
+
} else {
|
|
125
|
+
newConfig += `\n${key}=${value}`;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
await fs.writeFile(configPath, newConfig);
|
|
130
|
+
console.log('The application.conf has been updated.');
|
|
131
|
+
|
|
132
|
+
const commitMessage = jiraTicket
|
|
133
|
+
? `${jiraTicket} Auth0 SAML metadata update for site: (${site}) in env: (${env})`
|
|
134
|
+
: `Auth0 SAML metadata update for site: (${site}) in env: (${env})`;
|
|
135
|
+
console.log('Commit message:', commitMessage);
|
|
136
|
+
await pushDeployConfig(connectDeployRepoPath, commitMessage);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error('Error:', error.message);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -1,186 +1,186 @@
|
|
|
1
|
-
import { jest } from '@jest/globals';
|
|
2
|
-
|
|
3
|
-
// ----- Mocks (ESM-safe) -----
|
|
4
|
-
jest.unstable_mockModule('fs-extra', () => ({
|
|
5
|
-
default: {
|
|
6
|
-
readFile: jest.fn(),
|
|
7
|
-
writeFile: jest.fn(),
|
|
8
|
-
ensureDir: jest.fn(),
|
|
9
|
-
},
|
|
10
|
-
}));
|
|
11
|
-
|
|
12
|
-
jest.unstable_mockModule('axios', () => ({
|
|
13
|
-
default: {
|
|
14
|
-
get: jest.fn(),
|
|
15
|
-
},
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
jest.unstable_mockModule('path', () => ({
|
|
19
|
-
join: jest.fn((...args) => args.join('/')),
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
jest.unstable_mockModule('../next/deploy.js', () => ({
|
|
23
|
-
cloneRepository: jest.fn(),
|
|
24
|
-
pushDeployConfig: jest.fn(),
|
|
25
|
-
}));
|
|
26
|
-
|
|
27
|
-
jest.unstable_mockModule('./configure.js', () => ({
|
|
28
|
-
getAuth0AuthToken: jest.fn(),
|
|
29
|
-
}));
|
|
30
|
-
|
|
31
|
-
jest.unstable_mockModule('./vault.js', () => ({
|
|
32
|
-
getVaultSecrets: jest.fn(),
|
|
33
|
-
}));
|
|
34
|
-
|
|
35
|
-
jest.unstable_mockModule('../next/config.js', () => ({
|
|
36
|
-
getVaultSecretsConfigs: jest.fn(),
|
|
37
|
-
}));
|
|
38
|
-
|
|
39
|
-
// ----- Imports AFTER mocks -----
|
|
40
|
-
const { getAuth0Metadata } = await import('./metadata.js');
|
|
41
|
-
const fs = (await import('fs-extra')).default;
|
|
42
|
-
const axios = (await import('axios')).default;
|
|
43
|
-
const { cloneRepository, pushDeployConfig } = await import('../next/deploy.js');
|
|
44
|
-
const { getAuth0AuthToken } = await import('./configure.js');
|
|
45
|
-
const { getVaultSecrets } = await import('./vault.js');
|
|
46
|
-
const { getVaultSecretsConfigs } = await import('../next/config.js');
|
|
47
|
-
|
|
48
|
-
// ----- Shared Setup -----
|
|
49
|
-
beforeAll(() => {
|
|
50
|
-
jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
51
|
-
jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
afterAll(() => {
|
|
55
|
-
console.log.mockRestore();
|
|
56
|
-
console.error.mockRestore();
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe('getAuth0Metadata', () => {
|
|
60
|
-
const site = 'example';
|
|
61
|
-
const env = 'dev';
|
|
62
|
-
const auth0Tenant = '';
|
|
63
|
-
const fakeRepoPath = '/tmp/repo';
|
|
64
|
-
const fakeAppConf = `
|
|
65
|
-
application.context = "/connect"
|
|
66
|
-
docupace.baseUrlClient = "https://example.docupace.com"
|
|
67
|
-
`;
|
|
68
|
-
|
|
69
|
-
const mockSecrets = {
|
|
70
|
-
AUTH0_CLIENT_ID: 'client-id',
|
|
71
|
-
AUTH0_CLIENT_SECRET: 'client-secret',
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
const auth0Token = 'fake-token';
|
|
75
|
-
const clientId = 'abc123';
|
|
76
|
-
const fakeMetadata = '<xml>'.padEnd(2500, 'a') + '</xml>';
|
|
77
|
-
|
|
78
|
-
let originalChdir;
|
|
79
|
-
let exitSpy;
|
|
80
|
-
|
|
81
|
-
beforeEach(() => {
|
|
82
|
-
jest.clearAllMocks();
|
|
83
|
-
axios.get.mockReset();
|
|
84
|
-
|
|
85
|
-
cloneRepository.mockResolvedValue(fakeRepoPath);
|
|
86
|
-
getVaultSecretsConfigs.mockReturnValue({
|
|
87
|
-
siteCommonSecretsPath: 'vault/path',
|
|
88
|
-
});
|
|
89
|
-
getVaultSecrets.mockResolvedValue(mockSecrets);
|
|
90
|
-
getAuth0AuthToken.mockResolvedValue(auth0Token);
|
|
91
|
-
fs.readFile.mockResolvedValue(fakeAppConf);
|
|
92
|
-
fs.writeFile.mockResolvedValue();
|
|
93
|
-
fs.ensureDir.mockResolvedValue();
|
|
94
|
-
pushDeployConfig.mockResolvedValue();
|
|
95
|
-
|
|
96
|
-
originalChdir = process.chdir;
|
|
97
|
-
process.chdir = jest.fn();
|
|
98
|
-
|
|
99
|
-
exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
afterEach(() => {
|
|
103
|
-
process.chdir = originalChdir;
|
|
104
|
-
exitSpy.mockRestore();
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('should download and write metadata, update config, and push changes', async () => {
|
|
108
|
-
axios.get
|
|
109
|
-
.mockResolvedValueOnce({
|
|
110
|
-
data: [{ name: 'docupace-connect', client_id: clientId }],
|
|
111
|
-
}) // clients
|
|
112
|
-
.mockResolvedValueOnce({ data: fakeMetadata }); // metadata
|
|
113
|
-
|
|
114
|
-
await getAuth0Metadata(site, env, auth0Tenant);
|
|
115
|
-
|
|
116
|
-
expect(cloneRepository).toHaveBeenCalledWith('connect-deploy');
|
|
117
|
-
expect(fs.readFile).toHaveBeenCalledWith('conf/application.conf', 'utf-8');
|
|
118
|
-
expect(fs.ensureDir).toHaveBeenCalledWith('conf/saml');
|
|
119
|
-
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
120
|
-
'conf/saml/auth0.xml',
|
|
121
|
-
expect.stringContaining('<xml'),
|
|
122
|
-
);
|
|
123
|
-
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
124
|
-
'conf/application.conf',
|
|
125
|
-
expect.stringContaining('useSAML=true'),
|
|
126
|
-
);
|
|
127
|
-
expect(pushDeployConfig).toHaveBeenCalled();
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('should exit if docupace-connect client is not found', async () => {
|
|
131
|
-
axios.get.mockResolvedValueOnce({ data: [] }); // empty clients
|
|
132
|
-
|
|
133
|
-
await getAuth0Metadata(site, env, auth0Tenant);
|
|
134
|
-
|
|
135
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
136
|
-
expect(console.error).toHaveBeenCalledWith(
|
|
137
|
-
'Client "docupace-connect" not found.',
|
|
138
|
-
);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('should exit if metadata is too small', async () => {
|
|
142
|
-
axios.get
|
|
143
|
-
.mockResolvedValueOnce({
|
|
144
|
-
data: [{ name: 'docupace-connect', client_id: clientId }],
|
|
145
|
-
}) // clients
|
|
146
|
-
.mockResolvedValueOnce({ data: '<small>' }); // small metadata
|
|
147
|
-
|
|
148
|
-
await getAuth0Metadata(site, env, auth0Tenant);
|
|
149
|
-
|
|
150
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
151
|
-
expect(console.error).toHaveBeenCalledWith(
|
|
152
|
-
'Downloaded metadata looks incorrect (too small). Exiting...',
|
|
153
|
-
);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it('should exit if connectContext is missing', async () => {
|
|
157
|
-
fs.readFile.mockResolvedValue(`docupace.baseUrlClient = "https://abc.com"`); // no context
|
|
158
|
-
|
|
159
|
-
await getAuth0Metadata(site, env, auth0Tenant);
|
|
160
|
-
|
|
161
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
162
|
-
expect(console.error).toHaveBeenCalledWith(
|
|
163
|
-
'connectContext not found in application.conf. Exiting...',
|
|
164
|
-
);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('should exit if siteHost is missing', async () => {
|
|
168
|
-
fs.readFile.mockResolvedValue(`application.context = "/abc"`); // no host
|
|
169
|
-
|
|
170
|
-
await getAuth0Metadata(site, env, auth0Tenant);
|
|
171
|
-
|
|
172
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
173
|
-
expect(console.error).toHaveBeenCalledWith(
|
|
174
|
-
'siteHost not found in application.conf. Exiting...',
|
|
175
|
-
);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('should exit on unknown error', async () => {
|
|
179
|
-
getVaultSecrets.mockRejectedValue(new Error('boom'));
|
|
180
|
-
|
|
181
|
-
await getAuth0Metadata(site, env, auth0Tenant);
|
|
182
|
-
|
|
183
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
184
|
-
expect(console.error).toHaveBeenCalledWith('Error:', 'boom');
|
|
185
|
-
});
|
|
186
|
-
});
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
|
|
3
|
+
// ----- Mocks (ESM-safe) -----
|
|
4
|
+
jest.unstable_mockModule('fs-extra', () => ({
|
|
5
|
+
default: {
|
|
6
|
+
readFile: jest.fn(),
|
|
7
|
+
writeFile: jest.fn(),
|
|
8
|
+
ensureDir: jest.fn(),
|
|
9
|
+
},
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
jest.unstable_mockModule('axios', () => ({
|
|
13
|
+
default: {
|
|
14
|
+
get: jest.fn(),
|
|
15
|
+
},
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
jest.unstable_mockModule('path', () => ({
|
|
19
|
+
join: jest.fn((...args) => args.join('/')),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
jest.unstable_mockModule('../next/deploy.js', () => ({
|
|
23
|
+
cloneRepository: jest.fn(),
|
|
24
|
+
pushDeployConfig: jest.fn(),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
jest.unstable_mockModule('./configure.js', () => ({
|
|
28
|
+
getAuth0AuthToken: jest.fn(),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
jest.unstable_mockModule('./vault.js', () => ({
|
|
32
|
+
getVaultSecrets: jest.fn(),
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
jest.unstable_mockModule('../next/config.js', () => ({
|
|
36
|
+
getVaultSecretsConfigs: jest.fn(),
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
// ----- Imports AFTER mocks -----
|
|
40
|
+
const { getAuth0Metadata } = await import('./metadata.js');
|
|
41
|
+
const fs = (await import('fs-extra')).default;
|
|
42
|
+
const axios = (await import('axios')).default;
|
|
43
|
+
const { cloneRepository, pushDeployConfig } = await import('../next/deploy.js');
|
|
44
|
+
const { getAuth0AuthToken } = await import('./configure.js');
|
|
45
|
+
const { getVaultSecrets } = await import('./vault.js');
|
|
46
|
+
const { getVaultSecretsConfigs } = await import('../next/config.js');
|
|
47
|
+
|
|
48
|
+
// ----- Shared Setup -----
|
|
49
|
+
beforeAll(() => {
|
|
50
|
+
jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
51
|
+
jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
afterAll(() => {
|
|
55
|
+
console.log.mockRestore();
|
|
56
|
+
console.error.mockRestore();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('getAuth0Metadata', () => {
|
|
60
|
+
const site = 'example';
|
|
61
|
+
const env = 'dev';
|
|
62
|
+
const auth0Tenant = '';
|
|
63
|
+
const fakeRepoPath = '/tmp/repo';
|
|
64
|
+
const fakeAppConf = `
|
|
65
|
+
application.context = "/connect"
|
|
66
|
+
docupace.baseUrlClient = "https://example.docupace.com"
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
const mockSecrets = {
|
|
70
|
+
AUTH0_CLIENT_ID: 'client-id',
|
|
71
|
+
AUTH0_CLIENT_SECRET: 'client-secret',
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const auth0Token = 'fake-token';
|
|
75
|
+
const clientId = 'abc123';
|
|
76
|
+
const fakeMetadata = '<xml>'.padEnd(2500, 'a') + '</xml>';
|
|
77
|
+
|
|
78
|
+
let originalChdir;
|
|
79
|
+
let exitSpy;
|
|
80
|
+
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
jest.clearAllMocks();
|
|
83
|
+
axios.get.mockReset();
|
|
84
|
+
|
|
85
|
+
cloneRepository.mockResolvedValue(fakeRepoPath);
|
|
86
|
+
getVaultSecretsConfigs.mockReturnValue({
|
|
87
|
+
siteCommonSecretsPath: 'vault/path',
|
|
88
|
+
});
|
|
89
|
+
getVaultSecrets.mockResolvedValue(mockSecrets);
|
|
90
|
+
getAuth0AuthToken.mockResolvedValue(auth0Token);
|
|
91
|
+
fs.readFile.mockResolvedValue(fakeAppConf);
|
|
92
|
+
fs.writeFile.mockResolvedValue();
|
|
93
|
+
fs.ensureDir.mockResolvedValue();
|
|
94
|
+
pushDeployConfig.mockResolvedValue();
|
|
95
|
+
|
|
96
|
+
originalChdir = process.chdir;
|
|
97
|
+
process.chdir = jest.fn();
|
|
98
|
+
|
|
99
|
+
exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
afterEach(() => {
|
|
103
|
+
process.chdir = originalChdir;
|
|
104
|
+
exitSpy.mockRestore();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should download and write metadata, update config, and push changes', async () => {
|
|
108
|
+
axios.get
|
|
109
|
+
.mockResolvedValueOnce({
|
|
110
|
+
data: [{ name: 'docupace-connect', client_id: clientId }],
|
|
111
|
+
}) // clients
|
|
112
|
+
.mockResolvedValueOnce({ data: fakeMetadata }); // metadata
|
|
113
|
+
|
|
114
|
+
await getAuth0Metadata(site, env, auth0Tenant);
|
|
115
|
+
|
|
116
|
+
expect(cloneRepository).toHaveBeenCalledWith('connect-deploy');
|
|
117
|
+
expect(fs.readFile).toHaveBeenCalledWith('conf/application.conf', 'utf-8');
|
|
118
|
+
expect(fs.ensureDir).toHaveBeenCalledWith('conf/saml');
|
|
119
|
+
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
120
|
+
'conf/saml/auth0.xml',
|
|
121
|
+
expect.stringContaining('<xml'),
|
|
122
|
+
);
|
|
123
|
+
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
124
|
+
'conf/application.conf',
|
|
125
|
+
expect.stringContaining('useSAML=true'),
|
|
126
|
+
);
|
|
127
|
+
expect(pushDeployConfig).toHaveBeenCalled();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should exit if docupace-connect client is not found', async () => {
|
|
131
|
+
axios.get.mockResolvedValueOnce({ data: [] }); // empty clients
|
|
132
|
+
|
|
133
|
+
await getAuth0Metadata(site, env, auth0Tenant);
|
|
134
|
+
|
|
135
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
136
|
+
expect(console.error).toHaveBeenCalledWith(
|
|
137
|
+
'Client "docupace-connect" not found.',
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should exit if metadata is too small', async () => {
|
|
142
|
+
axios.get
|
|
143
|
+
.mockResolvedValueOnce({
|
|
144
|
+
data: [{ name: 'docupace-connect', client_id: clientId }],
|
|
145
|
+
}) // clients
|
|
146
|
+
.mockResolvedValueOnce({ data: '<small>' }); // small metadata
|
|
147
|
+
|
|
148
|
+
await getAuth0Metadata(site, env, auth0Tenant);
|
|
149
|
+
|
|
150
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
151
|
+
expect(console.error).toHaveBeenCalledWith(
|
|
152
|
+
'Downloaded metadata looks incorrect (too small). Exiting...',
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should exit if connectContext is missing', async () => {
|
|
157
|
+
fs.readFile.mockResolvedValue(`docupace.baseUrlClient = "https://abc.com"`); // no context
|
|
158
|
+
|
|
159
|
+
await getAuth0Metadata(site, env, auth0Tenant);
|
|
160
|
+
|
|
161
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
162
|
+
expect(console.error).toHaveBeenCalledWith(
|
|
163
|
+
'connectContext not found in application.conf. Exiting...',
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should exit if siteHost is missing', async () => {
|
|
168
|
+
fs.readFile.mockResolvedValue(`application.context = "/abc"`); // no host
|
|
169
|
+
|
|
170
|
+
await getAuth0Metadata(site, env, auth0Tenant);
|
|
171
|
+
|
|
172
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
173
|
+
expect(console.error).toHaveBeenCalledWith(
|
|
174
|
+
'siteHost not found in application.conf. Exiting...',
|
|
175
|
+
);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should exit on unknown error', async () => {
|
|
179
|
+
getVaultSecrets.mockRejectedValue(new Error('boom'));
|
|
180
|
+
|
|
181
|
+
await getAuth0Metadata(site, env, auth0Tenant);
|
|
182
|
+
|
|
183
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
184
|
+
expect(console.error).toHaveBeenCalledWith('Error:', 'boom');
|
|
185
|
+
});
|
|
186
|
+
});
|