mirror of
				https://github.com/actions/download-artifact.git
				synced 2025-10-31 16:23:41 +00:00 
			
		
		
		
	
						commit
						76a6eb5cbc
					
				
							
								
								
									
										224
									
								
								__tests__/download.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								__tests__/download.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,224 @@ | |||||||
|  | import * as core from '@actions/core' | ||||||
|  | import artifact, {ArtifactNotFoundError} from '@actions/artifact' | ||||||
|  | import {run} from '../src/download-artifact' | ||||||
|  | import {Inputs} from '../src/constants' | ||||||
|  | 
 | ||||||
|  | jest.mock('@actions/github', () => ({ | ||||||
|  |   context: { | ||||||
|  |     repo: { | ||||||
|  |       owner: 'actions', | ||||||
|  |       repo: 'toolkit' | ||||||
|  |     }, | ||||||
|  |     runId: 123, | ||||||
|  |     serverUrl: 'https://github.com' | ||||||
|  |   } | ||||||
|  | })) | ||||||
|  | 
 | ||||||
|  | jest.mock('@actions/core') | ||||||
|  | 
 | ||||||
|  | /* eslint-disable no-unused-vars */ /* eslint-disable  @typescript-eslint/no-explicit-any */ | ||||||
|  | const mockInputs = (overrides?: Partial<{[K in Inputs]?: any}>) => { | ||||||
|  |   const inputs = { | ||||||
|  |     [Inputs.Name]: 'artifact-name', | ||||||
|  |     [Inputs.Path]: '/some/artifact/path', | ||||||
|  |     [Inputs.GitHubToken]: 'warn', | ||||||
|  |     [Inputs.Repository]: 'owner/some-repository', | ||||||
|  |     [Inputs.RunID]: 'some-run-id', | ||||||
|  |     [Inputs.Pattern]: 'some-pattern', | ||||||
|  |     ...overrides | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   ;(core.getInput as jest.Mock).mockImplementation((name: string) => { | ||||||
|  |     return inputs[name] | ||||||
|  |   }) | ||||||
|  |   ;(core.getBooleanInput as jest.Mock).mockImplementation((name: string) => { | ||||||
|  |     return inputs[name] | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   return inputs | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | describe('download', () => { | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     mockInputs() | ||||||
|  |     jest.clearAllMocks() | ||||||
|  | 
 | ||||||
|  |     // Mock artifact client methods
 | ||||||
|  |     jest | ||||||
|  |       .spyOn(artifact, 'listArtifacts') | ||||||
|  |       .mockImplementation(() => Promise.resolve({artifacts: []})) | ||||||
|  |     jest.spyOn(artifact, 'getArtifact').mockImplementation(name => { | ||||||
|  |       throw new ArtifactNotFoundError(`Artifact '${name}' not found`) | ||||||
|  |     }) | ||||||
|  |     jest | ||||||
|  |       .spyOn(artifact, 'downloadArtifact') | ||||||
|  |       .mockImplementation(() => Promise.resolve({digestMismatch: false})) | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   test('downloads a single artifact by name', async () => { | ||||||
|  |     const mockArtifact = { | ||||||
|  |       id: 123, | ||||||
|  |       name: 'artifact-name', | ||||||
|  |       size: 1024, | ||||||
|  |       digest: 'abc123' | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     jest | ||||||
|  |       .spyOn(artifact, 'getArtifact') | ||||||
|  |       .mockImplementation(() => Promise.resolve({artifact: mockArtifact})) | ||||||
|  | 
 | ||||||
|  |     await run() | ||||||
|  | 
 | ||||||
|  |     expect(artifact.downloadArtifact).toHaveBeenCalledWith( | ||||||
|  |       mockArtifact.id, | ||||||
|  |       expect.objectContaining({ | ||||||
|  |         expectedHash: mockArtifact.digest | ||||||
|  |       }) | ||||||
|  |     ) | ||||||
|  |     expect(core.info).toHaveBeenCalledWith('Total of 1 artifact(s) downloaded') | ||||||
|  | 
 | ||||||
|  |     expect(core.setOutput).toHaveBeenCalledWith( | ||||||
|  |       'download-path', | ||||||
|  |       expect.any(String) | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     expect(core.info).toHaveBeenCalledWith( | ||||||
|  |       'Download artifact has finished successfully' | ||||||
|  |     ) | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   test('downloads multiple artifacts when no name or pattern provided', async () => { | ||||||
|  |     jest.clearAllMocks() | ||||||
|  |     mockInputs({ | ||||||
|  |       [Inputs.Name]: '', | ||||||
|  |       [Inputs.Pattern]: '' | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     const mockArtifacts = [ | ||||||
|  |       {id: 123, name: 'artifact1', size: 1024, digest: 'abc123'}, | ||||||
|  |       {id: 456, name: 'artifact2', size: 2048, digest: 'def456'} | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     // Set up artifact mock after clearing mocks
 | ||||||
|  |     jest | ||||||
|  |       .spyOn(artifact, 'listArtifacts') | ||||||
|  |       .mockImplementation(() => Promise.resolve({artifacts: mockArtifacts})) | ||||||
|  | 
 | ||||||
|  |     // Reset downloadArtifact mock as well
 | ||||||
|  |     jest | ||||||
|  |       .spyOn(artifact, 'downloadArtifact') | ||||||
|  |       .mockImplementation(() => Promise.resolve({digestMismatch: false})) | ||||||
|  | 
 | ||||||
|  |     await run() | ||||||
|  | 
 | ||||||
|  |     expect(core.info).toHaveBeenCalledWith( | ||||||
|  |       'No input name or pattern filtered specified, downloading all artifacts' | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     expect(core.info).toHaveBeenCalledWith('Total of 2 artifact(s) downloaded') | ||||||
|  |     expect(artifact.downloadArtifact).toHaveBeenCalledTimes(2) | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   test('sets download path output even when no artifacts are found', async () => { | ||||||
|  |     mockInputs({[Inputs.Name]: ''}) | ||||||
|  | 
 | ||||||
|  |     await run() | ||||||
|  | 
 | ||||||
|  |     expect(core.setOutput).toHaveBeenCalledWith( | ||||||
|  |       'download-path', | ||||||
|  |       expect.any(String) | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     expect(core.info).toHaveBeenCalledWith( | ||||||
|  |       'Download artifact has finished successfully' | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     expect(core.info).toHaveBeenCalledWith('Total of 0 artifact(s) downloaded') | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   test('filters artifacts by pattern', async () => { | ||||||
|  |     const mockArtifacts = [ | ||||||
|  |       {id: 123, name: 'test-artifact', size: 1024, digest: 'abc123'}, | ||||||
|  |       {id: 456, name: 'prod-artifact', size: 2048, digest: 'def456'} | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     jest | ||||||
|  |       .spyOn(artifact, 'listArtifacts') | ||||||
|  |       .mockImplementation(() => Promise.resolve({artifacts: mockArtifacts})) | ||||||
|  | 
 | ||||||
|  |     mockInputs({ | ||||||
|  |       [Inputs.Name]: '', | ||||||
|  |       [Inputs.Pattern]: 'test-*' | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     await run() | ||||||
|  | 
 | ||||||
|  |     expect(artifact.downloadArtifact).toHaveBeenCalledTimes(1) | ||||||
|  |     expect(artifact.downloadArtifact).toHaveBeenCalledWith( | ||||||
|  |       123, | ||||||
|  |       expect.anything() | ||||||
|  |     ) | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   test('uses token and repository information when provided', async () => { | ||||||
|  |     const token = 'ghp_testtoken123' | ||||||
|  | 
 | ||||||
|  |     mockInputs({ | ||||||
|  |       [Inputs.Name]: '', | ||||||
|  |       [Inputs.GitHubToken]: token, | ||||||
|  |       [Inputs.Repository]: 'myorg/myrepo', | ||||||
|  |       [Inputs.RunID]: '789' | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     jest | ||||||
|  |       .spyOn(artifact, 'listArtifacts') | ||||||
|  |       .mockImplementation(() => Promise.resolve({artifacts: []})) | ||||||
|  | 
 | ||||||
|  |     await run() | ||||||
|  | 
 | ||||||
|  |     expect(artifact.listArtifacts).toHaveBeenCalledWith( | ||||||
|  |       expect.objectContaining({ | ||||||
|  |         findBy: { | ||||||
|  |           token, | ||||||
|  |           workflowRunId: 789, | ||||||
|  |           repositoryName: 'myrepo', | ||||||
|  |           repositoryOwner: 'myorg' | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     ) | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   test('throws error when repository format is invalid', async () => { | ||||||
|  |     mockInputs({ | ||||||
|  |       [Inputs.GitHubToken]: 'some-token', | ||||||
|  |       [Inputs.Repository]: 'invalid-format' // Missing the owner/repo format
 | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     await expect(run()).rejects.toThrow( | ||||||
|  |       "Invalid repository: 'invalid-format'. Must be in format owner/repo" | ||||||
|  |     ) | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   test('warns when digest validation fails', async () => { | ||||||
|  |     const mockArtifact = { | ||||||
|  |       id: 123, | ||||||
|  |       name: 'corrupted-artifact', | ||||||
|  |       size: 1024, | ||||||
|  |       digest: 'abc123' | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     jest | ||||||
|  |       .spyOn(artifact, 'getArtifact') | ||||||
|  |       .mockImplementation(() => Promise.resolve({artifact: mockArtifact})) | ||||||
|  | 
 | ||||||
|  |     jest | ||||||
|  |       .spyOn(artifact, 'downloadArtifact') | ||||||
|  |       .mockImplementation(() => Promise.resolve({digestMismatch: true})) | ||||||
|  | 
 | ||||||
|  |     await run() | ||||||
|  | 
 | ||||||
|  |     expect(core.warning).toHaveBeenCalledWith( | ||||||
|  |       expect.stringContaining('digest validation failed') | ||||||
|  |     ) | ||||||
|  |   }) | ||||||
|  | }) | ||||||
							
								
								
									
										3
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							| @ -118760,7 +118760,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { | |||||||
|     return (mod && mod.__esModule) ? mod : { "default": mod }; |     return (mod && mod.__esModule) ? mod : { "default": mod }; | ||||||
| }; | }; | ||||||
| Object.defineProperty(exports, "__esModule", ({ value: true })); | Object.defineProperty(exports, "__esModule", ({ value: true })); | ||||||
| exports.chunk = void 0; | exports.run = exports.chunk = void 0; | ||||||
| const os = __importStar(__nccwpck_require__(22037)); | const os = __importStar(__nccwpck_require__(22037)); | ||||||
| const path = __importStar(__nccwpck_require__(71017)); | const path = __importStar(__nccwpck_require__(71017)); | ||||||
| const core = __importStar(__nccwpck_require__(42186)); | const core = __importStar(__nccwpck_require__(42186)); | ||||||
| @ -118863,6 +118863,7 @@ function run() { | |||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  | exports.run = run; | ||||||
| run().catch(err => core.setFailed(`Unable to download artifact(s): ${err.message}`)); | run().catch(err => core.setFailed(`Unable to download artifact(s): ${err.message}`)); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								jest.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								jest.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | module.exports = { | ||||||
|  |   clearMocks: true, | ||||||
|  |   moduleFileExtensions: ['js', 'ts'], | ||||||
|  |   roots: ['<rootDir>'], | ||||||
|  |   testEnvironment: 'node', | ||||||
|  |   testMatch: ['**/*.test.ts'], | ||||||
|  |   testRunner: 'jest-circus/runner', | ||||||
|  |   transform: { | ||||||
|  |     '^.+\\.ts$': 'ts-jest' | ||||||
|  |   }, | ||||||
|  |   verbose: true | ||||||
|  | } | ||||||
							
								
								
									
										8305
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8305
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -9,7 +9,8 @@ | |||||||
|     "check-all": "concurrently \"npm:format-check\" \"npm:lint\" \"npm:build\"", |     "check-all": "concurrently \"npm:format-check\" \"npm:lint\" \"npm:build\"", | ||||||
|     "format": "prettier --write **/*.ts", |     "format": "prettier --write **/*.ts", | ||||||
|     "format-check": "prettier --check **/*.ts", |     "format-check": "prettier --check **/*.ts", | ||||||
|     "lint": "eslint **/*.ts" |     "lint": "eslint **/*.ts", | ||||||
|  |     "test": "jest" | ||||||
|   }, |   }, | ||||||
|   "repository": { |   "repository": { | ||||||
|     "type": "git", |     "type": "git", | ||||||
| @ -34,6 +35,7 @@ | |||||||
|     "minimatch": "^9.0.3" |     "minimatch": "^9.0.3" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|  |     "@types/jest": "^29.5.14", | ||||||
|     "@types/node": "^12.12.6", |     "@types/node": "^12.12.6", | ||||||
|     "@typescript-eslint/eslint-plugin": "^6.14.0", |     "@typescript-eslint/eslint-plugin": "^6.14.0", | ||||||
|     "@vercel/ncc": "^0.33.4", |     "@vercel/ncc": "^0.33.4", | ||||||
| @ -41,7 +43,10 @@ | |||||||
|     "eslint": "^8.55.0", |     "eslint": "^8.55.0", | ||||||
|     "eslint-plugin-github": "^4.10.1", |     "eslint-plugin-github": "^4.10.1", | ||||||
|     "eslint-plugin-prettier": "^5.0.1", |     "eslint-plugin-prettier": "^5.0.1", | ||||||
|  |     "jest": "^29.7.0", | ||||||
|     "prettier": "^3.1.1", |     "prettier": "^3.1.1", | ||||||
|  |     "ts-jest": "^29.2.6", | ||||||
|  |     "ts-node": "^10.9.2", | ||||||
|     "typescript": "^5.3.3" |     "typescript": "^5.3.3" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -15,7 +15,7 @@ export const chunk = <T>(arr: T[], n: number): T[][] => | |||||||
|     return acc |     return acc | ||||||
|   }, [] as T[][]) |   }, [] as T[][]) | ||||||
| 
 | 
 | ||||||
| async function run(): Promise<void> { | export async function run(): Promise<void> { | ||||||
|   const inputs = { |   const inputs = { | ||||||
|     name: core.getInput(Inputs.Name, {required: false}), |     name: core.getInput(Inputs.Name, {required: false}), | ||||||
|     path: core.getInput(Inputs.Path, {required: false}), |     path: core.getInput(Inputs.Path, {required: false}), | ||||||
| @ -138,7 +138,6 @@ async function run(): Promise<void> { | |||||||
|         ) |         ) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     core.info(`Total of ${artifacts.length} artifact(s) downloaded`) |     core.info(`Total of ${artifacts.length} artifact(s) downloaded`) | ||||||
|     core.setOutput(Outputs.DownloadPath, resolvedPath) |     core.setOutput(Outputs.DownloadPath, resolvedPath) | ||||||
|     core.info('Download artifact has finished successfully') |     core.info('Download artifact has finished successfully') | ||||||
|  | |||||||
| @ -9,5 +9,5 @@ | |||||||
|     "moduleResolution": "node", |     "moduleResolution": "node", | ||||||
|     "esModuleInterop": true |     "esModuleInterop": true | ||||||
|   }, |   }, | ||||||
|   "exclude": ["node_modules", "**/*.test.ts"] |   "exclude": ["node_modules", "**/*.test.ts", "jest.config.ts", "__tests__"] | ||||||
| } | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user