Skip to content

Commit ff634db

Browse files
romanlutzCopilot
andauthored
FIX Restore targets for deep-linked conversations (#2362)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 76a89e67-8a71-4803-8b12-9ac41600db0a
1 parent 96730ae commit ff634db

15 files changed

Lines changed: 1507 additions & 63 deletions

File tree

frontend/e2e/routing.spec.ts

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,45 @@ const KNOWN_ATTACKS: Record<string, { outcome: "success" | "failure" }> = {
1212
"atk-success": { outcome: "success" },
1313
"atk-failure": { outcome: "failure" },
1414
};
15+
const ROUTING_TARGET_HASH = "routing-target-full-hash";
16+
const ROUTING_TARGET = {
17+
target_registry_name: "routing-target",
18+
identifier: {
19+
class_name: "OpenAIChatTarget",
20+
hash: ROUTING_TARGET_HASH,
21+
endpoint: null,
22+
model_name: "gpt-4o",
23+
},
24+
capabilities: {
25+
supports_multi_turn: true,
26+
supports_json_schema: false,
27+
supports_json_output: false,
28+
supports_system_prompt: false,
29+
supported_input_modalities: ["text"],
30+
supported_output_modalities: ["text"],
31+
},
32+
target_specific_params: null,
33+
inner_targets: null,
34+
};
1535

1636
/** Build an attack summary for the single-attack (getAttack) endpoint. */
1737
function makeAttackSummary(attackResultId: string, outcome: "success" | "failure") {
1838
return {
1939
attack_result_id: attackResultId,
2040
conversation_id: `conv-${attackResultId}`,
2141
attack_type: "SingleTurnAttack",
22-
target: { target_type: "OpenAIChatTarget", model_name: "gpt-4o" },
42+
target: {
43+
target_type: "OpenAIChatTarget",
44+
target_registry_name: ROUTING_TARGET.target_registry_name,
45+
model_name: "gpt-4o",
46+
identifier_hash: ROUTING_TARGET_HASH,
47+
},
2348
converters: [],
2449
outcome,
2550
last_message_preview: null,
2651
message_count: 1,
2752
related_conversation_ids: [],
28-
labels: { operator: "alice" },
53+
labels: {},
2954
created_at: new Date().toISOString(),
3055
updated_at: new Date().toISOString(),
3156
};
@@ -78,12 +103,18 @@ const MARKDOWN_PREFERENCE_STORAGE_KEY = "pyrit.chatMarkdownMode";
78103

79104
/** Register every API mock the routing tests rely on. */
80105
async function mockRoutingAPIs(page: Page) {
81-
// No active target configured – the chat ribbon shows "No target selected".
82106
await page.route(/\/api\/targets/, async (route) => {
107+
const pathname = new URL(route.request().url()).pathname;
108+
const body = pathname.endsWith(`/${ROUTING_TARGET.target_registry_name}`)
109+
? ROUTING_TARGET
110+
: {
111+
items: [ROUTING_TARGET],
112+
pagination: { limit: 200, has_more: false, next_cursor: null, prev_cursor: null },
113+
};
83114
await route.fulfill({
84115
status: 200,
85116
contentType: "application/json",
86-
body: JSON.stringify({ items: [] }),
117+
body: JSON.stringify(body),
87118
});
88119
});
89120

@@ -214,13 +245,21 @@ test.describe("URL-driven routing", () => {
214245
await expect(page.getByTestId("attack-row-atk-failure")).not.toBeVisible();
215246
});
216247

217-
test("deep-links into an attack and hydrates its conversation", async ({ page }) => {
248+
test("deep-links into an attack and restores its target after reload", async ({ page }) => {
218249
await page.goto("/attacks/atk-1");
219250

220251
// The router keeps the deep link, and the attack named by the URL – not
221252
// some default/empty conversation – drives the chat window.
222253
await expect(page).toHaveURL(/\/attacks\/atk-1$/);
223254
await expect(page.getByText("Loaded from atk-1")).toBeVisible({ timeout: 10_000 });
255+
await expect(page.getByTestId("chat-input")).toBeEnabled();
256+
await expect(page.getByTestId("no-target-banner")).not.toBeVisible();
257+
258+
await page.reload();
259+
260+
await expect(page.getByText("Loaded from atk-1")).toBeVisible({ timeout: 10_000 });
261+
await expect(page.getByTestId("chat-input")).toBeEnabled();
262+
await expect(page.getByTestId("no-target-banner")).not.toBeVisible();
224263
});
225264

226265
test("shows the not-found screen for an unknown attack id", async ({ page }) => {

0 commit comments

Comments
 (0)