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 6890984

Browse filesBrowse files
authored
Merge branch 'main' into main
2 parents f9415c0 + 76a6eb5 commit 6890984
Copy full SHA for 6890984

File tree

Expand file treeCollapse file tree

7 files changed

+14671
-6302
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

7 files changed

+14671
-6302
lines changed
Open diff view settings
Collapse file

‎__tests__/download.test.ts‎

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

‎dist/index.js‎

Copy file name to clipboardExpand all lines: dist/index.js
+5-4Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118760,7 +118760,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
118760118760
return (mod && mod.__esModule) ? mod : { "default": mod };
118761118761
};
118762118762
Object.defineProperty(exports, "__esModule", ({ value: true }));
118763-
exports.chunk = void 0;
118763+
exports.run = exports.chunk = void 0;
118764118764
const os = __importStar(__nccwpck_require__(22037));
118765118765
const path = __importStar(__nccwpck_require__(71017));
118766118766
const core = __importStar(__nccwpck_require__(42186));
@@ -118857,12 +118857,13 @@ function run() {
118857118857
core.warning(`Artifact '${artifactName}' digest validation failed. Please verify the integrity of the artifact.`);
118858118858
}
118859118859
}
118860+
core.info(`Total of ${artifacts.length} artifact(s) downloaded`);
118861+
core.setOutput(constants_1.Outputs.DownloadPath, resolvedPath);
118862+
core.info('Download artifact has finished successfully');
118860118863
}
118861-
core.info(`Total of ${artifacts.length} artifact(s) downloaded`);
118862-
core.setOutput(constants_1.Outputs.DownloadPath, resolvedPath);
118863-
core.info('Download artifact has finished successfully');
118864118864
});
118865118865
}
118866+
exports.run = run;
118866118867
run().catch(err => core.setFailed(`Unable to download artifact(s): ${err.message}`));
118867118868

118868118869

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.