fix(e2e): #2788 residual — shell-safe chat seed JSON + Activity API auth #2792

Merged
devops-engineer merged 1 commits from fix/2788-e2e-chat-residual into main 2026-06-14 00:19:48 +00:00
2 changed files with 16 additions and 3 deletions
+4
View File
@@ -191,6 +191,7 @@ test.describe("Activity API Source Filter", () => {
test("source=canvas returns only canvas-initiated entries", async ({ request }) => {
const res = await request.get(
`${API}/workspaces/${workspaceId}/activity?source=canvas`,
{ headers: { Authorization: `Bearer ${authToken}` } },
);
expect(res.ok()).toBeTruthy();
const entries = (await res.json()) as Array<{ source_id: unknown }>;
@@ -205,6 +206,7 @@ test.describe("Activity API Source Filter", () => {
test("source=agent returns only agent-initiated entries", async ({ request }) => {
const res = await request.get(
`${API}/workspaces/${workspaceId}/activity?source=agent`,
{ headers: { Authorization: `Bearer ${authToken}` } },
);
expect(res.ok()).toBeTruthy();
const entries = (await res.json()) as Array<{ source_id: unknown }>;
@@ -219,6 +221,7 @@ test.describe("Activity API Source Filter", () => {
test("source=invalid returns 400", async ({ request }) => {
const res = await request.get(
`${API}/workspaces/${workspaceId}/activity?source=bogus`,
{ headers: { Authorization: `Bearer ${authToken}` } },
);
expect(res.status()).toBe(400);
});
@@ -226,6 +229,7 @@ test.describe("Activity API Source Filter", () => {
test("source+type filters combine correctly", async ({ request }) => {
const res = await request.get(
`${API}/workspaces/${workspaceId}/activity?type=a2a_receive&source=canvas`,
{ headers: { Authorization: `Bearer ${authToken}` } },
);
expect(res.ok()).toBeTruthy();
const entries = (await res.json()) as Array<{
+12 -3
View File
@@ -178,6 +178,10 @@ export function startHeartbeat(
* a2a_receive rows with source_id NULL (canvas-origin) so the
* /chat-history hydrator picks them up. Each message becomes its own row
* so arbitrary user/agent sequences can be seeded.
*
* The JSON payloads are passed through psql as dollar-quoted literals so
* message text containing quotes, backslashes, or other special characters
* is preserved exactly and cannot break the SQL string escaping.
*/
export async function seedChatHistory(
workspaceId: string,
@@ -185,8 +189,6 @@ export async function seedChatHistory(
): Promise<void> {
if (!process.env.E2E_DATABASE_URL) return;
const escape = (s: string) => s.replace(/'/g, "''").replace(/\\/g, "\\\\");
const rows = messages
.map((msg, i) => {
const offsetSec = messages.length - i;
@@ -208,7 +210,14 @@ export async function seedChatHistory(
},
})
: "{}";
return `('${randomUUID()}', '${workspaceId}', 'a2a_receive', NULL, NULL, 'message/send', NULL, '${escape(requestBody)}'::jsonb, '${escape(responseBody)}'::jsonb, 0, 'ok', NOW() - INTERVAL '${offsetSec} seconds')`;
// Use a per-row random dollar-quoting tag so the message content
// cannot accidentally close the literal.
const tag = `J${randomUUID().replace(/[^A-Za-z0-9]/g, "")}`;
const reqLit = `$${tag}$${requestBody}$${tag}$`;
const respLit = `$${tag}$${responseBody}$${tag}$`;
return `('${randomUUID()}', '${workspaceId}', 'a2a_receive', NULL, NULL, 'message/send', NULL, ${reqLit}::jsonb, ${respLit}::jsonb, 0, 'ok', NOW() - INTERVAL '${offsetSec} seconds')`;
})
.join(",");