Skip to content

Commit 52899c8

Browse files
authored
Merge pull request #504 from actions/robherley/reorganize
Reorganize upload code in prep for merge logic & add more tests
2 parents 694cdab + da58a3f commit 52899c8

14 files changed

+593
-256
lines changed

.github/workflows/check-dist.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
run: npm ci
3535

3636
- name: Rebuild the dist/ directory
37-
run: npm run build
37+
run: npm run release
3838

3939
- name: Compare the expected and actual dist/ directories
4040
run: |

__tests__/search.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as core from '@actions/core'
22
import * as path from 'path'
33
import * as io from '@actions/io'
44
import {promises as fs} from 'fs'
5-
import {findFilesToUpload} from '../src/search'
5+
import {findFilesToUpload} from '../src/shared/search'
66

77
const root = path.join(__dirname, '_temp', 'search')
88
const searchItem1Path = path.join(

__tests__/upload.test.ts

+231
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
import * as core from '@actions/core'
2+
import * as github from '@actions/github'
3+
import artifact, {ArtifactNotFoundError} from '@actions/artifact'
4+
import {run} from '../src/upload/upload-artifact'
5+
import {Inputs} from '../src/upload/constants'
6+
import * as search from '../src/shared/search'
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.IfNoFilesFound]: 'warn',
36+
[Inputs.RetentionDays]: 0,
37+
[Inputs.CompressionLevel]: 6,
38+
[Inputs.Overwrite]: false,
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('upload', () => {
53+
beforeEach(async () => {
54+
mockInputs()
55+
56+
jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({
57+
filesToUpload: fixtures.filesToUpload,
58+
rootDirectory: fixtures.rootDirectory
59+
})
60+
61+
jest.spyOn(artifact, 'uploadArtifact').mockResolvedValue({
62+
size: 123,
63+
id: 1337
64+
})
65+
})
66+
67+
it('uploads a single file', async () => {
68+
jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({
69+
filesToUpload: [fixtures.filesToUpload[0]],
70+
rootDirectory: fixtures.rootDirectory
71+
})
72+
73+
await run()
74+
75+
expect(artifact.uploadArtifact).toHaveBeenCalledWith(
76+
fixtures.artifactName,
77+
[fixtures.filesToUpload[0]],
78+
fixtures.rootDirectory,
79+
{compressionLevel: 6}
80+
)
81+
})
82+
83+
it('uploads multiple files', async () => {
84+
await run()
85+
86+
expect(artifact.uploadArtifact).toHaveBeenCalledWith(
87+
fixtures.artifactName,
88+
fixtures.filesToUpload,
89+
fixtures.rootDirectory,
90+
{compressionLevel: 6}
91+
)
92+
})
93+
94+
it('sets outputs', async () => {
95+
await run()
96+
97+
expect(core.setOutput).toHaveBeenCalledWith('artifact-id', 1337)
98+
expect(core.setOutput).toHaveBeenCalledWith(
99+
'artifact-url',
100+
`${github.context.serverUrl}/${github.context.repo.owner}/${
101+
github.context.repo.repo
102+
}/actions/runs/${github.context.runId}/artifacts/${1337}`
103+
)
104+
})
105+
106+
it('supports custom compression level', async () => {
107+
mockInputs({
108+
[Inputs.CompressionLevel]: 2
109+
})
110+
111+
await run()
112+
113+
expect(artifact.uploadArtifact).toHaveBeenCalledWith(
114+
fixtures.artifactName,
115+
fixtures.filesToUpload,
116+
fixtures.rootDirectory,
117+
{compressionLevel: 2}
118+
)
119+
})
120+
121+
it('supports custom retention days', async () => {
122+
mockInputs({
123+
[Inputs.RetentionDays]: 7
124+
})
125+
126+
await run()
127+
128+
expect(artifact.uploadArtifact).toHaveBeenCalledWith(
129+
fixtures.artifactName,
130+
fixtures.filesToUpload,
131+
fixtures.rootDirectory,
132+
{retentionDays: 7, compressionLevel: 6}
133+
)
134+
})
135+
136+
it('supports warn if-no-files-found', async () => {
137+
mockInputs({
138+
[Inputs.IfNoFilesFound]: 'warn'
139+
})
140+
141+
jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({
142+
filesToUpload: [],
143+
rootDirectory: fixtures.rootDirectory
144+
})
145+
146+
await run()
147+
148+
expect(core.warning).toHaveBeenCalledWith(
149+
`No files were found with the provided path: ${fixtures.rootDirectory}. No artifacts will be uploaded.`
150+
)
151+
})
152+
153+
it('supports error if-no-files-found', async () => {
154+
mockInputs({
155+
[Inputs.IfNoFilesFound]: 'error'
156+
})
157+
158+
jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({
159+
filesToUpload: [],
160+
rootDirectory: fixtures.rootDirectory
161+
})
162+
163+
await run()
164+
165+
expect(core.setFailed).toHaveBeenCalledWith(
166+
`No files were found with the provided path: ${fixtures.rootDirectory}. No artifacts will be uploaded.`
167+
)
168+
})
169+
170+
it('supports ignore if-no-files-found', async () => {
171+
mockInputs({
172+
[Inputs.IfNoFilesFound]: 'ignore'
173+
})
174+
175+
jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({
176+
filesToUpload: [],
177+
rootDirectory: fixtures.rootDirectory
178+
})
179+
180+
await run()
181+
182+
expect(core.info).toHaveBeenCalledWith(
183+
`No files were found with the provided path: ${fixtures.rootDirectory}. No artifacts will be uploaded.`
184+
)
185+
})
186+
187+
it('supports overwrite', async () => {
188+
mockInputs({
189+
[Inputs.Overwrite]: true
190+
})
191+
192+
jest.spyOn(artifact, 'deleteArtifact').mockResolvedValue({
193+
id: 1337
194+
})
195+
196+
await run()
197+
198+
expect(artifact.uploadArtifact).toHaveBeenCalledWith(
199+
fixtures.artifactName,
200+
fixtures.filesToUpload,
201+
fixtures.rootDirectory,
202+
{compressionLevel: 6}
203+
)
204+
205+
expect(artifact.deleteArtifact).toHaveBeenCalledWith(fixtures.artifactName)
206+
})
207+
208+
it('supports overwrite and continues if not found', async () => {
209+
mockInputs({
210+
[Inputs.Overwrite]: true
211+
})
212+
213+
jest
214+
.spyOn(artifact, 'deleteArtifact')
215+
.mockRejectedValue(new ArtifactNotFoundError('not found'))
216+
217+
await run()
218+
219+
expect(artifact.uploadArtifact).toHaveBeenCalledWith(
220+
fixtures.artifactName,
221+
fixtures.filesToUpload,
222+
fixtures.rootDirectory,
223+
{compressionLevel: 6}
224+
)
225+
226+
expect(artifact.deleteArtifact).toHaveBeenCalledWith(fixtures.artifactName)
227+
expect(core.debug).toHaveBeenCalledWith(
228+
`Skipping deletion of '${fixtures.artifactName}', it does not exist`
229+
)
230+
})
231+
})

action.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,4 @@ outputs:
5858
Common uses cases for such a download URL can be adding download links to artifacts in descriptions or comments on pull requests or issues.
5959
runs:
6060
using: 'node20'
61-
main: 'dist/index.js'
61+
main: 'dist/upload/index.js'

0 commit comments

Comments
 (0)