Files

854 lines
26 KiB
TypeScript
Raw Permalink Normal View History

import {run} from '../src/labeler';
import * as github from '@actions/github';
import * as core from '@actions/core';
import path from 'path';
import fs from 'fs';
2021-06-04 14:46:22 -04:00
jest.mock('@actions/core');
jest.mock('@actions/github');
2021-06-04 14:46:22 -04:00
const gh = github.getOctokit('_');
const setLabelsMock = jest.spyOn(gh.rest.issues, 'setLabels');
const reposMock = jest.spyOn(gh.rest.repos, 'getContent');
const paginateMock = jest.spyOn(gh, 'paginate');
const getPullMock = jest.spyOn(gh.rest.pulls, 'get');
const readFileSyncMock = jest.spyOn(fs, 'readFileSync');
const existsSyncMock = jest.spyOn(fs, 'existsSync');
const coreErrorMock = jest.spyOn(core, 'error');
const coreWarningMock = jest.spyOn(core, 'warning');
const coreSetFailedMock = jest.spyOn(core, 'setFailed');
2023-06-29 03:45:24 -07:00
const setOutputSpy = jest.spyOn(core, 'setOutput');
2021-06-04 14:46:22 -04:00
class HttpError extends Error {
constructor(message: string) {
super(message);
this.name = 'HttpError';
}
}
class NotFound extends Error {
constructor(message: string) {
super(message);
this.name = 'NotFound';
}
}
2021-06-04 14:46:22 -04:00
const yamlFixtures = {
2023-01-11 17:09:44 -05:00
'branches.yml': fs.readFileSync('__tests__/fixtures/branches.yml'),
'only_pdfs.yml': fs.readFileSync('__tests__/fixtures/only_pdfs.yml'),
2023-05-17 07:05:19 -04:00
'not_supported.yml': fs.readFileSync('__tests__/fixtures/not_supported.yml'),
'any_and_all.yml': fs.readFileSync('__tests__/fixtures/any_and_all.yml'),
'mixed_labels.yml': fs.readFileSync('__tests__/fixtures/mixed_labels.yml'),
'limit_0.yml': fs.readFileSync('__tests__/fixtures/limit_0.yml'),
'limit_1.yml': fs.readFileSync('__tests__/fixtures/limit_1.yml'),
'limit_2.yml': fs.readFileSync('__tests__/fixtures/limit_2.yml'),
'limit_3.yml': fs.readFileSync('__tests__/fixtures/limit_3.yml'),
'mixed_rules.yml': fs.readFileSync('__tests__/fixtures/mixed_rules.yml'),
'max_files_5.yml': fs.readFileSync('__tests__/fixtures/max_files_5.yml'),
'max_files_with_branch.yml': fs.readFileSync(
'__tests__/fixtures/max_files_with_branch.yml'
)
2021-06-04 14:46:22 -04:00
};
2022-02-04 13:26:54 +00:00
const configureInput = (
mockInput: Partial<{
'repo-token': string;
'configuration-path': string;
'sync-labels': boolean;
2023-05-31 01:50:00 +01:00
dot: boolean;
'pr-number': string[];
2022-02-04 13:26:54 +00:00
}>
) => {
jest
.spyOn(core, 'getInput')
2022-02-04 13:26:54 +00:00
.mockImplementation((name: string, ...opts) => mockInput[name]);
jest
.spyOn(core, 'getMultilineInput')
.mockImplementation((name: string, ...opts) => mockInput[name]);
2023-05-31 18:46:23 +01:00
jest
.spyOn(core, 'getBooleanInput')
.mockImplementation((name: string, ...opts) => mockInput[name]);
2022-02-04 13:26:54 +00:00
};
2021-06-04 14:46:22 -04:00
afterAll(() => jest.restoreAllMocks());
describe('run', () => {
it('(with dot: false) adds labels to PRs that match our glob patterns', async () => {
2022-02-04 13:26:54 +00:00
configureInput({});
usingLabelerConfigYaml('only_pdfs.yml');
mockGitHubResponseChangedFiles('foo.pdf');
getPullMock.mockResolvedValue(<any>{
data: {
labels: []
}
});
2021-06-04 14:46:22 -04:00
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(1);
2023-06-29 03:45:24 -07:00
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
2021-06-04 14:46:22 -04:00
issue_number: 123,
labels: ['touched-a-pdf-file']
2021-06-04 14:46:22 -04:00
});
2023-06-29 03:45:24 -07:00
expect(setOutputSpy).toHaveBeenCalledWith(
'new-labels',
'touched-a-pdf-file'
);
expect(setOutputSpy).toHaveBeenCalledWith(
'all-labels',
'touched-a-pdf-file'
);
2021-06-04 14:46:22 -04:00
});
it('(with dot: true) adds labels to PRs that match our glob patterns', async () => {
2023-05-31 01:50:00 +01:00
configureInput({dot: true});
usingLabelerConfigYaml('only_pdfs.yml');
mockGitHubResponseChangedFiles('.foo.pdf');
2021-06-04 14:46:22 -04:00
getPullMock.mockResolvedValue(<any>{
data: {
labels: []
}
2021-06-04 14:46:22 -04:00
});
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
2021-06-04 14:46:22 -04:00
issue_number: 123,
labels: ['touched-a-pdf-file']
2021-06-04 14:46:22 -04:00
});
2023-06-29 03:45:24 -07:00
expect(setOutputSpy).toHaveBeenCalledWith(
'new-labels',
'touched-a-pdf-file'
);
expect(setOutputSpy).toHaveBeenCalledWith(
'all-labels',
'touched-a-pdf-file'
);
2021-06-04 14:46:22 -04:00
});
it('(with dot: false) does not add labels to PRs that do not match our glob patterns', async () => {
2022-02-04 13:26:54 +00:00
configureInput({});
usingLabelerConfigYaml('only_pdfs.yml');
mockGitHubResponseChangedFiles('.foo.pdf');
2021-06-04 14:46:22 -04:00
getPullMock.mockResolvedValue(<any>{
data: {
labels: []
}
2021-06-04 14:46:22 -04:00
});
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(0);
2023-06-29 03:45:24 -07:00
expect(setOutputSpy).toHaveBeenCalledWith('new-labels', '');
expect(setOutputSpy).toHaveBeenCalledWith('all-labels', '');
2022-02-04 13:26:54 +00:00
});
it('(with dot: true) does not add labels to PRs that do not match our glob patterns', async () => {
2023-05-31 01:50:00 +01:00
configureInput({dot: true});
usingLabelerConfigYaml('only_pdfs.yml');
mockGitHubResponseChangedFiles('foo.txt');
2021-06-04 14:46:22 -04:00
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(0);
2021-06-04 14:46:22 -04:00
});
2023-10-24 12:11:31 +02:00
it('does not add a label when the match config options are not supported', async () => {
configureInput({});
usingLabelerConfigYaml('not_supported.yml');
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(0);
2021-06-04 14:46:22 -04:00
});
2023-02-19 12:17:15 -05:00
it('adds labels based on the branch names that match the regexp pattern', async () => {
2023-10-24 12:11:31 +02:00
configureInput({});
2023-01-11 17:09:44 -05:00
github.context.payload.pull_request!.head = {ref: 'test/testing-time'};
usingLabelerConfigYaml('branches.yml');
await run();
2023-10-24 12:11:31 +02:00
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
2023-01-11 17:09:44 -05:00
owner: 'monalisa',
repo: 'helloworld',
issue_number: 123,
2023-01-11 17:09:44 -05:00
labels: ['test-branch']
});
2023-10-24 12:11:31 +02:00
expect(setOutputSpy).toHaveBeenCalledWith('new-labels', 'test-branch');
expect(setOutputSpy).toHaveBeenCalledWith('all-labels', 'test-branch');
});
2023-02-19 12:17:15 -05:00
it('adds multiple labels based on branch names that match different regexp patterns', async () => {
2023-10-24 12:11:31 +02:00
configureInput({});
2021-09-15 09:58:18 -08:00
github.context.payload.pull_request!.head = {
2023-01-11 17:09:44 -05:00
ref: 'test/feature/123'
2021-09-15 09:58:18 -08:00
};
2023-01-11 17:09:44 -05:00
usingLabelerConfigYaml('branches.yml');
await run();
2023-10-24 12:11:31 +02:00
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
2023-01-11 17:09:44 -05:00
owner: 'monalisa',
repo: 'helloworld',
issue_number: 123,
2023-01-11 17:09:44 -05:00
labels: ['test-branch', 'feature-branch']
});
2023-10-24 12:11:31 +02:00
expect(setOutputSpy).toHaveBeenCalledWith(
'new-labels',
'test-branch,feature-branch'
);
expect(setOutputSpy).toHaveBeenCalledWith(
'all-labels',
'test-branch,feature-branch'
);
});
2021-09-15 10:14:24 -08:00
2023-03-18 16:46:54 -04:00
it('can support multiple branches by batching', async () => {
2023-10-24 12:11:31 +02:00
configureInput({});
2023-01-11 17:09:44 -05:00
github.context.payload.pull_request!.head = {ref: 'fix/123'};
usingLabelerConfigYaml('branches.yml');
2021-09-15 10:14:24 -08:00
await run();
2023-10-24 12:11:31 +02:00
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
2023-01-11 17:09:44 -05:00
owner: 'monalisa',
repo: 'helloworld',
2021-09-15 10:14:24 -08:00
issue_number: 123,
2023-01-11 17:09:44 -05:00
labels: ['bug-branch']
2021-09-15 10:14:24 -08:00
});
2023-10-24 12:11:31 +02:00
expect(setOutputSpy).toHaveBeenCalledWith('new-labels', 'bug-branch');
expect(setOutputSpy).toHaveBeenCalledWith('all-labels', 'bug-branch');
2021-09-15 10:14:24 -08:00
});
2021-09-29 10:32:05 -04:00
2023-05-17 07:26:44 -04:00
it('can support multiple branches by providing an array', async () => {
2023-10-24 12:11:31 +02:00
configureInput({});
2023-05-17 07:26:44 -04:00
github.context.payload.pull_request!.head = {ref: 'array/123'};
usingLabelerConfigYaml('branches.yml');
await run();
2023-10-24 12:11:31 +02:00
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
2023-05-17 07:26:44 -04:00
owner: 'monalisa',
repo: 'helloworld',
issue_number: 123,
labels: ['array-branch']
});
2023-10-24 12:11:31 +02:00
expect(setOutputSpy).toHaveBeenCalledWith('new-labels', 'array-branch');
expect(setOutputSpy).toHaveBeenCalledWith('all-labels', 'array-branch');
2023-05-17 07:26:44 -04:00
});
2023-05-17 07:05:19 -04:00
it('adds a label when matching any and all patterns are provided', async () => {
2023-10-24 12:11:31 +02:00
configureInput({});
2023-05-17 07:05:19 -04:00
usingLabelerConfigYaml('any_and_all.yml');
mockGitHubResponseChangedFiles('tests/test.ts');
2021-09-29 10:32:05 -04:00
await run();
2023-10-24 12:11:31 +02:00
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
2023-01-11 17:09:44 -05:00
owner: 'monalisa',
repo: 'helloworld',
2021-09-29 10:32:05 -04:00
issue_number: 123,
2023-05-17 07:05:19 -04:00
labels: ['tests']
2021-09-29 10:32:05 -04:00
});
2023-10-24 12:11:31 +02:00
expect(setOutputSpy).toHaveBeenCalledWith('new-labels', 'tests');
expect(setOutputSpy).toHaveBeenCalledWith('all-labels', 'tests');
2021-09-29 10:32:05 -04:00
});
2023-05-17 07:05:19 -04:00
it('does not add a label when not all any and all patterns are matched', async () => {
2023-10-24 12:11:31 +02:00
configureInput({});
2023-05-17 07:05:19 -04:00
usingLabelerConfigYaml('any_and_all.yml');
mockGitHubResponseChangedFiles('tests/requirements.txt');
await run();
2023-10-24 12:11:31 +02:00
expect(setLabelsMock).toHaveBeenCalledTimes(0);
2023-05-17 07:05:19 -04:00
});
2023-10-24 12:11:31 +02:00
it('(with sync-labels: true) it deletes preexisting PR labels that no longer match the glob pattern', async () => {
2023-03-27 09:44:05 +01:00
configureInput({
'repo-token': 'foo',
'configuration-path': 'bar',
'sync-labels': true
2023-03-27 09:44:05 +01:00
});
2021-06-04 14:46:22 -04:00
usingLabelerConfigYaml('only_pdfs.yml');
mockGitHubResponseChangedFiles('foo.txt');
2021-06-04 14:46:22 -04:00
getPullMock.mockResolvedValue(<any>{
data: {
labels: [{name: 'touched-a-pdf-file'}, {name: 'manually-added'}]
}
2021-06-04 14:46:22 -04:00
});
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
2021-06-04 14:46:22 -04:00
issue_number: 123,
labels: ['manually-added']
2021-06-04 14:46:22 -04:00
});
2023-10-24 12:11:31 +02:00
2023-06-29 03:45:24 -07:00
expect(setOutputSpy).toHaveBeenCalledWith('new-labels', '');
expect(setOutputSpy).toHaveBeenCalledWith('all-labels', 'manually-added');
2021-06-04 14:46:22 -04:00
});
it('(with sync-labels: false) it issues no delete calls even when there are preexisting PR labels that no longer match the glob pattern', async () => {
2023-03-27 09:44:05 +01:00
configureInput({
'repo-token': 'foo',
'configuration-path': 'bar',
2023-05-31 01:45:27 +01:00
'sync-labels': false
2023-03-27 09:44:05 +01:00
});
2021-06-04 14:46:22 -04:00
usingLabelerConfigYaml('only_pdfs.yml');
mockGitHubResponseChangedFiles('foo.txt');
2021-06-04 14:46:22 -04:00
getPullMock.mockResolvedValue(<any>{
data: {
labels: [{name: 'touched-a-pdf-file'}, {name: 'manually-added'}]
}
2021-06-04 14:46:22 -04:00
});
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(0);
2023-06-29 03:45:24 -07:00
expect(setOutputSpy).toHaveBeenCalledWith('new-labels', '');
expect(setOutputSpy).toHaveBeenCalledWith(
'all-labels',
'touched-a-pdf-file,manually-added'
);
});
it('(with sync-labels: false) it only logs the excess labels', async () => {
configureInput({
'repo-token': 'foo',
'configuration-path': 'bar',
'sync-labels': false
});
usingLabelerConfigYaml('only_pdfs.yml');
mockGitHubResponseChangedFiles('foo.pdf');
const existingLabels = Array.from({length: 100}).map((_, idx) => ({
name: `existing-label-${idx}`
}));
getPullMock.mockResolvedValue(<any>{
data: {
labels: existingLabels
}
});
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(0);
expect(coreWarningMock).toHaveBeenCalledTimes(1);
expect(coreWarningMock).toHaveBeenCalledWith(
'Maximum of 100 labels allowed. Excess labels: touched-a-pdf-file',
{title: 'Label limit for a PR exceeded'}
);
2023-06-29 03:45:24 -07:00
const allLabels: string = existingLabels.map(i => i.name).join(',');
expect(setOutputSpy).toHaveBeenCalledWith('new-labels', '');
expect(setOutputSpy).toHaveBeenCalledWith('all-labels', allLabels);
2021-06-04 14:46:22 -04:00
});
it('(with pr-number: array of one item, uses the PR number specified in the parameters', async () => {
configureInput({
'repo-token': 'foo',
'configuration-path': 'bar',
'pr-number': ['104']
});
usingLabelerConfigYaml('only_pdfs.yml');
mockGitHubResponseChangedFiles('foo.pdf');
getPullMock.mockResolvedValue(<any>{
data: {
labels: [{name: 'manually-added'}]
}
});
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
issue_number: 104,
labels: ['manually-added', 'touched-a-pdf-file']
});
expect(setOutputSpy).toHaveBeenCalledWith(
'new-labels',
'touched-a-pdf-file'
);
expect(setOutputSpy).toHaveBeenCalledWith(
'all-labels',
'manually-added,touched-a-pdf-file'
);
});
it('(with pr-number: array of two items, uses the PR number specified in the parameters', async () => {
configureInput({
'repo-token': 'foo',
'configuration-path': 'bar',
'pr-number': ['104', '150']
});
usingLabelerConfigYaml('only_pdfs.yml');
mockGitHubResponseChangedFiles('foo.pdf');
getPullMock.mockResolvedValueOnce(<any>{
data: {
labels: [{name: 'manually-added'}]
}
});
getPullMock.mockResolvedValueOnce(<any>{
data: {
labels: []
}
});
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(2);
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
issue_number: 104,
labels: ['manually-added', 'touched-a-pdf-file']
});
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
issue_number: 150,
labels: ['touched-a-pdf-file']
});
expect(setOutputSpy).toHaveBeenCalledWith(
'new-labels',
'touched-a-pdf-file'
);
expect(setOutputSpy).toHaveBeenCalledWith(
'all-labels',
'manually-added,touched-a-pdf-file'
);
});
it('does not add labels to PRs that have no changed files', async () => {
usingLabelerConfigYaml('only_pdfs.yml');
mockGitHubResponseChangedFiles();
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(0);
});
describe('changed-files-labels-limit', () => {
it('applies all labels when count is within limit', async () => {
configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'};
usingLabelerConfigYaml('limit_3.yml');
mockGitHubResponseChangedFiles(
'components/a/file.ts',
'components/b/file.ts'
);
getPullMock.mockResolvedValue(<any>{
data: {labels: []}
});
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
issue_number: 123,
labels: ['component-a', 'component-b']
});
});
it('skips changed-files labels when count exceeds limit', async () => {
configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'};
usingLabelerConfigYaml('limit_2.yml');
mockGitHubResponseChangedFiles(
'components/a/file.ts',
'components/b/file.ts',
'components/c/file.ts'
);
getPullMock.mockResolvedValue(<any>{
data: {labels: []}
});
await run();
// No labels should be applied since changed-files labels exceed limit
expect(setLabelsMock).toHaveBeenCalledTimes(0);
});
it('still applies branch-based labels when changed-files limit is exceeded', async () => {
configureInput({});
github.context.payload.pull_request!.head = {ref: 'test/some-feature'};
usingLabelerConfigYaml('limit_1.yml');
mockGitHubResponseChangedFiles(
'components/a/file.ts',
'components/b/file.ts',
'components/c/file.ts'
);
getPullMock.mockResolvedValue(<any>{
data: {labels: []}
});
await run();
// Only the branch-based label should be applied
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
issue_number: 123,
labels: ['test-branch']
});
});
it('applies all labels when no limit is set', async () => {
configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'};
usingLabelerConfigYaml('mixed_labels.yml');
mockGitHubResponseChangedFiles(
'components/a/file.ts',
'components/b/file.ts',
'components/c/file.ts',
'components/d/file.ts'
);
getPullMock.mockResolvedValue(<any>{
data: {labels: []}
});
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
issue_number: 123,
labels: ['component-a', 'component-b', 'component-c', 'component-d']
});
});
it('does not count preexisting labels toward the limit', async () => {
configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'};
usingLabelerConfigYaml('limit_2.yml');
mockGitHubResponseChangedFiles(
'components/a/file.ts',
'components/b/file.ts',
'components/c/file.ts',
'components/d/file.ts'
);
getPullMock.mockResolvedValue(<any>{
data: {labels: [{name: 'component-a'}, {name: 'component-b'}]}
});
await run();
// component-a and component-b are preexisting, so only 2 new labels (c, d) would be added
// which equals the limit of 2, so labels should be applied
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
issue_number: 123,
labels: ['component-a', 'component-b', 'component-c', 'component-d']
});
});
it('skips new labels when new count exceeds limit even with preexisting', async () => {
configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'};
usingLabelerConfigYaml('limit_2.yml');
mockGitHubResponseChangedFiles(
'components/a/file.ts',
'components/b/file.ts',
'components/c/file.ts',
'components/d/file.ts'
);
getPullMock.mockResolvedValue(<any>{
data: {labels: [{name: 'component-a'}]}
});
await run();
// component-a is preexisting, so 3 new labels (b, c, d) would be added
// which exceeds the limit of 2, so no new changed-files labels are applied
expect(setLabelsMock).toHaveBeenCalledTimes(0);
});
it('applies labels when new count equals the limit', async () => {
configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'};
usingLabelerConfigYaml('limit_2.yml');
mockGitHubResponseChangedFiles(
'components/a/file.ts',
'components/b/file.ts'
);
getPullMock.mockResolvedValue(<any>{
data: {labels: []}
});
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
issue_number: 123,
labels: ['component-a', 'component-b']
});
});
it('skips all changed-files labels when limit is 0', async () => {
configureInput({});
github.context.payload.pull_request!.head = {ref: 'test/some-feature'};
usingLabelerConfigYaml('limit_0.yml');
mockGitHubResponseChangedFiles('components/a/file.ts');
getPullMock.mockResolvedValue(<any>{
data: {labels: []}
});
await run();
// With limit 0, only branch-based labels should be applied
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
issue_number: 123,
labels: ['test-branch']
});
});
it('treats labels with mixed rules as changed-files labels', async () => {
// A label that has both branch and changed-files rules is considered
// a "changed-files label" and subject to the limit, even if it matches
// via the branch rule
configureInput({});
github.context.payload.pull_request!.head = {ref: 'test/some-feature'};
usingLabelerConfigYaml('mixed_rules.yml');
mockGitHubResponseChangedFiles('unrelated/file.ts');
getPullMock.mockResolvedValue(<any>{
data: {labels: []}
});
await run();
// The mixed-label matches via branch rule but is still subject to limit
// because it contains a changed-files rule in its definition.
// Only pure-branch-label should be applied.
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
issue_number: 123,
labels: ['pure-branch-label']
});
});
});
describe('max-files-changed', () => {
it('applies labels when changed files count is within limit', async () => {
configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'};
usingLabelerConfigYaml('max_files_5.yml');
mockGitHubResponseChangedFiles(
'components/a/file.ts',
'components/b/file.ts',
'components/c/file.ts'
);
getPullMock.mockResolvedValue(<any>{
data: {labels: []}
});
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
issue_number: 123,
labels: ['component-a', 'component-b', 'component-c']
});
});
it('skips file-based labels when changed files exceed limit', async () => {
configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'};
usingLabelerConfigYaml('max_files_5.yml');
mockGitHubResponseChangedFiles(
'components/a/file1.ts',
'components/a/file2.ts',
'components/b/file1.ts',
'components/b/file2.ts',
'components/c/file1.ts',
'components/c/file2.ts' // 6 files > limit of 5
);
getPullMock.mockResolvedValue(<any>{
data: {labels: []}
});
await run();
// No labels should be applied since changed files exceed limit
expect(setLabelsMock).toHaveBeenCalledTimes(0);
});
it('applies labels when changed files count equals limit', async () => {
configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'};
usingLabelerConfigYaml('max_files_5.yml');
mockGitHubResponseChangedFiles(
'components/a/file1.ts',
'components/a/file2.ts',
'components/b/file1.ts',
'components/b/file2.ts',
'components/c/file.ts' // exactly 5 files = limit
);
getPullMock.mockResolvedValue(<any>{
data: {labels: []}
});
await run();
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
issue_number: 123,
labels: ['component-a', 'component-b', 'component-c']
});
});
it('still applies branch-based labels when max-files-changed is exceeded', async () => {
configureInput({});
github.context.payload.pull_request!.head = {ref: 'test/some-feature'};
usingLabelerConfigYaml('max_files_with_branch.yml');
mockGitHubResponseChangedFiles(
'components/a/file1.ts',
'components/a/file2.ts',
'components/b/file1.ts',
'components/b/file2.ts' // 4 files > limit of 3
);
getPullMock.mockResolvedValue(<any>{
data: {labels: []}
});
await run();
// Only the branch-based label should be applied
expect(setLabelsMock).toHaveBeenCalledTimes(1);
expect(setLabelsMock).toHaveBeenCalledWith({
owner: 'monalisa',
repo: 'helloworld',
issue_number: 123,
labels: ['test-branch']
});
});
it('preserves preexisting changed-files labels with sync-labels when max-files-changed is exceeded', async () => {
configureInput({'sync-labels': true});
github.context.payload.pull_request!.head = {ref: 'main'};
usingLabelerConfigYaml('max_files_5.yml');
mockGitHubResponseChangedFiles(
'unrelated/file1.ts',
'unrelated/file2.ts',
'unrelated/file3.ts',
'unrelated/file4.ts',
'unrelated/file5.ts',
'unrelated/file6.ts' // 6 files > limit of 5
);
getPullMock.mockResolvedValue(<any>{
data: {labels: [{name: 'component-a'}]} // preexisting label
});
await run();
// No setLabels call because labels should remain unchanged
// (component-a is preserved, not removed by sync-labels)
expect(setLabelsMock).toHaveBeenCalledTimes(0);
});
});
it('should use local configuration file if it exists', async () => {
const configFile = 'only_pdfs.yml';
const configFilePath = path.join(__dirname, 'fixtures', configFile);
mockGitHubResponseChangedFiles('foo.pdf');
const readFileSyncOptions = {encoding: 'utf8'};
configureInput({
'configuration-path': configFilePath
});
await run();
expect(existsSyncMock).toHaveBeenCalledWith(configFilePath);
expect(readFileSyncMock).toHaveBeenCalledWith(
configFilePath,
readFileSyncOptions
);
expect(reposMock).not.toHaveBeenCalled();
});
it('should fetch configuration file from API if it does not exist locally', async () => {
const configFilePath = 'non_existed_path/labeler.yml';
mockGitHubResponseChangedFiles('foo.pdf');
configureInput({
'configuration-path': configFilePath
});
await run();
expect(existsSyncMock).toHaveBeenCalledWith(configFilePath);
expect(readFileSyncMock).not.toHaveBeenCalled();
expect(reposMock).toHaveBeenCalled();
});
2023-07-11 14:57:50 +02:00
test.each([
[new HttpError('Error message')],
[new NotFound('Error message')]
2023-07-11 14:57:50 +02:00
])(
'should warn if configuration file could not be fetched through the API, log error and fail the action',
async error => {
const configFilePath = 'non_existed_path/labeler.yml';
reposMock.mockImplementation(() => {
throw error;
});
const warningMessage = `The config file was not found at ${configFilePath}. Make sure it exists and that this action has the correct access rights.`;
mockGitHubResponseChangedFiles('foo.pdf');
configureInput({
'configuration-path': configFilePath
});
await run();
expect(coreWarningMock).toHaveBeenCalledWith(warningMessage);
expect(coreErrorMock).toHaveBeenCalledWith(error);
expect(coreSetFailedMock).toHaveBeenCalledWith(error.message);
}
);
2021-06-04 14:46:22 -04:00
});
function usingLabelerConfigYaml(fixtureName: keyof typeof yamlFixtures): void {
reposMock.mockResolvedValue(<any>{
data: {content: yamlFixtures[fixtureName], encoding: 'utf8'}
2021-06-04 14:46:22 -04:00
});
}
function mockGitHubResponseChangedFiles(...files: string[]): void {
const returnValue = files.map(f => ({filename: f}));
2021-06-04 14:46:22 -04:00
paginateMock.mockReturnValue(<any>returnValue);
}