fix(jira): surface dispatch failures (#11918)

This commit is contained in:
Hugo Pereira Brito
2026-07-09 16:09:12 +01:00
committed by GitHub
parent 01c004a7af
commit be78fe8374
10 changed files with 632 additions and 66 deletions
@@ -0,0 +1,152 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
const { pollTaskUntilSettledMock } = vi.hoisted(() => ({
pollTaskUntilSettledMock: vi.fn(),
}));
vi.mock("@/actions/task/poll", () => ({
pollTaskUntilSettled: pollTaskUntilSettledMock,
}));
vi.mock("@/lib", () => ({
apiBaseUrl: "https://api.example.com/api/v1",
getAuthHeaders: vi.fn(),
}));
vi.mock("@/lib/server-actions-helper", () => ({
handleApiError: vi.fn(),
}));
import { pollJiraDispatchTask } from "./jira-dispatch";
describe("pollJiraDispatchTask", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("should return the backend error when a completed task has failed Jira dispatches", async () => {
// Given
pollTaskUntilSettledMock.mockResolvedValue({
ok: true,
state: "completed",
result: {
created_count: 0,
failed_count: 1,
error: "Jira project requires custom fields: Team is required",
},
});
// When
const result = await pollJiraDispatchTask("task-123");
// Then
expect(result).toEqual({
success: false,
error: "Jira project requires custom fields: Team is required",
});
});
it("should return a fallback error when a completed task has failures without an error", async () => {
// Given
pollTaskUntilSettledMock.mockResolvedValue({
ok: true,
state: "completed",
result: {
created_count: 0,
failed_count: 1,
},
});
// When
const result = await pollJiraDispatchTask("task-123");
// Then
expect(result).toEqual({
success: false,
error: "Failed to create Jira issue.",
});
});
it("should return a plural fallback error when a completed task has multiple failures without an error", async () => {
// Given
pollTaskUntilSettledMock.mockResolvedValue({
ok: true,
state: "completed",
result: {
created_count: 0,
failed_count: 3,
},
});
// When
const result = await pollJiraDispatchTask("task-123");
// Then
expect(result).toEqual({
success: false,
error: "Failed to create 3 Jira issues.",
});
});
it("should surface task failure result errors", async () => {
// Given
pollTaskUntilSettledMock.mockResolvedValue({
ok: true,
state: "failed",
result: {
error: "Jira credentials are invalid.",
},
});
// When
const result = await pollJiraDispatchTask("task-123");
// Then
expect(result).toEqual({
success: false,
error: "Jira credentials are invalid.",
});
});
it("should return success when a completed task has no failures", async () => {
// Given
pollTaskUntilSettledMock.mockResolvedValue({
ok: true,
state: "completed",
result: {
created_count: 1,
failed_count: 0,
},
});
// When
const result = await pollJiraDispatchTask("task-123");
// Then
expect(result).toEqual({
success: true,
message: "Finding successfully sent to Jira!",
});
});
it("should return a fallback error when no Jira issue was created", async () => {
// Given
pollTaskUntilSettledMock.mockResolvedValue({
ok: true,
state: "completed",
result: {
created_count: 0,
failed_count: 0,
},
});
// When
const result = await pollJiraDispatchTask("task-123");
// Then
expect(result).toEqual({
success: false,
error: "Failed to create Jira issue.",
});
});
});
+8 -2
View File
@@ -159,12 +159,18 @@ export const pollJiraDispatchTask = async (
const jiraResult = result as JiraTaskResult | undefined;
if (state === "completed") {
if (!jiraResult?.error) {
const createdCount = jiraResult?.created_count ?? 0;
const failedCount = jiraResult?.failed_count ?? 0;
if (!jiraResult?.error && failedCount === 0 && createdCount > 0) {
return { success: true, message: "Finding successfully sent to Jira!" };
}
return {
success: false,
error: jiraResult?.error || "Failed to create Jira issue.",
error:
jiraResult?.error ||
(failedCount > 1
? `Failed to create ${failedCount} Jira issues.`
: "Failed to create Jira issue."),
};
}
@@ -0,0 +1 @@
Jira dispatch polling now reports failed issue creation tasks instead of treating partial failures as successful
+2
View File
@@ -64,6 +64,8 @@ export interface JiraDispatchResponse {
message?: string;
issue_url?: string;
issue_key?: string;
created_count?: number;
failed_count?: number;
} | null;
task_args: Record<string, unknown> | null;
metadata: Record<string, unknown> | null;