Skip to content

Commit b8a836b

Browse files
Add session event subscription tests for all SDKs (#16)
* Add session event subscription tests for all SDKs - Rename .NET test from Should_SessionEvt_Subscribed to Should_Receive_Session_Events - Add equivalent tests for Node.js, Python, and Go SDKs - Add shared snapshot file for test replay * Update session event tests to assert response content - Change prompt to 'What is 100+200?' - Assert assistant response contains '300' - Regenerate snapshot file * Fix formatting in session.test.ts
1 parent b16d752 commit b8a836b

File tree

5 files changed

+159
-2
lines changed

5 files changed

+159
-2
lines changed

dotnet/test/SessionTests.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ public async Task Should_Pass_Streaming_Option_To_Session_Creation()
287287
}
288288

289289
[Fact]
290-
public async Task Should_SessionEvt_Subscribed()
290+
public async Task Should_Receive_Session_Events()
291291
{
292292
var session = await Client.CreateSessionAsync();
293293
var receivedEvents = new List<SessionEvent>();
@@ -303,7 +303,7 @@ public async Task Should_SessionEvt_Subscribed()
303303
});
304304

305305
// Send a message to trigger events
306-
await session.SendAsync(new MessageOptions { Prompt = "Hello!" });
306+
await session.SendAsync(new MessageOptions { Prompt = "What is 100+200?" });
307307

308308
// Wait for session to become idle (indicating message processing is complete)
309309
var completed = await Task.WhenAny(idleReceived.Task, Task.Delay(TimeSpan.FromSeconds(60)));
@@ -315,6 +315,11 @@ public async Task Should_SessionEvt_Subscribed()
315315
Assert.Contains(receivedEvents, evt => evt is AssistantMessageEvent);
316316
Assert.Contains(receivedEvents, evt => evt is SessionIdleEvent);
317317

318+
// Verify the assistant response contains the expected answer
319+
var assistantMessage = await TestHelper.GetFinalAssistantMessageAsync(session);
320+
Assert.NotNull(assistantMessage);
321+
Assert.Contains("300", assistantMessage!.Data.Content);
322+
318323
await session.DisposeAsync();
319324
}
320325
}

go/e2e/session_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,79 @@ func TestSession(t *testing.T) {
614614
t.Errorf("Expected assistant message to contain '2', got %v", assistantMessage.Data.Content)
615615
}
616616
})
617+
618+
t.Run("should receive session events", func(t *testing.T) {
619+
ctx.ConfigureForTest(t)
620+
621+
session, err := client.CreateSession(nil)
622+
if err != nil {
623+
t.Fatalf("Failed to create session: %v", err)
624+
}
625+
626+
var receivedEvents []copilot.SessionEvent
627+
idle := make(chan bool)
628+
629+
session.On(func(event copilot.SessionEvent) {
630+
receivedEvents = append(receivedEvents, event)
631+
if event.Type == "session.idle" {
632+
select {
633+
case idle <- true:
634+
default:
635+
}
636+
}
637+
})
638+
639+
// Send a message to trigger events
640+
_, err = session.Send(copilot.MessageOptions{Prompt: "What is 100+200?"})
641+
if err != nil {
642+
t.Fatalf("Failed to send message: %v", err)
643+
}
644+
645+
// Wait for session to become idle
646+
select {
647+
case <-idle:
648+
case <-time.After(60 * time.Second):
649+
t.Fatal("Timed out waiting for session.idle")
650+
}
651+
652+
// Should have received multiple events
653+
if len(receivedEvents) == 0 {
654+
t.Error("Expected to receive events, got none")
655+
}
656+
657+
hasUserMessage := false
658+
hasAssistantMessage := false
659+
hasSessionIdle := false
660+
for _, evt := range receivedEvents {
661+
switch evt.Type {
662+
case "user.message":
663+
hasUserMessage = true
664+
case "assistant.message":
665+
hasAssistantMessage = true
666+
case "session.idle":
667+
hasSessionIdle = true
668+
}
669+
}
670+
671+
if !hasUserMessage {
672+
t.Error("Expected to receive user.message event")
673+
}
674+
if !hasAssistantMessage {
675+
t.Error("Expected to receive assistant.message event")
676+
}
677+
if !hasSessionIdle {
678+
t.Error("Expected to receive session.idle event")
679+
}
680+
681+
// Verify the assistant response contains the expected answer
682+
assistantMessage, err := testharness.GetFinalAssistantMessage(session, 60*time.Second)
683+
if err != nil {
684+
t.Fatalf("Failed to get assistant message: %v", err)
685+
}
686+
if assistantMessage.Data.Content == nil || !strings.Contains(*assistantMessage.Data.Content, "300") {
687+
t.Errorf("Expected assistant message to contain '300', got %v", assistantMessage.Data.Content)
688+
}
689+
})
617690
}
618691

619692
func getSystemMessage(exchange testharness.ParsedHttpExchange) string {

nodejs/test/e2e/session.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,41 @@ describe("Sessions", async () => {
299299
const assistantMessage = await getFinalAssistantMessage(session);
300300
expect(assistantMessage.data.content).toContain("2");
301301
});
302+
303+
it("should receive session events", async () => {
304+
const session = await client.createSession();
305+
const receivedEvents: Array<{ type: string }> = [];
306+
let idleResolve: () => void;
307+
const idlePromise = new Promise<void>((resolve) => {
308+
idleResolve = resolve;
309+
});
310+
311+
session.on((event) => {
312+
receivedEvents.push(event);
313+
if (event.type === "session.idle") {
314+
idleResolve();
315+
}
316+
});
317+
318+
// Send a message to trigger events
319+
await session.send({ prompt: "What is 100+200?" });
320+
321+
// Wait for session to become idle
322+
await Promise.race([
323+
idlePromise,
324+
new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), 60000)),
325+
]);
326+
327+
// Should have received multiple events
328+
expect(receivedEvents.length).toBeGreaterThan(0);
329+
expect(receivedEvents.some((e) => e.type === "user.message")).toBe(true);
330+
expect(receivedEvents.some((e) => e.type === "assistant.message")).toBe(true);
331+
expect(receivedEvents.some((e) => e.type === "session.idle")).toBe(true);
332+
333+
// Verify the assistant response contains the expected answer
334+
const assistantMessage = await getFinalAssistantMessage(session);
335+
expect(assistantMessage.data.content).toContain("300");
336+
});
302337
});
303338

304339
function getSystemMessage(exchange: ParsedHttpExchange): string | undefined {

python/e2e/test_session.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,40 @@ async def test_should_pass_streaming_option_to_session_creation(self, ctx: E2ETe
326326
assistant_message = await get_final_assistant_message(session)
327327
assert "2" in assistant_message.data.content
328328

329+
async def test_should_receive_session_events(self, ctx: E2ETestContext):
330+
import asyncio
331+
332+
session = await ctx.client.create_session()
333+
received_events = []
334+
idle_event = asyncio.Event()
335+
336+
def on_event(event):
337+
received_events.append(event)
338+
if event.type.value == "session.idle":
339+
idle_event.set()
340+
341+
session.on(on_event)
342+
343+
# Send a message to trigger events
344+
await session.send({"prompt": "What is 100+200?"})
345+
346+
# Wait for session to become idle
347+
try:
348+
await asyncio.wait_for(idle_event.wait(), timeout=60)
349+
except asyncio.TimeoutError:
350+
pytest.fail("Timed out waiting for session.idle")
351+
352+
# Should have received multiple events
353+
assert len(received_events) > 0
354+
event_types = [e.type.value for e in received_events]
355+
assert "user.message" in event_types
356+
assert "assistant.message" in event_types
357+
assert "session.idle" in event_types
358+
359+
# Verify the assistant response contains the expected answer
360+
assistant_message = await get_final_assistant_message(session)
361+
assert "300" in assistant_message.data.content
362+
329363

330364
def _get_system_message(exchange: dict) -> str:
331365
messages = exchange.get("request", {}).get("messages", [])
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
models:
2+
- claude-sonnet-4.5
3+
conversations:
4+
- messages:
5+
- role: system
6+
content: ${system}
7+
- role: user
8+
content: What is 100+200?
9+
- role: assistant
10+
content: 100 + 200 = 300

0 commit comments

Comments
 (0)