Ctrl K
ComunidadGuías…

tests/e2e/workspace-chat-history-recovery.spec.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { expect, test } from "@playwright/test";
import type { UIMessage } from "ai";
import { enableScopedE2EAuth, scopedPath } from "./helpers/auth";
import {
  fulfillJson,
  initializeTrackedEvents,
  mockActivationFlow,
  modelId,
} from "./helpers/activation-fixtures";
import { buildSandboxBackedCall } from "./helpers/sandbox-fixtures";

test("reloaded history reconciles a terminal run without replaying or losing completed results", async ({
  page,
}) => {
  await initializeTrackedEvents(page);
  await enableScopedE2EAuth(page);
  await mockActivationFlow(page);
  const savedCallId = "00000000-0000-4000-8000-000000000001";
  let savedMessages: UIMessage[] = [
    {
      id: "saved-response",
      role: "assistant",
      metadata: { ai_call_id: savedCallId },
      parts: [
        { type: "text", text: "Checking the project", state: "done" },
        {
          type: "tool-bash",
          toolCallId: "completed",
          state: "output-available",
          input: { command: "pwd" },
          output: "preserved result",
        },
        {
          type: "tool-bash",
          toolCallId: "unfinished",
          state: "input-available",
          input: { command: "check-project" },
        },
      ],
    },
  ];
  let conversationId: string | null = null;
  let chatRequests = 0;
  let resolveSaved!: () => void;
  const recoveredSaved = new Promise<void>((resolve) => {
    resolveSaved = resolve;
  });
  await page.route(/\/api\/chat(?:\?.*)?$/, async (route) => {
    chatRequests += 1;
    await route.abort();
  });
  await page.route(/\/api\/conversations(?:\?.*)?$/, async (route) => {
    const request = route.request();
    const id = new URL(request.url()).searchParams.get("id");
    if (request.method() === "GET" && id) {
      conversationId = id;
      await fulfillJson(route, {
        id,
        repo_id: "repo-1",
        model: modelId,
        mode: "AUTO",
        messages: savedMessages,
        local_msgs: [],
        title: "Saved check",
      });
    } else if (request.method() === "PUT") {
      const body = request.postDataJSON() as {
        id: string;
        messages: UIMessage[];
      };
      savedMessages = body.messages;
      if (
        body.messages.some((message) =>
          message.parts.some(
            (part) =>
              part.type === "tool-bash" &&
              part.toolCallId === "unfinished" &&
              part.state === "output-error"
          )
        )
      )
        resolveSaved();
      await fulfillJson(route, { ok: true, conversation: body });
    } else await fulfillJson(route, []);
  });
  await page.route("**/api/observability/calls?*", async (route) => {
    const params = new URL(route.request().url()).searchParams;
    const call = {
      ...buildSandboxBackedCall({
        repoId: "repo-1",
        sandboxRecordId: "sandbox-record-repo-1",
        sandboxId: "sandbox-1",
        previewUrl: null,
        computeBillingSource: "platform",
        billingProjectId: null,
        billingTeamId: null,
        aiBillingSource: "platform",
        callId: savedCallId,
      }),
      status: "failed",
      conversation_id: conversationId,
    };
    const newerCalls = Array.from({ length: 101 }, (_, index) => ({
      ...call,
      id: `00000000-0000-4000-8000-${String(index + 2).padStart(12, "0")}`,
    }));
    const calls =
      params.get("conversation_id") === conversationId &&
      params.get("live_only") !== "true"
        ? params.get("call_ids")?.split(",").includes(savedCallId)
          ? [call]
          : newerCalls.slice(0, 100)
        : [];
    await fulfillJson(route, {
      calls,
      total: params.has("call_ids") ? calls.length : 102,
      page: 1,
      limit: 100,
    });
  });
  await page.goto(scopedPath("projects/workspace"));
  await page.getByTestId("home-sync-repos").click();
  await page.getByTestId("home-open-workspace-repo-1").click();
  const failedTool = page.getByRole("button", { name: /bash.*error/ });
  await expect(failedTool).toBeVisible();
  await expect(page.getByText("running...", { exact: true })).toHaveCount(0);
  await failedTool.click();
  await expect(
    page.getByText(/No completion result was received/)
  ).toBeVisible();
  await page.getByRole("button", { name: /bash.*done/ }).click();
  await expect(page.getByText("preserved result")).toBeVisible();
  await recoveredSaved;
  await page.reload();
  await expect(failedTool).toBeVisible();
  await expect(page.getByText("running...", { exact: true })).toHaveCount(0);
  expect(chatRequests).toBe(0);
});