Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions .eslintrc.json

This file was deleted.

3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules/
lib/
__tests__/_temp/
__tests__/_temp/
.DS_Store
149 changes: 97 additions & 52 deletions __tests__/merge.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,65 @@
import * as core from '@actions/core'
import artifact from '@actions/artifact'
import {run} from '../src/merge/merge-artifacts'
import {Inputs} from '../src/merge/constants'
import * as search from '../src/shared/search'
import {jest, describe, test, expect, beforeEach} from '@jest/globals'

// Mock @actions/github before importing modules that use it
jest.unstable_mockModule('@actions/github', () => ({
context: {
repo: {
owner: 'actions',
repo: 'toolkit'
},
runId: 123,
serverUrl: 'https://github.com'
},
getOctokit: jest.fn()
}))

// Mock @actions/core
jest.unstable_mockModule('@actions/core', () => ({
getInput: jest.fn(),
getBooleanInput: jest.fn(),
setOutput: jest.fn(),
setFailed: jest.fn(),
setSecret: jest.fn(),
info: jest.fn(),
warning: jest.fn(),
debug: jest.fn(),
error: jest.fn(),
notice: jest.fn(),
startGroup: jest.fn(),
endGroup: jest.fn(),
isDebug: jest.fn(() => false),
getState: jest.fn(),
saveState: jest.fn(),
exportVariable: jest.fn(),
addPath: jest.fn(),
group: jest.fn((name: string, fn: () => Promise<unknown>) => fn()),
toPlatformPath: jest.fn((p: string) => p),
toWin32Path: jest.fn((p: string) => p),
toPosixPath: jest.fn((p: string) => p)
}))

// Mock fs/promises
const actualFsPromises = await import('fs/promises')
jest.unstable_mockModule('fs/promises', () => ({
...actualFsPromises,
mkdtemp:

Check failure on line 45 in __tests__/merge.test.ts

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest)

Insert `·jest`

Check failure on line 45 in __tests__/merge.test.ts

View workflow job for this annotation

GitHub Actions / Build (macos-latest)

Insert `·jest`
jest.fn<() => Promise<string>>().mockResolvedValue('/tmp/merge-artifact'),

Check failure on line 46 in __tests__/merge.test.ts

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest)

Replace `jest.fn<()·=>·Promise<string>>()` with `.fn<()·=>·Promise<string>>()⏎····`

Check failure on line 46 in __tests__/merge.test.ts

View workflow job for this annotation

GitHub Actions / Build (macos-latest)

Replace `jest.fn<()·=>·Promise<string>>()` with `.fn<()·=>·Promise<string>>()⏎····`
rm: jest.fn<() => Promise<void>>().mockResolvedValue(undefined)
}))
Comment thread
danwkennedy marked this conversation as resolved.

// Mock shared search module
const mockFindFilesToUpload = jest.fn<

Check failure on line 51 in __tests__/merge.test.ts

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest)

Replace `·jest.fn<⏎··()·=>·Promise<{filesToUpload:·string[];·rootDirectory:·string}>⏎` with `⏎··jest.fn<()·=>·Promise<{filesToUpload:·string[];·rootDirectory:·string}>`

Check failure on line 51 in __tests__/merge.test.ts

View workflow job for this annotation

GitHub Actions / Build (macos-latest)

Replace `·jest.fn<⏎··()·=>·Promise<{filesToUpload:·string[];·rootDirectory:·string}>⏎` with `⏎··jest.fn<()·=>·Promise<{filesToUpload:·string[];·rootDirectory:·string}>`
() => Promise<{filesToUpload: string[]; rootDirectory: string}>
>()
jest.unstable_mockModule('../src/shared/search.js', () => ({
findFilesToUpload: mockFindFilesToUpload
}))

// Dynamic imports after mocking
const core = await import('@actions/core')
const artifact = await import('@actions/artifact')
const {run} = await import('../src/merge/merge-artifacts.js')
const {Inputs} = await import('../src/merge/constants.js')

const fixtures = {
artifactName: 'my-merged-artifact',
Expand Down Expand Up @@ -34,27 +91,10 @@
]
}

jest.mock('@actions/github', () => ({
context: {
repo: {
owner: 'actions',
repo: 'toolkit'
},
runId: 123,
serverUrl: 'https://github.com'
}
}))

jest.mock('@actions/core')

jest.mock('fs/promises', () => ({
mkdtemp: jest.fn().mockResolvedValue('/tmp/merge-artifact'),
rm: jest.fn().mockResolvedValue(undefined)
}))

/* eslint-disable no-unused-vars */
const mockInputs = (overrides?: Partial<{[K in Inputs]?: any}>) => {
const inputs = {
const mockInputs = (
overrides?: Partial<{[K in (typeof Inputs)[keyof typeof Inputs]]?: any}>
) => {
const inputs: Record<string, any> = {
[Inputs.Name]: 'my-merged-artifact',
[Inputs.Pattern]: '*',
[Inputs.SeparateDirectories]: false,
Expand All @@ -64,10 +104,14 @@
...overrides
}

;(core.getInput as jest.Mock).mockImplementation((name: string) => {
return inputs[name]
})
;(core.getBooleanInput as jest.Mock).mockImplementation((name: string) => {
;(core.getInput as jest.Mock<typeof core.getInput>).mockImplementation(
(name: string) => {
return inputs[name]
}
)
;(
core.getBooleanInput as jest.Mock<typeof core.getBooleanInput>
).mockImplementation((name: string) => {
return inputs[name]
})

Expand All @@ -77,99 +121,100 @@
describe('merge', () => {
beforeEach(async () => {
mockInputs()
jest.clearAllMocks()

jest
.spyOn(artifact, 'listArtifacts')
.spyOn(artifact.default, 'listArtifacts')
.mockResolvedValue({artifacts: fixtures.artifacts})

jest.spyOn(artifact, 'downloadArtifact').mockResolvedValue({
jest.spyOn(artifact.default, 'downloadArtifact').mockResolvedValue({
downloadPath: fixtures.tmpDirectory
})

jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({
mockFindFilesToUpload.mockResolvedValue({
filesToUpload: fixtures.filesToUpload,
rootDirectory: fixtures.tmpDirectory
})

jest.spyOn(artifact, 'uploadArtifact').mockResolvedValue({
jest.spyOn(artifact.default, 'uploadArtifact').mockResolvedValue({
size: 123,
id: 1337
})

jest
.spyOn(artifact, 'deleteArtifact')
.mockImplementation(async artifactName => {
const artifact = fixtures.artifacts.find(a => a.name === artifactName)
if (!artifact) throw new Error(`Artifact ${artifactName} not found`)
return {id: artifact.id}
.spyOn(artifact.default, 'deleteArtifact')
.mockImplementation(async (artifactName: string) => {
const found = fixtures.artifacts.find(a => a.name === artifactName)
if (!found) throw new Error(`Artifact ${artifactName} not found`)
return {id: found.id}
})
})

it('merges artifacts', async () => {
test('merges artifacts', async () => {
await run()

for (const a of fixtures.artifacts) {
expect(artifact.downloadArtifact).toHaveBeenCalledWith(a.id, {
expect(artifact.default.downloadArtifact).toHaveBeenCalledWith(a.id, {
path: fixtures.tmpDirectory
})
}

expect(artifact.uploadArtifact).toHaveBeenCalledWith(
expect(artifact.default.uploadArtifact).toHaveBeenCalledWith(
fixtures.artifactName,
fixtures.filesToUpload,
fixtures.tmpDirectory,
{compressionLevel: 6}
)
})

it('fails if no artifacts found', async () => {
test('fails if no artifacts found', async () => {
mockInputs({[Inputs.Pattern]: 'this-does-not-match'})

expect(run()).rejects.toThrow()
await expect(run()).rejects.toThrow()

expect(artifact.uploadArtifact).not.toBeCalled()
expect(artifact.downloadArtifact).not.toBeCalled()
expect(artifact.default.uploadArtifact).not.toHaveBeenCalled()
expect(artifact.default.downloadArtifact).not.toHaveBeenCalled()
})

it('supports custom compression level', async () => {
test('supports custom compression level', async () => {
mockInputs({
[Inputs.CompressionLevel]: 2
})

await run()

expect(artifact.uploadArtifact).toHaveBeenCalledWith(
expect(artifact.default.uploadArtifact).toHaveBeenCalledWith(
fixtures.artifactName,
fixtures.filesToUpload,
fixtures.tmpDirectory,
{compressionLevel: 2}
)
})

it('supports custom retention days', async () => {
test('supports custom retention days', async () => {
mockInputs({
[Inputs.RetentionDays]: 7
})

await run()

expect(artifact.uploadArtifact).toHaveBeenCalledWith(
expect(artifact.default.uploadArtifact).toHaveBeenCalledWith(
fixtures.artifactName,
fixtures.filesToUpload,
fixtures.tmpDirectory,
{retentionDays: 7, compressionLevel: 6}
)
})

it('supports deleting artifacts after merge', async () => {
test('supports deleting artifacts after merge', async () => {
mockInputs({
[Inputs.DeleteMerged]: true
})

await run()

for (const a of fixtures.artifacts) {
expect(artifact.deleteArtifact).toHaveBeenCalledWith(a.name)
expect(artifact.default.deleteArtifact).toHaveBeenCalledWith(a.name)
}
})
})
Loading
Loading