Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit b907aa1

Browse files
committed
test(trdl-actions): cover install/src/action
Signed-off-by: Alexandr Zaytsev <[email protected]>
1 parent 04a60e0 commit b907aa1

File tree

11 files changed

+205
-32
lines changed

11 files changed

+205
-32
lines changed

install/src/action.test.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { describe, jest, it, beforeEach } from '@jest/globals'
2+
import * as core from '../../test/mocks/core'
3+
import * as toolCache from '../../test/mocks/tool-cache'
4+
import * as fs from '../../test/mocks/fs'
5+
import { trdlCli } from '../../test/mocks/trdl-cli'
6+
import { gpgCli } from '../../test/mocks/gpg-cli'
7+
8+
// Mocks should be declared before the module being tested is imported.
9+
jest.unstable_mockModule('@actions/core', () => core)
10+
jest.unstable_mockModule('@actions/tool-cache', () => toolCache)
11+
jest.unstable_mockModule('node:fs', () => fs)
12+
13+
// The module being tested should be imported dynamically. This ensures that the
14+
// mocks are used in place of any actual dependencies.
15+
const { parseInputs, getOptions, formatDownloadUrls, Do } = await import('./action')
16+
17+
describe('install/action.ts', function () {
18+
describe('parseInputs', function () {
19+
it('should work w/o required fields', function () {
20+
core.getInput.mockReturnValue('')
21+
expect(parseInputs()).toEqual({})
22+
})
23+
it('should work with all fields', function () {
24+
const channel = 'some channel'
25+
const version = 'some version'
26+
core.getInput.mockReturnValueOnce(channel)
27+
core.getInput.mockReturnValueOnce(version)
28+
expect(parseInputs()).toEqual({ channel, version })
29+
})
30+
})
31+
describe('getOptions', function () {
32+
const defaults = {
33+
repo: 'trdl',
34+
group: '0',
35+
channel: 'stable'
36+
}
37+
it('should work w/o required inputs', async function () {
38+
const opts = await getOptions({}, defaults)
39+
expect(opts).toHaveProperty('channel', defaults.channel)
40+
expect(opts).toHaveProperty('version')
41+
expect(opts.version).toMatch(/[0-9.]+/)
42+
})
43+
it('should work with all inputs', async function () {
44+
const inputs = {
45+
channel: 'some channel',
46+
version: 'some version'
47+
}
48+
const opts = await getOptions(inputs, defaults)
49+
expect(opts).toEqual(inputs)
50+
})
51+
})
52+
describe('formatDownloadUrls', function () {
53+
it('should work for platform=linux and arch=x64', function () {
54+
const version = '1.2.3'
55+
const plat = 'linux'
56+
const arch = 'amd64'
57+
const result = formatDownloadUrls(version)
58+
expect(result).toEqual([
59+
`https://tuf.trdl.dev/targets/releases/${version}/${plat}-${arch}/bin/trdl`,
60+
`https://tuf.trdl.dev/targets/signatures/${version}/${plat}-${arch}/bin/trdl.sig`,
61+
`https://trdl.dev/trdl-client.asc`
62+
])
63+
})
64+
})
65+
describe('Do', function () {
66+
const inputs = {
67+
channel: 'test-channel',
68+
version: 'test-version'
69+
}
70+
const defaults = {
71+
repo: 'trdl',
72+
group: '0',
73+
channel: 'stable'
74+
}
75+
beforeEach(function () {
76+
trdlCli.defaults.mockReturnValue(defaults)
77+
})
78+
it('should not install trdl if tool cache is found', async function () {
79+
const someCache = '/path/to/tool'
80+
toolCache.find.mockReturnValueOnce(someCache)
81+
82+
await Do(trdlCli, gpgCli, inputs)
83+
84+
expect(toolCache.find).toHaveBeenCalledWith(defaults.repo, inputs.version)
85+
expect(trdlCli.mustExist).toHaveBeenCalled()
86+
expect(trdlCli.update).toHaveBeenCalledWith(defaults)
87+
})
88+
it('should install trdl if tool cache is not found', async function () {
89+
const binPath = 'bin path'
90+
const sigPath = 'sig path'
91+
const ascPath = 'asc path'
92+
93+
toolCache.downloadTool.mockResolvedValueOnce(binPath)
94+
toolCache.downloadTool.mockResolvedValueOnce(sigPath)
95+
toolCache.downloadTool.mockResolvedValueOnce(ascPath)
96+
97+
const installedPath = '/tmp/installed/path'
98+
toolCache.cacheFile.mockResolvedValueOnce(installedPath)
99+
100+
await Do(trdlCli, gpgCli, inputs)
101+
102+
expect(toolCache.find).toHaveBeenCalledWith(defaults.repo, inputs.version)
103+
expect(gpgCli.mustGnuGP).toHaveBeenCalled()
104+
expect(gpgCli.import).toHaveBeenCalledWith(ascPath)
105+
expect(gpgCli.verify).toHaveBeenCalledWith(sigPath, binPath)
106+
expect(toolCache.cacheFile).toHaveBeenCalledWith(binPath, defaults.repo, defaults.repo, inputs.version)
107+
expect(fs.chmodSync).toHaveBeenCalledWith(installedPath, 0o755)
108+
expect(core.addPath).toHaveBeenCalledWith(installedPath)
109+
})
110+
})
111+
})

install/src/action.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { downloadTool, find, cacheFile } from '@actions/tool-cache'
44
import { chmodSync } from 'node:fs'
55
import { GpgCli } from '../../lib/gpg-cli'
66
import { Defaults, TrdlCli } from '../../lib/trdl-cli'
7-
import { optionalToObject } from '../../lib/optional'
87
import { format } from 'util'
98

109
interface inputs {
@@ -17,10 +16,12 @@ interface options {
1716
version: string
1817
}
1918

20-
function parseInputs(): inputs {
19+
export function parseInputs(): inputs {
20+
const channel = getInput('channel')
21+
const version = getInput('version')
2122
return {
22-
...optionalToObject('channel', getInput('channel')), // optional field
23-
...optionalToObject('version', getInput('version')) // optional field
23+
...(channel !== '' ? { channel } : {}), // optional field
24+
...(version !== '' ? { version } : {}) // optional field
2425
}
2526
}
2627

@@ -31,17 +32,17 @@ async function fetchVersion(group: string, channel: string): Promise<string> {
3132
return version.trim()
3233
}
3334

34-
async function getOptions(inputs: inputs, defaults: Defaults): Promise<options> {
35-
const channel = inputs?.channel || defaults.channel
36-
const version = inputs?.version || await fetchVersion(defaults.group, defaults.channel) // prettier-ignore
35+
export async function getOptions(inputs: inputs, defaults: Defaults): Promise<options> {
36+
const channel = inputs.channel ?? defaults.channel
37+
const version = inputs.version ?? await fetchVersion(defaults.group, defaults.channel) // prettier-ignore
3738

3839
return {
3940
channel,
4041
version
4142
}
4243
}
4344

44-
function formatDownloadUrls(version: string): string[] {
45+
export function formatDownloadUrls(version: string): string[] {
4546
// https://github.com/actions/toolkit/blob/main/packages/core/README.md#platform-helper
4647
const plat = translateNodeJSPlatformToTrdlPlatform(platform.platform)
4748
const arch = translateNodeJSArchToTrdlArch(platform.arch)
@@ -100,12 +101,13 @@ async function installTrdl(toolName: string, toolVersion: string, binPath: strin
100101

101102
export async function Run(): Promise<void> {
102103
const trdlCli = new TrdlCli()
104+
const gpgCli = new GpgCli()
103105
const inputs = parseInputs()
104106

105-
await Do(trdlCli, inputs)
107+
await Do(trdlCli, gpgCli, inputs)
106108
}
107109

108-
export async function Do(trdlCli: TrdlCli, inputs: inputs): Promise<void> {
110+
export async function Do(trdlCli: TrdlCli, gpgCli: GpgCli, inputs: inputs): Promise<void> {
109111
startGroup('Install or self-update trdl.')
110112
debug(format(`parsed inputs=%o`, inputs))
111113

@@ -128,7 +130,6 @@ export async function Do(trdlCli: TrdlCli, inputs: inputs): Promise<void> {
128130
return
129131
}
130132

131-
const gpgCli = new GpgCli()
132133
await gpgCli.mustGnuGP()
133134

134135
const [binUrl, sigUrl, ascUrl] = formatDownloadUrls(options.version)

lib/optional.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.

lib/trdl-cli.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { which } from '@actions/io'
22
import { execOutput } from './exec'
3-
import { optionalToArray, optionalToObject } from './optional'
43

54
export class TrdlCli {
6-
private readonly name: string
5+
readonly name: string
76

87
constructor() {
98
this.name = 'trdl'
@@ -35,7 +34,8 @@ export class TrdlCli {
3534
async update(args: UpdateArgs, opts?: UpdateOptions) {
3635
const { repo, group, channel } = args
3736
const env = { ...(process.env as execOptionsEnvs), ...(opts && toUpdateEnvs(opts)) }
38-
await execOutput(this.name, ['update', repo, group, ...optionalToArray(channel)], { env })
37+
const channelOpt = channel !== undefined ? [channel] : [] // optional field
38+
await execOutput(this.name, ['update', repo, group, ...channelOpt], { env })
3939
}
4040

4141
async binPath(args: UpdateArgs): Promise<string> {
@@ -44,7 +44,8 @@ export class TrdlCli {
4444
failOnStdErr: false,
4545
ignoreReturnCode: true
4646
}
47-
const { stdout } = await execOutput(this.name, ['bin-path', repo, group, ...optionalToArray(channel)], execOpts)
47+
const channelOpt = channel !== undefined ? [channel] : [] // optional field
48+
const { stdout } = await execOutput(this.name, ['bin-path', repo, group, ...channelOpt], execOpts)
4849
return stdout.join('')
4950
}
5051

@@ -92,7 +93,7 @@ function parseLineToItem(line: string): ListItem {
9293
name,
9394
url,
9495
default: default_,
95-
...optionalToObject('channel', channel)
96+
...(channel !== undefined ? { channel } : {}) // optional field
9697
}
9798
}
9899

setup-app/src/action.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { parsePresetInput } from './preset'
22
import { TrdlCli } from '../../lib/trdl-cli'
3+
import { GpgCli } from '../../lib/gpg-cli'
34
import { Do as DoInstall } from '../../install/src/action'
45
import { Do as DoAdd } from './add'
56
import { Do as DoUse } from './use'
67

78
export async function Run(): Promise<void> {
89
const p = parsePresetInput()
9-
const cli = new TrdlCli()
10+
const trdlCli = new TrdlCli()
11+
const gpgCli = new GpgCli()
1012

11-
await DoInstall(cli, {})
12-
await DoAdd(cli, p)
13-
await DoUse(cli, p)
13+
await DoInstall(trdlCli, gpgCli, {})
14+
await DoAdd(trdlCli, p)
15+
await DoUse(trdlCli, p)
1416
}

setup-app/src/use.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { addPath, debug, endGroup, exportVariable, getBooleanInput, getInput, info, startGroup } from '@actions/core'
22
import { TrdlCli, UpdateArgs } from '../../lib/trdl-cli'
33
import { getUpdateArgs, preset } from './preset'
4-
import { optionalToObject } from '../../lib/optional'
54
import { format } from 'util'
65
import slugify from 'slugify'
76

@@ -15,11 +14,12 @@ interface envVar {
1514
}
1615

1716
function parseInputs(required: boolean): inputs {
17+
const channel = getInput('channel')
1818
return {
1919
force: getBooleanInput('force', { required }),
2020
repo: getInput('repo', { required }),
2121
group: getInput('group', { required }),
22-
...optionalToObject('channel', getInput('channel')) // optional field
22+
...(channel !== '' ? { channel } : {}) // optional field
2323
}
2424
}
2525

@@ -28,7 +28,7 @@ function mapInputsToCmdArgs(inputs: inputs): UpdateArgs {
2828
return {
2929
repo,
3030
group,
31-
...optionalToObject('channel', channel) // optional field
31+
...(channel !== undefined ? { channel } : {}) // optional field
3232
}
3333
}
3434

test/mocks/core.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { jest } from '@jest/globals'
2+
import type * as core from '@actions/core'
3+
4+
export const debug = jest.fn<typeof core.debug>()
5+
// export const error = jest.fn<typeof core.error>()
6+
export const info = jest.fn<typeof core.info>()
7+
export const getInput = jest.fn<typeof core.getInput>()
8+
export const getBooleanInput = jest.fn<typeof core.getBooleanInput>()
9+
// export const setOutput = jest.fn<typeof core.setOutput>()
10+
export const setFailed = jest.fn<typeof core.setFailed>()
11+
export const addPath = jest.fn<typeof core.addPath>()
12+
export const startGroup = jest.fn<typeof core.startGroup>()
13+
export const endGroup = jest.fn<typeof core.endGroup>()
14+
15+
export const platform = Object.create(
16+
{},
17+
{
18+
platform: {
19+
get: jest.fn().mockReturnValue('linux')
20+
},
21+
arch: {
22+
get: jest.fn().mockReturnValue('x64')
23+
},
24+
isWindows: {
25+
get: jest.fn().mockReturnValue(false)
26+
}
27+
}
28+
)

test/mocks/fs.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { jest } from '@jest/globals'
2+
import type * as fs from 'node:fs'
3+
4+
export const chmodSync = jest.fn<typeof fs.chmodSync>()

test/mocks/gpg-cli.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { GpgCli } from '../../lib/gpg-cli'
2+
import { jest } from '@jest/globals'
3+
4+
// instance
5+
const cli = new GpgCli()
6+
7+
export const gpgCli = {
8+
name: cli.name,
9+
mustGnuGP: jest.fn<typeof cli.mustGnuGP>(),
10+
import: jest.fn<typeof cli.import>(),
11+
verify: jest.fn<typeof cli.verify>(),
12+
help: jest.fn<typeof cli.help>()
13+
}

test/mocks/tool-cache.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { jest } from '@jest/globals'
2+
import type * as toolCache from '@actions/tool-cache'
3+
4+
export const downloadTool = jest.fn<typeof toolCache.downloadTool>()
5+
export const find = jest.fn<typeof toolCache.find>()
6+
export const cacheFile = jest.fn<typeof toolCache.cacheFile>()

0 commit comments

Comments
 (0)