-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
934 additions
and
296 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,3 @@ | ||
import core from '@actions/core'; | ||
import { main } from './main.js'; | ||
|
||
import { getStaleIssues } from './lib/get-stale-issues.js'; | ||
import { closeIssue } from './lib/close-issue.js'; | ||
import { getConfig } from './lib/get-config.js'; | ||
|
||
try { | ||
const config = await getConfig(); | ||
|
||
const issues = getStaleIssues(config); | ||
|
||
const closedIssues = []; | ||
|
||
for await (const issue of issues) { | ||
await closeIssue(config, issue); | ||
closedIssues.push(issue.number); | ||
} | ||
|
||
core.setOutput('closed-issues', closedIssues); | ||
} | ||
catch (error) { | ||
const message = error instanceof Error ? error.message : String(error); | ||
core.setFailed(message); | ||
} | ||
await main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { afterEach, expect, it, vi } from 'vitest'; | ||
import core from '@actions/core'; | ||
|
||
import type { Config } from './get-config.js'; | ||
import type { Issue } from './get-stale-issues.js'; | ||
import { closeIssue } from './close-issue.js'; | ||
|
||
const mockOctokit = { | ||
rest: { | ||
issues: { | ||
createComment: vi.fn(), | ||
update: vi.fn(), | ||
}, | ||
}, | ||
} as unknown as Config['octokit']; | ||
|
||
const mockConfig = { | ||
octokit: mockOctokit, | ||
ownerRepo: { owner: 'directus', repo: 'stale-issues-action' }, | ||
closeMessage: 'Closing this issue as it has become stale.', | ||
} as Config; | ||
|
||
const mockIssue = { | ||
number: 1, | ||
url: 'https://github.com/directus/stale-issues-action/issues/1', | ||
staleSince: '1 day', | ||
} as Issue; | ||
|
||
const infoSpy = vi.spyOn(core, 'info').mockImplementation(() => {}); | ||
|
||
afterEach(() => { | ||
vi.clearAllMocks(); | ||
}); | ||
|
||
it('creates a comment and closes the issue', async () => { | ||
await closeIssue(mockConfig, mockIssue); | ||
|
||
expect(mockOctokit.rest.issues.createComment).toBeCalledWith({ | ||
...mockConfig.ownerRepo, | ||
issue_number: mockIssue.number, | ||
body: mockConfig.closeMessage, | ||
}); | ||
|
||
expect(mockOctokit.rest.issues.update).toBeCalledWith({ | ||
...mockConfig.ownerRepo, | ||
issue_number: mockIssue.number, | ||
state: 'closed', | ||
state_reason: 'not_planned', | ||
}); | ||
|
||
expect(infoSpy).toBeCalledWith( | ||
`Issue #${mockIssue.number} (${mockIssue.url}) is stale since ${mockIssue.staleSince} and has been closed`, | ||
); | ||
}); | ||
|
||
it('prints info only in dry-run mode', async () => { | ||
await closeIssue({ ...mockConfig, dryRun: true }, mockIssue); | ||
|
||
expect(mockOctokit.rest.issues.createComment).not.toHaveBeenCalled(); | ||
expect(mockOctokit.rest.issues.update).not.toHaveBeenCalled(); | ||
|
||
expect(infoSpy).toBeCalledWith( | ||
`Issue #${mockIssue.number} (${mockIssue.url}) is stale since ${mockIssue.staleSince} and would have been closed (dry-run)`, | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import core from '@actions/core'; | ||
import github from '@actions/github'; | ||
import { beforeEach, expect, it, vi } from 'vitest'; | ||
|
||
import { type Config, getConfig } from './get-config.js'; | ||
|
||
vi.mock('@actions/github'); | ||
|
||
let actionInputs: Record<string, string> = {}; | ||
|
||
function getInput(name: string) { | ||
return actionInputs[name] || ''; | ||
} | ||
|
||
vi.spyOn(core, 'getInput').mockImplementation(getInput); | ||
vi.spyOn(core, 'getBooleanInput').mockImplementation((name) => Boolean(getInput(name))); | ||
|
||
const mockOctokit = { request: vi.fn() }; | ||
|
||
vi.spyOn(github, 'getOctokit').mockReturnValue(mockOctokit as unknown as Config['octokit']); | ||
|
||
beforeEach(() => { | ||
actionInputs = { | ||
'github-token': 'token', | ||
'github-repo': 'directus/stale-issues-action', | ||
'stale-label': 'stale', | ||
'days-before-close': '7', | ||
'close-message': 'Closing this issue as it has become stale.', | ||
}; | ||
}); | ||
|
||
it('returns the config if all inputs are valid', async () => { | ||
await expect(getConfig()).resolves.toStrictEqual({ | ||
closeMessage: 'Closing this issue as it has become stale.', | ||
daysBeforeClose: 7, | ||
dryRun: false, | ||
octokit: mockOctokit, | ||
ownerRepo: { | ||
owner: 'directus', | ||
repo: 'stale-issues-action', | ||
}, | ||
staleLabel: 'stale', | ||
|
||
}); | ||
}); | ||
|
||
it('throws if the format of "github-repo" is invalid', async () => { | ||
actionInputs['github-repo'] = 'invalid'; | ||
|
||
await expect(getConfig()).rejects.toThrow('"github-repo" must be provided in form of "<owner>/<repo>"'); | ||
}); | ||
|
||
it('throws if the given "stale-label" could not be found', async () => { | ||
mockOctokit.request.mockRejectedValueOnce(new Error('label not found')); | ||
|
||
await expect(getConfig()).rejects.toThrow(`"stale-label" doesn't refer to an existing label or repository cannot be accessed`); | ||
}); | ||
|
||
it('warns if the given "stale-label" could not be found in dry-run mode', async () => { | ||
actionInputs['dry-run'] = 'true'; | ||
|
||
const warnSpy = vi.spyOn(core, 'warning').mockImplementation(() => {}); | ||
mockOctokit.request.mockRejectedValueOnce(new Error('label not found')); | ||
|
||
await expect(getConfig()).resolves.toBeDefined(); | ||
expect(warnSpy).toBeCalledWith(`"stale-label" doesn't refer to an existing label or repository cannot be accessed`); | ||
}); | ||
|
||
it('throws if "days-before-close" is not a number', async () => { | ||
actionInputs['days-before-close'] = 'invalid'; | ||
|
||
await expect(getConfig()).rejects.toThrow('"days-before-close" must be a number'); | ||
}); |
Oops, something went wrong.