test: use random ports for local verdaccio npm servers

This commit is contained in:
Jason Bedard 2022-05-10 11:32:34 -07:00 committed by Douglas Parker
parent ba93117e78
commit aa30bb156d
4 changed files with 108 additions and 86 deletions

View File

@ -1,17 +1,24 @@
import { ChildProcess, spawn } from 'child_process';
import { copyFileSync, mkdtempSync, realpathSync } from 'fs';
import { spawn } from 'child_process';
import { mkdtempSync, realpathSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { writeFile } from './fs';
import { getGlobalVariable } from './env';
import { writeFile, readFile } from './fs';
export function createNpmRegistry(withAuthentication = false): ChildProcess {
export async function createNpmRegistry(
port: number,
httpsPort: number,
withAuthentication = false,
) {
// Setup local package registry
const registryPath = mkdtempSync(join(realpathSync(tmpdir()), 'angular-cli-e2e-registry-'));
copyFileSync(
let configContent = await readFile(
join(__dirname, '../../', withAuthentication ? 'verdaccio_auth.yaml' : 'verdaccio.yaml'),
join(registryPath, 'verdaccio.yaml'),
);
configContent = configContent.replace(/\$\{HTTP_PORT\}/g, String(port));
configContent = configContent.replace(/\$\{HTTPS_PORT\}/g, String(httpsPort));
await writeFile(join(registryPath, 'verdaccio.yaml'), configContent);
return spawn('node', [require.resolve('verdaccio/bin/verdaccio'), '-c', './verdaccio.yaml'], {
cwd: registryPath,
@ -21,7 +28,6 @@ export function createNpmRegistry(withAuthentication = false): ChildProcess {
// Token was generated using `echo -n 'testing:s3cret' | openssl base64`.
const VALID_TOKEN = `dGVzdGluZzpzM2NyZXQ=`;
const SECURE_REGISTRY = `//localhost:4876/`;
export function createNpmConfigForAuthentication(
/**
@ -42,7 +48,7 @@ export function createNpmConfigForAuthentication(
invalidToken = false,
): Promise<void> {
const token = invalidToken ? `invalid=` : VALID_TOKEN;
const registry = SECURE_REGISTRY;
const registry = (getGlobalVariable('package-secure-registry') as string).replace(/^\w+:/, '');
return writeFile(
'.npmrc',
@ -68,7 +74,7 @@ export function setNpmEnvVarsForAuthentication(
delete process.env['NPM_CONFIG_REGISTRY'];
const registryKey = useYarnEnvVariable ? 'YARN_REGISTRY' : 'NPM_CONFIG_REGISTRY';
process.env[registryKey] = `http:${SECURE_REGISTRY}`;
process.env[registryKey] = getGlobalVariable('package-secure-registry');
process.env['NPM_CONFIG__AUTH'] = invalidToken ? `invalid=` : VALID_TOKEN;

View File

@ -9,6 +9,7 @@ import * as path from 'path';
import { setGlobalVariable } from './e2e/utils/env';
import { gitClean } from './e2e/utils/git';
import { createNpmRegistry } from './e2e/utils/registry';
import { AddressInfo, createServer, Server } from 'net';
Error.stackTraceLimit = Infinity;
@ -122,93 +123,99 @@ if (testsToRun.length == allTests.length) {
setGlobalVariable('argv', argv);
setGlobalVariable('ci', process.env['CI']?.toLowerCase() === 'true' || process.env['CI'] === '1');
setGlobalVariable('package-manager', argv.yarn ? 'yarn' : 'npm');
setGlobalVariable('package-registry', 'http://localhost:4873');
const registryProcess = createNpmRegistry();
const secureRegistryProcess = createNpmRegistry(true);
Promise.all([findFreePort(), findFreePort()])
.then(async ([httpPort, httpsPort]) => {
setGlobalVariable('package-registry', 'http://localhost:' + httpPort);
setGlobalVariable('package-secure-registry', 'http://localhost:' + httpsPort);
testsToRun
.reduce((previous, relativeName, testIndex) => {
// Make sure this is a windows compatible path.
let absoluteName = path.join(e2eRoot, relativeName);
if (/^win/.test(process.platform)) {
absoluteName = absoluteName.replace(/\\/g, path.posix.sep);
}
const registryProcess = await createNpmRegistry(httpPort, httpPort);
const secureRegistryProcess = await createNpmRegistry(httpPort, httpsPort, true);
return previous.then(() => {
currentFileName = relativeName.replace(/\.ts$/, '');
const start = +new Date();
return testsToRun
.reduce((previous, relativeName, testIndex) => {
// Make sure this is a windows compatible path.
let absoluteName = path.join(e2eRoot, relativeName);
if (/^win/.test(process.platform)) {
absoluteName = absoluteName.replace(/\\/g, path.posix.sep);
}
const module = require(absoluteName);
const originalEnvVariables = {
...process.env,
};
return previous.then(() => {
currentFileName = relativeName.replace(/\.ts$/, '');
const start = +new Date();
const fn: (skipClean?: () => void) => Promise<void> | void =
typeof module == 'function'
? module
: typeof module.default == 'function'
? module.default
: () => {
throw new Error('Invalid test module.');
};
const module = require(absoluteName);
const originalEnvVariables = {
...process.env,
};
let clean = true;
let previousDir = null;
const fn: (skipClean?: () => void) => Promise<void> | void =
typeof module == 'function'
? module
: typeof module.default == 'function'
? module.default
: () => {
throw new Error('Invalid test module.');
};
return Promise.resolve()
.then(() => printHeader(currentFileName, testIndex))
.then(() => (previousDir = process.cwd()))
.then(() => logStack.push(lastLogger().createChild(currentFileName)))
.then(() => fn(() => (clean = false)))
.then(
() => logStack.pop(),
(err) => {
logStack.pop();
throw err;
},
)
.then(() => console.log('----'))
.then(() => {
// If we're not in a setup, change the directory back to where it was before the test.
// This allows tests to chdir without worrying about keeping the original directory.
if (!allSetups.includes(relativeName) && previousDir) {
process.chdir(previousDir);
let clean = true;
let previousDir = null;
// Restore env variables before each test.
console.log(' Restoring original environment variables...');
process.env = originalEnvVariables;
}
})
.then(() => {
// Only clean after a real test, not a setup step. Also skip cleaning if the test
// requested an exception.
if (!allSetups.includes(relativeName) && clean) {
logStack.push(new logging.NullLogger());
return gitClean().then(
return Promise.resolve()
.then(() => printHeader(currentFileName, testIndex))
.then(() => (previousDir = process.cwd()))
.then(() => logStack.push(lastLogger().createChild(currentFileName)))
.then(() => fn(() => (clean = false)))
.then(
() => logStack.pop(),
(err) => {
logStack.pop();
throw err;
},
)
.then(() => console.log('----'))
.then(() => {
// If we're not in a setup, change the directory back to where it was before the test.
// This allows tests to chdir without worrying about keeping the original directory.
if (!allSetups.includes(relativeName) && previousDir) {
process.chdir(previousDir);
// Restore env variables before each test.
console.log(' Restoring original environment variables...');
process.env = originalEnvVariables;
}
})
.then(() => {
// Only clean after a real test, not a setup step. Also skip cleaning if the test
// requested an exception.
if (!allSetups.includes(relativeName) && clean) {
logStack.push(new logging.NullLogger());
return gitClean().then(
() => logStack.pop(),
(err) => {
logStack.pop();
throw err;
},
);
}
})
.then(
() => printFooter(currentFileName, start),
(err) => {
printFooter(currentFileName, start);
console.error(err);
throw err;
},
);
}
})
.then(
() => printFooter(currentFileName, start),
(err) => {
printFooter(currentFileName, start);
console.error(err);
throw err;
},
);
});
}, Promise.resolve())
});
}, Promise.resolve())
.finally(() => {
registryProcess.kill();
secureRegistryProcess.kill();
});
})
.then(
() => {
registryProcess.kill();
secureRegistryProcess.kill();
console.log(colors.green('Done.'));
process.exit(0);
},
@ -218,9 +225,6 @@ testsToRun
console.error(colors.red(err.message));
console.error(colors.red(err.stack));
registryProcess.kill();
secureRegistryProcess.kill();
if (argv.debug) {
console.log(`Current Directory: ${process.cwd()}`);
console.log('Will loop forever while you debug... CTRL-C to quit.');
@ -257,3 +261,15 @@ function printFooter(testName: string, startTime: number) {
console.log(colors.green('Last step took ') + colors.bold.blue('' + t) + colors.green('s...'));
console.log('');
}
function findFreePort() {
return new Promise<number>((resolve, reject) => {
const srv = createServer();
srv.once('listening', () => {
const port = (srv.address() as AddressInfo).port;
srv.close((e) => (e ? reject(e) : resolve(port)));
});
srv.once('error', (e) => srv.close(() => reject(e)));
srv.listen();
});
}

View File

@ -3,7 +3,7 @@ storage: ./storage
auth:
auth-memory:
users: {}
listen: localhost:4873
listen: localhost:${HTTP_PORT}
uplinks:
npmjs:
url: https://registry.npmjs.org/

View File

@ -5,10 +5,10 @@ auth:
testing:
name: testing
password: s3cret
listen: localhost:4876
listen: localhost:${HTTPS_PORT}
uplinks:
local:
url: http://localhost:4873
url: http://localhost:${HTTP_PORT}
cache: false
maxage: 20m
max_fails: 32