diff --git a/dotnet/test/SessionTests.cs b/dotnet/test/SessionTests.cs index f3425c69..e72fe270 100644 --- a/dotnet/test/SessionTests.cs +++ b/dotnet/test/SessionTests.cs @@ -287,7 +287,7 @@ public async Task Should_Pass_Streaming_Option_To_Session_Creation() } [Fact] - public async Task Should_SessionEvt_Subscribed() + public async Task Should_Receive_Session_Events() { var session = await Client.CreateSessionAsync(); var receivedEvents = new List(); @@ -303,7 +303,7 @@ public async Task Should_SessionEvt_Subscribed() }); // Send a message to trigger events - await session.SendAsync(new MessageOptions { Prompt = "Hello!" }); + await session.SendAsync(new MessageOptions { Prompt = "What is 100+200?" }); // Wait for session to become idle (indicating message processing is complete) var completed = await Task.WhenAny(idleReceived.Task, Task.Delay(TimeSpan.FromSeconds(60))); @@ -315,6 +315,11 @@ public async Task Should_SessionEvt_Subscribed() Assert.Contains(receivedEvents, evt => evt is AssistantMessageEvent); Assert.Contains(receivedEvents, evt => evt is SessionIdleEvent); + // Verify the assistant response contains the expected answer + var assistantMessage = await TestHelper.GetFinalAssistantMessageAsync(session); + Assert.NotNull(assistantMessage); + Assert.Contains("300", assistantMessage!.Data.Content); + await session.DisposeAsync(); } } diff --git a/go/e2e/session_test.go b/go/e2e/session_test.go index 310b78e3..02cea5bd 100644 --- a/go/e2e/session_test.go +++ b/go/e2e/session_test.go @@ -614,6 +614,79 @@ func TestSession(t *testing.T) { t.Errorf("Expected assistant message to contain '2', got %v", assistantMessage.Data.Content) } }) + + t.Run("should receive session events", func(t *testing.T) { + ctx.ConfigureForTest(t) + + session, err := client.CreateSession(nil) + if err != nil { + t.Fatalf("Failed to create session: %v", err) + } + + var receivedEvents []copilot.SessionEvent + idle := make(chan bool) + + session.On(func(event copilot.SessionEvent) { + receivedEvents = append(receivedEvents, event) + if event.Type == "session.idle" { + select { + case idle <- true: + default: + } + } + }) + + // Send a message to trigger events + _, err = session.Send(copilot.MessageOptions{Prompt: "What is 100+200?"}) + if err != nil { + t.Fatalf("Failed to send message: %v", err) + } + + // Wait for session to become idle + select { + case <-idle: + case <-time.After(60 * time.Second): + t.Fatal("Timed out waiting for session.idle") + } + + // Should have received multiple events + if len(receivedEvents) == 0 { + t.Error("Expected to receive events, got none") + } + + hasUserMessage := false + hasAssistantMessage := false + hasSessionIdle := false + for _, evt := range receivedEvents { + switch evt.Type { + case "user.message": + hasUserMessage = true + case "assistant.message": + hasAssistantMessage = true + case "session.idle": + hasSessionIdle = true + } + } + + if !hasUserMessage { + t.Error("Expected to receive user.message event") + } + if !hasAssistantMessage { + t.Error("Expected to receive assistant.message event") + } + if !hasSessionIdle { + t.Error("Expected to receive session.idle event") + } + + // Verify the assistant response contains the expected answer + assistantMessage, err := testharness.GetFinalAssistantMessage(session, 60*time.Second) + if err != nil { + t.Fatalf("Failed to get assistant message: %v", err) + } + if assistantMessage.Data.Content == nil || !strings.Contains(*assistantMessage.Data.Content, "300") { + t.Errorf("Expected assistant message to contain '300', got %v", assistantMessage.Data.Content) + } + }) } func getSystemMessage(exchange testharness.ParsedHttpExchange) string { diff --git a/nodejs/test/e2e/session.test.ts b/nodejs/test/e2e/session.test.ts index 6beb41aa..a25f00c0 100644 --- a/nodejs/test/e2e/session.test.ts +++ b/nodejs/test/e2e/session.test.ts @@ -299,6 +299,41 @@ describe("Sessions", async () => { const assistantMessage = await getFinalAssistantMessage(session); expect(assistantMessage.data.content).toContain("2"); }); + + it("should receive session events", async () => { + const session = await client.createSession(); + const receivedEvents: Array<{ type: string }> = []; + let idleResolve: () => void; + const idlePromise = new Promise((resolve) => { + idleResolve = resolve; + }); + + session.on((event) => { + receivedEvents.push(event); + if (event.type === "session.idle") { + idleResolve(); + } + }); + + // Send a message to trigger events + await session.send({ prompt: "What is 100+200?" }); + + // Wait for session to become idle + await Promise.race([ + idlePromise, + new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), 60000)), + ]); + + // Should have received multiple events + expect(receivedEvents.length).toBeGreaterThan(0); + expect(receivedEvents.some((e) => e.type === "user.message")).toBe(true); + expect(receivedEvents.some((e) => e.type === "assistant.message")).toBe(true); + expect(receivedEvents.some((e) => e.type === "session.idle")).toBe(true); + + // Verify the assistant response contains the expected answer + const assistantMessage = await getFinalAssistantMessage(session); + expect(assistantMessage.data.content).toContain("300"); + }); }); function getSystemMessage(exchange: ParsedHttpExchange): string | undefined { diff --git a/python/e2e/test_session.py b/python/e2e/test_session.py index 18d7ac0d..30d24f60 100644 --- a/python/e2e/test_session.py +++ b/python/e2e/test_session.py @@ -326,6 +326,40 @@ async def test_should_pass_streaming_option_to_session_creation(self, ctx: E2ETe assistant_message = await get_final_assistant_message(session) assert "2" in assistant_message.data.content + async def test_should_receive_session_events(self, ctx: E2ETestContext): + import asyncio + + session = await ctx.client.create_session() + received_events = [] + idle_event = asyncio.Event() + + def on_event(event): + received_events.append(event) + if event.type.value == "session.idle": + idle_event.set() + + session.on(on_event) + + # Send a message to trigger events + await session.send({"prompt": "What is 100+200?"}) + + # Wait for session to become idle + try: + await asyncio.wait_for(idle_event.wait(), timeout=60) + except asyncio.TimeoutError: + pytest.fail("Timed out waiting for session.idle") + + # Should have received multiple events + assert len(received_events) > 0 + event_types = [e.type.value for e in received_events] + assert "user.message" in event_types + assert "assistant.message" in event_types + assert "session.idle" in event_types + + # Verify the assistant response contains the expected answer + assistant_message = await get_final_assistant_message(session) + assert "300" in assistant_message.data.content + def _get_system_message(exchange: dict) -> str: messages = exchange.get("request", {}).get("messages", []) diff --git a/test/snapshots/session/should_receive_session_events.yaml b/test/snapshots/session/should_receive_session_events.yaml new file mode 100644 index 00000000..229563a4 --- /dev/null +++ b/test/snapshots/session/should_receive_session_events.yaml @@ -0,0 +1,10 @@ +models: + - claude-sonnet-4.5 +conversations: + - messages: + - role: system + content: ${system} + - role: user + content: What is 100+200? + - role: assistant + content: 100 + 200 = 300