use github_copilot_sdk::session_events::SessionEventType; use super::support::{ assistant_message_content, collect_until_idle, event_types, wait_for_condition, }; #[tokio::test] async fn should_list_created_sessions_after_sending_a_message() { super::support::with_shared_e2e_context( &E2E, "session_lifecycle", "should_list_created_sessions_after_sending_a_message", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let client = ctx.start_client().await; let session1 = client .create_session(ctx.approve_all_session_config()) .await .expect("create first session"); let session2 = client .create_session(ctx.approve_all_session_config()) .await .expect("create second session"); session1.send_and_wait("Say hello").await.expect("send one"); session2.send_and_wait("Say world").await.expect("send two"); wait_for_condition("both sessions to appear in list", || { let client = client.clone(); let id1 = session1.id().clone(); let id2 = session2.id().clone(); async move { client.list_sessions(None).await.is_ok_and(|sessions| { let ids: std::collections::HashSet<_> = sessions .into_iter() .map(|session| session.session_id) .collect(); ids.contains(&id1) && ids.contains(&id2) }) } }) .await; session1 .disconnect() .await .expect("disconnect first session"); session2 .disconnect() .await .expect("disconnect second session"); client.stop().await.expect("stop client"); }) }, ) .await; } #[tokio::test] async fn should_delete_session_permanently() { super::support::with_shared_e2e_context( &E2E, "session_lifecycle", "should_delete_session_permanently", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let client = ctx.start_client().await; let session = client .create_session(ctx.approve_all_session_config()) .await .expect("create session"); let session_id = session.id().clone(); session.send_and_wait("Say hi").await.expect("send"); wait_for_condition("session to appear in list before delete", || { let client = client.clone(); let session_id = session_id.clone(); async move { client.list_sessions(None).await.is_ok_and(|sessions| { sessions .iter() .any(|session| session.session_id == session_id) }) } }) .await; session.disconnect().await.expect("disconnect session"); client .delete_session(&session_id) .await .expect("delete session"); let after = client.list_sessions(None).await.expect("list sessions"); assert!(!after.iter().any(|session| session.session_id == session_id)); client.stop().await.expect("stop client"); }) }, ) .await; } #[tokio::test] async fn should_return_events_via_getmessages_after_conversation() { super::support::with_shared_e2e_context( &E2E, "session_lifecycle", "should_return_events_via_getmessages_after_conversation", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let client = ctx.start_client().await; let session = client .create_session(ctx.approve_all_session_config()) .await .expect("create session"); session .send_and_wait("What is 2+2? Reply with just the number.") .await .expect("send"); let messages = session.get_events().await.expect("get messages"); let types = event_types(&messages); assert!(types.contains(&"session.start")); assert!(types.contains(&"user.message")); assert!(types.contains(&"assistant.message")); session.disconnect().await.expect("disconnect session"); client.stop().await.expect("stop client"); }) }, ) .await; } #[tokio::test] async fn should_support_multiple_concurrent_sessions() { super::support::with_shared_e2e_context( &E2E, "session_lifecycle", "should_support_multiple_concurrent_sessions", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let client = ctx.start_client().await; let session1 = client .create_session(ctx.approve_all_session_config()) .await .expect("create first session"); let session2 = client .create_session(ctx.approve_all_session_config()) .await .expect("create second session"); let (first, second) = tokio::join!( session1.send_and_wait("What is 1+1? Reply with just the number."), session2.send_and_wait("What is 3+3? Reply with just the number.") ); let first = first.expect("first send").expect("first assistant message"); let second = second .expect("second send") .expect("second assistant message"); assert!(assistant_message_content(&first).contains('2')); assert!(assistant_message_content(&second).contains('6')); session1 .disconnect() .await .expect("disconnect first session"); session2 .disconnect() .await .expect("disconnect second session"); client.stop().await.expect("stop client"); }) }, ) .await; } #[tokio::test] async fn should_isolate_events_between_concurrent_sessions() { super::support::with_shared_e2e_context( &E2E, "session_lifecycle", "should_isolate_events_between_concurrent_sessions", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let client = ctx.start_client().await; let session1 = client .create_session(ctx.approve_all_session_config()) .await .expect("create first session"); let session2 = client .create_session(ctx.approve_all_session_config()) .await .expect("create second session"); let events1 = session1.subscribe(); let events2 = session2.subscribe(); session1 .send_and_wait("Say 'session_one_response'.") .await .expect("send one"); session2 .send_and_wait("Say 'session_two_response'.") .await .expect("send two"); let observed1 = collect_until_idle(events1).await; let observed2 = collect_until_idle(events2).await; let messages1: Vec<_> = observed1 .iter() .filter(|event| event.parsed_type() == SessionEventType::AssistantMessage) .map(assistant_message_content) .collect(); let messages2: Vec<_> = observed2 .iter() .filter(|event| event.parsed_type() == SessionEventType::AssistantMessage) .map(assistant_message_content) .collect(); assert!( messages1 .iter() .any(|message| message.contains("session_one_response")) ); assert!( !messages1 .iter() .any(|message| message.contains("session_two_response")) ); assert!( messages2 .iter() .any(|message| message.contains("session_two_response")) ); assert!( !messages2 .iter() .any(|message| message.contains("session_one_response")) ); session1 .disconnect() .await .expect("disconnect first session"); session2 .disconnect() .await .expect("disconnect second session"); client.stop().await.expect("stop client"); }) }, ) .await; } static E2E: super::support::SharedE2eGroup = super::support::SharedE2eGroup::standard("session_lifecycle", 5);