fc341e6cff
Co-authored-by: Kandev Agent <agent@kandev.dev>
267 lines
7.6 KiB
TypeScript
267 lines
7.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { buildSidebarItem } from "./task-session-sidebar-item";
|
|
|
|
type SidebarTask = Parameters<typeof buildSidebarItem>[0];
|
|
type SidebarContext = Parameters<typeof buildSidebarItem>[1];
|
|
const UPDATED_AT = "2026-07-22T00:00:00Z";
|
|
const ACTIVITY_AT = "2026-07-24T00:00:00Z";
|
|
const CREATED_AT = "2026-07-20T00:00:00Z";
|
|
const SUMMARY_UPDATED_AT = "2026-07-25T00:00:00Z";
|
|
|
|
function emptyContext(): SidebarContext {
|
|
return {
|
|
repositorySlugById: new Map(),
|
|
titleById: new Map(),
|
|
workflowNameById: new Map(),
|
|
stepTitleById: new Map(),
|
|
};
|
|
}
|
|
|
|
function task(overrides: Partial<SidebarTask> = {}): SidebarTask {
|
|
return {
|
|
id: "t1",
|
|
_workflowId: "wf1",
|
|
title: "Task",
|
|
workflowStepId: "step-1",
|
|
...overrides,
|
|
} as SidebarTask;
|
|
}
|
|
|
|
function repositoryProjectionTask(): SidebarTask {
|
|
return task({
|
|
repositoryId: "repo-a",
|
|
repositories: [
|
|
{ id: "link-b", repository_id: "repo-b", base_branch: "main", position: 2 },
|
|
{ id: "link-a", repository_id: "repo-a", base_branch: "main", position: 1 },
|
|
{
|
|
id: "link-a-duplicate",
|
|
repository_id: "repo-a",
|
|
base_branch: "main",
|
|
position: 3,
|
|
},
|
|
],
|
|
});
|
|
}
|
|
|
|
function repositoryProjectionContext(): SidebarContext {
|
|
return {
|
|
...emptyContext(),
|
|
repositorySlugById: new Map([
|
|
["repo-a", "owner/repo-a"],
|
|
["repo-b", "owner/repo-b"],
|
|
]),
|
|
};
|
|
}
|
|
|
|
describe("buildSidebarItem", () => {
|
|
it("projects unique repository slugs in attachment order", () => {
|
|
const item = buildSidebarItem(repositoryProjectionTask(), repositoryProjectionContext());
|
|
|
|
expect(item.repositories).toEqual(["owner/repo-a", "owner/repo-b"]);
|
|
expect(item.repositoryLinks).toHaveLength(3);
|
|
});
|
|
|
|
it("keeps the PR aggregate state for the row icon", () => {
|
|
const item = buildSidebarItem(
|
|
task({
|
|
statusSummary: {
|
|
revision: 1,
|
|
updated_at: UPDATED_AT,
|
|
pull_request: {
|
|
number: 42,
|
|
state: "open",
|
|
aggregate_state: "pending",
|
|
},
|
|
},
|
|
}),
|
|
emptyContext(),
|
|
);
|
|
|
|
expect(item.prInfo).toEqual({ number: 42, state: "Open", aggregateState: "pending" });
|
|
});
|
|
|
|
it("uses summary presence as the authority for cleared session and pending fields", () => {
|
|
const item = buildSidebarItem(
|
|
task({
|
|
taskPendingAction: "clarification",
|
|
primarySessionState: "RUNNING",
|
|
primarySessionId: "legacy-session",
|
|
foregroundActivity: "background",
|
|
updatedAt: "legacy-update",
|
|
statusSummary: { revision: 2, updated_at: UPDATED_AT },
|
|
}),
|
|
emptyContext(),
|
|
);
|
|
|
|
expect(item.hasPendingClarification).toBe(false);
|
|
expect(item.sessionState).toBeUndefined();
|
|
expect(item.primarySessionId).toBeNull();
|
|
expect(item.foregroundActivity).toBeUndefined();
|
|
expect(item.updatedAt).toBe(UPDATED_AT);
|
|
});
|
|
|
|
it("honors the summary error acknowledgement stamp", () => {
|
|
const item = buildSidebarItem(
|
|
task({
|
|
statusSummary: {
|
|
revision: 3,
|
|
updated_at: UPDATED_AT,
|
|
active_error: {
|
|
session_id: "session-1",
|
|
stamp: "error-3",
|
|
occurred_at: UPDATED_AT,
|
|
preview: "Agent failed",
|
|
},
|
|
},
|
|
}),
|
|
{
|
|
...emptyContext(),
|
|
acknowledgedAgentErrors: { "session-1": "error-3" },
|
|
},
|
|
);
|
|
|
|
expect(item.agentErrorMessage).toBeNull();
|
|
});
|
|
|
|
it("preserves the archived marker for projected rows", () => {
|
|
const item = buildSidebarItem(task({ isArchived: true }), emptyContext());
|
|
|
|
expect(item.isArchived).toBe(true);
|
|
});
|
|
|
|
it("carries the primary executor identity for exact remote status", () => {
|
|
const item = buildSidebarItem(
|
|
task({ primaryExecutorId: "executor-1", primaryExecutorType: "k8s" }),
|
|
emptyContext(),
|
|
);
|
|
|
|
expect(item.remoteExecutorId).toBe("executor-1");
|
|
});
|
|
|
|
it("carries the queued prompt count from the status summary", () => {
|
|
const item = buildSidebarItem(
|
|
task({ statusSummary: { revision: 4, updated_at: UPDATED_AT, queued_prompt_count: 3 } }),
|
|
emptyContext(),
|
|
);
|
|
|
|
expect(item.queuedCount).toBe(3);
|
|
});
|
|
|
|
it("leaves queuedCount undefined when the summary has no queued prompts", () => {
|
|
const item = buildSidebarItem(
|
|
task({ statusSummary: { revision: 5, updated_at: UPDATED_AT } }),
|
|
emptyContext(),
|
|
);
|
|
|
|
expect(item.queuedCount).toBeUndefined();
|
|
});
|
|
|
|
it("carries WIP queue status separately from queued prompts", () => {
|
|
const wipQueue = { position: 2, total: 4, destinationTitle: "Review" };
|
|
const item = buildSidebarItem(task(), {
|
|
...emptyContext(),
|
|
wipQueueByTaskId: new Map([["t1", wipQueue]]),
|
|
});
|
|
|
|
expect(item.wipQueue).toEqual(wipQueue);
|
|
expect(item.queuedCount).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("buildSidebarItem Office identity", () => {
|
|
// @covers AC-TASKS-SUBTASK-REPARENTING-DRAG-DROP-001.4
|
|
it("preserves isFromOffice for candidate filtering", () => {
|
|
const item = buildSidebarItem(task({ isFromOffice: true }), emptyContext());
|
|
|
|
expect(item.isFromOffice).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("buildSidebarItem — parked on background work", () => {
|
|
it("carries the parked-on-background-work projection through to the sidebar row", () => {
|
|
const item = buildSidebarItem(task({ parkedOnBackgroundWork: true }), emptyContext());
|
|
|
|
expect(item.parkedOnBackgroundWork).toBe(true);
|
|
});
|
|
|
|
it("leaves parkedOnBackgroundWork undefined when the task record has no projection", () => {
|
|
const item = buildSidebarItem(task(), emptyContext());
|
|
|
|
expect(item.parkedOnBackgroundWork).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("buildSidebarItem priority", () => {
|
|
it("carries task priority into the desktop sidebar row", () => {
|
|
const item = buildSidebarItem(task({ priority: "high" }), emptyContext());
|
|
|
|
expect(item.priority).toBe("high");
|
|
});
|
|
});
|
|
|
|
// @covers AC-INTEGRATIONS-GITHUB-PR-MERGE-QUEUE-002.10
|
|
describe("buildSidebarItem automation indicators", () => {
|
|
it("carries bounded automation indicators into the row PR info", () => {
|
|
const item = buildSidebarItem(
|
|
task({
|
|
statusSummary: {
|
|
revision: 2,
|
|
updated_at: UPDATED_AT,
|
|
pull_request: {
|
|
number: 42,
|
|
state: "open",
|
|
auto_fix_enabled: true,
|
|
auto_merge_enabled: true,
|
|
},
|
|
},
|
|
}),
|
|
emptyContext(),
|
|
);
|
|
|
|
expect(item.prInfo).toEqual({
|
|
number: 42,
|
|
state: "Open",
|
|
autoFixEnabled: true,
|
|
autoMergeEnabled: true,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("buildSidebarItem activity", () => {
|
|
it("maps summary activity and falls back through task update to creation", () => {
|
|
const projected = buildSidebarItem(
|
|
task({
|
|
updatedAt: UPDATED_AT,
|
|
createdAt: CREATED_AT,
|
|
statusSummary: {
|
|
revision: 6,
|
|
updated_at: SUMMARY_UPDATED_AT,
|
|
last_activity_at: ACTIVITY_AT,
|
|
},
|
|
}),
|
|
emptyContext(),
|
|
);
|
|
expect(projected.updatedAt).toBe(SUMMARY_UPDATED_AT);
|
|
expect(projected.lastActivityAt).toBe(ACTIVITY_AT);
|
|
|
|
const updatedFallback = buildSidebarItem(
|
|
task({
|
|
updatedAt: UPDATED_AT,
|
|
createdAt: CREATED_AT,
|
|
statusSummary: { revision: 7, updated_at: SUMMARY_UPDATED_AT },
|
|
}),
|
|
emptyContext(),
|
|
);
|
|
expect(updatedFallback.lastActivityAt).toBe(UPDATED_AT);
|
|
|
|
const createdFallback = buildSidebarItem(
|
|
task({
|
|
createdAt: CREATED_AT,
|
|
statusSummary: { revision: 8, updated_at: SUMMARY_UPDATED_AT },
|
|
}),
|
|
emptyContext(),
|
|
);
|
|
expect(createdFallback.lastActivityAt).toBe(CREATED_AT);
|
|
});
|
|
});
|