Skip to content

Commit 7e25895

Browse files
committed
test: use random ports for local verdaccio npm servers
1 parent e72fbcd commit 7e25895

File tree

4 files changed

+149
-121
lines changed

4 files changed

+149
-121
lines changed

tests/legacy-cli/e2e/utils/registry.ts

+15-9
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
1-
import { ChildProcess, spawn } from 'child_process';
2-
import { copyFileSync, mkdtempSync, realpathSync } from 'fs';
1+
import { spawn } from 'child_process';
2+
import { mkdtempSync, realpathSync } from 'fs';
33
import { tmpdir } from 'os';
44
import { join } from 'path';
5-
import { writeFile } from './fs';
5+
import { getGlobalVariable } from './env';
6+
import { writeFile, readFile } from './fs';
67

7-
export function createNpmRegistry(withAuthentication = false): ChildProcess {
8+
export async function createNpmRegistry(
9+
port: number,
10+
httpsPort: number,
11+
withAuthentication = false,
12+
) {
813
// Setup local package registry
914
const registryPath = mkdtempSync(join(realpathSync(tmpdir()), 'angular-cli-e2e-registry-'));
1015

11-
copyFileSync(
16+
let configContent = await readFile(
1217
join(__dirname, '../../', withAuthentication ? 'verdaccio_auth.yaml' : 'verdaccio.yaml'),
13-
join(registryPath, 'verdaccio.yaml'),
1418
);
19+
configContent = configContent.replace(/\$\{HTTP_PORT\}/g, String(port));
20+
configContent = configContent.replace(/\$\{HTTPS_PORT\}/g, String(httpsPort));
21+
await writeFile(join(registryPath, 'verdaccio.yaml'), configContent);
1522

1623
return spawn('node', [require.resolve('verdaccio/bin/verdaccio'), '-c', './verdaccio.yaml'], {
1724
cwd: registryPath,
@@ -21,7 +28,6 @@ export function createNpmRegistry(withAuthentication = false): ChildProcess {
2128

2229
// Token was generated using `echo -n 'testing:s3cret' | openssl base64`.
2330
const VALID_TOKEN = `dGVzdGluZzpzM2NyZXQ=`;
24-
const SECURE_REGISTRY = `//localhost:4876/`;
2531

2632
export function createNpmConfigForAuthentication(
2733
/**
@@ -42,7 +48,7 @@ export function createNpmConfigForAuthentication(
4248
invalidToken = false,
4349
): Promise<void> {
4450
const token = invalidToken ? `invalid=` : VALID_TOKEN;
45-
const registry = SECURE_REGISTRY;
51+
const registry = (getGlobalVariable('package-registry') as string).replace(/^\w+:/, '');
4652

4753
return writeFile(
4854
'.npmrc',
@@ -68,7 +74,7 @@ export function setNpmEnvVarsForAuthentication(
6874
delete process.env['NPM_CONFIG_REGISTRY'];
6975

7076
const registryKey = useYarnEnvVariable ? 'YARN_REGISTRY' : 'NPM_CONFIG_REGISTRY';
71-
process.env[registryKey] = `http:${SECURE_REGISTRY}`;
77+
process.env[registryKey] = getGlobalVariable('package-registry');
7278

7379
process.env['NPM_CONFIG__AUTH'] = invalidToken ? `invalid=` : VALID_TOKEN;
7480

tests/legacy-cli/e2e_runner.ts

+131-109
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import * as path from 'path';
99
import { setGlobalVariable } from './e2e/utils/env';
1010
import { gitClean } from './e2e/utils/git';
1111
import { createNpmRegistry } from './e2e/utils/registry';
12+
import { AddressInfo, createServer } from 'net';
1213

1314
Error.stackTraceLimit = Infinity;
1415

@@ -122,118 +123,121 @@ if (testsToRun.length == allTests.length) {
122123
setGlobalVariable('argv', argv);
123124
setGlobalVariable('ci', process.env['CI']?.toLowerCase() === 'true' || process.env['CI'] === '1');
124125
setGlobalVariable('package-manager', argv.yarn ? 'yarn' : 'npm');
125-
setGlobalVariable('package-registry', 'http://localhost:4873');
126-
127-
const registryProcess = createNpmRegistry();
128-
const secureRegistryProcess = createNpmRegistry(true);
129-
130-
testsToRun
131-
.reduce((previous, relativeName, testIndex) => {
132-
// Make sure this is a windows compatible path.
133-
let absoluteName = path.join(e2eRoot, relativeName);
134-
if (/^win/.test(process.platform)) {
135-
absoluteName = absoluteName.replace(/\\/g, path.posix.sep);
136-
}
137-
138-
return previous.then(() => {
139-
currentFileName = relativeName.replace(/\.ts$/, '');
140-
const start = +new Date();
141-
142-
const module = require(absoluteName);
143-
const originalEnvVariables = {
144-
...process.env,
145-
};
146-
147-
const fn: (skipClean?: () => void) => Promise<void> | void =
148-
typeof module == 'function'
149-
? module
150-
: typeof module.default == 'function'
151-
? module.default
152-
: () => {
153-
throw new Error('Invalid test module.');
154-
};
155-
156-
let clean = true;
157-
let previousDir = null;
158-
159-
return Promise.resolve()
160-
.then(() => printHeader(currentFileName, testIndex))
161-
.then(() => (previousDir = process.cwd()))
162-
.then(() => logStack.push(lastLogger().createChild(currentFileName)))
163-
.then(() => fn(() => (clean = false)))
164-
.then(
165-
() => logStack.pop(),
166-
(err) => {
167-
logStack.pop();
168-
throw err;
169-
},
170-
)
171-
.then(() => console.log('----'))
172-
.then(() => {
173-
// If we're not in a setup, change the directory back to where it was before the test.
174-
// This allows tests to chdir without worrying about keeping the original directory.
175-
if (!allSetups.includes(relativeName) && previousDir) {
176-
process.chdir(previousDir);
177-
178-
// Restore env variables before each test.
179-
console.log(' Restoring original environment variables...');
180-
process.env = originalEnvVariables;
181-
}
182-
})
183-
.then(() => {
184-
// Only clean after a real test, not a setup step. Also skip cleaning if the test
185-
// requested an exception.
186-
if (!allSetups.includes(relativeName) && clean) {
187-
logStack.push(new logging.NullLogger());
188-
return gitClean().then(
189-
() => logStack.pop(),
190-
(err) => {
191-
logStack.pop();
192-
throw err;
193-
},
194-
);
126+
127+
Promise.all([findFreePort(), findFreePort()]).then(async ([httpPort, httpsPort]) => {
128+
setGlobalVariable('package-registry', 'http://localhost:' + httpPort);
129+
130+
const registryProcess = await createNpmRegistry(httpPort, httpPort);
131+
const secureRegistryProcess = await createNpmRegistry(httpPort, httpsPort, true);
132+
133+
return testsToRun
134+
.reduce((previous, relativeName, testIndex) => {
135+
// Make sure this is a windows compatible path.
136+
let absoluteName = path.join(e2eRoot, relativeName);
137+
if (/^win/.test(process.platform)) {
138+
absoluteName = absoluteName.replace(/\\/g, path.posix.sep);
139+
}
140+
141+
return previous.then(() => {
142+
currentFileName = relativeName.replace(/\.ts$/, '');
143+
const start = +new Date();
144+
145+
const module = require(absoluteName);
146+
const originalEnvVariables = {
147+
...process.env,
148+
};
149+
150+
const fn: (skipClean?: () => void) => Promise<void> | void =
151+
typeof module == 'function'
152+
? module
153+
: typeof module.default == 'function'
154+
? module.default
155+
: () => {
156+
throw new Error('Invalid test module.');
157+
};
158+
159+
let clean = true;
160+
let previousDir = null;
161+
162+
return Promise.resolve()
163+
.then(() => printHeader(currentFileName, testIndex))
164+
.then(() => (previousDir = process.cwd()))
165+
.then(() => logStack.push(lastLogger().createChild(currentFileName)))
166+
.then(() => fn(() => (clean = false)))
167+
.then(
168+
() => logStack.pop(),
169+
(err) => {
170+
logStack.pop();
171+
throw err;
172+
},
173+
)
174+
.then(() => console.log('----'))
175+
.then(() => {
176+
// If we're not in a setup, change the directory back to where it was before the test.
177+
// This allows tests to chdir without worrying about keeping the original directory.
178+
if (!allSetups.includes(relativeName) && previousDir) {
179+
process.chdir(previousDir);
180+
181+
// Restore env variables before each test.
182+
console.log(' Restoring original environment variables...');
183+
process.env = originalEnvVariables;
184+
}
185+
})
186+
.then(() => {
187+
// Only clean after a real test, not a setup step. Also skip cleaning if the test
188+
// requested an exception.
189+
if (!allSetups.includes(relativeName) && clean) {
190+
logStack.push(new logging.NullLogger());
191+
return gitClean().then(
192+
() => logStack.pop(),
193+
(err) => {
194+
logStack.pop();
195+
throw err;
196+
},
197+
);
198+
}
199+
})
200+
.then(
201+
() => printFooter(currentFileName, start),
202+
(err) => {
203+
printFooter(currentFileName, start);
204+
console.error(err);
205+
throw err;
206+
},
207+
);
208+
});
209+
}, Promise.resolve())
210+
.then(
211+
() => {
212+
registryProcess.kill();
213+
secureRegistryProcess.kill();
214+
215+
console.log(colors.green('Done.'));
216+
process.exit(0);
217+
},
218+
(err) => {
219+
console.log('\n');
220+
console.error(colors.red(`Test "${currentFileName}" failed...`));
221+
console.error(colors.red(err.message));
222+
console.error(colors.red(err.stack));
223+
224+
registryProcess.kill();
225+
secureRegistryProcess.kill();
226+
227+
if (argv.debug) {
228+
console.log(`Current Directory: ${process.cwd()}`);
229+
console.log('Will loop forever while you debug... CTRL-C to quit.');
230+
231+
/* eslint-disable no-constant-condition */
232+
while (1) {
233+
// That's right!
195234
}
196-
})
197-
.then(
198-
() => printFooter(currentFileName, start),
199-
(err) => {
200-
printFooter(currentFileName, start);
201-
console.error(err);
202-
throw err;
203-
},
204-
);
205-
});
206-
}, Promise.resolve())
207-
.then(
208-
() => {
209-
registryProcess.kill();
210-
secureRegistryProcess.kill();
211-
212-
console.log(colors.green('Done.'));
213-
process.exit(0);
214-
},
215-
(err) => {
216-
console.log('\n');
217-
console.error(colors.red(`Test "${currentFileName}" failed...`));
218-
console.error(colors.red(err.message));
219-
console.error(colors.red(err.stack));
220-
221-
registryProcess.kill();
222-
secureRegistryProcess.kill();
223-
224-
if (argv.debug) {
225-
console.log(`Current Directory: ${process.cwd()}`);
226-
console.log('Will loop forever while you debug... CTRL-C to quit.');
227-
228-
/* eslint-disable no-constant-condition */
229-
while (1) {
230-
// That's right!
231235
}
232-
}
233236

234-
process.exit(1);
235-
},
236-
);
237+
process.exit(1);
238+
},
239+
);
240+
});
237241

238242
function printHeader(testName: string, testIndex: number) {
239243
const text = `${testIndex + 1} of ${testsToRun.length}`;
@@ -257,3 +261,21 @@ function printFooter(testName: string, startTime: number) {
257261
console.log(colors.green('Last step took ') + colors.bold.blue('' + t) + colors.green('s...'));
258262
console.log('');
259263
}
264+
265+
async function findFreePort(): Promise<number> {
266+
return new Promise((resolve, reject) => {
267+
const srv = createServer((sock) => {
268+
sock.end(() =>
269+
srv.close((err) => {
270+
if (err) {
271+
reject(err);
272+
} else {
273+
resolve((srv.address() as AddressInfo).port);
274+
}
275+
}),
276+
);
277+
278+
srv.listen(0);
279+
});
280+
});
281+
}

tests/legacy-cli/verdaccio.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ storage: ./storage
33
auth:
44
auth-memory:
55
users: {}
6-
listen: localhost:4873
6+
listen: localhost:${HTTP_PORT}
77
uplinks:
88
npmjs:
99
url: https://registry.npmjs.org/

tests/legacy-cli/verdaccio_auth.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ auth:
55
testing:
66
name: testing
77
password: s3cret
8-
listen: localhost:4876
8+
listen: localhost:${HTTPS_PORT}
99
uplinks:
1010
local:
11-
url: http://localhost:4873
11+
url: http://localhost:${HTTP_PORT}
1212
cache: false
1313
maxage: 20m
1414
max_fails: 32

0 commit comments

Comments
 (0)