mirror of
https://github.com/Azure/setup-kubectl.git
synced 2026-05-22 17:02:04 +00:00
526 lines
No EOL
21 KiB
TypeScript
526 lines
No EOL
21 KiB
TypeScript
import {vi, describe, test, expect, beforeEach} from 'vitest'
|
|
import * as path from 'path'
|
|
import * as util from 'util'
|
|
import {Readable} from 'stream'
|
|
|
|
vi.mock('os')
|
|
vi.mock('fs')
|
|
vi.mock('@actions/tool-cache', async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import('@actions/tool-cache')>()
|
|
return {
|
|
...actual,
|
|
downloadTool: vi.fn(),
|
|
find: vi.fn(),
|
|
cacheFile: vi.fn()
|
|
}
|
|
})
|
|
vi.mock('@actions/core')
|
|
vi.mock('@actions/http-client', () => {
|
|
const get = vi.fn()
|
|
return {
|
|
HttpClient: vi.fn().mockImplementation(function () {
|
|
return {get}
|
|
})
|
|
}
|
|
})
|
|
|
|
const os = await import('os')
|
|
const fs = await import('fs')
|
|
const toolCache = await import('@actions/tool-cache')
|
|
const core = await import('@actions/core')
|
|
const httpClient = await import('@actions/http-client')
|
|
const run = await import('./run.js')
|
|
const {
|
|
DEFAULT_KUBECTL_BASE_URL,
|
|
getkubectlDownloadURL,
|
|
getKubectlArch,
|
|
getExecutableExtension,
|
|
getLatestPatchVersion,
|
|
validateBaseURL,
|
|
validateVersion
|
|
} = await import('./helpers.js')
|
|
|
|
function mockInputs(inputs: Record<string, string>) {
|
|
vi.mocked(core.getInput).mockImplementation(
|
|
(name: string) => inputs[name] ?? ''
|
|
)
|
|
}
|
|
|
|
function fakeHttpResponse(opts: {
|
|
status: number
|
|
body?: string
|
|
location?: string
|
|
}) {
|
|
const body = Readable.from([Buffer.from(opts.body ?? '')])
|
|
const message: any = body
|
|
message.statusCode = opts.status
|
|
message.headers = opts.location ? {location: opts.location} : {}
|
|
return {message, readBody: async () => opts.body ?? ''}
|
|
}
|
|
|
|
function mockHttpGet(response: ReturnType<typeof fakeHttpResponse>) {
|
|
;(httpClient.HttpClient as any).mockImplementation(function () {
|
|
return {get: vi.fn().mockResolvedValue(response)}
|
|
})
|
|
}
|
|
|
|
describe('Testing all functions in run file.', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
test('getExecutableExtension() - return .exe when os is Windows', () => {
|
|
vi.mocked(os.type).mockReturnValue('Windows_NT')
|
|
expect(getExecutableExtension()).toBe('.exe')
|
|
})
|
|
test('getExecutableExtension() - return empty string for non-windows OS', () => {
|
|
vi.mocked(os.type).mockReturnValue('Darwin')
|
|
expect(getExecutableExtension()).toBe('')
|
|
})
|
|
test.each([
|
|
['arm', 'arm'],
|
|
['arm64', 'arm64'],
|
|
['x64', 'amd64']
|
|
])(
|
|
'getKubectlArch() - return on %s os arch %s kubectl arch',
|
|
(osArch, kubectlArch) => {
|
|
vi.mocked(os.arch).mockReturnValue(osArch as NodeJS.Architecture)
|
|
expect(getKubectlArch()).toBe(kubectlArch)
|
|
}
|
|
)
|
|
|
|
test.each([['arm'], ['arm64'], ['amd64']])(
|
|
'getkubectlDownloadURL() - default base URL, Linux %s',
|
|
(arch) => {
|
|
vi.mocked(os.type).mockReturnValue('Linux')
|
|
const expected = util.format(
|
|
'https://dl.k8s.io/release/v1.15.0/bin/linux/%s/kubectl',
|
|
arch
|
|
)
|
|
expect(getkubectlDownloadURL('v1.15.0', arch)).toBe(expected)
|
|
}
|
|
)
|
|
test.each([['arm'], ['arm64'], ['amd64']])(
|
|
'getkubectlDownloadURL() - default base URL, Darwin %s',
|
|
(arch) => {
|
|
vi.mocked(os.type).mockReturnValue('Darwin')
|
|
const expected = util.format(
|
|
'https://dl.k8s.io/release/v1.15.0/bin/darwin/%s/kubectl',
|
|
arch
|
|
)
|
|
expect(getkubectlDownloadURL('v1.15.0', arch)).toBe(expected)
|
|
}
|
|
)
|
|
test.each([['arm'], ['arm64'], ['amd64']])(
|
|
'getkubectlDownloadURL() - default base URL, Windows %s',
|
|
(arch) => {
|
|
vi.mocked(os.type).mockReturnValue('Windows_NT')
|
|
const expected = util.format(
|
|
'https://dl.k8s.io/release/v1.15.0/bin/windows/%s/kubectl.exe',
|
|
arch
|
|
)
|
|
expect(getkubectlDownloadURL('v1.15.0', arch)).toBe(expected)
|
|
}
|
|
)
|
|
|
|
test('getkubectlDownloadURL() - custom base URL', () => {
|
|
vi.mocked(os.type).mockReturnValue('Linux')
|
|
expect(
|
|
getkubectlDownloadURL('v1.15.0', 'amd64', 'https://mirror.example.com')
|
|
).toBe(
|
|
'https://mirror.example.com/release/v1.15.0/bin/linux/amd64/kubectl'
|
|
)
|
|
})
|
|
test('getkubectlDownloadURL() - strips trailing slash from base URL', () => {
|
|
vi.mocked(os.type).mockReturnValue('Darwin')
|
|
expect(
|
|
getkubectlDownloadURL(
|
|
'v1.15.0',
|
|
'arm64',
|
|
'https://mirror.example.com/'
|
|
)
|
|
).toBe(
|
|
'https://mirror.example.com/release/v1.15.0/bin/darwin/arm64/kubectl'
|
|
)
|
|
})
|
|
test('getkubectlDownloadURL() - base URL with path prefix', () => {
|
|
vi.mocked(os.type).mockReturnValue('Linux')
|
|
expect(
|
|
getkubectlDownloadURL(
|
|
'v1.30.0',
|
|
'amd64',
|
|
'https://mirror.example.com/k8s'
|
|
)
|
|
).toBe(
|
|
'https://mirror.example.com/k8s/release/v1.30.0/bin/linux/amd64/kubectl'
|
|
)
|
|
})
|
|
|
|
test.each([
|
|
['https://mirror.example.com'],
|
|
['https://mirror.example.com:8443'], // non-standard port
|
|
['https://172.32.0.1'] // outside RFC1918 172.16/12 range
|
|
])('validateBaseURL() - accepts %s', (input) => {
|
|
expect(() => validateBaseURL(input)).not.toThrow()
|
|
})
|
|
|
|
test.each([
|
|
['not a url', /not a valid URL/],
|
|
['http://mirror.example.com', /must use https/],
|
|
['https://user:pass@mirror.example.com', /userinfo/],
|
|
['https://localhost', /loopback\/link-local\/private/],
|
|
['https://127.0.0.1', /loopback\/link-local\/private/],
|
|
['https://[::1]', /loopback\/link-local\/private/], // caught a real bug
|
|
['https://169.254.169.254', /loopback\/link-local\/private/], // IMDS
|
|
['https://10.0.0.5', /loopback\/link-local\/private/], // RFC1918
|
|
['https://192.168.1.1', /loopback\/link-local\/private/], // RFC1918
|
|
['https://172.16.0.1', /loopback\/link-local\/private/], // RFC1918 low edge
|
|
['https://172.31.255.254', /loopback\/link-local\/private/] // RFC1918 high edge
|
|
])('validateBaseURL() - rejects %s', (input, expected) => {
|
|
expect(() => validateBaseURL(input)).toThrow(expected)
|
|
})
|
|
|
|
test.each([['v1.30.0'], ['1.30.0']])(
|
|
'validateVersion() - accepts %s',
|
|
(input) => {
|
|
expect(() => validateVersion(input)).not.toThrow()
|
|
}
|
|
)
|
|
|
|
test.each([
|
|
[''],
|
|
['v1.30.0\n'], // trailing newline
|
|
['v1.2.3.4'], // extra segment
|
|
[' v1.30.0'] // leading whitespace
|
|
])('validateVersion() - rejects %s', (input) => {
|
|
expect(() => validateVersion(input)).toThrow(/Invalid kubectl version/)
|
|
})
|
|
|
|
test('getStableKubectlVersion() - default base URL', async () => {
|
|
vi.mocked(toolCache.downloadTool).mockResolvedValue('pathToTool')
|
|
vi.mocked(fs.readFileSync).mockReturnValue('v1.20.4')
|
|
expect(await run.getStableKubectlVersion()).toBe('v1.20.4')
|
|
expect(toolCache.downloadTool).toHaveBeenCalledWith(
|
|
'https://dl.k8s.io/release/stable.txt'
|
|
)
|
|
})
|
|
test('getStableKubectlVersion() - honours custom base URL (air-gap fix)', async () => {
|
|
vi.mocked(toolCache.downloadTool).mockResolvedValue('pathToTool')
|
|
vi.mocked(fs.readFileSync).mockReturnValue('v1.20.4')
|
|
expect(
|
|
await run.getStableKubectlVersion('https://mirror.example.com')
|
|
).toBe('v1.20.4')
|
|
expect(toolCache.downloadTool).toHaveBeenCalledWith(
|
|
'https://mirror.example.com/release/stable.txt'
|
|
)
|
|
})
|
|
test('getStableKubectlVersion() - path-prefixed mirror composes correctly', async () => {
|
|
vi.mocked(toolCache.downloadTool).mockResolvedValue('pathToTool')
|
|
vi.mocked(fs.readFileSync).mockReturnValue('v1.20.4')
|
|
await run.getStableKubectlVersion('https://mirror.example.com/k8s/')
|
|
expect(toolCache.downloadTool).toHaveBeenCalledWith(
|
|
'https://mirror.example.com/k8s/release/stable.txt'
|
|
)
|
|
})
|
|
test('getStableKubectlVersion() - falls back to v1.15.0 on empty file', async () => {
|
|
vi.mocked(toolCache.downloadTool).mockResolvedValue('pathToTool')
|
|
vi.mocked(fs.readFileSync).mockReturnValue('')
|
|
expect(await run.getStableKubectlVersion()).toBe('v1.15.0')
|
|
})
|
|
test('getStableKubectlVersion() - falls back to v1.15.0 on download failure', async () => {
|
|
vi.mocked(toolCache.downloadTool).mockRejectedValue('Unable to download.')
|
|
expect(await run.getStableKubectlVersion()).toBe('v1.15.0')
|
|
})
|
|
|
|
test('downloadKubectl() - download and cache, default base URL', async () => {
|
|
vi.mocked(toolCache.find).mockReturnValue('')
|
|
vi.mocked(toolCache.downloadTool).mockResolvedValue('pathToTool')
|
|
vi.mocked(toolCache.cacheFile).mockResolvedValue('pathToCachedTool')
|
|
vi.mocked(os.type).mockReturnValue('Windows_NT')
|
|
vi.mocked(fs.chmodSync).mockImplementation(() => {})
|
|
expect(await run.downloadKubectl('v1.15.0')).toBe(
|
|
path.join('pathToCachedTool', 'kubectl.exe')
|
|
)
|
|
expect(toolCache.downloadTool).toHaveBeenCalledWith(
|
|
'https://dl.k8s.io/release/v1.15.0/bin/windows/amd64/kubectl.exe'
|
|
)
|
|
})
|
|
test('downloadKubectl() - rejects invalid version (path traversal)', async () => {
|
|
await expect(run.downloadKubectl('../etc')).rejects.toThrow(
|
|
/Invalid kubectl version/
|
|
)
|
|
})
|
|
test('downloadKubectl() - throws DownloadKubectlFailed on generic error', async () => {
|
|
vi.mocked(toolCache.find).mockReturnValue('')
|
|
vi.mocked(toolCache.downloadTool).mockRejectedValue('boom')
|
|
await expect(run.downloadKubectl('v1.15.0')).rejects.toThrow(
|
|
'DownloadKubectlFailed'
|
|
)
|
|
})
|
|
test('downloadKubectl() - 404 maps to "not found" message', async () => {
|
|
const kubectlVersion = 'v1.15.0'
|
|
const arch = 'arm64'
|
|
vi.mocked(os.arch).mockReturnValue(arch as NodeJS.Architecture)
|
|
vi.mocked(toolCache.find).mockReturnValue('')
|
|
vi.mocked(toolCache.downloadTool).mockImplementation(() => {
|
|
throw new toolCache.HTTPError(404)
|
|
})
|
|
await expect(run.downloadKubectl(kubectlVersion)).rejects.toThrow(
|
|
util.format(
|
|
"Kubectl '%s' for '%s' arch not found.",
|
|
kubectlVersion,
|
|
arch
|
|
)
|
|
)
|
|
})
|
|
test('downloadKubectl() - returns existing cache without redownloading', async () => {
|
|
vi.mocked(toolCache.find).mockReturnValue('pathToCachedTool')
|
|
vi.mocked(os.type).mockReturnValue('Windows_NT')
|
|
vi.mocked(fs.chmodSync).mockImplementation(() => {})
|
|
expect(await run.downloadKubectl('v1.15.0')).toBe(
|
|
path.join('pathToCachedTool', 'kubectl.exe')
|
|
)
|
|
expect(toolCache.downloadTool).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('downloadKubectl() - rejects malformed checksum input', async () => {
|
|
vi.mocked(toolCache.find).mockReturnValue('')
|
|
vi.mocked(toolCache.downloadTool).mockResolvedValue('pathToTool')
|
|
vi.mocked(os.type).mockReturnValue('Linux')
|
|
vi.mocked(os.arch).mockReturnValue('x64')
|
|
vi.mocked(fs.readFileSync).mockReturnValue(Buffer.from('hi') as never)
|
|
await expect(
|
|
run.downloadKubectl('v1.15.0', DEFAULT_KUBECTL_BASE_URL, 'not-a-hash')
|
|
).rejects.toThrow(/Invalid checksum input/)
|
|
})
|
|
test('downloadKubectl() - rejects checksum mismatch', async () => {
|
|
vi.mocked(toolCache.find).mockReturnValue('')
|
|
vi.mocked(toolCache.downloadTool).mockResolvedValue('pathToTool')
|
|
vi.mocked(os.type).mockReturnValue('Linux')
|
|
vi.mocked(os.arch).mockReturnValue('x64')
|
|
vi.mocked(fs.readFileSync).mockReturnValue(Buffer.from('hi') as never)
|
|
const wrong = 'a'.repeat(64)
|
|
await expect(
|
|
run.downloadKubectl('v1.15.0', DEFAULT_KUBECTL_BASE_URL, wrong)
|
|
).rejects.toThrow(/Checksum mismatch/)
|
|
})
|
|
test('downloadKubectl() - accepts matching checksum', async () => {
|
|
vi.mocked(toolCache.find).mockReturnValue('')
|
|
vi.mocked(toolCache.downloadTool).mockResolvedValue('pathToTool')
|
|
vi.mocked(toolCache.cacheFile).mockResolvedValue('pathToCachedTool')
|
|
vi.mocked(os.type).mockReturnValue('Linux')
|
|
vi.mocked(os.arch).mockReturnValue('x64')
|
|
vi.mocked(fs.chmodSync).mockImplementation(() => {})
|
|
// sha256('hi') = 8f434346648f6b96df89dda901c5176b10a6d83961dd3c1ac88b59b2dc327aa4
|
|
vi.mocked(fs.readFileSync).mockReturnValue(Buffer.from('hi') as never)
|
|
const correct =
|
|
'8f434346648f6b96df89dda901c5176b10a6d83961dd3c1ac88b59b2dc327aa4'
|
|
await expect(
|
|
run.downloadKubectl('v1.15.0', DEFAULT_KUBECTL_BASE_URL, correct)
|
|
).resolves.toBe(path.join('pathToCachedTool', 'kubectl'))
|
|
})
|
|
|
|
test.each([[301], [302], [307], [308]])(
|
|
'downloadKubectl() - custom mirror: rejects %i redirect (SSRF guard)',
|
|
async (status) => {
|
|
vi.mocked(toolCache.find).mockReturnValue('')
|
|
vi.mocked(os.type).mockReturnValue('Linux')
|
|
vi.mocked(os.arch).mockReturnValue('x64')
|
|
mockHttpGet(
|
|
fakeHttpResponse({
|
|
status,
|
|
location: 'http://169.254.169.254/latest/meta-data/'
|
|
})
|
|
)
|
|
await expect(
|
|
run.downloadKubectl('v1.15.0', 'https://mirror.example.com')
|
|
).rejects.toThrow('DownloadKubectlFailed')
|
|
expect(toolCache.downloadTool).not.toHaveBeenCalled()
|
|
}
|
|
)
|
|
|
|
test('downloadKubectl() - custom mirror: non-200/non-404 (500) fails', async () => {
|
|
vi.mocked(toolCache.find).mockReturnValue('')
|
|
vi.mocked(os.type).mockReturnValue('Linux')
|
|
vi.mocked(os.arch).mockReturnValue('x64')
|
|
mockHttpGet(fakeHttpResponse({status: 500}))
|
|
await expect(
|
|
run.downloadKubectl('v1.15.0', 'https://mirror.example.com')
|
|
).rejects.toThrow('DownloadKubectlFailed')
|
|
expect(toolCache.downloadTool).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('downloadKubectl() - custom mirror: 404 maps to "not found"', async () => {
|
|
vi.mocked(toolCache.find).mockReturnValue('')
|
|
vi.mocked(os.type).mockReturnValue('Linux')
|
|
const arch = 'amd64'
|
|
vi.mocked(os.arch).mockReturnValue('x64')
|
|
mockHttpGet(fakeHttpResponse({status: 404}))
|
|
await expect(
|
|
run.downloadKubectl('v1.15.0', 'https://mirror.example.com')
|
|
).rejects.toThrow(
|
|
util.format("Kubectl '%s' for '%s' arch not found.", 'v1.15.0', arch)
|
|
)
|
|
expect(toolCache.downloadTool).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('downloadKubectl() - custom mirror: 200 streams body to temp file and caches', async () => {
|
|
vi.mocked(toolCache.find).mockReturnValue('')
|
|
vi.mocked(toolCache.cacheFile).mockResolvedValue('pathToCachedTool')
|
|
vi.mocked(os.type).mockReturnValue('Linux')
|
|
vi.mocked(os.arch).mockReturnValue('x64')
|
|
vi.mocked(os.tmpdir).mockReturnValue('/tmp')
|
|
vi.mocked(fs.chmodSync).mockImplementation(() => {})
|
|
vi.mocked(fs.writeFileSync).mockImplementation(() => {})
|
|
mockHttpGet(fakeHttpResponse({status: 200, body: 'kubectl-bytes'}))
|
|
|
|
await expect(
|
|
run.downloadKubectl('v1.15.0', 'https://mirror.example.com')
|
|
).resolves.toBe(path.join('pathToCachedTool', 'kubectl'))
|
|
expect(toolCache.downloadTool).not.toHaveBeenCalled()
|
|
expect(toolCache.cacheFile).toHaveBeenCalled()
|
|
expect(fs.writeFileSync).toHaveBeenCalled()
|
|
})
|
|
|
|
test('getLatestPatchVersion() - default base URL', async () => {
|
|
vi.mocked(toolCache.downloadTool).mockResolvedValue('pathToTool')
|
|
vi.mocked(fs.readFileSync).mockReturnValue('v1.27.15')
|
|
expect(await getLatestPatchVersion('1', '27')).toBe('v1.27.15')
|
|
expect(toolCache.downloadTool).toHaveBeenCalledWith(
|
|
'https://dl.k8s.io/release/stable-1.27.txt'
|
|
)
|
|
})
|
|
test('getLatestPatchVersion() - honours custom base URL', async () => {
|
|
vi.mocked(toolCache.downloadTool).mockResolvedValue('pathToTool')
|
|
vi.mocked(fs.readFileSync).mockReturnValue('v1.27.15')
|
|
expect(
|
|
await getLatestPatchVersion('1', '27', 'https://mirror.example.com')
|
|
).toBe('v1.27.15')
|
|
expect(toolCache.downloadTool).toHaveBeenCalledWith(
|
|
'https://mirror.example.com/release/stable-1.27.txt'
|
|
)
|
|
})
|
|
test('getLatestPatchVersion() - path-prefixed mirror composes correctly', async () => {
|
|
vi.mocked(toolCache.downloadTool).mockResolvedValue('pathToTool')
|
|
vi.mocked(fs.readFileSync).mockReturnValue('v1.27.15')
|
|
await getLatestPatchVersion('1', '27', 'https://mirror.example.com/k8s')
|
|
expect(toolCache.downloadTool).toHaveBeenCalledWith(
|
|
'https://mirror.example.com/k8s/release/stable-1.27.txt'
|
|
)
|
|
})
|
|
test('getLatestPatchVersion() - throws on empty file', async () => {
|
|
vi.mocked(toolCache.downloadTool).mockResolvedValue('pathToTool')
|
|
vi.mocked(fs.readFileSync).mockReturnValue('')
|
|
await expect(getLatestPatchVersion('1', '27')).rejects.toThrow(
|
|
'Failed to get latest patch version for 1.27'
|
|
)
|
|
})
|
|
test('getLatestPatchVersion() - throws on download failure', async () => {
|
|
vi.mocked(toolCache.downloadTool).mockRejectedValue(new Error('Network'))
|
|
await expect(getLatestPatchVersion('1', '27')).rejects.toThrow(
|
|
'Failed to get latest patch version for 1.27'
|
|
)
|
|
})
|
|
|
|
test('resolveKubectlVersion() - expands major.minor', async () => {
|
|
vi.mocked(toolCache.downloadTool).mockResolvedValue('pathToTool')
|
|
vi.mocked(fs.readFileSync).mockReturnValue('v1.27.15')
|
|
expect(await run.resolveKubectlVersion('1.27')).toBe('v1.27.15')
|
|
})
|
|
test('resolveKubectlVersion() - returns full version unchanged', async () => {
|
|
expect(await run.resolveKubectlVersion('v1.27.15')).toBe('v1.27.15')
|
|
})
|
|
test('resolveKubectlVersion() - adds v prefix', async () => {
|
|
expect(await run.resolveKubectlVersion('1.27.15')).toBe('v1.27.15')
|
|
})
|
|
|
|
test('run() - reads version, downloadBaseURL, checksum inputs; defaults applied', async () => {
|
|
mockInputs({version: 'v1.15.5'})
|
|
vi.mocked(toolCache.find).mockReturnValue('pathToCachedTool')
|
|
vi.mocked(os.type).mockReturnValue('Windows_NT')
|
|
vi.mocked(fs.chmodSync).mockImplementation()
|
|
vi.mocked(core.addPath).mockImplementation()
|
|
vi.mocked(core.setOutput).mockImplementation()
|
|
|
|
await expect(run.run()).resolves.toBeUndefined()
|
|
expect(core.getInput).toHaveBeenCalledWith('version', {required: true})
|
|
expect(core.getInput).toHaveBeenCalledWith('downloadBaseURL', {
|
|
required: false
|
|
})
|
|
expect(core.getInput).toHaveBeenCalledWith('checksum', {required: false})
|
|
// Default base URL: no audit notice should fire.
|
|
expect(core.notice).not.toHaveBeenCalled()
|
|
expect(core.setOutput).toHaveBeenCalledWith(
|
|
'kubectl-path',
|
|
path.join('pathToCachedTool', 'kubectl.exe')
|
|
)
|
|
})
|
|
|
|
test('run() - latest + custom mirror routes stable.txt through the mirror', async () => {
|
|
mockInputs({
|
|
version: 'latest',
|
|
downloadBaseURL: 'https://mirror.example.com'
|
|
})
|
|
vi.mocked(toolCache.downloadTool).mockResolvedValue('pathToTool')
|
|
vi.mocked(fs.readFileSync).mockReturnValue('v1.20.4')
|
|
vi.mocked(toolCache.find).mockReturnValue('pathToCachedTool')
|
|
vi.mocked(os.type).mockReturnValue('Windows_NT')
|
|
vi.mocked(fs.chmodSync).mockImplementation()
|
|
vi.mocked(core.addPath).mockImplementation()
|
|
vi.mocked(core.setOutput).mockImplementation()
|
|
|
|
await expect(run.run()).resolves.toBeUndefined()
|
|
// The fix: stable.txt comes from the custom mirror, not dl.k8s.io.
|
|
expect(toolCache.downloadTool).toHaveBeenCalledWith(
|
|
'https://mirror.example.com/release/stable.txt'
|
|
)
|
|
// Audit notice fires for any non-default base URL.
|
|
expect(core.notice).toHaveBeenCalledWith(
|
|
expect.stringContaining('https://mirror.example.com')
|
|
)
|
|
// No checksum + custom mirror => loud warning.
|
|
expect(core.warning).toHaveBeenCalledWith(
|
|
expect.stringContaining('checksum')
|
|
)
|
|
})
|
|
|
|
test('run() - empty/whitespace downloadBaseURL falls back to default', async () => {
|
|
mockInputs({version: 'v1.15.5', downloadBaseURL: ' '})
|
|
vi.mocked(toolCache.find).mockReturnValue('pathToCachedTool')
|
|
vi.mocked(os.type).mockReturnValue('Windows_NT')
|
|
vi.mocked(fs.chmodSync).mockImplementation()
|
|
vi.mocked(core.addPath).mockImplementation()
|
|
vi.mocked(core.setOutput).mockImplementation()
|
|
|
|
await expect(run.run()).resolves.toBeUndefined()
|
|
// Default fallback => no audit notice.
|
|
expect(core.notice).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('run() - invalid downloadBaseURL (http) fails fast', async () => {
|
|
mockInputs({version: 'v1.15.5', downloadBaseURL: 'http://insecure'})
|
|
await expect(run.run()).rejects.toThrow(/must use https/)
|
|
})
|
|
|
|
test('run() - uppercase checksum input is normalized and accepted', async () => {
|
|
// sha256('hi') = 8f43...aa4 — supplied uppercase to prove run() lowercases it
|
|
// before validateChecksum's strict /^[a-f0-9]{64}$/ regex sees it.
|
|
const correctLower =
|
|
'8f434346648f6b96df89dda901c5176b10a6d83961dd3c1ac88b59b2dc327aa4'
|
|
mockInputs({
|
|
version: 'v1.15.5',
|
|
checksum: correctLower.toUpperCase()
|
|
})
|
|
vi.mocked(toolCache.find).mockReturnValue('')
|
|
vi.mocked(toolCache.downloadTool).mockResolvedValue('pathToTool')
|
|
vi.mocked(toolCache.cacheFile).mockResolvedValue('pathToCachedTool')
|
|
vi.mocked(os.type).mockReturnValue('Linux')
|
|
vi.mocked(os.arch).mockReturnValue('x64')
|
|
vi.mocked(fs.readFileSync).mockReturnValue(Buffer.from('hi') as never)
|
|
vi.mocked(fs.chmodSync).mockImplementation()
|
|
vi.mocked(core.addPath).mockImplementation()
|
|
vi.mocked(core.setOutput).mockImplementation()
|
|
|
|
await expect(run.run()).resolves.toBeUndefined()
|
|
})
|
|
}) |