e2e/issue-task-history.spec.ts
Rama main · réplica de lectura del repositorio original.
import { test, expect } from "@playwright/test";
import pg from "pg";
import { createTestApi } from "./helpers";
test("issue loads compact runs until Runs requests full own or subtree history", async ({ page }) => {
const api = await createTestApi();
const db = new pg.Client(process.env.DATABASE_URL);
await db.connect();
let runtimeId = "";
let agentId = "";
try {
const workspace = (await api.getWorkspaces())[0]!;
const userId = (await db.query('SELECT id FROM "user" WHERE email=$1', [api.getEmail()])).rows[0].id;
const issue = await api.createIssue("On-demand run history");
const child = await api.createIssue("Child run history", { parent_issue_id: issue.id });
// Seed finished runs without dispatching any work to a real runtime.
runtimeId = (await db.query(`
INSERT INTO agent_runtime (workspace_id,name,runtime_mode,provider,status,device_info,metadata,owner_id)
VALUES ($1,'History test runtime','cloud','test','offline','Test','{}',$2) RETURNING id
`, [workspace.id, userId])).rows[0].id;
agentId = (await db.query(`
INSERT INTO agent (workspace_id,name,description,instructions,runtime_mode,runtime_config,runtime_id,visibility,permission_mode,max_concurrent_tasks,owner_id)
VALUES ($1,'History test agent','','','cloud','{}',$2,'private','private',1,$3) RETURNING id
`, [workspace.id, runtimeId, userId])).rows[0].id;
for (const issueId of [issue.id, child.id]) {
await db.query(`
INSERT INTO agent_task_queue (agent_id,runtime_id,issue_id,status,result,handoff_note,started_at,completed_at)
VALUES ($1,$2,$3,'completed',$4,'Full handoff details',now()-interval '1 minute',now())
`, [agentId, runtimeId, issueId, JSON.stringify({
comment: "Inline run summary",
output: `Full provider output for ${issueId}`,
session_id: "history-test-session",
})]);
}
await page.setViewportSize({ width: 1440, height: 900 });
await page.addInitScript((token) => {
localStorage.setItem("multica_token", token!);
localStorage.setItem("multica:chat:isOpen", "false");
}, api.getToken());
const isIssueTasks = (url: URL) => url.pathname.endsWith(`/issues/${issue.id}/task-runs`);
const requests: URL[] = [];
page.on("request", (request) => {
const url = new URL(request.url());
if (isIssueTasks(url)) requests.push(url);
});
const compactResponse = page.waitForResponse((response) => {
const url = new URL(response.url());
return isIssueTasks(url) && url.searchParams.get("view") === "timeline";
});
await page.goto(`/${workspace.slug}/issues/${issue.id}`);
const compact = await (await compactResponse).json();
expect(compact).toHaveLength(1);
expect(compact[0].result).toEqual({ comment: "Inline run summary" });
expect(compact[0].handoff_note).toBeFalsy();
const openRuns = page.getByRole("button", { name: /^Open timeline/ });
await expect(openRuns).toBeVisible();
expect(requests.length).toBeGreaterThan(0);
expect(requests.every((url) => url.searchParams.get("view") === "timeline")).toBe(true);
const ownHistory = page.waitForResponse((response) => {
const url = new URL(response.url());
return isIssueTasks(url) && !url.searchParams.has("view") && !url.searchParams.has("scope");
});
await openRuns.click();
const own = await (await ownHistory).json();
expect(own).toHaveLength(1);
expect(own[0].result.output).toBe(`Full provider output for ${issue.id}`);
expect(own[0].handoff_note).toBe("Full handoff details");
const subtreeHistory = page.waitForResponse((response) => {
const url = new URL(response.url());
return isIssueTasks(url) && !url.searchParams.has("view") && url.searchParams.get("scope") === "subtree";
});
const includeChildren = page.getByRole("dialog").getByRole("checkbox", { name: "Include sub-issues" });
await includeChildren.check();
const subtree = await (await subtreeHistory).json();
expect(subtree).toHaveLength(2);
expect(subtree.map((task: { result: { output: string } }) => task.result.output).sort()).toEqual(
[issue.id, child.id].map((id) => `Full provider output for ${id}`).sort(),
);
expect(subtree.every((task: { handoff_note: string }) => task.handoff_note === "Full handoff details")).toBe(true);
await expect(includeChildren).toBeChecked();
} finally {
await api.cleanup();
if (agentId) await db.query("DELETE FROM agent_task_queue WHERE agent_id=$1", [agentId]);
if (agentId) await db.query("DELETE FROM agent WHERE id=$1", [agentId]);
if (runtimeId) await db.query("DELETE FROM agent_runtime WHERE id=$1", [runtimeId]);
await db.end();
}
});