mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-15 18:30:39 +09:00
Improve issue assignee update responsiveness
This commit is contained in:
parent
bac5afa647
commit
038dd2bb82
5 changed files with 491 additions and 10 deletions
|
|
@ -1,8 +1,12 @@
|
|||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import type { Issue } from "@paperclipai/shared";
|
||||
import {
|
||||
applyOptimisticIssueFieldUpdate,
|
||||
applyOptimisticIssueFieldUpdateToCollection,
|
||||
applyOptimisticIssueCommentUpdate,
|
||||
createOptimisticIssueComment,
|
||||
isQueuedIssueComment,
|
||||
matchesIssueRef,
|
||||
mergeIssueComments,
|
||||
upsertIssueComment,
|
||||
} from "./optimistic-issue-comments";
|
||||
|
|
@ -177,6 +181,267 @@ describe("optimistic issue comments", () => {
|
|||
expect(next?.assigneeUserId).toBe("board-2");
|
||||
});
|
||||
|
||||
it("applies optimistic field updates for issue property edits", () => {
|
||||
const next = applyOptimisticIssueFieldUpdate(
|
||||
{
|
||||
id: "issue-1",
|
||||
companyId: "company-1",
|
||||
projectId: "project-1",
|
||||
projectWorkspaceId: "workspace-1",
|
||||
goalId: null,
|
||||
parentId: null,
|
||||
title: "Fix property pane",
|
||||
description: null,
|
||||
status: "todo",
|
||||
priority: "medium",
|
||||
assigneeAgentId: "agent-1",
|
||||
assigneeUserId: null,
|
||||
checkoutRunId: null,
|
||||
executionRunId: null,
|
||||
executionAgentNameKey: null,
|
||||
executionLockedAt: null,
|
||||
createdByAgentId: null,
|
||||
createdByUserId: "board-1",
|
||||
issueNumber: 1,
|
||||
identifier: "PAP-1",
|
||||
originKind: "manual",
|
||||
originId: null,
|
||||
originRunId: null,
|
||||
requestDepth: 0,
|
||||
billingCode: null,
|
||||
assigneeAdapterOverrides: null,
|
||||
executionWorkspaceId: "exec-1",
|
||||
executionWorkspacePreference: "shared_workspace",
|
||||
executionWorkspaceSettings: null,
|
||||
startedAt: null,
|
||||
completedAt: null,
|
||||
cancelledAt: null,
|
||||
hiddenAt: null,
|
||||
labelIds: ["label-1", "label-2"],
|
||||
labels: [
|
||||
{
|
||||
id: "label-1",
|
||||
companyId: "company-1",
|
||||
name: "One",
|
||||
color: "#111111",
|
||||
createdAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
updatedAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
},
|
||||
{
|
||||
id: "label-2",
|
||||
companyId: "company-1",
|
||||
name: "Two",
|
||||
color: "#222222",
|
||||
createdAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
updatedAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
},
|
||||
],
|
||||
blockedBy: [
|
||||
{
|
||||
id: "issue-2",
|
||||
identifier: "PAP-2",
|
||||
title: "First blocker",
|
||||
status: "todo",
|
||||
priority: "medium",
|
||||
assigneeAgentId: null,
|
||||
assigneeUserId: null,
|
||||
},
|
||||
{
|
||||
id: "issue-3",
|
||||
identifier: "PAP-3",
|
||||
title: "Second blocker",
|
||||
status: "todo",
|
||||
priority: "medium",
|
||||
assigneeAgentId: null,
|
||||
assigneeUserId: null,
|
||||
},
|
||||
],
|
||||
blocks: [],
|
||||
project: {
|
||||
id: "project-1",
|
||||
companyId: "company-1",
|
||||
urlKey: "project-one",
|
||||
goalId: null,
|
||||
goalIds: [],
|
||||
goals: [],
|
||||
name: "Project one",
|
||||
description: null,
|
||||
status: "in_progress",
|
||||
leadAgentId: null,
|
||||
targetDate: null,
|
||||
color: null,
|
||||
env: null,
|
||||
pauseReason: null,
|
||||
pausedAt: null,
|
||||
executionWorkspacePolicy: null,
|
||||
codebase: {
|
||||
workspaceId: null,
|
||||
repoUrl: null,
|
||||
repoRef: null,
|
||||
defaultRef: null,
|
||||
repoName: null,
|
||||
localFolder: null,
|
||||
managedFolder: "/tmp/paperclip",
|
||||
effectiveLocalFolder: "/tmp/paperclip",
|
||||
origin: "local_folder",
|
||||
},
|
||||
workspaces: [],
|
||||
primaryWorkspace: null,
|
||||
archivedAt: null,
|
||||
createdAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
updatedAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
},
|
||||
currentExecutionWorkspace: {
|
||||
id: "exec-1",
|
||||
companyId: "company-1",
|
||||
projectId: "project-1",
|
||||
projectWorkspaceId: null,
|
||||
sourceIssueId: "issue-1",
|
||||
mode: "shared_workspace",
|
||||
strategyType: "project_primary",
|
||||
branchName: null,
|
||||
status: "active",
|
||||
name: "Execution workspace",
|
||||
cwd: "/tmp/paperclip",
|
||||
repoUrl: null,
|
||||
baseRef: null,
|
||||
providerType: "local_fs",
|
||||
providerRef: null,
|
||||
derivedFromExecutionWorkspaceId: null,
|
||||
lastUsedAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
cleanupEligibleAt: null,
|
||||
cleanupReason: null,
|
||||
config: null,
|
||||
metadata: null,
|
||||
createdAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
updatedAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
openedAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
closedAt: null,
|
||||
},
|
||||
createdAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
updatedAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
},
|
||||
{
|
||||
status: "in_review",
|
||||
assigneeAgentId: null,
|
||||
assigneeUserId: "board-2",
|
||||
labelIds: ["label-2"],
|
||||
blockedByIssueIds: ["issue-3"],
|
||||
projectId: "project-2",
|
||||
executionWorkspaceId: "exec-2",
|
||||
},
|
||||
);
|
||||
|
||||
expect(next?.status).toBe("in_review");
|
||||
expect(next?.assigneeAgentId).toBeNull();
|
||||
expect(next?.assigneeUserId).toBe("board-2");
|
||||
expect(next?.labelIds).toEqual(["label-2"]);
|
||||
expect(next?.labels?.map((label) => label.id)).toEqual(["label-2"]);
|
||||
expect(next?.blockedBy?.map((relation) => relation.id)).toEqual(["issue-3"]);
|
||||
expect(next?.projectId).toBe("project-2");
|
||||
expect(next?.project).toBeNull();
|
||||
expect(next?.executionWorkspaceId).toBe("exec-2");
|
||||
expect(next?.currentExecutionWorkspace).toBeNull();
|
||||
});
|
||||
|
||||
it("matches issues by either uuid or identifier reference", () => {
|
||||
expect(matchesIssueRef({ id: "issue-1", identifier: "PAP-1" } as const, ["issue-1"])).toBe(true);
|
||||
expect(matchesIssueRef({ id: "issue-1", identifier: "PAP-1" } as const, ["PAP-1"])).toBe(true);
|
||||
expect(matchesIssueRef({ id: "issue-1", identifier: "PAP-1" } as const, ["issue-2", "PAP-2"])).toBe(false);
|
||||
});
|
||||
|
||||
it("applies optimistic field updates across cached issue collections", () => {
|
||||
const issues: Issue[] = [
|
||||
{
|
||||
id: "issue-1",
|
||||
companyId: "company-1",
|
||||
projectId: null,
|
||||
projectWorkspaceId: null,
|
||||
goalId: null,
|
||||
parentId: null,
|
||||
title: "Fix property pane",
|
||||
description: null,
|
||||
status: "todo",
|
||||
priority: "medium",
|
||||
assigneeAgentId: "agent-1",
|
||||
assigneeUserId: null,
|
||||
checkoutRunId: null,
|
||||
executionRunId: null,
|
||||
executionAgentNameKey: null,
|
||||
executionLockedAt: null,
|
||||
createdByAgentId: null,
|
||||
createdByUserId: "board-1",
|
||||
issueNumber: 1,
|
||||
identifier: "PAP-1",
|
||||
originKind: "manual",
|
||||
originId: null,
|
||||
originRunId: null,
|
||||
requestDepth: 0,
|
||||
billingCode: null,
|
||||
assigneeAdapterOverrides: null,
|
||||
executionWorkspaceId: null,
|
||||
executionWorkspacePreference: null,
|
||||
executionWorkspaceSettings: null,
|
||||
startedAt: null,
|
||||
completedAt: null,
|
||||
cancelledAt: null,
|
||||
hiddenAt: null,
|
||||
labelIds: [],
|
||||
labels: [],
|
||||
blockedBy: [],
|
||||
blocks: [],
|
||||
createdAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
updatedAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
},
|
||||
{
|
||||
id: "issue-2",
|
||||
companyId: "company-1",
|
||||
projectId: null,
|
||||
projectWorkspaceId: null,
|
||||
goalId: null,
|
||||
parentId: null,
|
||||
title: "Leave me alone",
|
||||
description: null,
|
||||
status: "todo",
|
||||
priority: "medium",
|
||||
assigneeAgentId: "agent-2",
|
||||
assigneeUserId: null,
|
||||
checkoutRunId: null,
|
||||
executionRunId: null,
|
||||
executionAgentNameKey: null,
|
||||
executionLockedAt: null,
|
||||
createdByAgentId: null,
|
||||
createdByUserId: "board-1",
|
||||
issueNumber: 2,
|
||||
identifier: "PAP-2",
|
||||
originKind: "manual",
|
||||
originId: null,
|
||||
originRunId: null,
|
||||
requestDepth: 0,
|
||||
billingCode: null,
|
||||
assigneeAdapterOverrides: null,
|
||||
executionWorkspaceId: null,
|
||||
executionWorkspacePreference: null,
|
||||
executionWorkspaceSettings: null,
|
||||
startedAt: null,
|
||||
completedAt: null,
|
||||
cancelledAt: null,
|
||||
hiddenAt: null,
|
||||
labelIds: [],
|
||||
labels: [],
|
||||
blockedBy: [],
|
||||
blocks: [],
|
||||
createdAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
updatedAt: new Date("2026-03-28T14:00:00.000Z"),
|
||||
},
|
||||
];
|
||||
|
||||
const next = applyOptimisticIssueFieldUpdateToCollection(issues, ["PAP-1"], { assigneeAgentId: "agent-9" });
|
||||
|
||||
expect(next?.[0]?.assigneeAgentId).toBe("agent-9");
|
||||
expect(next?.[1]?.assigneeAgentId).toBe("agent-2");
|
||||
});
|
||||
|
||||
it("treats comments without a run id as queued when they arrive during an active run", () => {
|
||||
expect(
|
||||
isQueuedIssueComment({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue