fix(e2e): assert echo runtime received A2A request in chat-desktop specs (core#2796 follow-up) #2837

Merged
devops-engineer merged 1 commits from fix/2796-e2e-server-received-assertion into main 2026-06-14 09:34:13 +00:00
2 changed files with 26 additions and 12 deletions
+3 -5
View File
@@ -124,12 +124,10 @@ jobs:
# renamed/moved spec or stray test.only can no longer green the lane.
# - REQUIRE-LIVE guard in "Run Playwright E2E tests" → chat==true must
# actually execute >=1 test, else exit 1.
# - chat-desktop.spec.ts asserts echo-runtime.lastRequest after each
# round-trip, so an optimistic client-side render cannot pass without
# a real A2A request reaching the server (core#2796 follow-up).
# STILL BLOCKS PROMOTION:
# - The echo round-trip asserts on rendered "Echo: ..." text but never
# asserts the echo runtime actually RECEIVED the A2A request
# (fixtures/echo-runtime.ts exposes lastRequest, unused) — an
# optimistic client-side render could pass without a real round-trip.
# Add a server-received assertion before required.
# - The "No-op pass" path (detect-changes chat!=true) is a legitimate
# paths-filter skip, but a required gate needs it to be a neutral
# check, not a green "success", so a skipped heavy lane can't be
+23 -7
View File
@@ -1,6 +1,6 @@
import { test, expect } from "@playwright/test";
import type { Page } from "@playwright/test";
import { startEchoRuntime } from "./fixtures/echo-runtime";
import { startEchoRuntime, type EchoRuntime } from "./fixtures/echo-runtime";
import { seedWorkspace, startHeartbeat, cleanupWorkspace, runPsql } from "./fixtures/chat-seed";
/** Enter the Org-map view so the Canvas (React Flow graph) mounts. */
@@ -14,17 +14,18 @@ test.describe("Desktop ChatTab", () => {
let cleanup: () => Promise<void> = async () => {};
let workspaceId = "";
let workspaceName = "";
let echoRuntime: EchoRuntime;
test.beforeAll(async () => {
const echo = await startEchoRuntime();
const ws = await seedWorkspace(echo.baseURL);
echoRuntime = await startEchoRuntime();
const ws = await seedWorkspace(echoRuntime.baseURL);
workspaceId = ws.id;
workspaceName = ws.name;
const stopHeartbeat = startHeartbeat(ws.id, ws.authToken);
cleanup = async () => {
stopHeartbeat();
await echo.stop();
await echoRuntime.stop();
};
});
@@ -86,6 +87,11 @@ test.describe("Desktop ChatTab", () => {
await expect(chat.getByText("What is the weather?", { exact: true })).toBeVisible({ timeout: 5_000 });
await expect(chat.getByText("Echo: What is the weather?")).toBeVisible({ timeout: 15_000 });
// Regression guard: assert the echo runtime actually RECEIVED the A2A
// request, not just that the UI rendered something that looks like an echo.
expect(echoRuntime.lastRequest).not.toBeNull();
expect(echoRuntime.lastRequest!.text).toBe("What is the weather?");
});
test("history persists across reload", async ({ page }) => {
@@ -96,6 +102,10 @@ test.describe("Desktop ChatTab", () => {
await expect(chat.getByText("Echo: Persistence test")).toBeVisible({ timeout: 15_000 });
// Confirm the round-trip reached the echo runtime before reloading.
expect(echoRuntime.lastRequest).not.toBeNull();
expect(echoRuntime.lastRequest!.text).toBe("Persistence test");
await page.reload();
await enterMapView(page);
await page.waitForSelector(".react-flow__node", { timeout: 10_000 });
@@ -126,6 +136,11 @@ test.describe("Desktop ChatTab", () => {
await page.getByRole("button", { name: /Send/ }).first().click();
await expect(chat.getByText("Echo: Please read this file")).toBeVisible({ timeout: 15_000 });
// Confirm the file payload reached the echo runtime, not just the UI.
expect(echoRuntime.lastRequest).not.toBeNull();
expect(echoRuntime.lastRequest!.text).toBe("Please read this file");
expect(echoRuntime.lastRequest!.files).toHaveLength(1);
});
});
@@ -133,17 +148,18 @@ test.describe("Desktop ChatTab — Markdown rendering", () => {
let cleanup: () => Promise<void> = async () => {};
let workspaceId = "";
let workspaceName = "";
let echoRuntime: EchoRuntime;
test.beforeAll(async () => {
const echo = await startEchoRuntime();
const ws = await seedWorkspace(echo.baseURL);
echoRuntime = await startEchoRuntime();
const ws = await seedWorkspace(echoRuntime.baseURL);
workspaceId = ws.id;
workspaceName = ws.name;
const stopHeartbeat = startHeartbeat(ws.id, ws.authToken);
cleanup = async () => {
stopHeartbeat();
await echo.stop();
await echoRuntime.stop();
};
});