Skip to content

Commit e64ae22

Browse files
committed
test: stabilize transport reload and resume assertions
1 parent b7b6a52 commit e64ae22

2 files changed

Lines changed: 94 additions & 13 deletions

File tree

apps/server/src/services/workspace_runtime.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,4 +813,80 @@ mod tests {
813813
assert_eq!(runtime.lifecycle_events[0].source_event, "PreToolUse");
814814
assert_eq!(runtime.lifecycle_events[0].seq, 1);
815815
}
816+
817+
#[test]
818+
fn workspace_runtime_attach_keeps_created_session_view_and_claude_id() {
819+
let app = test_app();
820+
let workspace_id = launch_test_workspace(&app, "/tmp/ws-runtime-session-view-test");
821+
let session = create_workspace_session(app.state(), &workspace_id, SessionMode::Branch)
822+
.expect("session should be created");
823+
824+
patch_workspace_view_state(
825+
app.state(),
826+
&workspace_id,
827+
WorkspaceViewPatch {
828+
active_session_id: Some(session.id.to_string()),
829+
active_pane_id: Some(format!("pane-{}", session.id)),
830+
active_terminal_id: None,
831+
pane_layout: Some(json!({
832+
"type": "leaf",
833+
"id": format!("pane-{}", session.id),
834+
"sessionId": session.id.to_string(),
835+
})),
836+
file_preview: None,
837+
},
838+
)
839+
.expect("view state should be updated");
840+
update_workspace_session(
841+
app.state(),
842+
&workspace_id,
843+
session.id,
844+
SessionPatch {
845+
title: None,
846+
status: Some(SessionStatus::Interrupted),
847+
mode: None,
848+
auto_feed: None,
849+
queue: None,
850+
messages: None,
851+
stream: None,
852+
unread: None,
853+
last_active_at: None,
854+
claude_session_id: Some("claude-runtime-attach".to_string()),
855+
},
856+
)
857+
.expect("session should be updated");
858+
859+
let runtime = workspace_runtime_attach(
860+
workspace_id,
861+
"device-a".to_string(),
862+
"client-a".to_string(),
863+
app.clone(),
864+
app.state(),
865+
)
866+
.expect("runtime attach should succeed");
867+
868+
let restored = runtime
869+
.snapshot
870+
.sessions
871+
.iter()
872+
.find(|candidate| candidate.id == session.id)
873+
.expect("created session should be present");
874+
assert_eq!(restored.status, SessionStatus::Interrupted);
875+
assert_eq!(
876+
restored.claude_session_id.as_deref(),
877+
Some("claude-runtime-attach")
878+
);
879+
assert_eq!(
880+
runtime.snapshot.view_state.active_session_id,
881+
session.id.to_string()
882+
);
883+
assert_eq!(
884+
runtime.snapshot.view_state.active_pane_id,
885+
format!("pane-{}", session.id)
886+
);
887+
assert_eq!(
888+
runtime.snapshot.view_state.pane_layout["sessionId"].as_str(),
889+
Some(session.id.to_string().as_str())
890+
);
891+
}
816892
}

tests/e2e/transport.spec.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,6 @@ test.describe('workspace transport baseline', () => {
542542
await page.reload();
543543
await waitForWorkspaceTopbar(page);
544544
await waitForBackendSocket(page);
545-
const sessionCard = page.locator(`.agent-pane-card[data-session-id="${session.id}"]`).first();
546545
const controllerAfterReload = await currentWorkspaceController(page, workspace.workspaceId, ids);
547546
await invokeRpc(page, 'agent_start', {
548547
...controllerAfterReload,
@@ -562,18 +561,31 @@ test.describe('workspace transport baseline', () => {
562561
await waitForWorkspaceTopbar(page);
563562
await waitForBackendSocket(page);
564563
const runtimeAfterReload = await invokeRpc<{
564+
snapshot: {
565+
sessions: Array<{ id: number }>;
566+
view_state: {
567+
active_session_id: string;
568+
active_pane_id: string;
569+
};
570+
};
565571
lifecycle_events?: Array<{ session_id: string; kind: string }>;
566572
}>(page, 'workspace_runtime_attach', {
567573
workspaceId: workspace.workspaceId,
568574
deviceId: ids.deviceId,
569575
clientId: ids.clientId,
570576
});
577+
expect(runtimeAfterReload.snapshot.sessions.some((candidate) => candidate.id === session.id)).toBe(true);
578+
expect(runtimeAfterReload.snapshot.view_state.active_session_id).toBe(String(session.id));
579+
expect(runtimeAfterReload.snapshot.view_state.active_pane_id).toBe(`pane-${session.id}`);
571580
expect(runtimeAfterReload.lifecycle_events?.some((event) =>
572581
event.session_id === String(session.id) && event.kind === 'tool_started'
573582
)).toBe(true);
574583
let lastReloadStatus: string | null = null;
575584
await expect.poll(async () => {
576-
lastReloadStatus = await sessionCard.getAttribute('data-session-status');
585+
lastReloadStatus = await page
586+
.locator(`.agent-pane-card[data-session-id="${session.id}"]`)
587+
.first()
588+
.getAttribute('data-session-status');
577589
return lastReloadStatus === 'running' || lastReloadStatus === 'background';
578590
}, {
579591
timeout: 20000,
@@ -923,25 +935,18 @@ test.describe('workspace transport baseline', () => {
923935
await expect(page.getByTestId('workspace-agent-recovery-banner')).toBeVisible({
924936
timeout: 10000,
925937
});
938+
await expect(page.getByTestId('workspace-agent-recovery-action')).toHaveText('Resume agent');
926939
await page.getByTestId('workspace-agent-recovery-action').click();
927940

928-
await waitForWsEvent(
929-
page,
930-
'agent://event',
931-
(payload) =>
932-
payload.workspace_id === workspace.workspaceId
933-
&& payload.session_id === String(session.id)
934-
&& typeof payload.data === 'string'
935-
&& payload.data.includes(`--resume ${resumeClaudeSessionId}`),
936-
TRANSPORT_EVENT_TIMEOUT_MS,
937-
);
938941
await waitForWsEvent(
939942
page,
940943
'agent://lifecycle',
941944
(payload) =>
942945
payload.workspace_id === workspace.workspaceId
943946
&& payload.session_id === String(session.id)
944-
&& payload.kind === 'tool_started',
947+
&& payload.kind === 'tool_started'
948+
&& typeof payload.data === 'string'
949+
&& payload.data.includes(resumeClaudeSessionId),
945950
TRANSPORT_EVENT_TIMEOUT_MS,
946951
);
947952

0 commit comments

Comments
 (0)