mirror of
https://gitea.com/actions/setup-java.git
synced 2026-10-02 21:25:25 +08:00
d0e6e4dbf7
* chore(deps): bump the monthly-npm-updates group with 13 updates Bumps the monthly-npm-updates group with 13 updates: | Package | From | To | | --- | --- | --- | | [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) | `5.11.0` | `5.11.1` | | [@jest/globals](https://github.com/jestjs/jest/tree/HEAD/packages/jest-globals) | `30.4.1` | `30.5.2` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.2.0` | `26.6.2` | | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.67.0` | `8.70.1` | | [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.67.0` | `8.70.1` | | [eslint](https://github.com/eslint/eslint) | `10.8.1` | `10.11.0` | | [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) | `29.16.1` | `29.16.6` | | [globals](https://github.com/sindresorhus/globals) | `17.11.0` | `17.12.0` | | [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) | `30.4.2` | `30.5.2` | | [lint-staged](https://github.com/lint-staged/lint-staged) | `17.3.0` | `17.5.1` | | [prettier](https://github.com/prettier/prettier) | `3.9.6` | `3.9.9` | | [ts-jest](https://github.com/kulshekhar/ts-jest) | `29.4.12` | `29.4.13` | | [typescript](https://github.com/microsoft/TypeScript) | `6.0.3` | `7.0.2` | Updates `fast-xml-parser` from 5.11.0 to 5.11.1 - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.11.0...v5.11.1) Updates `@jest/globals` from 30.4.1 to 30.5.2 - [Release notes](https://github.com/jestjs/jest/releases) - [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/jestjs/jest/commits/v30.5.2/packages/jest-globals) Updates `@types/node` from 26.2.0 to 26.6.2 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `@typescript-eslint/eslint-plugin` from 8.67.0 to 8.70.1 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.1/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 8.67.0 to 8.70.1 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.1/packages/parser) Updates `eslint` from 10.8.1 to 10.11.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v10.8.1...v10.11.0) Updates `eslint-plugin-jest` from 29.16.1 to 29.16.6 - [Release notes](https://github.com/jest-community/eslint-plugin-jest/releases) - [Changelog](https://github.com/jest-community/eslint-plugin-jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/jest-community/eslint-plugin-jest/compare/v29.16.1...v29.16.6) Updates `globals` from 17.11.0 to 17.12.0 - [Release notes](https://github.com/sindresorhus/globals/releases) - [Commits](https://github.com/sindresorhus/globals/compare/v17.11.0...v17.12.0) Updates `jest` from 30.4.2 to 30.5.2 - [Release notes](https://github.com/jestjs/jest/releases) - [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/jestjs/jest/commits/v30.5.2/packages/jest) Updates `lint-staged` from 17.3.0 to 17.5.1 - [Release notes](https://github.com/lint-staged/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/main/CHANGELOG.md) - [Commits](https://github.com/lint-staged/lint-staged/compare/v17.3.0...v17.5.1) Updates `prettier` from 3.9.6 to 3.9.9 - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.9.6...3.9.9) Updates `ts-jest` from 29.4.12 to 29.4.13 - [Release notes](https://github.com/kulshekhar/ts-jest/releases) - [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.4.12...v29.4.13) Updates `typescript` from 6.0.3 to 7.0.2 - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Commits](https://github.com/microsoft/TypeScript/compare/v6.0.3...v7.0.2) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-version: 5.11.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: monthly-npm-updates - dependency-name: "@jest/globals" dependency-version: 30.5.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: monthly-npm-updates - dependency-name: "@types/node" dependency-version: 26.6.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: monthly-npm-updates - dependency-name: "@typescript-eslint/eslint-plugin" dependency-version: 8.70.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: monthly-npm-updates - dependency-name: "@typescript-eslint/parser" dependency-version: 8.70.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: monthly-npm-updates - dependency-name: eslint dependency-version: 10.11.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: monthly-npm-updates - dependency-name: eslint-plugin-jest dependency-version: 29.16.6 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: monthly-npm-updates - dependency-name: globals dependency-version: 17.12.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: monthly-npm-updates - dependency-name: jest dependency-version: 30.5.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: monthly-npm-updates - dependency-name: lint-staged dependency-version: 17.5.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: monthly-npm-updates - dependency-name: prettier dependency-version: 3.9.9 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: monthly-npm-updates - dependency-name: ts-jest dependency-version: 29.4.13 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: monthly-npm-updates - dependency-name: typescript dependency-version: 7.0.2 dependency-type: direct:development update-type: version-update:semver-major dependency-group: monthly-npm-updates ... Signed-off-by: dependabot[bot] <support@github.com> * Fix monthly npm update checks Keep TypeScript on the compatible 6.x line for the current @typescript-eslint peer range, rebuild dist, and refresh the fast-xml-parser license cache. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fix monthly npm validation failures Update vulnerable transitive packages in the lockfile, rebuild dist, and correct the JetBrains test expectation for mocked Windows availability. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * chore: update licensed cache for npm updates Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bruno Borges <brborges@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
573 lines
17 KiB
TypeScript
573 lines
17 KiB
TypeScript
import {
|
|
jest,
|
|
describe,
|
|
it,
|
|
expect,
|
|
beforeEach,
|
|
afterEach,
|
|
beforeAll,
|
|
afterAll
|
|
} from '@jest/globals';
|
|
import https from 'https';
|
|
import {HttpClient, HttpClientResponse} from '@actions/http-client';
|
|
import type {IncomingMessage} from 'http';
|
|
import {Readable} from 'stream';
|
|
|
|
import manifestData from '../data/jetbrains.json' with {type: 'json'};
|
|
|
|
// Mock @actions/core before importing source modules that depend on it
|
|
jest.unstable_mockModule('@actions/core', () => ({
|
|
info: jest.fn(),
|
|
warning: jest.fn(),
|
|
debug: jest.fn(),
|
|
error: jest.fn(),
|
|
notice: jest.fn(),
|
|
setFailed: jest.fn(),
|
|
setOutput: jest.fn(),
|
|
getInput: jest.fn(),
|
|
getBooleanInput: jest.fn(),
|
|
getMultilineInput: jest.fn(),
|
|
addPath: jest.fn(),
|
|
exportVariable: jest.fn(),
|
|
saveState: jest.fn(),
|
|
getState: jest.fn(),
|
|
setSecret: jest.fn(),
|
|
isDebug: jest.fn(() => false),
|
|
startGroup: jest.fn(),
|
|
endGroup: jest.fn(),
|
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
|
toPlatformPath: jest.fn((p: string) => p),
|
|
toWin32Path: jest.fn((p: string) => p),
|
|
toPosixPath: jest.fn((p: string) => p)
|
|
}));
|
|
|
|
// Dynamic imports after mocking
|
|
const core = await import('@actions/core');
|
|
const {JetBrainsDistribution} =
|
|
await import('../../src/distributions/jetbrains/installer.js');
|
|
const {RetryingHttpClient} = await import('../../src/retrying-http-client.js');
|
|
const {MAX_PAGINATION_PAGES} = await import('../../src/util.js');
|
|
|
|
const JETBRAINS_RELEASES_URL =
|
|
'https://api.github.com/repos/JetBrains/JetBrainsRuntime/releases?per_page=100';
|
|
|
|
function release(tagName: string, prerelease: boolean) {
|
|
return {
|
|
tag_name: tagName,
|
|
name: tagName,
|
|
prerelease
|
|
};
|
|
}
|
|
|
|
function nextPageHeader(page: number) {
|
|
return {
|
|
link: `<${JETBRAINS_RELEASES_URL}&page=${page}>; rel="next"`
|
|
};
|
|
}
|
|
|
|
function response(
|
|
statusCode: number,
|
|
body = '',
|
|
headers: IncomingMessage['headers'] = {}
|
|
): HttpClientResponse {
|
|
const message = Readable.from([Buffer.from(body)]) as IncomingMessage;
|
|
message.statusCode = statusCode;
|
|
message.headers = headers;
|
|
return new HttpClientResponse(message);
|
|
}
|
|
|
|
describe('getAvailableVersions', () => {
|
|
jest.setTimeout(10_000);
|
|
|
|
let spyHttpClient: any;
|
|
let spyHttpClientHead: any;
|
|
let spyCoreError: any;
|
|
const originalGitHubToken = process.env.GITHUB_TOKEN;
|
|
|
|
beforeEach(() => {
|
|
delete process.env.GITHUB_TOKEN;
|
|
(core.getInput as jest.Mock).mockReturnValue('');
|
|
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
|
spyHttpClient.mockReturnValue({
|
|
statusCode: 200,
|
|
headers: {},
|
|
result: []
|
|
});
|
|
spyHttpClientHead = jest.spyOn(HttpClient.prototype, 'head');
|
|
spyHttpClientHead.mockResolvedValue({
|
|
message: {statusCode: 200}
|
|
} as any);
|
|
|
|
// Mock core.error to suppress error logs
|
|
spyCoreError = core.error as jest.Mock;
|
|
spyCoreError.mockImplementation(() => {});
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (originalGitHubToken === undefined) {
|
|
delete process.env.GITHUB_TOKEN;
|
|
} else {
|
|
process.env.GITHUB_TOKEN = originalGitHubToken;
|
|
}
|
|
jest.resetAllMocks();
|
|
jest.clearAllMocks();
|
|
jest.restoreAllMocks();
|
|
});
|
|
|
|
it('load available versions', async () => {
|
|
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
|
spyHttpClient
|
|
.mockReturnValueOnce({
|
|
statusCode: 200,
|
|
headers: {},
|
|
result: manifestData as any
|
|
})
|
|
.mockReturnValue({
|
|
statusCode: 200,
|
|
headers: {},
|
|
result: []
|
|
});
|
|
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '17',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
const availableVersions = await distribution['getAvailableVersions']();
|
|
expect(availableVersions).not.toBeNull();
|
|
|
|
expect(availableVersions.length).toBe(manifestData.length + 2);
|
|
}, 10_000);
|
|
|
|
it('continues a stable request after an all-prerelease page', async () => {
|
|
jest.spyOn(HttpClient.prototype, 'head').mockResolvedValue({
|
|
message: {statusCode: 200}
|
|
} as any);
|
|
spyHttpClient
|
|
.mockResolvedValueOnce({
|
|
statusCode: 200,
|
|
headers: nextPageHeader(2),
|
|
result: [release('jbr-release-26.0.0b1.1', true)]
|
|
})
|
|
.mockResolvedValueOnce({
|
|
statusCode: 200,
|
|
headers: {},
|
|
result: [release('jbr-release-21.0.11b1163.116', false)]
|
|
});
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '21',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
|
|
const availableVersions = await distribution['getAvailableVersions']();
|
|
|
|
expect(availableVersions.map(version => version.tag_name)).toContain(
|
|
'jbr-release-21.0.11b1163.116'
|
|
);
|
|
expect(availableVersions.map(version => version.tag_name)).not.toContain(
|
|
'jbr-release-26.0.0b1.1'
|
|
);
|
|
expect(spyHttpClient).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it('continues an EA request after an all-stable page', async () => {
|
|
jest.spyOn(HttpClient.prototype, 'head').mockResolvedValue({
|
|
message: {statusCode: 200}
|
|
} as any);
|
|
spyHttpClient
|
|
.mockResolvedValueOnce({
|
|
statusCode: 200,
|
|
headers: nextPageHeader(2),
|
|
result: [release('jbr-release-21.0.11b1163.116', false)]
|
|
})
|
|
.mockResolvedValueOnce({
|
|
statusCode: 200,
|
|
headers: {},
|
|
result: [release('jbr-release-26.0.0b1.1', true)]
|
|
});
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '26-ea',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
|
|
const availableVersions = await distribution['getAvailableVersions']();
|
|
|
|
expect(availableVersions.map(version => version.tag_name)).toEqual([
|
|
'jbr-release-26.0.0b1.1'
|
|
]);
|
|
expect(spyHttpClient).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it('uses the token input for every paginated GitHub Releases request', async () => {
|
|
(core.getInput as jest.Mock).mockReturnValue('input-token');
|
|
spyHttpClient
|
|
.mockResolvedValueOnce({
|
|
statusCode: 200,
|
|
headers: nextPageHeader(2),
|
|
result: [release('jbr-release-21.0.11b1163.116', false)]
|
|
})
|
|
.mockResolvedValueOnce({
|
|
statusCode: 200,
|
|
headers: {},
|
|
result: [release('jbr-release-21.0.10b1087.6', false)]
|
|
});
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '21',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
|
|
await distribution['getAvailableVersions']();
|
|
|
|
expect(spyHttpClient).toHaveBeenNthCalledWith(1, JETBRAINS_RELEASES_URL, {
|
|
Accept: 'application/vnd.github+json',
|
|
Authorization: 'Bearer input-token'
|
|
});
|
|
expect(spyHttpClient).toHaveBeenNthCalledWith(
|
|
2,
|
|
`${JETBRAINS_RELEASES_URL}&page=2`,
|
|
{
|
|
Accept: 'application/vnd.github+json',
|
|
Authorization: 'Bearer input-token'
|
|
}
|
|
);
|
|
});
|
|
|
|
it('prefers the token input over the environment fallback', async () => {
|
|
(core.getInput as jest.Mock).mockReturnValue('input-token');
|
|
process.env.GITHUB_TOKEN = 'environment-token';
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '21',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
|
|
await distribution['getAvailableVersions']();
|
|
|
|
expect(spyHttpClient).toHaveBeenCalledWith(JETBRAINS_RELEASES_URL, {
|
|
Accept: 'application/vnd.github+json',
|
|
Authorization: 'Bearer input-token'
|
|
});
|
|
});
|
|
|
|
it('falls back to GITHUB_TOKEN when the token input is empty', async () => {
|
|
process.env.GITHUB_TOKEN = 'environment-token';
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '21',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
|
|
await distribution['getAvailableVersions']();
|
|
|
|
expect(spyHttpClient).toHaveBeenCalledWith(JETBRAINS_RELEASES_URL, {
|
|
Accept: 'application/vnd.github+json',
|
|
Authorization: 'Bearer environment-token'
|
|
});
|
|
});
|
|
|
|
it('omits authorization when no GitHub token is available', async () => {
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '21',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
|
|
await distribution['getAvailableVersions']();
|
|
|
|
expect(spyHttpClient).toHaveBeenCalledWith(JETBRAINS_RELEASES_URL, {
|
|
Accept: 'application/vnd.github+json'
|
|
});
|
|
});
|
|
|
|
it('stops pagination when a raw GitHub page is empty', async () => {
|
|
spyHttpClient
|
|
.mockResolvedValueOnce({
|
|
statusCode: 200,
|
|
headers: nextPageHeader(2),
|
|
result: [release('jbr-release-21.0.11b1163.116', false)]
|
|
})
|
|
.mockResolvedValueOnce({
|
|
statusCode: 200,
|
|
headers: nextPageHeader(3),
|
|
result: []
|
|
});
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '26-ea',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
|
|
await distribution['getAvailableVersions']();
|
|
|
|
expect(spyHttpClient).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it('stops at the pagination safeguard', async () => {
|
|
spyHttpClient.mockResolvedValue({
|
|
statusCode: 200,
|
|
headers: nextPageHeader(2),
|
|
result: [release('jbr-release-21.0.11b1163.116', false)]
|
|
});
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '26-ea',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
|
|
const availableVersions = await distribution['getAvailableVersions']();
|
|
|
|
expect(availableVersions).toEqual([]);
|
|
expect(spyHttpClient).toHaveBeenCalledTimes(MAX_PAGINATION_PAGES);
|
|
expect(core.warning).toHaveBeenCalledWith(
|
|
`Reached pagination safeguard limit (${MAX_PAGINATION_PAGES} pages) while listing JetBrains Runtime releases.`
|
|
);
|
|
});
|
|
|
|
it('ignores pagination links with an unexpected origin', async () => {
|
|
spyHttpClient.mockResolvedValueOnce({
|
|
statusCode: 200,
|
|
headers: {
|
|
link: '<https://example.com/releases?page=2>; rel="next"'
|
|
},
|
|
result: [release('jbr-release-21.0.11b1163.116', false)]
|
|
});
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '26-ea',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
|
|
await distribution['getAvailableVersions']();
|
|
|
|
expect(spyHttpClient).toHaveBeenCalledTimes(1);
|
|
expect(core.warning).toHaveBeenCalledWith(
|
|
'Ignoring pagination link with unexpected origin: https://example.com/releases?page=2'
|
|
);
|
|
});
|
|
|
|
it('retries a GitHub rate limit using Retry-After', async () => {
|
|
spyHttpClient.mockRestore();
|
|
spyHttpClientHead.mockRestore();
|
|
const sleep = jest.fn(async () => undefined);
|
|
const requestRaw = jest
|
|
.spyOn(HttpClient.prototype, 'requestRaw')
|
|
.mockResolvedValueOnce(response(429, '', {'retry-after': '2'}))
|
|
.mockResolvedValueOnce(response(200, '[]'))
|
|
.mockResolvedValueOnce(response(200))
|
|
.mockResolvedValueOnce(response(200));
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '17',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
distribution['http'] = new RetryingHttpClient('test', {
|
|
sleep,
|
|
random: () => 0
|
|
});
|
|
|
|
const availableVersions = await distribution['getAvailableVersions']();
|
|
|
|
expect(availableVersions).toHaveLength(2);
|
|
expect(requestRaw).toHaveBeenCalledTimes(4);
|
|
expect(requestRaw.mock.calls[0][0].options.path).toBe(
|
|
requestRaw.mock.calls[1][0].options.path
|
|
);
|
|
expect(requestRaw.mock.calls[0][0].options.path).toContain(
|
|
'/repos/JetBrains/JetBrainsRuntime/releases'
|
|
);
|
|
expect(sleep).toHaveBeenCalledWith(2000);
|
|
expect(core.info).toHaveBeenCalledWith(
|
|
'Request attempt 1 of 4 failed (HTTP 429); retrying in 2000 ms'
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('findPackageForDownload', () => {
|
|
let spyHttpClientGet: any;
|
|
|
|
const JETBRAINS_CHECKSUM = 'c'.repeat(128);
|
|
|
|
beforeEach(() => {
|
|
// Every resolved release fetches `${url}.checksum` (sha512, GNU
|
|
// `<hex> <filename>` format); stub it so tests never reach the real
|
|
// network, except the dedicated 'version %s can be downloaded' test
|
|
// below which intentionally exercises real HTTPS HEAD requests.
|
|
spyHttpClientGet = jest
|
|
.spyOn(HttpClient.prototype, 'get')
|
|
.mockResolvedValue({
|
|
message: {statusCode: 200},
|
|
readBody: async () => `${JETBRAINS_CHECKSUM} jbrsdk.tar.gz\n`
|
|
} as any);
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.restoreAllMocks();
|
|
});
|
|
|
|
it.each([
|
|
['17', '17.0.11+1207.24'],
|
|
['11.0', '11.0.16+2043.64'],
|
|
['11.0.11', '11.0.11+1542.1'],
|
|
['21.0.2', '21.0.2+375.1'],
|
|
['21', '21.0.3+465.3'],
|
|
['x', '21.0.3+465.3']
|
|
])('version is resolved correctly %s -> %s', async (input, expected) => {
|
|
const distribution = new JetBrainsDistribution({
|
|
version: input,
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
distribution['getAvailableVersions'] = async () => manifestData as any;
|
|
const resolvedVersion = await distribution['findPackageForDownload'](input);
|
|
expect(resolvedVersion.version).toBe(expected);
|
|
});
|
|
|
|
it.each(['17', '11.0', '11.0.11', '21.0.2', '21'])(
|
|
'version %s can be downloaded',
|
|
async input => {
|
|
const distribution = new JetBrainsDistribution({
|
|
version: input,
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
distribution['getAvailableVersions'] = async () => manifestData as any;
|
|
const resolvedVersion =
|
|
await distribution['findPackageForDownload'](input);
|
|
const url = resolvedVersion.url;
|
|
const options = {method: 'HEAD'};
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
const request = https.request(url, options, res => {
|
|
let assertionError: unknown;
|
|
|
|
try {
|
|
// JetBrains uses 403 for non-existent packages
|
|
expect(res.statusCode).not.toBe(403);
|
|
} catch (error) {
|
|
assertionError = error;
|
|
}
|
|
|
|
res.resume();
|
|
res.once('error', reject);
|
|
res.once('end', () =>
|
|
assertionError ? reject(assertionError as Error) : resolve()
|
|
);
|
|
});
|
|
|
|
request.on('error', reject);
|
|
request.end();
|
|
});
|
|
}
|
|
);
|
|
|
|
it('version is not found', async () => {
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '8.0.452',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
distribution['getAvailableVersions'] = async () => manifestData as any;
|
|
await expect(distribution['findPackageForDownload']('8.x')).rejects.toThrow(
|
|
/No matching version found for SemVer */
|
|
);
|
|
});
|
|
|
|
it('version list is empty', async () => {
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '8',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
distribution['getAvailableVersions'] = async () => [];
|
|
await expect(distribution['findPackageForDownload']('8')).rejects.toThrow(
|
|
/No matching version found for SemVer */
|
|
);
|
|
});
|
|
|
|
it('fetches the authoritative sha512 checksum only for the resolved version', async () => {
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '21',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
distribution['getAvailableVersions'] = async () => manifestData as any;
|
|
|
|
const result = await distribution['findPackageForDownload']('21');
|
|
|
|
expect(result.checksum).toEqual({
|
|
algorithm: 'sha512',
|
|
value: JETBRAINS_CHECKSUM,
|
|
source: `${result.url}.checksum`
|
|
});
|
|
// Only the single resolved/winning version's checksum is requested,
|
|
// not one per candidate considered during version resolution.
|
|
expect(spyHttpClientGet).toHaveBeenCalledWith(`${result.url}.checksum`);
|
|
expect(spyHttpClientGet).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('parses only the first whitespace-delimited token from the GNU checksum payload', async () => {
|
|
spyHttpClientGet.mockResolvedValue({
|
|
message: {statusCode: 200},
|
|
readBody: async () => `${JETBRAINS_CHECKSUM} jbrsdk-21.tar.gz\n`
|
|
} as any);
|
|
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '21',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
distribution['getAvailableVersions'] = async () => manifestData as any;
|
|
|
|
const result = await distribution['findPackageForDownload']('21');
|
|
|
|
expect(result.checksum?.value).toBe(JETBRAINS_CHECKSUM);
|
|
});
|
|
|
|
it('falls back to a sha256 checksum for older JBR builds that only publish one', async () => {
|
|
// Older JBR 11 builds (e.g. jbrsdk_nomod-11_0_16-*-b2043.64.tar.gz) publish
|
|
// a SHA-256 digest at the generic `.checksum` sibling instead of SHA-512.
|
|
const sha256Checksum = 'a'.repeat(64);
|
|
spyHttpClientGet.mockResolvedValue({
|
|
message: {statusCode: 200},
|
|
readBody: async () =>
|
|
`${sha256Checksum} jbrsdk_nomod-11_0_16-osx-x64-b2043.64.tar.gz\n`
|
|
} as any);
|
|
|
|
const distribution = new JetBrainsDistribution({
|
|
version: '21',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
distribution['getAvailableVersions'] = async () => manifestData as any;
|
|
|
|
const result = await distribution['findPackageForDownload']('21');
|
|
|
|
expect(result.checksum).toEqual({
|
|
algorithm: 'sha256',
|
|
value: sha256Checksum,
|
|
source: `${result.url}.checksum`
|
|
});
|
|
});
|
|
});
|