mirror of
https://github.com/actions/labeler
synced 2026-09-25 15:11:47 +02:00
Preserve externally managed labels during synchronization (#957)
* Preserve externally managed pull request labels Replace whole-set label writes with batched selective additions and removals so sync-labels only manages configured labels. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 44452010-1cd5-4e90-8abe-f03547c04182 * Reconcile ambiguous label addition failures Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 44452010-1cd5-4e90-8abe-f03547c04182 * Handle paginated label reconciliation Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 44452010-1cd5-4e90-8abe-f03547c04182 --------- Copilot-Session: 44452010-1cd5-4e90-8abe-f03547c04182
This commit is contained in:
co-authored by
Copilot App
parent
bf12e9b00b
commit
b7a8804475
@@ -9,7 +9,8 @@ import type {
|
||||
// Define API mock functions at module level
|
||||
const getPullRequestsMock = jest.fn<any>();
|
||||
const getLabelConfigsMock = jest.fn<any>();
|
||||
const setLabelsMock = jest.fn<any>();
|
||||
const addLabelsMock = jest.fn<any>();
|
||||
const removeLabelsMock = jest.fn<any>();
|
||||
const getChangedFilesMock = jest.fn<any>();
|
||||
const getContentMock = jest.fn<any>();
|
||||
|
||||
@@ -42,7 +43,8 @@ jest.unstable_mockModule('@actions/github', () => ({
|
||||
jest.unstable_mockModule('../src/api/index.js', () => ({
|
||||
getPullRequests: getPullRequestsMock,
|
||||
getLabelConfigs: getLabelConfigsMock,
|
||||
setLabels: setLabelsMock,
|
||||
addLabels: addLabelsMock,
|
||||
removeLabels: removeLabelsMock,
|
||||
getChangedFiles: getChangedFilesMock,
|
||||
getContent: getContentMock
|
||||
}));
|
||||
@@ -478,7 +480,7 @@ describe('labeler error handling', () => {
|
||||
});
|
||||
|
||||
it('throws a custom error for HttpError 403 with "unauthorized" message', async () => {
|
||||
setLabelsMock.mockRejectedValue({
|
||||
addLabelsMock.mockRejectedValue({
|
||||
name: 'HttpError',
|
||||
status: 403,
|
||||
message: 'Request failed with status code 403: Unauthorized'
|
||||
@@ -495,7 +497,7 @@ describe('labeler error handling', () => {
|
||||
status: 404,
|
||||
message: 'Not Found'
|
||||
};
|
||||
setLabelsMock.mockRejectedValue(unexpectedError);
|
||||
addLabelsMock.mockRejectedValue(unexpectedError);
|
||||
|
||||
// NOTE: In the current implementation, labeler rethrows the raw error object (not an Error instance).
|
||||
// `rejects.toThrow` only works with real Error objects, so here we must use `rejects.toEqual`.
|
||||
@@ -508,7 +510,7 @@ describe('labeler error handling', () => {
|
||||
name: 'HttpError',
|
||||
message: 'Resource not accessible by integration'
|
||||
};
|
||||
setLabelsMock.mockRejectedValue(error);
|
||||
addLabelsMock.mockRejectedValue(error);
|
||||
|
||||
await labeler();
|
||||
|
||||
@@ -518,4 +520,33 @@ describe('labeler error handling', () => {
|
||||
);
|
||||
expect(core.setFailed).toHaveBeenCalledWith(error.message);
|
||||
});
|
||||
|
||||
it('reports the configured labels when a bulk removal fails', async () => {
|
||||
(core.getBooleanInput as jest.Mock).mockReturnValue(true);
|
||||
getPullRequestsMock.mockReturnValue([
|
||||
{
|
||||
number: 123,
|
||||
data: {
|
||||
node_id: 'PR_node_id',
|
||||
labels: [{name: 'stale-label', node_id: 'label_node_id'}]
|
||||
},
|
||||
changedFiles: ['file.txt']
|
||||
}
|
||||
]);
|
||||
getLabelConfigsMock.mockResolvedValue({
|
||||
labelConfigs: new Map([
|
||||
[
|
||||
'stale-label',
|
||||
[{any: [{changedFiles: [{anyGlobToAnyFile: ['*.pdf']}]}]}]
|
||||
]
|
||||
]),
|
||||
changedFilesLimit: undefined
|
||||
});
|
||||
removeLabelsMock.mockRejectedValue(new Error('GraphQL request failed'));
|
||||
|
||||
await expect(labeler()).rejects.toThrow(
|
||||
"Failed to remove configured labels 'stale-label' from PR #123"
|
||||
);
|
||||
expect(addLabelsMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user