Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand test coverage for required status scenarios #9

Merged
merged 10 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion .github/workflows/check-dist.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# If this workflow is run from a feature branch, it will act as an additional CI
# check and fail if the checked-in `dist/` directory does not match what is
# expected from the build.
name: Check Transpiled JavaScript
name: Bundle JavaScript

on:
pull_request:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Continuous Integration
name: CI

on:
pull_request:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/linter.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Lint Codebase
name: Lint

on:
pull_request:
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
Configure required workflows in your repository. Use path filters. Leave your
GitHub org administrator alone.

Use this Action to configuring required workflows in a workflow file protected
by `CODEOWNERS` in your repository instead of in your repository settings.
Use this Action to configure required workflows in a workflow file protected by
`CODEOWNERS` in your repository instead of in your repository settings.

[![Lint](https://github.com/urcomputeringpal/optional-required-workflows/actions/workflows/linter.yml/badge.svg)](https://github.com/urcomputeringpal/optional-required-workflows/actions/workflows/linter.yml?query=branch%3Amain)
[![CI](https://github.com/urcomputeringpal/optional-required-workflows/actions/workflows/ci.yml/badge.svg)](https://github.com/urcomputeringpal/optional-required-workflows/actions/workflows/ci.yml?query=branch%3Amain)
[![Bundling](https://github.com/urcomputeringpal/optional-required-workflows/actions/workflows/check-dist.yml/badge.svg)](https://github.com/urcomputeringpal/optional-required-workflows/actions/workflows/check-dist.yml?query=branch%3Amain)
[![Coverage](./badges/coverage.svg)](./badges/coverage.svg)

## Problem(s)

Expand Down
174 changes: 174 additions & 0 deletions __tests__/required.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import { required } from '../src/required'
import { GitHub } from '@actions/github/lib/utils'
import { Context } from '@actions/github/lib/context'
// eslint-disable-next-line import/no-unresolved
import { WorkflowRunCompletedEvent } from '@octokit/webhooks-types'

describe('required', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let mockOctokit: any
let context: Context
let event: WorkflowRunCompletedEvent

beforeEach(() => {
process.env.GITHUB_REPOSITORY = 'owner/repo' // Setting GITHUB_REPOSITORY environment variable
mockOctokit = {
rest: {
repos: {
createCommitStatus: jest.fn()
},
actions: {
// eslint-disable-next-line @typescript-eslint/promise-function-async
getWorkflowRun: jest.fn().mockImplementation(() => {
return Promise.resolve({
data: {
id: 123,
name: 'Test Workflow',
head_sha: 'abc123',
conclusion: 'success',
html_url: 'http://example.com'
}
}) // Simulating a specific workflow run object
}),
// eslint-disable-next-line @typescript-eslint/promise-function-async
listWorkflowRunsForRepo: jest.fn().mockImplementation(() => {
return Promise.resolve({
data: {
workflow_runs: [
{ id: 123, name: 'Test Workflow', conclusion: 'success' }
]
}
}) // Simulating a successful response for listWorkflowRunsForRepo
})
}
}
}
context = new Context()
event = {
workflow_run: {
id: 123,
name: 'Test Workflow',
head_sha: 'abc123',
conclusion: 'success',
html_url: 'http://example.com'
}
} as unknown as WorkflowRunCompletedEvent
})

it('should handle all required statuses completing successfully', async () => {
// Mock setup to simulate all required statuses completing successfully
mockOctokit.rest.actions.listWorkflowRunsForRepo.mockResolvedValue({
data: {
workflow_runs: [
{ id: 123, name: 'Test Workflow', conclusion: 'success' },
{ id: 124, name: 'Another Workflow', conclusion: 'success' }
]
}
})

await required({
octokit: mockOctokit as unknown as InstanceType<typeof GitHub>,
context,
event,
workflows: ['Test Workflow', 'Another Workflow'],
statusName: 'Required'
})

expect(mockOctokit.rest.repos.createCommitStatus).toHaveBeenCalledWith(
expect.objectContaining({
state: 'success',
description: 'All 2 observed required workflows have succeeded'
})
)
})

it('should handle only one of the required statuses reporting success', async () => {
// Mock setup to simulate only one of the required statuses reporting success
mockOctokit.rest.actions.listWorkflowRunsForRepo.mockResolvedValue({
data: {
workflow_runs: [
{ id: 123, name: 'Test Workflow', conclusion: 'success' },
{ id: 124, name: 'Another Workflow', conclusion: null }
]
}
})

await required({
octokit: mockOctokit as unknown as InstanceType<typeof GitHub>,
context,
event,
workflows: ['Test Workflow', 'Another Workflow'],
statusName: 'Required'
})

expect(mockOctokit.rest.repos.createCommitStatus).toHaveBeenCalledWith(
expect.objectContaining({
state: 'pending',
description: expect.stringContaining(
'required workflows are still pending...'
)
})
)
})

it('should handle one of the required statuses reporting failure', async () => {
// Mock setup to simulate one of the required statuses reporting failure
mockOctokit.rest.actions.listWorkflowRunsForRepo.mockResolvedValue({
data: {
workflow_runs: [
{ id: 123, name: 'Test Workflow', conclusion: 'success' },
{ id: 124, name: 'Another Workflow', conclusion: 'failure' }
]
}
})

await required({
octokit: mockOctokit as unknown as InstanceType<typeof GitHub>,
context,
event,
workflows: ['Test Workflow', 'Another Workflow'],
statusName: 'Required'
})

expect(mockOctokit.rest.repos.createCommitStatus).toHaveBeenCalledWith(
expect.objectContaining({
state: 'failure',
description: expect.stringContaining(
'required workflows were not successful'
)
})
)
})

it('should handle replication lag scenario', async () => {
// Mock setup to simulate replication lag scenario where "Test workflow" isn't in the results
mockOctokit.rest.actions.listWorkflowRunsForRepo.mockResolvedValue({
data: {
workflow_runs: [
{ id: 124, name: 'Another Workflow', conclusion: 'success' },
{
id: 125,
name: 'Another Not Required Workflow 2',
conclusion: 'success'
}
]
}
})

await required({
octokit: mockOctokit as unknown as InstanceType<typeof GitHub>,
context,
event,
workflows: ['Test Workflow', 'Another Workflow'],
statusName: 'Required'
})

expect(mockOctokit.rest.repos.createCommitStatus).toHaveBeenCalledWith(
expect.objectContaining({
state: 'pending',
description:
'Waiting for conclusion to be reported for Test Workflow...'
})
)
})
})
11 changes: 11 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ inputs:
A multi-line string of the names of workflows that must succeed if a run
is observed.
required: true
retries:
description: >
The number of times to query for updated WorkflowRun statues if we don't
observe the conclusion of the run we're looking for before giving up.
required: false
default: '3'
delay:
description: >
The delay in milliseconds to wait between retries.
required: false
default: '100'

runs:
using: node20
Expand Down
2 changes: 1 addition & 1 deletion badges/coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading