40.4 KiB · text 806cbc3
import assert from 'node:assert'
import http from 'node:http'
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { describe, it } from 'node:test'
import * as csv from 'csv-parse/sync'
import elixirMatchers from '../matchers/elixir-matchers.json' with { type: 'json' }
import erlangMatchers from '../matchers/erlang-matchers.json' with { type: 'json' }
const { problemMatcher } = elixirMatchers
const { problemMatcher: erlangProblemMatcher } = erlangMatchers
process.env.NODE_ENV = 'test'
simulateInput('otp-version', '25.1.2')
simulateInput('otp-architecture', '64')
simulateInput('elixir-version', '1.14.2')
simulateInput('rebar3-version', '3.20')
simulateInput('install-rebar', 'true')
simulateInput('install-hex', 'true')
simulateInput('github-token', process.env.GITHUB_TOKEN)
simulateInput('hexpm-mirrors', 'https://builds.hex.pm', { multiline: true })
const { default: setupBeam } = await import('../src/setup-beam.js')
const matrix = {
otp: {
'ubuntu-18.04': parseBuild('test/otp/ubuntu-18.04/builds.txt'),
'ubuntu-20.04': parseBuild('test/otp/ubuntu-20.04/builds.txt'),
'ubuntu-22.04': parseBuild('test/otp/ubuntu-22.04/builds.txt'),
'ubuntu-24.04': parseBuild('test/otp/ubuntu-24.04/builds.txt'),
'ubuntu-26.04': parseBuild('test/otp/ubuntu-26.04/builds.txt'),
windows: parseReleases('test/otp/releases.json'),
'macos-aarch64': parseCsv('test/otp/macos/aarch64-apple-darwin.csv'),
'macos-x86_64': parseCsv('test/otp/macos/x86_64-apple-darwin.csv'),
},
elixir: parseBuild('test/elixir/builds.txt'),
gleam: parseReleases('test/gleam/releases.json'),
rebar3: parseReleases('test/rebar3/releases.json'),
}
function parseBuild(version) {
let versions = {}
fs.readFileSync(path.join(process.cwd(), version), 'utf8')
.trim()
.split('\n')
.forEach((line) => (versions[line.split(' ')[0]] = line.split(' ')[0]))
return versions
}
function parseReleases(version) {
let versions = {}
let data = fs.readFileSync(path.join(process.cwd(), version), 'utf8')
let json = JSON.parse(data)
if (version.includes('otp')) {
json
.map((x) => x.assets)
.flat()
.filter((x) => x.name.match(/^otp_win64_.*.exe$/))
.forEach((x) => {
let v = x.name.match(/^otp_win64_(.*).exe$/)[1]
versions[v] = v
})
return versions
} else {
json.map((x) => (versions[x.tag_name] = x.tag_name))
return versions
}
}
function parseCsv(file) {
let versions = {}
let fileH = fs.readFileSync(file, 'utf8')
csv
.parse(fileH, { columns: true })
.forEach((line) => (versions[line.ref_name] = line.ref_name))
return versions
}
describe('OTP install', () => {
it('fails for invalid OS version', async () => {
const otpOSVersion = 'ubuntu-08.04'
const otpVersion = 'OTP-23.2'
await assert.rejects(
async () => {
await setupBeam.install('otp', {
hexMirror: 'https://builds.hex.pm',
otpOSVersion,
otpVersion,
})
},
(err) => {
assert.ok(err instanceof Error)
return true
},
`Installing Erlang/OTP ${otpVersion} over ${otpOSVersion} is supposed to fail`,
)
})
})
describe('Elixir install', () => {
it('fails for version 0.11 without OTP', async () => {
const exVersion = '0.11'
await assert.rejects(
async () => {
await setupBeam.install('elixir', {
hexMirror: 'https://builds.hex.pm',
exVersion,
})
},
(err) => {
assert.ok(err instanceof Error)
return true
},
`Installing Elixir ${exVersion} is supposed to fail`,
)
})
it('fails for version 1.0.0 on OTP 17 (without OTP)', async () => {
const exVersion = 'v1.0.0-otp-17'
await assert.rejects(
async () => {
await setupBeam.install('elixir', {
hexMirror: 'https://builds.hex.pm',
exVersion,
})
},
(err) => {
assert.ok(err instanceof Error)
return true
},
`Installing Elixir ${exVersion} is supposed to fail`,
)
})
})
describe('Gleam install', () => {
it('fails for unknown OTP', async () => {
const gleamVersion = '0.1.3'
await assert.rejects(
async () => {
await setupBeam.install('gleam', { gleamVersion })
},
(err) => {
assert.ok(err instanceof Error)
return true
},
`Installing Gleam ${gleamVersion} is supposed to fail`,
)
})
})
describe('rebar3 install', () => {
it('fails for unknown OTP', async () => {
const r3Version = '0.14.4'
await assert.rejects(
async () => {
await setupBeam.install('rebar3', { r3Version })
},
(err) => {
assert.ok(err instanceof Error)
return true
},
`Installing rebar3 ${r3Version} is supposed to fail`,
)
})
})
describe('.getOTPVersion(_) - Erlang', () => {
let got
let expected
let spec
let osVersion
let before
const hexMirrors = simulateInput(
'hexpm-mirrors',
'https://repo.hex.pm, https://cdn.jsdelivr.net/hex',
{ multiline: true },
)
if (process.platform === 'linux') {
it('is Ok for known linux version', async () => {
before = simulateInput('version-type', 'strict')
spec = '27.0'
osVersion = 'ubuntu-24.04'
expected = 'OTP-27.0'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
before = simulateInput('version-type', 'strict')
spec = '27.0'
osVersion = 'ubuntu-26.04'
expected = 'OTP-27.0'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
before = simulateInput('version-type', 'strict')
spec = '25.3.2.1'
osVersion = 'ubuntu-20.04'
expected = 'OTP-25.3.2.1'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
spec = '20.3.x'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.3.8.26'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '^20.3.8'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.3.8.26'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '^20.3'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.3.8.26'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '20'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.3.8.26'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '20.3.8.26'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.3.8.26'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '20.x'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.3.8.26'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '20.0'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.0.5'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '20.0.x'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.0.5'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = 'maint'
osVersion = 'ubuntu-22.04'
expected = 'maint'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = 'master'
osVersion = 'ubuntu-22.04'
expected = 'master'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
})
}
if (process.platform === 'win32') {
it('is Ok for known win32 version', async () => {
spec = '24.0.1'
osVersion = 'windows-latest'
expected = '24.0.1'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '23.2.x'
osVersion = 'windows-2016'
expected = '23.2.7'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '23.0'
osVersion = 'windows-2022'
expected = '23.0.4'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
// Check we get the same results for 32-bit OTP
before = simulateInput('otp-architecture', '32')
spec = '24.0.1'
osVersion = 'windows-latest'
expected = '24.0.1'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '23.2.x'
osVersion = 'windows-2016'
expected = '23.2.7'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '23.0'
osVersion = 'windows-2022'
expected = '23.0.4'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
simulateInput('otp-architecture', before)
})
}
if (process.platform === 'linux') {
it('is main-... only if strict/maint-... is used as input', async () => {
const arm64Options = setupBeam.githubARMRunnerArchs()
const previousRunnerArch = process.env.RUNNER_ARCH
process.env.RUNNER_ARCH =
arm64Options[Math.floor(Math.random() * arm64Options.length)]
before = simulateInput('version-type', 'strict')
await assert.rejects(
async () => {
await setupBeam.getOTPVersion('27', 'ubuntu-24.04')
},
(err) => {
assert.ok(err instanceof Error)
return true
},
`Fetching strict OTP version maint-<v> with input <v> is supposed to fail`,
)
simulateInput('version-type', before)
before = simulateInput('version-type', 'strict')
assert.strictEqual(
await setupBeam.getOTPVersion('maint-27', 'ubuntu-24.04'),
'maint-27',
)
simulateInput('version-type', before)
before = simulateInput('version-type', 'loose')
assert.strictEqual(
await setupBeam.getOTPVersion('maint-27', 'ubuntu-24.04'),
'maint-27',
)
simulateInput('version-type', before)
process.env.RUNNER_ARCH = previousRunnerArch
})
it('is Ok for known linux ARM64 version', async () => {
const arm64Options = setupBeam.githubARMRunnerArchs()
const previousRunnerArch = process.env.RUNNER_ARCH
process.env.RUNNER_ARCH =
arm64Options[Math.floor(Math.random() * arm64Options.length)]
before = simulateInput('version-type', 'strict')
spec = '27.0'
osVersion = 'ubuntu-24.04'
expected = 'OTP-27.0'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
before = simulateInput('version-type', 'strict')
spec = '25.3.2.1'
osVersion = 'ubuntu-20.04'
expected = 'OTP-25.3.2.1'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
spec = '20'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.3.8.26'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '20.3.8.26'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.3.8.26'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '20.x'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.3.8.26'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '20.0'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.0.5'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '20.0.x'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.0.5'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = 'maint'
osVersion = 'ubuntu-22.04'
expected = 'maint'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = 'master'
osVersion = 'ubuntu-22.04'
expected = 'master'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
process.env.RUNNER_ARCH = previousRunnerArch
})
it('is Ok for known linux AMD64 version', async () => {
const amd64Options = setupBeam.githubAMDRunnerArchs()
const previousRunnerArch = process.env.RUNNER_ARCH
process.env.RUNNER_ARCH =
amd64Options[Math.floor(Math.random() * amd64Options.length)]
before = simulateInput('version-type', 'strict')
spec = '27.0'
osVersion = 'ubuntu-24.04'
expected = 'OTP-27.0'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
before = simulateInput('version-type', 'strict')
spec = '25.3.2.1'
osVersion = 'ubuntu-20.04'
expected = 'OTP-25.3.2.1'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
spec = '19.3.x'
osVersion = 'ubuntu-16.04'
expected = 'OTP-19.3.6.13'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '^19.3.6'
osVersion = 'ubuntu-16.04'
expected = 'OTP-19.3.6.13'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '^19.3'
osVersion = 'ubuntu-18.04'
expected = 'OTP-19.3.6.13'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '20'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.3.8.26'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '20.3.8.26'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.3.8.26'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '20.x'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.3.8.26'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '20.0'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.0.5'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '20.0.x'
osVersion = 'ubuntu-20.04'
expected = 'OTP-20.0.5'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = 'maint'
osVersion = 'ubuntu-22.04'
expected = 'maint'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = 'master'
osVersion = 'ubuntu-22.04'
expected = 'master'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
process.env.RUNNER_ARCH = previousRunnerArch
})
}
if (process.platform === 'darwin') {
it('is main-... only if strict/maint-... is used as input', async () => {
const arm64Options = setupBeam.githubARMRunnerArchs()
const previousRunnerArch = process.env.RUNNER_ARCH
process.env.RUNNER_ARCH =
arm64Options[Math.floor(Math.random() * arm64Options.length)]
before = simulateInput('version-type', 'strict')
await assert.rejects(
async () => {
await setupBeam.getOTPVersion('27')
},
(err) => {
assert.ok(err instanceof Error)
return true
},
`Fetching strict OTP version maint-<v> with input <v> is supposed to fail`,
)
simulateInput('version-type', before)
before = simulateInput('version-type', 'strict')
assert.strictEqual(await setupBeam.getOTPVersion('maint-27'), 'maint-27')
simulateInput('version-type', before)
before = simulateInput('version-type', 'loose')
assert.strictEqual(await setupBeam.getOTPVersion('maint-27'), 'maint-27')
simulateInput('version-type', before)
process.env.RUNNER_ARCH = previousRunnerArch
})
it('is Ok for known macos ARM64 version', async () => {
const arm64Options = setupBeam.githubARMRunnerArchs()
const previousRunnerArch = process.env.RUNNER_ARCH
process.env.RUNNER_ARCH =
arm64Options[Math.floor(Math.random() * arm64Options.length)]
before = simulateInput('version-type', 'strict')
spec = '28.0'
osVersion = 'macos-15-arm64'
expected = 'OTP-28.0'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
process.env.RUNNER_ARCH = previousRunnerArch
})
it('is Ok for known macos AMD64 version', async () => {
const amd64Options = setupBeam.githubARMRunnerArchs()
const previousRunnerArch = process.env.RUNNER_ARCH
process.env.RUNNER_ARCH =
amd64Options[Math.floor(Math.random() * amd64Options.length)]
before = simulateInput('version-type', 'strict')
spec = '28.0'
osVersion = 'macos-15'
expected = 'OTP-28.0'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
process.env.RUNNER_ARCH = previousRunnerArch
})
}
simulateInput('hexpm-mirrors', hexMirrors, { multiline: true })
})
describe('OTP arch-specific install', () => {
it('fails for invalid GitHub runner arch.', async () => {
const previousRunnerArch = process.env.RUNNER_ARCH
process.env.RUNNER_ARCH = 'invalid'
if (process.platform === 'linux') {
const spec = '26'
const osVersion = 'ubuntu-24.04'
await assert.rejects(
async () => {
await setupBeam.getOTPVersion(spec, osVersion)
},
(err) => {
assert.ok(err instanceof Error)
return true
},
`Fetching OTP Version with invalid Github runner architecture is supposed to fail`,
)
}
process.env.RUNNER_ARCH = previousRunnerArch
})
})
describe('.getRunnerOSVersion(_)', () => {
it('maps x86 ImageOS values to their container', async () => {
const previousImageOS = process.env.ImageOS
process.env.ImageOS = 'ubuntu22'
assert.strictEqual(setupBeam.getRunnerOSVersion(), 'ubuntu-22.04')
process.env.ImageOS = 'ubuntu24'
assert.strictEqual(setupBeam.getRunnerOSVersion(), 'ubuntu-24.04')
process.env.ImageOS = previousImageOS
})
it('maps arm64 ImageOS variants to the same container as x86', async () => {
const previousImageOS = process.env.ImageOS
process.env.ImageOS = 'ubuntu22-arm64'
assert.strictEqual(setupBeam.getRunnerOSVersion(), 'ubuntu-22.04')
process.env.ImageOS = 'ubuntu24-arm64'
assert.strictEqual(setupBeam.getRunnerOSVersion(), 'ubuntu-24.04')
process.env.ImageOS = previousImageOS
})
it('throws for genuinely unknown ImageOS values', async () => {
const previousImageOS = process.env.ImageOS
process.env.ImageOS = 'plan9'
assert.throws(
() => setupBeam.getRunnerOSVersion(),
/Tried to map a target OS/,
)
process.env.ImageOS = previousImageOS
})
})
describe('.getOTPVersion(_) - Elixir', () => {
let got
let expected
let spec
let otpVersion
let before
if (process.platform === 'linux') {
it('returns the expected value', async () => {
spec = '1.18.x'
otpVersion = 'OTP-27'
expected = 'v1.18.4-otp-27'
await setupBeam.installOTP(otpVersion)
got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
spec = '1.18.2'
otpVersion = 'OTP-27'
expected = 'v1.18.2-otp-27'
await setupBeam.installOTP(otpVersion)
got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
spec = '1.16.2-otp-26'
otpVersion = 'OTP-27'
expected = 'v1.16.2-otp-26'
await setupBeam.installOTP(otpVersion)
got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
spec = '1.12.1'
otpVersion = 'OTP-24.3.4'
expected = 'v1.12.1-otp-24'
await setupBeam.installOTP(otpVersion)
got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
before = simulateInput('version-type', 'strict')
spec = '1.19'
otpVersion = 'master'
expected = 'v1.19-otp-28'
await setupBeam.installOTP(otpVersion)
got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
before = simulateInput('version-type', 'strict')
spec = 'v1.15.0-rc.2'
otpVersion = 'OTP-26.0'
expected = 'v1.15.0-rc.2-otp-26'
await setupBeam.installOTP(otpVersion)
got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
before = simulateInput('version-type', 'strict')
spec = 'main'
otpVersion = '25.2'
expected = 'main-otp-25'
await setupBeam.installOTP(otpVersion)
got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
})
}
})
describe('.getOTPVersion(_) - Gleam', () => {
let got
let expected
let spec
let otpVersion
it('returns the expected value', async () => {
spec = 'v0.3.0'
otpVersion = 'OTP-23'
expected = 'v0.3.0'
got = await setupBeam.getGleamVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
spec = 'nightly'
otpVersion = '28'
expected = 'nightly'
got = await setupBeam.getGleamVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
spec = '0.13.2'
otpVersion = 'OTP-24'
expected = 'v0.13.2'
got = await setupBeam.getGleamVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
const before = simulateInput('version-type', 'strict')
spec = 'v0.14.0-rc2'
otpVersion = 'OTP-22'
expected = 'v0.14.0-rc2'
got = await setupBeam.getGleamVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
})
})
describe('.getOTPVersion(_) - rebar3', () => {
let got
let expected
let spec
it('returns the expected value', async () => {
spec = '3.26.x'
expected = '3.26.0'
got = await setupBeam.getRebar3Version(spec)
assert.deepStrictEqual(got, expected)
spec = '3.25'
expected = '3.25.1'
got = await setupBeam.getRebar3Version(spec)
assert.deepStrictEqual(got, expected)
spec = '3.26'
expected = '3.26.0'
got = await setupBeam.getRebar3Version(spec)
assert.deepStrictEqual(got, expected)
})
})
describe('.getVersionFromSpec(_)', () => {
let got
let expected
let spec
let before
const versions0 = [
'3.2.30.5',
'3.2.3.5',
'1',
'2',
'3.2.3.4.1',
'1.0.9',
'3.2.3.40.1',
'1.0.2',
'2.0',
'2.10',
'2.9',
'1.0',
'3.2.3.4.2',
'1.1.0',
'3.4.5.4',
'3.4.5.3',
'3.4.5.4.1',
'24.0-rc3',
'24.0-rc2',
'24.0',
'23.3.4',
'23.3.3',
'22.3.4.9.1',
'22.3.4.12.1',
'22.3.4.10.1',
'22.3.4.2',
'main',
'v11.11.0-rc.0-otp-23',
'22.3.4.2.1.0',
'12.1.2.2',
'12.1.2.4',
'12.1.2.0',
'12.1.2.3',
'12.1.2.3.0',
]
let versions = {}
versions0.forEach((version) => {
versions[version] = version
})
it('returns the expected value', async () => {
spec = '1'
expected = '1.1.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '1.0'
expected = '1.0.9'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
before = simulateInput('version-type', 'strict')
spec = '1'
expected = '1'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
before = simulateInput('version-type', 'strict')
spec = '1.0'
expected = '1.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
spec = '2'
expected = '2.10'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '3'
expected = '3.4.5.4.1'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '3.2'
expected = '3.2.30.5'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '>20'
expected = '24.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '24.0'
expected = '24.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
before = simulateInput('version-type', 'strict')
spec = '24.0-rc3'
expected = '24.0-rc3'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
spec = '24.0-rc2'
expected = '24.0-rc2'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '22.3'
expected = '22.3.4.12.1'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '23.3.3'
expected = '23.3.3'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '24'
expected = '24.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '23.3'
expected = '23.3.4'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
before = simulateInput('version-type', 'strict')
spec = 'main'
expected = 'main'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
before = simulateInput('version-type', 'strict')
spec = '22.3.4.2'
expected = '22.3.4.2'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
spec = '22.3.4.2'
expected = '22.3.4.2.1.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '22.3.4.2.1'
expected = '22.3.4.2.1.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '22.3.4.2.1.0'
expected = '22.3.4.2.1.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '12.1.2.3'
expected = '12.1.2.3.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '22.3.4.2.1.0.1'
expected = null
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
before = simulateInput('version-type', 'strict')
spec = '22.3.4.3'
expected = null
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', before)
spec = 'latest'
expected = '24.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
versions = {
'27.0.0-rc3': '27.0.0-rc3',
'27.0.0-rc2': '27.0.0-rc2',
}
spec = 'latest'
expected = '27.0.0-rc3'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
versions = {
'27.0.0-rc3': '27.0.0-rc3',
'27.0.0-rc2': '27.0.0-rc2',
'27.0.0': '27.0.0',
}
spec = 'latest'
expected = '27.0.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
versions = { '25.3.2.8': '25.3.2.8', '25.3.2.12': '25.3.2.12' }
spec = 'latest'
expected = '25.3.2.12'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = 'latest'
expected = 'OTP-26.2.1'
got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-18.04'])
assert.deepStrictEqual(got, expected)
spec = 'latest'
expected = 'OTP-27.0-rc3'
got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-20.04'])
assert.deepStrictEqual(got, expected)
spec = '> 0'
expected = 'OTP-26.2.5'
got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-22.04'])
assert.deepStrictEqual(got, expected)
spec = 'latest'
expected = 'OTP-27.0-rc3'
got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-22.04'])
assert.deepStrictEqual(got, expected)
spec = '> 0'
expected = 'OTP-27.0'
got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-24.04'])
assert.deepStrictEqual(got, expected)
spec = 'latest'
expected = 'OTP-27.0'
got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-24.04'])
assert.deepStrictEqual(got, expected)
spec = '> 0'
expected = 'OTP-29.0.2'
got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-26.04'])
assert.deepStrictEqual(got, expected)
spec = 'latest'
expected = 'OTP-29.0.2'
got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-26.04'])
assert.deepStrictEqual(got, expected)
spec = 'latest'
expected = '27.0-rc3'
got = setupBeam.getVersionFromSpec(spec, matrix.otp.windows)
assert.deepStrictEqual(got, expected)
spec = 'latest'
expected = 'OTP-28.0'
got = setupBeam.getVersionFromSpec(spec, matrix.otp['macos-aarch64'])
assert.deepStrictEqual(got, expected)
spec = 'latest'
expected = 'OTP-28.0'
got = setupBeam.getVersionFromSpec(spec, matrix.otp['macos-x86_64'])
assert.deepStrictEqual(got, expected)
spec = 'latest'
expected = 'v1.16.2'
got = setupBeam.getVersionFromSpec(spec, matrix.elixir)
assert.deepStrictEqual(got, expected)
spec = 'latest'
expected = 'v1.1.0'
got = setupBeam.getVersionFromSpec(spec, matrix.gleam)
assert.deepStrictEqual(got, expected)
spec = 'latest'
expected = '3.27.0'
got = setupBeam.getVersionFromSpec(spec, matrix.rebar3)
assert.deepStrictEqual(got, expected)
})
})
describe('.tool-versions file', () => {
const otpVersion = unsimulateInput('otp-version')
const elixirVersion = unsimulateInput('elixir-version')
const gleamVersion = unsimulateInput('gleam-version')
it('is parsed correctly', async () => {
const erlang = '27.3.4.1'
const elixir = '1.18.4'
const gleam = '1.9.1'
let toolVersions = `# a comment
erlang ref:v${erlang}
elixir ref:${elixir} # comment, with space and ref:
not-gleam 0.23 # not picked up
gleam ${gleam} \n`
const filename = 'test/.tool-versions'
if (process.platform === 'win32') {
// Force \r\n to test in Windows
toolVersions = toolVersions.replace(/\n/g, '\r\n')
}
fs.writeFileSync(filename, toolVersions)
process.env.GITHUB_WORKSPACE = ''
const appVersions = setupBeam.parseVersionFile(filename)
assert.strictEqual(appVersions.get('erlang'), erlang)
assert.strictEqual(appVersions.get('elixir'), elixir)
const absoluteFilename = path.join(os.tmpdir(), '.tool-versions')
fs.writeFileSync(absoluteFilename, toolVersions)
process.env.GITHUB_WORKSPACE = process.cwd()
const absoluteAppVersions = setupBeam.parseVersionFile(absoluteFilename)
assert.strictEqual(absoluteAppVersions.get('erlang'), erlang)
assert.strictEqual(absoluteAppVersions.get('elixir'), elixir)
assert.ok(async () => {
await setupBeam.install('otp', { toolVersion: erlang })
})
assert.ok(async () => {
await setupBeam.install('elixir', { toolVersion: elixir })
})
assert.ok(async () => {
await setupBeam.install('gleam', { toolVersion: gleam })
})
})
simulateInput('otp-version', otpVersion)
simulateInput('elixir-version', elixirVersion)
simulateInput('gleam-version', gleamVersion)
})
describe('mise.toml file', () => {
const otpVersion = unsimulateInput('otp-version')
const elixirVersion = unsimulateInput('elixir-version')
const gleamVersion = unsimulateInput('gleam-version')
it('is parsed correctly', async () => {
const erlang = '27.3.4.1'
const elixir = '1.18.4'
const gleam = '1.9.1'
let miseToml = `[tools]
"erlang" = "${erlang}"
elixir = { version = "${elixir}", postinstall="mix deps.get" } # comment, with space and ref:
"not-gleam" = 0.23 # not picked up
"gleam" = "${gleam}"
[env]
_.file = ".env"\n`
const filename = 'test/mise.toml'
if (process.platform === 'win32') {
// Force \r\n to test in Windows
miseToml = miseToml.replace(/\n/g, '\r\n')
}
fs.writeFileSync(filename, miseToml)
process.env.GITHUB_WORKSPACE = ''
const appVersions = setupBeam.parseVersionFile(filename)
assert.strictEqual(appVersions.get('erlang'), erlang)
assert.strictEqual(appVersions.get('elixir'), elixir)
const absoluteFilename = path.join(os.tmpdir(), 'mise.toml')
fs.writeFileSync(absoluteFilename, miseToml)
process.env.GITHUB_WORKSPACE = process.cwd()
const absoluteAppVersions = setupBeam.parseVersionFile(absoluteFilename)
assert.strictEqual(absoluteAppVersions.get('erlang'), erlang)
assert.strictEqual(absoluteAppVersions.get('elixir'), elixir)
assert.ok(async () => {
await setupBeam.install('otp', { toolVersion: erlang })
})
assert.ok(async () => {
await setupBeam.install('elixir', { toolVersion: elixir })
})
assert.ok(async () => {
await setupBeam.install('gleam', { toolVersion: gleam })
})
})
simulateInput('otp-version', otpVersion)
simulateInput('elixir-version', elixirVersion)
simulateInput('gleam-version', gleamVersion)
})
describe('.get(_)', () => {
it('retries as expected', async () => {
let attempt = 0
const server = http.createServer((req, res) => {
attempt++
if (attempt == 2) {
res.write('correct!')
res.end()
}
})
try {
server.listen(0)
const port = server.address().port
const response = await setupBeam.get(`http://localhost:${port}`, [])
assert.equal(response, 'correct!')
assert.equal(attempt, 2)
} finally {
server.close()
}
})
})
describe("Elixir Mix matcher's", () => {
it('errors are properly matched', async () => {
const [matcher] = problemMatcher.find(
({ owner }) => owner === 'elixir-mixCompileError',
).pattern
const output = '** (CompileError) lib/test.ex:16: undefined function err/0'
const [message, , file, line] = output.match(matcher.regexp)
assert.equal(file, 'lib/test.ex')
assert.equal(line, '16')
assert.equal(message, output)
})
it('warnings are properly matched', async () => {
const [messagePattern, filePattern] = problemMatcher.find(
({ owner }) => owner === 'elixir-mixCompileWarning',
).pattern
const firstOutput =
'warning: variable "err" does not exist and is being expanded to "err()"'
const secondOutput = ' lib/test.ex:16: Test.hello/0'
const [, message] = firstOutput.match(messagePattern.regexp)
assert.equal(
message,
'variable "err" does not exist and is being expanded to "err()"',
)
const [, file, line] = secondOutput.match(filePattern.regexp)
assert.equal(file, 'lib/test.ex')
assert.equal(line, '16')
})
it('failures are properly matched', async () => {
const [messagePattern, filePattern] = problemMatcher.find(
({ owner }) => owner === 'elixir-mixTestFailure',
).pattern
const firstOutput = '1) test throws (TestTest)'
const secondOutput = ' test/test_test.exs:9'
const [, message] = firstOutput.match(messagePattern.regexp)
assert.equal(message, 'test throws (TestTest)')
const [, file, line] = secondOutput.match(filePattern.regexp)
assert.equal(file, 'test/test_test.exs')
assert.equal(line, '9')
})
it('Credo output is properly matched', async () => {
const [messagePattern, filePattern] = problemMatcher.find(
({ owner }) => owner === 'elixir-credoOutputDefault',
).pattern
const firstOutput = '┃ [F] → Function is too complex (CC is 29, max is 9).'
const secondOutput = '┃ lib/test.ex:15:7 #(Test.hello)'
const [, message] = firstOutput.match(messagePattern.regexp)
assert.equal(message, 'Function is too complex (CC is 29, max is 9).')
const [, file, line, column] = secondOutput.match(filePattern.regexp)
assert.equal(file, 'lib/test.ex')
assert.equal(line, '15')
assert.equal(column, '7')
})
})
describe("Erlang matcher's", () => {
it('erlc errors are properly matched', () => {
const [matcher] = erlangProblemMatcher.find(
({ owner }) => owner === 'erlc-error',
).pattern
const output = 'src/mymod.erl:42:5: head mismatch'
const match = output.match(matcher.regexp)
assert.equal(match[1], 'src/mymod.erl')
assert.equal(match[2], '42')
assert.equal(match[3], '5')
assert.equal(match[4], 'head mismatch')
})
it('erlc errors do not match warnings', () => {
const [matcher] = erlangProblemMatcher.find(
({ owner }) => owner === 'erlc-error',
).pattern
const output = 'src/mymod.erl:42:5: Warning: variable X is unused'
const match = output.match(matcher.regexp)
assert.equal(match, null)
})
it('erlc warnings are properly matched', () => {
const [matcher] = erlangProblemMatcher.find(
({ owner }) => owner === 'erlc-warning',
).pattern
const output = 'src/mymod.erl:10:3: Warning: variable X is unused'
const match = output.match(matcher.regexp)
assert.equal(match[1], 'src/mymod.erl')
assert.equal(match[2], '10')
assert.equal(match[3], '3')
assert.equal(match[4], 'variable X is unused')
})
it('dialyzer warnings are matched via erlc patterns', () => {
const [matcher] = erlangProblemMatcher.find(
({ owner }) => owner === 'erlc-warning',
).pattern
const output =
'src/mymod.erl:25:1: warning: Function foo/0 has no local return'
const match = output.match(matcher.regexp)
assert.equal(match[1], 'src/mymod.erl')
assert.equal(match[2], '25')
assert.equal(match[3], '1')
assert.equal(match[4], 'Function foo/0 has no local return')
})
it('CT failures are properly matched', () => {
const [filePattern, reasonPattern] = erlangProblemMatcher.find(
({ owner }) => owner === 'ct-failure',
).pattern
const firstOutput = 'test/mymod_SUITE.erl failed on line 55'
const secondOutput = 'Reason: {badmatch,{error,timeout}}'
const fileMatch = firstOutput.match(filePattern.regexp)
assert.equal(fileMatch[1], 'test/mymod_SUITE.erl')
assert.equal(fileMatch[2], '55')
const reasonMatch = secondOutput.match(reasonPattern.regexp)
assert.equal(reasonMatch[1], '{badmatch,{error,timeout}}')
})
it('EUnit failures are properly matched', () => {
const [headerPattern, funcPattern, callPattern, errorPattern] =
erlangProblemMatcher.find(
({ owner }) => owner === 'eunit-failure',
).pattern
const header = 'mymod_test: hello_test....*failed*'
const func =
'in function mymod_test:hello_test/0 (test/mymod_test.erl, line 12)'
const call = 'in call from mymod_test:hello_test/0'
const error = '**error:{badmatch,false}'
assert.ok(header.match(headerPattern.regexp))
const funcMatch = func.match(funcPattern.regexp)
assert.equal(funcMatch[1], 'test/mymod_test.erl')
assert.equal(funcMatch[2], '12')
assert.ok(call.match(callPattern.regexp))
const errorMatch = error.match(errorPattern.regexp)
assert.equal(errorMatch[1], '{badmatch,false}')
})
})
function unsimulateInput(key) {
return simulateInput(key, '')
}
function simulateInput(key, value0, opts) {
const { multiline } = opts || {}
const before = process.env[input(key, opts)]
let value = value0
if (multiline) {
if (value.indexOf(', ') !== -1) {
value = value0.replace(/, /g, '\n')
} else {
value = value0.replace(/\n/g, ', ')
}
}
process.env[input(key)] = value
return before
}
function input(key) {
return `INPUT_${key.replace(/ /g, '_').toUpperCase()}`
}