diff --git a/packages/artifact/__tests__/list-artifacts.test.ts b/packages/artifact/__tests__/list-artifacts.test.ts new file mode 100644 index 00000000..7c8699e7 --- /dev/null +++ b/packages/artifact/__tests__/list-artifacts.test.ts @@ -0,0 +1,242 @@ +import * as github from '@actions/github' +import type {RestEndpointMethods} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/method-types' +import type {RestEndpointMethodTypes} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/parameters-and-response-types' +import { + listArtifactsInternal, + listArtifactsPublic +} from '../src/internal/find/list-artifacts' +import * as config from '../src/internal/shared/config' +import {ArtifactServiceClientJSON, Timestamp} from '../src/generated' +import * as util from '../src/internal/shared/util' +import {noopLogs} from './common' +import {Artifact} from '../src/internal/shared/interfaces' + +type MockedListWorkflowRunArtifacts = jest.MockedFunction< + RestEndpointMethods['actions']['listWorkflowRunArtifacts'] +> + +jest.mock('@actions/github', () => ({ + getOctokit: jest.fn().mockReturnValue({ + rest: { + actions: { + listWorkflowRunArtifacts: jest.fn() + } + } + }) +})) + +const artifactsToListResponse = ( + artifacts: Artifact[] +): RestEndpointMethodTypes['actions']['listWorkflowRunArtifacts']['response']['data'] => { + return { + total_count: artifacts.length, + artifacts: artifacts.map(artifact => ({ + name: artifact.name, + id: artifact.id, + size_in_bytes: artifact.size, + created_at: artifact.createdAt?.toISOString() || '', + run_id: fixtures.runId, + // unused fields for tests + url: '', + archive_download_url: '', + expired: false, + expires_at: '', + node_id: '', + run_url: '', + type: '', + updated_at: '' + })) + } +} + +const fixtures = { + repo: 'toolkit', + owner: 'actions', + token: 'ghp_1234567890', + runId: 123, + backendIds: { + workflowRunBackendId: 'c4d7c21f-ba3f-4ddc-a8c8-6f2f626f8422', + workflowJobRunBackendId: '760803a1-f890-4d25-9a6e-a3fc01a0c7cf' + }, + artifacts: [ + { + id: 1, + name: 'my-artifact', + size: 456, + createdAt: new Date('2023-12-01') + }, + { + id: 2, + name: 'my-artifact', + size: 456, + createdAt: new Date('2023-12-02') + } + ] +} + +describe('list-artifact', () => { + beforeAll(() => { + noopLogs() + }) + + describe('public', () => { + it('should return a list of artifacts', async () => { + const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions + .listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts + + mockListArtifacts.mockResolvedValueOnce({ + status: 200, + headers: {}, + url: '', + data: artifactsToListResponse(fixtures.artifacts) + }) + + const response = await listArtifactsPublic( + fixtures.runId, + fixtures.owner, + fixtures.repo, + fixtures.token, + false + ) + + expect(response).toEqual({ + artifacts: fixtures.artifacts + }) + }) + + it('should return the latest artifact when latest is specified', async () => { + const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions + .listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts + + mockListArtifacts.mockResolvedValueOnce({ + status: 200, + headers: {}, + url: '', + data: artifactsToListResponse(fixtures.artifacts) + }) + + const response = await listArtifactsPublic( + fixtures.runId, + fixtures.owner, + fixtures.repo, + fixtures.token, + true + ) + + expect(response).toEqual({ + artifacts: [fixtures.artifacts[1]] + }) + }) + + it('can return empty artifacts', async () => { + const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions + .listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts + + mockListArtifacts.mockResolvedValueOnce({ + status: 200, + headers: {}, + url: '', + data: { + total_count: 0, + artifacts: [] + } + }) + + const response = await listArtifactsPublic( + fixtures.runId, + fixtures.owner, + fixtures.repo, + fixtures.token, + true + ) + + expect(response).toEqual({ + artifacts: [] + }) + }) + + it('should fail if non-200 response', async () => { + const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions + .listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts + + mockListArtifacts.mockRejectedValue(new Error('boom')) + + await expect( + listArtifactsPublic( + fixtures.runId, + fixtures.owner, + fixtures.repo, + fixtures.token, + false + ) + ).rejects.toThrow('boom') + }) + }) + + describe('internal', () => { + beforeEach(() => { + jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token') + jest + .spyOn(util, 'getBackendIdsFromToken') + .mockReturnValue(fixtures.backendIds) + jest + .spyOn(config, 'getResultsServiceUrl') + .mockReturnValue('https://results.local') + }) + + it('should return a list of artifacts', async () => { + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockResolvedValue({ + artifacts: fixtures.artifacts.map(artifact => ({ + ...fixtures.backendIds, + databaseId: artifact.id.toString(), + name: artifact.name, + size: artifact.size.toString(), + createdAt: Timestamp.fromDate(artifact.createdAt) + })) + }) + const response = await listArtifactsInternal(false) + expect(response).toEqual({ + artifacts: fixtures.artifacts + }) + }) + + it('should return the latest artifact when latest is specified', async () => { + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockResolvedValue({ + artifacts: fixtures.artifacts.map(artifact => ({ + ...fixtures.backendIds, + databaseId: artifact.id.toString(), + name: artifact.name, + size: artifact.size.toString(), + createdAt: Timestamp.fromDate(artifact.createdAt) + })) + }) + const response = await listArtifactsInternal(true) + expect(response).toEqual({ + artifacts: [fixtures.artifacts[1]] + }) + }) + + it('can return empty artifacts', async () => { + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockResolvedValue({ + artifacts: [] + }) + const response = await listArtifactsInternal(false) + expect(response).toEqual({ + artifacts: [] + }) + }) + + it('should fail if non-200 response', async () => { + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockRejectedValue(new Error('boom')) + await expect(listArtifactsInternal(false)).rejects.toThrow('boom') + }) + }) +})