Skip to content
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
80 changes: 80 additions & 0 deletions __tests__/cache-feature.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {jest, describe, it, expect, afterEach} from '@jest/globals';

jest.unstable_mockModule('@actions/cache', () => ({
isFeatureAvailable: jest.fn()
}));

jest.unstable_mockModule('@actions/core', () => ({
warning: jest.fn(),
debug: jest.fn(),
getInput: jest.fn(),
getBooleanInput: jest.fn(),
getMultilineInput: jest.fn(),
info: jest.fn(),
error: jest.fn(),
notice: jest.fn(),
setFailed: jest.fn(),
setOutput: 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)
}));

const cache = await import('@actions/cache');
const core = await import('@actions/core');
const {isCacheFeatureAvailable} = await import('../src/cache-feature.js');

describe('isCacheFeatureAvailable', () => {
it('is disabled on GHES when cache feature is unavailable', () => {
(cache.isFeatureAvailable as jest.Mock<any>).mockImplementation(
() => false
);
const warningMock = core.warning as jest.Mock;
const message =
'Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.';

try {
process.env['GITHUB_SERVER_URL'] = 'http://example.com';
expect(isCacheFeatureAvailable()).toBe(false);
expect(warningMock).toHaveBeenCalledWith(message);
} finally {
delete process.env['GITHUB_SERVER_URL'];
}
});

it('is disabled on dotcom when cache feature is unavailable', () => {
(cache.isFeatureAvailable as jest.Mock<any>).mockImplementation(
() => false
);
const warningMock = core.warning as jest.Mock;
const message =
'The runner was not able to contact the cache service. Caching will be skipped';

try {
process.env['GITHUB_SERVER_URL'] = 'http://github.com';
expect(isCacheFeatureAvailable()).toBe(false);
expect(warningMock).toHaveBeenCalledWith(message);
} finally {
delete process.env['GITHUB_SERVER_URL'];
}
});

it('is enabled when cache feature is available', () => {
(cache.isFeatureAvailable as jest.Mock<any>).mockImplementation(() => true);
expect(isCacheFeatureAvailable()).toBe(true);
});

afterEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {jest, describe, it, expect} from '@jest/globals';

jest.unstable_mockModule('../../src/distributions/zulu/installer.js', () => {
throw new Error(
'Zulu installer module must not be imported on the Temurin fast path'
);
});

const {getJavaDistribution} =
await import('../../src/distributions/distribution-factory.js');

describe('distribution factory lazy loading', () => {
it('does not load non-selected distribution installers for Temurin', async () => {
const distribution = await getJavaDistribution('temurin', {
version: '21',
architecture: 'x64',
packageType: 'jdk',
checkLatest: false
});

expect(distribution).not.toBeNull();
});
});
63 changes: 33 additions & 30 deletions __tests__/distributors/distribution-factory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ const installerOptions = (packageType: string, version = '25') => ({
describe('getJavaDistribution', () => {
it.each(supportedDistributionsOnCurrentPlatform)(
'uses the shared retrying HTTP client for %s',
distributionName => {
const distribution = getJavaDistribution(distributionName, {
async distributionName => {
const distribution = await getJavaDistribution(distributionName, {
version: '25',
architecture: 'x64',
packageType: 'jdk',
Expand All @@ -51,63 +51,66 @@ describe('getJavaDistribution', () => {
? packageTypes.map(packageType => [distributionName, packageType])
: []
)
)('accepts %s with java-package %s', (distributionName, packageType) => {
expect(
getJavaDistribution(
distributionName,
installerOptions(packageType as string)
)
).not.toBeNull();
});
)(
'accepts %s with java-package %s',
async (distributionName, packageType) => {
expect(
await getJavaDistribution(
distributionName,
installerOptions(packageType as string)
)
).not.toBeNull();
}
);

it.each(Object.entries(JAVA_PACKAGE_CAPABILITIES))(
'rejects unsupported java-package values for %s',
(distributionName, packageTypes) => {
expect(() =>
async (distributionName, packageTypes) => {
await expect(
getJavaDistribution(distributionName, installerOptions('jdk+typo'))
).toThrow(
).rejects.toThrow(
`Java package 'jdk+typo' is not supported for distribution '${distributionName}'. Supported package types: ${packageTypes.join(', ')}.`
);
}
);

it("rejects java-package 'jdk+jmods' for non-Temurin distributions", () => {
expect(() =>
it("rejects java-package 'jdk+jmods' for non-Temurin distributions", async () => {
await expect(
getJavaDistribution('zulu', installerOptions('jdk+jmods'))
).toThrow(
).rejects.toThrow(
"Java package 'jdk+jmods' is not supported for distribution 'zulu'. Supported package types: jdk, jre, jdk+fx, jre+fx, jdk+crac, jre+crac."
);
});

it.each(['8', '23.x', '23.0.1.1', '<24'])(
"rejects Temurin java-package 'jdk+jmods' for version %s",
version => {
expect(() =>
async version => {
await expect(
getJavaDistribution(
JavaDistribution.Temurin,
installerOptions('jdk+jmods', version)
)
).toThrow(
).rejects.toThrow(
`Java package 'jdk+jmods' is not supported for distribution 'temurin'. Supported package types: jdk, jre, jdk+jmods. Package 'jdk+jmods' requires Java 24 or later; requested version '${version}'.`
);
}
);

it.each(['24', '24.0.1.1', '25-ea', '>=21', 'latest'])(
"accepts Temurin java-package 'jdk+jmods' for version %s",
version => {
async version => {
expect(
getJavaDistribution(
await getJavaDistribution(
JavaDistribution.Temurin,
installerOptions('jdk+jmods', version)
)
).not.toBeNull();
}
);

it('preserves unsupported distribution handling', () => {
it('preserves unsupported distribution handling', async () => {
expect(
getJavaDistribution(
await getJavaDistribution(
'not-a-distribution',
installerOptions('not-a-package')
)
Expand All @@ -118,17 +121,17 @@ describe('getJavaDistribution', () => {
['amd64', 'x64'],
['ia32', 'x86'],
['arm64', 'aarch64']
])('passes normalized architecture %s as %s', (input, expected) => {
const normalized = getJavaDistribution(JavaDistribution.JdkFile, {
])('passes normalized architecture %s as %s', async (input, expected) => {
const normalized = await getJavaDistribution(JavaDistribution.JdkFile, {
...installerOptions('jdk'),
architecture: input
});

expect(normalized!['architecture']).toBe(expected);
});

it('uses the runner architecture when the input is empty', () => {
const distribution = getJavaDistribution(JavaDistribution.Temurin, {
it('uses the runner architecture when the input is empty', async () => {
const distribution = await getJavaDistribution(JavaDistribution.Temurin, {
...installerOptions('jdk'),
architecture: ''
});
Expand All @@ -137,12 +140,12 @@ describe('getJavaDistribution', () => {
expect(distribution!['architecture']).toBe(expected);
});

it('rejects an unsupported combination before creating an HTTP client', () => {
expect(() =>
it('rejects an unsupported combination before creating an HTTP client', async () => {
await expect(
getJavaDistribution(JavaDistribution.Oracle, {
...installerOptions('jdk'),
architecture: 'x86'
})
).toThrow(/does not support operating system/);
).rejects.toThrow(/does not support operating system/);
});
});
7 changes: 5 additions & 2 deletions __tests__/distributors/graalvm-installer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1403,8 +1403,11 @@ describe('distribution factory', () => {
checkLatest: false
};

it('should map graalvm-community to the community installer', () => {
const community = getJavaDistribution('graalvm-community', defaultOptions);
it('should map graalvm-community to the community installer', async () => {
const community = await getJavaDistribution(
'graalvm-community',
defaultOptions
);

expect(community).toBeInstanceOf(GraalVMCommunityDistribution);
});
Expand Down
4 changes: 2 additions & 2 deletions __tests__/distributors/openjdk-installer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@ describe('OpenJdkDistribution', () => {
expect(windowsRelease[0].url.endsWith('.tar.gz')).toBe(true);
});

it('is registered in the distribution factory', () => {
const distribution = getJavaDistribution('oracle-openjdk', {
it('is registered in the distribution factory', async () => {
const distribution = await getJavaDistribution('oracle-openjdk', {
version: '26',
architecture: 'x64',
packageType: 'jdk',
Expand Down
118 changes: 118 additions & 0 deletions __tests__/setup-java.module-loading.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import {jest, describe, it, expect, beforeEach} from '@jest/globals';

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((value: string) => value),
toWin32Path: jest.fn((value: string) => value),
toPosixPath: jest.fn((value: string) => value)
}));

jest.unstable_mockModule('fs', () => ({
default: {
readFileSync: jest.fn()
}
}));

jest.unstable_mockModule('../src/util.js', () => ({
getBooleanInput: jest.fn(),
getVersionFromFileContent: jest.fn()
}));

jest.unstable_mockModule('../src/toolchains.js', () => ({
validateToolchainIds: jest.fn(),
configureToolchains: jest.fn()
}));

jest.unstable_mockModule(
'../src/distributions/distribution-factory.js',
() => ({
getJavaDistribution: jest.fn()
})
);

jest.unstable_mockModule('../src/auth.js', () => ({
configureAuthentication: jest.fn()
}));

jest.unstable_mockModule('../src/maven-args.js', () => ({
configureMavenArgs: jest.fn()
}));

jest.unstable_mockModule('../src/problem-matcher.js', () => ({
configureProblemMatcher: jest.fn()
}));

// These modules should never be imported when `cache` input is empty.
jest.unstable_mockModule('../src/cache-feature.js', () => {
throw new Error('cache-feature module should not be loaded');
});
jest.unstable_mockModule('../src/cache.js', () => {
throw new Error('cache module should not be loaded');
});

const core = await import('@actions/core');
const util = await import('../src/util.js');
const toolchains = await import('../src/toolchains.js');
const factory = await import('../src/distributions/distribution-factory.js');
const {run} = await import('../src/setup-java.js');

describe('setup-java conditional module loading', () => {
const inputs = new Map<string, string>();
const multilineInputs = new Map<string, string[]>();
const booleanInputs = new Map<string, boolean>();

beforeEach(() => {
jest.resetAllMocks();
inputs.clear();
multilineInputs.clear();
booleanInputs.clear();

(core.getInput as jest.Mock).mockImplementation((name: unknown) => {
return inputs.get(name as string) ?? '';
});
(core.getMultilineInput as jest.Mock).mockImplementation(
(name: unknown) => {
return multilineInputs.get(name as string) ?? [];
}
);
(util.getBooleanInput as jest.Mock).mockImplementation(
(name: unknown, defaultValue: unknown) => {
return booleanInputs.get(name as string) ?? defaultValue;
}
);
(toolchains.configureToolchains as jest.Mock).mockResolvedValue(undefined);
});

it('does not import cache modules when cache input is not provided', async () => {
inputs.set('distribution', 'temurin');
multilineInputs.set('java-version', ['21']);
(factory.getJavaDistribution as jest.Mock).mockResolvedValue({
setupJava: jest.fn(async () => ({
version: '21.0.4+7',
path: '/opt/java/21'
}))
});

await run();

expect(core.setFailed).not.toHaveBeenCalled();
});
});
Loading
Loading