mirror of
				https://github.com/actions/download-artifact.git
				synced 2025-10-31 16:43:41 +00:00 
			
		
		
		
	Add tests & test dependencies
This commit is contained in:
		
							parent
							
								
									c7cfc3a2a3
								
							
						
					
					
						commit
						96a6f165f4
					
				
							
								
								
									
										226
									
								
								__tests__/download.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								__tests__/download.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,226 @@ | |||||||
|  | import * as core from '@actions/core' | ||||||
|  | import * as github from '@actions/github' | ||||||
|  | import * as os from 'os' | ||||||
|  | import artifact, {ArtifactNotFoundError} from '@actions/artifact' | ||||||
|  | import {run} from '../src/download-artifact' | ||||||
|  | import {Inputs} from '../src/constants' | ||||||
|  | 
 | ||||||
|  | const fixtures = { | ||||||
|  |   artifactName: 'artifact-name', | ||||||
|  |   rootDirectory: '/some/artifact/path', | ||||||
|  |   filesToUpload: [ | ||||||
|  |     '/some/artifact/path/file1.txt', | ||||||
|  |     '/some/artifact/path/file2.txt' | ||||||
|  |   ] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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 */ | ||||||
|  | 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') | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     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') | ||||||
|  |       ) | ||||||
|  |     }) | ||||||
|  |   }) | ||||||
							
								
								
									
										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 | ||||||
|  |   } | ||||||
							
								
								
									
										8309
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8309
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "download-artifact", |   "name": "download-artifact", | ||||||
|   "version": "4.2.1", |   "version": "4.2.0", | ||||||
|   "description": "Download an Actions Artifact from a workflow run", |   "description": "Download an Actions Artifact from a workflow run", | ||||||
|   "main": "dist/index.js", |   "main": "dist/index.js", | ||||||
|   "scripts": { |   "scripts": { | ||||||
| @ -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}), | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user