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') ) }) })