Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 96a6f16

Browse filesBrowse files
committed
Add tests & test dependencies
1 parent c7cfc3a commit 96a6f16
Copy full SHA for 96a6f16

File tree

Expand file treeCollapse file tree

5 files changed

+14664
-6294
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

5 files changed

+14664
-6294
lines changed
Open diff view settings
Collapse file

‎__tests__/download.test.ts‎

Copy file name to clipboard
+226Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
import * as core from '@actions/core'
2+
import * as github from '@actions/github'
3+
import * as os from 'os'
4+
import artifact, {ArtifactNotFoundError} from '@actions/artifact'
5+
import {run} from '../src/download-artifact'
6+
import {Inputs} from '../src/constants'
7+
8+
const fixtures = {
9+
artifactName: 'artifact-name',
10+
rootDirectory: '/some/artifact/path',
11+
filesToUpload: [
12+
'/some/artifact/path/file1.txt',
13+
'/some/artifact/path/file2.txt'
14+
]
15+
}
16+
17+
jest.mock('@actions/github', () => ({
18+
context: {
19+
repo: {
20+
owner: 'actions',
21+
repo: 'toolkit'
22+
},
23+
runId: 123,
24+
serverUrl: 'https://github.com'
25+
}
26+
}))
27+
28+
jest.mock('@actions/core')
29+
30+
/* eslint-disable no-unused-vars */
31+
const mockInputs = (overrides?: Partial<{[K in Inputs]?: any}>) => {
32+
const inputs = {
33+
[Inputs.Name]: 'artifact-name',
34+
[Inputs.Path]: '/some/artifact/path',
35+
[Inputs.GitHubToken]: 'warn',
36+
[Inputs.Repository]: 'owner/some-repository',
37+
[Inputs.RunID]: 'some-run-id',
38+
[Inputs.Pattern]: 'some-pattern',
39+
...overrides
40+
}
41+
42+
;(core.getInput as jest.Mock).mockImplementation((name: string) => {
43+
return inputs[name]
44+
})
45+
;(core.getBooleanInput as jest.Mock).mockImplementation((name: string) => {
46+
return inputs[name]
47+
})
48+
49+
return inputs
50+
}
51+
52+
describe('download', () => {
53+
beforeEach(async () => {
54+
mockInputs()
55+
jest.clearAllMocks()
56+
57+
// Mock artifact client methods
58+
jest.spyOn(artifact, 'listArtifacts').mockImplementation(() =>
59+
Promise.resolve({ artifacts: [] })
60+
)
61+
jest.spyOn(artifact, 'getArtifact').mockImplementation((name) => {
62+
throw new ArtifactNotFoundError(`Artifact '${name}' not found`)
63+
})
64+
jest.spyOn(artifact, 'downloadArtifact').mockImplementation(() =>
65+
Promise.resolve({ digestMismatch: false })
66+
)
67+
})
68+
69+
test('downloads a single artifact by name', async () => {
70+
const mockArtifact = {
71+
id: 123,
72+
name: 'artifact-name',
73+
size: 1024,
74+
digest: 'abc123'
75+
}
76+
77+
jest.spyOn(artifact, 'getArtifact').mockImplementation(() =>
78+
Promise.resolve({ artifact: mockArtifact })
79+
)
80+
81+
await run()
82+
83+
expect(artifact.downloadArtifact).toHaveBeenCalledWith(
84+
mockArtifact.id,
85+
expect.objectContaining({
86+
expectedHash: mockArtifact.digest
87+
})
88+
)
89+
expect(core.info).toHaveBeenCalledWith('Total of 1 artifact(s) downloaded')
90+
})
91+
92+
test('downloads multiple artifacts when no name or pattern provided', async () => {
93+
jest.clearAllMocks()
94+
mockInputs({
95+
[Inputs.Name]: '',
96+
[Inputs.Pattern]: ''
97+
})
98+
99+
const mockArtifacts = [
100+
{ id: 123, name: 'artifact1', size: 1024, digest: 'abc123' },
101+
{ id: 456, name: 'artifact2', size: 2048, digest: 'def456' }
102+
]
103+
104+
// Set up artifact mock after clearing mocks
105+
jest.spyOn(artifact, 'listArtifacts').mockImplementation(() =>
106+
Promise.resolve({ artifacts: mockArtifacts })
107+
)
108+
109+
// Reset downloadArtifact mock as well
110+
jest.spyOn(artifact, 'downloadArtifact').mockImplementation(() =>
111+
Promise.resolve({ digestMismatch: false })
112+
)
113+
114+
await run()
115+
116+
expect(core.info).toHaveBeenCalledWith('No input name or pattern filtered specified, downloading all artifacts')
117+
118+
expect(core.info).toHaveBeenCalledWith('Total of 2 artifact(s) downloaded')
119+
expect(artifact.downloadArtifact).toHaveBeenCalledTimes(2)
120+
})
121+
122+
test('sets download path output even when no artifacts are found', async () => {
123+
mockInputs({ [Inputs.Name]: '' })
124+
125+
await run()
126+
127+
expect(core.setOutput).toHaveBeenCalledWith(
128+
'download-path',
129+
expect.any(String)
130+
)
131+
132+
expect(core.info).toHaveBeenCalledWith(
133+
'Download artifact has finished successfully'
134+
)
135+
136+
expect(core.info).toHaveBeenCalledWith(
137+
'Total of 0 artifact(s) downloaded'
138+
)
139+
})
140+
141+
test('filters artifacts by pattern', async () => {
142+
const mockArtifacts = [
143+
{ id: 123, name: 'test-artifact', size: 1024, digest: 'abc123' },
144+
{ id: 456, name: 'prod-artifact', size: 2048, digest: 'def456' }
145+
]
146+
147+
jest.spyOn(artifact, 'listArtifacts').mockImplementation(() =>
148+
Promise.resolve({ artifacts: mockArtifacts })
149+
)
150+
151+
mockInputs({
152+
[Inputs.Name]: '',
153+
[Inputs.Pattern]: 'test-*'
154+
})
155+
156+
await run()
157+
158+
expect(artifact.downloadArtifact).toHaveBeenCalledTimes(1)
159+
expect(artifact.downloadArtifact).toHaveBeenCalledWith(
160+
123,
161+
expect.anything()
162+
)
163+
})
164+
165+
test('uses token and repository information when provided', async () => {
166+
const token = 'ghp_testtoken123'
167+
168+
mockInputs({
169+
[Inputs.Name]: '',
170+
[Inputs.GitHubToken]: token,
171+
[Inputs.Repository]: 'myorg/myrepo',
172+
[Inputs.RunID]: '789'
173+
})
174+
175+
jest.spyOn(artifact, 'listArtifacts').mockImplementation(() =>
176+
Promise.resolve({ artifacts: [] })
177+
)
178+
179+
await run()
180+
181+
expect(artifact.listArtifacts).toHaveBeenCalledWith(
182+
expect.objectContaining({
183+
findBy: {
184+
token,
185+
workflowRunId: 789,
186+
repositoryName: 'myrepo',
187+
repositoryOwner: 'myorg'
188+
}
189+
})
190+
)
191+
})
192+
193+
test('throws error when repository format is invalid', async () => {
194+
mockInputs({
195+
[Inputs.GitHubToken]: 'some-token',
196+
[Inputs.Repository]: 'invalid-format' // Missing the owner/repo format
197+
})
198+
199+
await expect(run()).rejects.toThrow(
200+
"Invalid repository: 'invalid-format'. Must be in format owner/repo"
201+
)
202+
})
203+
204+
test('warns when digest validation fails', async () => {
205+
const mockArtifact = {
206+
id: 123,
207+
name: 'corrupted-artifact',
208+
size: 1024,
209+
digest: 'abc123'
210+
}
211+
212+
jest.spyOn(artifact, 'getArtifact').mockImplementation(() =>
213+
Promise.resolve({ artifact: mockArtifact })
214+
)
215+
216+
jest.spyOn(artifact, 'downloadArtifact').mockImplementation(() =>
217+
Promise.resolve({ digestMismatch: true })
218+
)
219+
220+
await run()
221+
222+
expect(core.warning).toHaveBeenCalledWith(
223+
expect.stringContaining('digest validation failed')
224+
)
225+
})
226+
})
Collapse file

‎jest.config.ts‎

Copy file name to clipboard
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module.exports = {
2+
clearMocks: true,
3+
moduleFileExtensions: ['js', 'ts'],
4+
roots: ['<rootDir>'],
5+
testEnvironment: 'node',
6+
testMatch: ['**/*.test.ts'],
7+
testRunner: 'jest-circus/runner',
8+
transform: {
9+
'^.+\\.ts$': 'ts-jest'
10+
},
11+
verbose: true
12+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.