Skip to content
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

Add mirror-url parameter to allow downloading Node.js from a custom URL #1211

Closed
wants to merge 26 commits into from
Closed
Changes from 1 commit
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
Prev Previous commit
Next Next commit
test cases
  • Loading branch information
aparnajyothi-y committed Jan 31, 2025
commit 19df1001b00025bd2ac1e25f74bd7e26497f6d89
126 changes: 126 additions & 0 deletions __tests__/main.test.ts
Original file line number Diff line number Diff line change
@@ -13,6 +13,10 @@ import each from 'jest-each';
import * as main from '../src/main';
import * as util from '../src/util';
import OfficialBuilds from '../src/distributions/official_builds/official_builds';
import * as installerFactory from '../src/distributions/installer-factory';
jest.mock('../src/distributions/installer-factory', () => ({
getNodejsDistribution: jest.fn()
}));

describe('main tests', () => {
let inputs = {} as any;
@@ -281,3 +285,125 @@ describe('main tests', () => {
});
});
});


// Mock the necessary modules
jest.mock('@actions/core');
jest.mock('./distributions/installer-factory');

// Create a mock object that satisfies the BaseDistribution type
const createMockNodejsDistribution = () => ({
setupNodeJs: jest.fn(),
// Mocking other properties required by the BaseDistribution type (adjust based on your actual types)
httpClient: {}, // Example for httpClient, replace with proper mock if necessary
osPlat: 'darwin', // Example platform ('darwin', 'win32', 'linux', etc.)
nodeInfo: {
version: '14.x',
arch: 'x64',
platform: 'darwin',
},
getDistributionUrl: jest.fn().mockReturnValue('https://nodejs.org/dist/'), // Default distribution URL
install: jest.fn(),
validate: jest.fn(),
// Mock any other methods/properties defined in BaseDistribution
});

// Define the mock structure for BaseDistribution type (adjust to your actual type)
interface BaseDistribution {
setupNodeJs: jest.Mock;
httpClient: object;
osPlat: string;
nodeInfo: {
version: string;
arch: string;
platform: string;
};
getDistributionUrl: jest.Mock;
install: jest.Mock;
validate: jest.Mock;
}

describe('Mirror URL Tests', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('should pass mirror URL correctly when provided', async () => {
(core.getInput as jest.Mock).mockImplementation((name: string) => {
if (name === 'mirror-url') return 'https://custom-mirror-url.com';
if (name === 'node-version') return '14.x';
return '';
});

const mockNodejsDistribution = createMockNodejsDistribution();
(installerFactory.getNodejsDistribution as unknown as jest.Mock<typeof installerFactory.getNodejsDistribution>).mockReturnValue(mockNodejsDistribution);

await main.run();

// Ensure setupNodeJs is called with the correct parameters, including the mirror URL
expect(mockNodejsDistribution.setupNodeJs).toHaveBeenCalledWith({
versionSpec: '14.x',
checkLatest: false,
auth: undefined,
stable: true,
arch: 'x64',
mirrorURL: 'https://custom-mirror-url.com',
});
});

it('should use default mirror URL when no mirror URL is provided', async () => {
(core.getInput as jest.Mock).mockImplementation((name: string) => {
if (name === 'mirror-url') return '';
if (name === 'node-version') return '14.x';
return '';
});

const mockNodejsDistribution = createMockNodejsDistribution();
(installerFactory.getNodejsDistribution as jest.Mock).mockReturnValue(mockNodejsDistribution);

await main.run();

// Expect that setupNodeJs is called with an empty mirror URL (which will default inside the function)
expect(mockNodejsDistribution.setupNodeJs).toHaveBeenCalledWith(expect.objectContaining({
mirrorURL: '', // Default URL is expected to be handled internally
}));
});

it('should handle mirror URL with spaces correctly', async () => {
(core.getInput as jest.Mock).mockImplementation((name: string) => {
if (name === 'mirror-url') return ' https://custom-mirror-url.com ';
if (name === 'node-version') return '14.x';
return '';
});

const mockNodejsDistribution = createMockNodejsDistribution();
(installerFactory.getNodejsDistribution as jest.Mock).mockReturnValue(mockNodejsDistribution);

await main.run();

// Expect that setupNodeJs is called with the trimmed mirror URL
expect(mockNodejsDistribution.setupNodeJs).toHaveBeenCalledWith(expect.objectContaining({
mirrorURL: 'https://custom-mirror-url.com',
}));
});

it('should warn if architecture is provided but node-version is missing', async () => {
(core.getInput as jest.Mock).mockImplementation((name: string) => {
if (name === 'architecture') return 'x64';
if (name === 'node-version') return '';
return '';
});

const mockWarning = jest.spyOn(core, 'warning');
const mockNodejsDistribution = createMockNodejsDistribution();
installerFactory.getNodejsDistribution.mockReturnValue(mockNodejsDistribution);

await main.run();

expect(mockWarning).toHaveBeenCalledWith(
'`architecture` is provided but `node-version` is missing. In this configuration, the version/architecture of Node will not be changed.'
);
expect(mockNodejsDistribution.setupNodeJs).not.toHaveBeenCalled(); // Setup Node should not be called
});
});

96 changes: 94 additions & 2 deletions __tests__/nightly-installer.test.ts
Original file line number Diff line number Diff line change
@@ -10,8 +10,8 @@ import osm from 'os';
import path from 'path';
import * as main from '../src/main';
import * as auth from '../src/authutil';
import {INodeVersion} from '../src/distributions/base-models';

import {INodeVersion, NodeInputs} from '../src/distributions/base-models';
import NightlyNodejs from '../src/distributions/nightly/nightly_builds';
import nodeTestManifest from './data/versions-manifest.json';
import nodeTestDist from './data/node-dist-index.json';
import nodeTestDistNightly from './data/node-nightly-index.json';
@@ -606,3 +606,95 @@ describe('setup-node', () => {
);
});
});
// Mock core.info to track the log output
jest.mock('@actions/core', () => ({
info: jest.fn(),
}));

// Create a subclass to access the protected method for testing purposes
class TestNightlyNodejs extends NightlyNodejs {
public getDistributionUrlPublic() {
return this.getDistributionUrl(); // This allows us to call the protected method
}
}

describe('NightlyNodejs', () => {

it('uses mirror URL when provided', async () => {
const mirrorURL = 'https://my.custom.mirror/nodejs/nightly';
const nodeInfo: NodeInputs = {
mirrorURL: '', versionSpec: '18.0.0-nightly', arch: 'x64',
checkLatest: false,
stable: false
};
const nightlyNode = new TestNightlyNodejs(nodeInfo);

const distributionUrl = nightlyNode.getDistributionUrlPublic();

expect(distributionUrl).toBe(mirrorURL);
expect(core.info).toHaveBeenCalledWith(`Using mirror URL: ${mirrorURL}`);
});

it('falls back to default distribution URL when no mirror URL is provided', async () => {
const nodeInfo: NodeInputs = {
versionSpec: '18.0.0-nightly', arch: 'x64',
checkLatest: false,
stable: false
}; const nightlyNode = new TestNightlyNodejs(nodeInfo);

const distributionUrl = nightlyNode.getDistributionUrlPublic();

expect(distributionUrl).toBe('https://nodejs.org/download/nightly');
expect(core.info).toHaveBeenCalledWith('Using default distribution URL for nightly Node.js.');
});

it('logs mirror URL when provided', async () => {
const mirrorURL = 'https://custom.mirror/nodejs/nightly';
const nodeInfo: NodeInputs = {
mirrorURL: '', versionSpec: '18.0.0-nightly', arch: 'x64',
checkLatest: false,
stable: false
};
const nightlyNode = new TestNightlyNodejs(nodeInfo);

nightlyNode.getDistributionUrlPublic();

expect(core.info).toHaveBeenCalledWith(`Using mirror URL: ${mirrorURL}`);
});

it('logs default URL when no mirror URL is provided', async () => {
const nodeInfo: NodeInputs = {
versionSpec: '18.0.0-nightly', arch: 'x64',
checkLatest: false,
stable: false
}; const nightlyNode = new TestNightlyNodejs(nodeInfo);

nightlyNode.getDistributionUrlPublic();

expect(core.info).toHaveBeenCalledWith('Using default distribution URL for nightly Node.js.');
});

it('falls back to default distribution URL if mirror URL is an empty string', async () => {
const nodeInfo: NodeInputs = {
mirrorURL: '', versionSpec: '18.0.0-nightly', arch: 'x64',
checkLatest: false,
stable: false
}; const nightlyNode = new TestNightlyNodejs(nodeInfo);

const distributionUrl = nightlyNode.getDistributionUrlPublic();

expect(distributionUrl).toBe('https://nodejs.org/download/nightly');
expect(core.info).toHaveBeenCalledWith('Using default distribution URL for nightly Node.js.');
});

it('falls back to default distribution URL if mirror URL is undefined', async () => {
const nodeInfo: NodeInputs = { nodeVersion: '18.0.0-nightly', architecture: 'x64', platform: 'linux' };
const nightlyNode = new TestNightlyNodejs(nodeInfo);

const distributionUrl = nightlyNode.getDistributionUrlPublic();

expect(distributionUrl).toBe('https://nodejs.org/download/nightly');
expect(core.info).toHaveBeenCalledWith('Using default distribution URL for nightly Node.js.');
});

});
98 changes: 98 additions & 0 deletions __tests__/official-installer.test.ts
Original file line number Diff line number Diff line change
@@ -829,3 +829,101 @@ describe('setup-node', () => {
);
});
});
describe('OfficialBuilds - Mirror URL functionality', () => {
const nodeInfo: NodeInputs = { nodeVersion: '18.0.0-nightly', architecture: 'x64', platform: 'linux', mirrorURL: '' };

it('should download using the mirror URL when provided', async () => {
const mirrorURL = 'https://my.custom.mirror/nodejs';
nodeInfo.mirrorURL = mirrorURL;
const officialBuilds = new OfficialBuilds(nodeInfo);

// Mock download from mirror URL
const mockDownloadPath = '/some/temp/path';
jest.spyOn(tc, 'downloadTool').mockResolvedValue(mockDownloadPath);

await officialBuilds.setupNodeJs();

expect(core.info).toHaveBeenCalledWith('Attempting to download using mirror URL...');
expect(core.info).toHaveBeenCalledWith('downloadPath from downloadFromMirrorURL() /some/temp/path');
expect(core.addPath).toHaveBeenCalledWith(mockDownloadPath);
});

it('should log a message when mirror URL is used', async () => {
const mirrorURL = 'https://my.custom.mirror/nodejs';
nodeInfo.mirrorURL = mirrorURL;
const officialBuilds = new OfficialBuilds(nodeInfo);

const mockDownloadPath = '/some/temp/path';
jest.spyOn(tc, 'downloadTool').mockResolvedValue(mockDownloadPath);

await officialBuilds.setupNodeJs();

expect(core.info).toHaveBeenCalledWith(`Using mirror URL: ${mirrorURL}`);
});

it('should fall back to default URL if mirror URL is not provided', async () => {
nodeInfo.mirrorURL = ''; // No mirror URL provided
const officialBuilds = new OfficialBuilds(nodeInfo);

const mockDownloadPath = '/some/temp/path';
jest.spyOn(tc, 'downloadTool').mockResolvedValue(mockDownloadPath);

await officialBuilds.setupNodeJs();

expect(core.info).toHaveBeenCalledWith('Attempting to download from default Node.js URL...');
expect(core.addPath).toHaveBeenCalledWith(mockDownloadPath);
});

it('should log an error and handle failure during mirror URL download', async () => {
const mirrorURL = 'https://my.custom.mirror/nodejs';
nodeInfo.mirrorURL = mirrorURL;
const officialBuilds = new OfficialBuilds(nodeInfo);

// Simulate an error during the download process
const errorMessage = 'Network error';
jest.spyOn(tc, 'downloadTool').mockRejectedValue(new Error(errorMessage));

await officialBuilds.setupNodeJs();

expect(core.info).toHaveBeenCalledWith('Attempting to download using mirror URL...');
expect(core.error).toHaveBeenCalledWith(errorMessage);
expect(core.debug).toHaveBeenCalledWith(expect.stringContaining('empty stack'));
});

it('should log a fallback message if downloading from the mirror URL fails', async () => {
const mirrorURL = 'https://my.custom.mirror/nodejs';
nodeInfo.mirrorURL = mirrorURL;
const officialBuilds = new OfficialBuilds(nodeInfo);

// Simulate download failure and fallback to default URL
const errorMessage = 'Network error';
jest.spyOn(tc, 'downloadTool').mockRejectedValue(new Error(errorMessage));

const mockDownloadPath = '/some/temp/path';
jest.spyOn(tc, 'downloadTool').mockResolvedValue(mockDownloadPath);

await officialBuilds.setupNodeJs();

expect(core.info).toHaveBeenCalledWith('Failed to download from mirror URL. Falling back to default Node.js URL...');
expect(core.addPath).toHaveBeenCalledWith(mockDownloadPath);
});

it('should throw an error if mirror URL is not provided and downloading from both mirror and default fails', async () => {
nodeInfo.mirrorURL = ''; // No mirror URL
const officialBuilds = new OfficialBuilds(nodeInfo);

// Simulate failure in both mirror and default download
const errorMessage = 'Network error';
jest.spyOn(tc, 'downloadTool').mockRejectedValue(new Error(errorMessage));

await expect(officialBuilds.setupNodeJs()).rejects.toThrowError(new Error('Unable to find Node version for platform linux and architecture x64.'));
});

it('should throw an error if mirror URL is undefined and not provided', async () => {
nodeInfo.mirrorURL = undefined; // Undefined mirror URL
const officialBuilds = new OfficialBuilds(nodeInfo);

// Simulate a missing mirror URL scenario
await expect(officialBuilds.setupNodeJs()).rejects.toThrowError('Mirror URL is undefined');
});
});
10 changes: 0 additions & 10 deletions dist/setup/index.js
Original file line number Diff line number Diff line change
@@ -100157,9 +100157,7 @@ class BaseDistribution {
getMirrorUrlVersions() {
return __awaiter(this, void 0, void 0, function* () {
const initialUrl = this.getDistributionMirrorUrl();
core.info('initialUrl from getDistributionMirrorUrl ' + initialUrl);
const dataUrl = `${initialUrl}/index.json`;
core.info('dataUrl from index ' + dataUrl);
const response = yield this.httpClient.getJson(dataUrl);
return response.result || [];
});
@@ -100186,9 +100184,7 @@ class BaseDistribution {
}
getNodejsMirrorURLInfo(version) {
const mirrorURL = this.nodeInfo.mirrorURL;
core.info('mirrorURL from getNodejsMirrorURLInfo ' + mirrorURL);
const osArch = this.translateArchToDistUrl(this.nodeInfo.arch);
core.info('osArch from translateArchToDistUrl ' + osArch);
version = semver_1.default.clean(version) || '';
const fileName = this.osPlat == 'win32'
? `node-v${version}-win-${osArch}`
@@ -100199,7 +100195,6 @@ class BaseDistribution {
: `${fileName}.7z`
: `${fileName}.tar.gz`;
const url = `${mirrorURL}/v${version}/${urlFileName}`;
core.info('url from construct ' + url);
return {
downloadUrl: url,
resolvedVersion: version,
@@ -100729,19 +100724,14 @@ class OfficialBuilds extends base_distribution_1.default {
downloadFromMirrorURL() {
return __awaiter(this, void 0, void 0, function* () {
const nodeJsVersions = yield this.getMirrorUrlVersions();
core.info('nodeJsVersions from getMirrorUrVersions ' + nodeJsVersions);
const versions = this.filterVersions(nodeJsVersions);
core.info('versions from filterVersions ' + versions);
const evaluatedVersion = this.evaluateVersions(versions);
core.info('evaluatedVersion from evaluatedVersions ' + evaluatedVersion);
if (!evaluatedVersion) {
throw new Error(`Unable to find Node version '${this.nodeInfo.versionSpec}' for platform ${this.osPlat} and architecture ${this.nodeInfo.arch}.`);
}
const toolName = this.getNodejsMirrorURLInfo(evaluatedVersion);
core.info('toolName from getNodejsMirrorURLInfo ' + toolName);
try {
const toolPath = yield this.downloadNodejs(toolName);
core.info('toolPath from downloadNodejs ' + toolPath);
return toolPath;
}
catch (error) {
Loading
Oops, something went wrong.
Loading
Oops, something went wrong.