From 47b7f925351c78a230b43b7ad864f72de6c43a7d Mon Sep 17 00:00:00 2001 From: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> Date: Tue, 20 Oct 2026 20:57:25 -0400 Subject: [PATCH] test(labeling): pin removeLabels continuing past an already-gone label and stopping at a real failure removeLabels is the shared removal path for /remove, /remove-, /hold cancel, /lgtm cancel, exclusive-prefix replacement, approve's label sync and require-matching-label's cleanup. Every existing 404 case removes a single label, so a loop that stopped at the first already-gone label passed the whole suite. Call removeLabels directly with three labels, answer one DELETE with 404, and assert all three are requested in order; answer one with 500 and assert the loop stops there with the label named. Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> --- __tests__/utils/labeling.test.ts | 41 +++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/__tests__/utils/labeling.test.ts b/__tests__/utils/labeling.test.ts index 1cac2508..f321e766 100644 --- a/__tests__/utils/labeling.test.ts +++ b/__tests__/utils/labeling.test.ts @@ -7,7 +7,7 @@ import { setupServer } from 'msw/node' import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' import { handleIssueComment } from '../../src/issueComment/handleIssueComment' -import { addPrefix, assertLabelsExist, getLabelConfig, labelIssue } from '../../src/utils/labeling' +import { addPrefix, assertLabelsExist, getLabelConfig, labelIssue, removeLabels } from '../../src/utils/labeling' import { newOctokit } from '../../src/utils/octokit' import issueCommentEvent from '../fixtures/issues/issueCommentEvent.json' @@ -162,6 +162,45 @@ describe('labelIssue refuses labels the repository does not have', () => { }) }) +describe('removeLabels', () => { + const context = new utils.MockContext(issueCommentEvent) + const repo = `${utils.api}/repos/Codertocat/Hello-World` + let octokit: Octokit + + beforeEach(() => { + utils.setupActionsEnv() + octokit = newOctokit('some-token') + }) + + it('keeps removing the later labels after an earlier one is already gone', async () => { + const deleted: string[] = [] + server.use( + http.delete(`${repo}/issues/1/labels/:name`, ({ params }) => { + deleted.push(String(params.name)) + return new Response(null, { status: params.name === 'gone' ? 404 : 200 }) + }), + ) + + await expect(removeLabels(octokit, context, 1, ['gone', 'kind/bug', 'still-there'])).resolves.toBeUndefined() + + expect(deleted).toEqual(['gone', 'kind/bug', 'still-there']) + }) + + it('stops at the first failure that is not a 404 and names the label', async () => { + const deleted: string[] = [] + server.use( + http.delete(`${repo}/issues/1/labels/:name`, ({ params }) => { + deleted.push(String(params.name)) + return new Response(null, { status: params.name === 'broken' ? 500 : 200 }) + }), + ) + + await expect(removeLabels(octokit, context, 1, ['first', 'broken', 'never'])).rejects.toThrow('could not remove label broken') + + expect(deleted).toEqual(['first', 'broken']) + }) +}) + describe('getLabelConfig', () => { const context = new utils.MockContext(issueCommentEvent) let octokit: Octokit