Skip to content

test: use random ports for local verdaccio npm servers #23114

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions tests/legacy-cli/e2e/utils/registry.ts
Original file line number Diff line number Diff line change
@@ -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));
Comment on lines +19 to +20
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT

Suggested change
configContent = configContent.replace(/\$\{HTTP_PORT\}/g, String(port));
configContent = configContent.replace(/\$\{HTTPS_PORT\}/g, String(httpsPort));
configContent = configContent
.replace(/\$\{HTTP_PORT\}/g, String(port))
.replace(/\$\{HTTPS_PORT\}/g, String(httpsPort));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 but looks like it's too late...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah sorry, didn't notice there was a change still pending. Not sure if you care enough to justify another PR for this cleanup @alan-agius4?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jbedard, feel free to include this suggested change in on of your upcoming PRs.

await writeFile(join(registryPath, 'verdaccio.yaml'), configContent);

return spawn('node', [require.resolve('verdaccio/bin/verdaccio'), '-c', './verdaccio.yaml'], {
cwd: registryPath,
Expand All @@ -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(
/**
Expand All @@ -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',
Expand All @@ -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;

Expand Down
164 changes: 90 additions & 74 deletions tests/legacy-cli/e2e_runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
},
Expand All @@ -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.');
Expand Down Expand Up @@ -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();
});
}
2 changes: 1 addition & 1 deletion tests/legacy-cli/verdaccio.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ storage: ./storage
auth:
auth-memory:
users: {}
listen: localhost:4873
listen: localhost:${HTTP_PORT}
uplinks:
npmjs:
url: https://registry.npmjs.org/
Expand Down
4 changes: 2 additions & 2 deletions tests/legacy-cli/verdaccio_auth.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down