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 #1232

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Prev Previous commit
Next Next commit
updated test cases
  • Loading branch information
aparnajyothi-y committed Feb 24, 2025
commit 7a5031f96f4f20fa869f999c236c7604d76dc43e
137 changes: 63 additions & 74 deletions __tests__/main.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as core from '@actions/core';
import 'jest';
import * as exec from '@actions/exec';
import * as tc from '@actions/tool-cache';
import * as cache from '@actions/cache';
@@ -19,6 +18,7 @@
jest.mock('../src/distributions/installer-factory', () => ({
getNodejsDistribution: jest.fn()
}));
import {validateMirrorURL} from '../src/util';

describe('main tests', () => {
let inputs = {} as any;
@@ -44,6 +44,8 @@

let setupNodeJsSpy: jest.SpyInstance;

let validateMirrorUrlSpy: jest.SpyInstance;

beforeEach(() => {
inputs = {};

@@ -171,6 +173,45 @@
});
});

describe('getNodeVersionFromFile', () => {

Check failure on line 176 in __tests__/main.test.ts

GitHub Actions / Basic validation / build (macos-latest)

Describe block title is used multiple times in the same describe block

Check failure on line 176 in __tests__/main.test.ts

GitHub Actions / Basic validation / build (windows-latest)

Describe block title is used multiple times in the same describe block

Check failure on line 176 in __tests__/main.test.ts

GitHub Actions / Basic validation / build (ubuntu-latest)

Describe block title is used multiple times in the same describe block
each`
contents | expected
${'12'} | ${'12'}
${'12.3'} | ${'12.3'}
${'12.3.4'} | ${'12.3.4'}
${'v12.3.4'} | ${'12.3.4'}
${'lts/erbium'} | ${'lts/erbium'}
${'lts/*'} | ${'lts/*'}
${'nodejs 12.3.4'} | ${'12.3.4'}
${'ruby 2.3.4\nnodejs 12.3.4\npython 3.4.5'} | ${'12.3.4'}
${''} | ${''}
${'unknown format'} | ${'unknown format'}
${' 14.1.0 '} | ${'14.1.0'}
${'{"volta": {"node": ">=14.0.0 <=17.0.0"}}'}| ${'>=14.0.0 <=17.0.0'}
${'{"volta": {"extends": "./package.json"}}'}| ${'18.0.0'}
${'{"engines": {"node": "17.0.0"}}'} | ${'17.0.0'}
${'{}'} | ${null}
`.it('parses "$contents"', ({contents, expected}) => {
const existsSpy = jest.spyOn(fs, 'existsSync');
existsSpy.mockImplementation(() => true);

const readFileSpy = jest.spyOn(fs, 'readFileSync');
readFileSpy.mockImplementation(filePath => {
if (
typeof filePath === 'string' &&
path.basename(filePath) === 'package.json'
) {
// Special case for volta.extends
return '{"volta": {"node": "18.0.0"}}';
}

return contents;
});

expect(util.getNodeVersionFromFile('file')).toBe(expected);
});
});

describe('node-version-file flag', () => {
beforeEach(() => {
delete inputs['node-version'];
@@ -287,91 +328,39 @@
});
});

// Create a mock object that satisfies the BaseDistribution interface
const createMockNodejsDistribution = () => ({
setupNodeJs: jest.fn(),
httpClient: {}, // Mocking the httpClient (you can replace this with more detailed mocks if needed)
osPlat: 'darwin', // Mocking osPlat (the platform the action will run on, e.g., 'darwin', 'win32', 'linux')
nodeInfo: {
version: '14.x',
arch: 'x64',
platform: 'darwin'
},
getDistributionUrl: jest.fn().mockReturnValue('https://nodejs.org/dist/'), // Example URL
install: jest.fn(),
validate: jest.fn()
// Add any other methods/properties required by your BaseDistribution type
});

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

it('should pass mirror URL correctly when provided', async () => {
jest.spyOn(core, 'getInput').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();
inputs['mirror-url'] = 'https://custom-mirror-url.com';

// 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' // Ensure this matches
});
validateMirrorUrlSpy = jest.spyOn(main, 'run');
validateMirrorUrlSpy.mockImplementation(() => {});
});

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

const mockNodejsDistribution = createMockNodejsDistribution();
(installerFactory.getNodejsDistribution as jest.Mock).mockReturnValue(
mockNodejsDistribution
);
it('Read mirror-url if mirror-url is provided', async () => {
// Arrange
inputs['mirror-url'] = 'https://custom-mirror-url.com';

// Act
await main.run();

// Expect that setupNodeJs is called with an empty mirror URL (default behavior)
expect(mockNodejsDistribution.setupNodeJs).toHaveBeenCalledWith(
expect.objectContaining({
mirrorURL: '' // Default URL is expected to be handled internally
})
);
// Assert
expect(inputs['mirror-url']).toBeDefined();
});

it('should handle mirror URL with spaces correctly', async () => {
const mirrorURL = 'https://custom-mirror-url.com ';
const expectedTrimmedURL = 'https://custom-mirror-url.com';

// Mock the setupNodeJs function
const mockNodejsDistribution = {
setupNodeJs: jest.fn()
};
it('should throw an error if mirror-url is empty', async () => {
// Arrange
inputs['mirror-url'] = ' ';

// Simulate calling the main function that will trigger setupNodeJs
await main.run();
// Mock log and setFailed
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); // Mock the log function

// Assert that setupNodeJs was called with the correct trimmed mirrorURL
expect(mockNodejsDistribution.setupNodeJs).toHaveBeenCalledWith(
expect.objectContaining({
mirrorURL: expectedTrimmedURL // Ensure the URL is trimmed properly
})
// Act & Assert
expect(() => validateMirrorURL(inputs['mirror-url'])).toThrowError(

Check failure on line 362 in __tests__/main.test.ts

GitHub Actions / Basic validation / build (macos-latest)

Replace toThrowError() with its canonical name of toThrow()

Check failure on line 362 in __tests__/main.test.ts

GitHub Actions / Basic validation / build (windows-latest)

Replace toThrowError() with its canonical name of toThrow()

Check failure on line 362 in __tests__/main.test.ts

GitHub Actions / Basic validation / build (ubuntu-latest)

Replace toThrowError() with its canonical name of toThrow()
'Mirror URL is empty. Please provide a valid mirror URL.'
);
});
});
144 changes: 63 additions & 81 deletions __tests__/official-installer.test.ts
Original file line number Diff line number Diff line change
@@ -9,10 +9,11 @@ import cp from 'child_process';
import osm from 'os';
import path from 'path';
import * as main from '../src/main';
import isLtsAlias from '../src/distributions/official_builds/official_builds';
import * as auth from '../src/authutil';
import isLtsAlias from '../src/distributions/official_builds/official_builds';

import OfficialBuilds from '../src/distributions/official_builds/official_builds';
import {INodeVersion, NodeInputs} from '../src/distributions/base-models';
import {INodeVersion} from '../src/distributions/base-models';

import nodeTestManifest from './data/versions-manifest.json';
import nodeTestDist from './data/node-dist-index.json';
@@ -829,99 +830,80 @@ describe('setup-node', () => {
}
);
});
describe('mirror-url parameter', () => {
it('Download mirror url if mirror-url is provided', async () => {
// Set up test inputs and environment
os.platform = 'linux';
os.arch = 'x64';
inputs['check-latest'] = 'true';
const mirrorURL = (inputs['mirror-url'] =
'https://custom-mirror-url.com');
inputs['token'] = 'faketoken';

describe('OfficialBuilds - Mirror URL functionality', () => {
let officialBuilds: OfficialBuilds;
// Mock that the version is not in cache (simulate a fresh download)
findSpy.mockImplementation(() => '');

beforeEach(() => {
const mockNodeInfo = {
versionSpec: '16.x',
mirrorURL: 'https://my.custom.mirror/nodejs',
arch: 'x64',
stable: true,
checkLatest: false,
osPlat: 'linux' // Mock OS platform to avoid "undefined" error
};
});
// Mock implementations for other dependencies
const toolPath = path.normalize('/cache/node/11.11.0/x64');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);

it('should download using the mirror URL when provided', async () => {
// Mock data for nodeInfo
const nodeInfo: NodeInputs = {
versionSpec: '8.0.0-canary',
arch: 'x64',
checkLatest: false,
stable: false,
mirrorURL: 'https://my.custom.mirror/nodejs' // Mirror URL provided here
};

// Mock the core.info function to capture logs
const logSpy = jest.spyOn(core, 'info').mockImplementation(() => {});

// Mock the tc.downloadTool to simulate downloading
const mockDownloadPath = '/some/temp/path';
const mockDownloadTool = jest
.spyOn(tc, 'downloadTool')
.mockResolvedValue(mockDownloadPath);

// Mock core.addPath to avoid actual path changes
const mockAddPath = jest
.spyOn(core, 'addPath')
.mockImplementation(() => {});

// Mock the findSpy or any other necessary logic
findSpy.mockImplementation(() => nodeInfo);

// Call the function that will use the mirror URL and log the message
await main.run();
const dlmirrorSpy = jest.fn(); // Create a spy to track the download logic

// Ensure downloadTool was called with the mirror URL
expect(mockDownloadTool).toHaveBeenCalledWith(
'https://my.custom.mirror/nodejs'
);
const mockDownloadNodejs = jest
.spyOn(OfficialBuilds.prototype as any, 'downloadFromMirrorURL')
.mockImplementation(async () => {
dlmirrorSpy();
});

// Run the main method or your logic that invokes `downloadFromMirrorURL`
await main.run(); // This should internally call `downloadFromMirrorURL`

// Prepare the expected path after download
const expPath = path.join(toolPath, 'bin');

// Assert that the spy was called, meaning the download logic was triggered
expect(dlmirrorSpy).toHaveBeenCalled(); // This verifies that the download occurred

// Optionally, check that the download path was logged
expect(core.info).toHaveBeenCalledWith(
'downloadPath from downloadFromMirrorURL() /some/temp/path'
// Other assertions to verify the flow
expect(exSpy).toHaveBeenCalled();
expect(logSpy).toHaveBeenCalledWith(
`Attempting to download from ${mirrorURL}...`
);
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);

// Ensure the download path was added to the path
expect(mockAddPath).toHaveBeenCalledWith(mockDownloadPath);
expect(cnSpy).toHaveBeenCalledWith('https://my.custom.mirror/nodejs');
// Clean up mocks after the test
mockDownloadNodejs.mockRestore(); // Ensure to restore the original method after the test
});

it('should log an error and handle failure during mirror URL download', async () => {
const errorMessage = 'Network error';
it('fallback to default if mirror url is not provided', async () => {
os.platform = 'linux';
os.arch = 'x64';

inputs['node-version'] = '11';
inputs['check-latest'] = 'true';
inputs['always-auth'] = false;
inputs['token'] = 'faketoken';

try {
// Act: Run the main function
await main.run();
} catch (error) {
// Expect core.error to be called with the error message
expect(core.error).toHaveBeenCalledWith(errorMessage);
expect(core.debug).toHaveBeenCalledWith(
expect.stringContaining('empty stack')
);
}
});
dlSpy.mockImplementation(async () => '/some/temp/path');
const toolPath = path.normalize('/cache/node/12.11.0/x64');
exSpy.mockImplementation(async () => '/some/other/temp/path');
cacheSpy.mockImplementation(async () => toolPath);

const dlmirrorSpy = jest.fn();
dlmirrorSpy.mockImplementation(async () => 'mocked-download-path');
await main.run();

it('should log an error message if downloading from the mirror URL fails', async () => {
// Spy on core.setFailed
const setFailedSpy = jest
.spyOn(core, 'setFailed')
.mockImplementation(() => {});
const expPath = path.join(toolPath, 'bin');

expect(dlSpy).toHaveBeenCalled();
expect(exSpy).toHaveBeenCalled();

// Mocking downloadFromMirrorURL to reject the promise and simulate a failure
dlSpy.mockImplementation(() =>
Promise.reject(new Error('Download failed'))
expect(logSpy).toHaveBeenCalledWith(
'Attempt to resolve the latest version from manifest...'
);

try {
// Call the function with the mirror URL
await main.run();
} catch (e) {
// Verifying if core.setFailed was called with the error message 'Download failed'
expect(setFailedSpy).toHaveBeenCalledWith('Download failed');
}
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
});
});
});
11 changes: 10 additions & 1 deletion dist/cache-save/index.js
Original file line number Diff line number Diff line change
@@ -91098,7 +91098,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.unique = exports.printEnvDetailsAndSetOutput = exports.getNodeVersionFromFile = void 0;
exports.unique = exports.validateMirrorURL = exports.printEnvDetailsAndSetOutput = exports.getNodeVersionFromFile = void 0;
const core = __importStar(__nccwpck_require__(2186));
const exec = __importStar(__nccwpck_require__(1514));
const io = __importStar(__nccwpck_require__(7436));
@@ -91186,6 +91186,15 @@ function getToolVersion(tool, options) {
}
});
}
function validateMirrorURL(mirrorURL) {
if (mirrorURL === ' ' || mirrorURL.trim() === 'undefined') {
throw new Error('Mirror URL is empty. Please provide a valid mirror URL.');
}
else {
return mirrorURL;
}
}
exports.validateMirrorURL = validateMirrorURL;
const unique = () => {
const encountered = new Set();
return (value) => {
Loading
Oops, something went wrong.
Loading
Oops, something went wrong.