/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ package com.github.copilot.sdk; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import com.github.copilot.sdk.events.AbstractSessionEvent; import com.github.copilot.sdk.events.AssistantMessageDeltaEvent; import com.github.copilot.sdk.events.AssistantMessageEvent; import com.github.copilot.sdk.json.MessageOptions; import com.github.copilot.sdk.json.PermissionHandler; import com.github.copilot.sdk.json.ResumeSessionConfig; import com.github.copilot.sdk.json.SessionConfig; /** * E2E tests for streaming fidelity — verifying that delta events are produced * when streaming is enabled and absent when it is disabled. * *

* Snapshots are stored in {@code test/snapshots/streaming_fidelity/}. *

*/ public class StreamingFidelityTest { private static E2ETestContext ctx; @BeforeAll static void setup() throws Exception { ctx = E2ETestContext.create(); } @AfterAll static void teardown() throws Exception { if (ctx != null) { ctx.close(); } } /** * Verifies that assistant.message_delta events are produced when streaming is * enabled. * * @see Snapshot: * streaming_fidelity/should_produce_delta_events_when_streaming_is_enabled */ @Test void testShouldProduceDeltaEventsWhenStreamingIsEnabled() throws Exception { ctx.configureForTest("streaming_fidelity", "should_produce_delta_events_when_streaming_is_enabled"); try (CopilotClient client = ctx.createClient()) { CopilotSession session = client.createSession( new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setStreaming(true)).get(); List events = new ArrayList<>(); session.on(events::add); session.sendAndWait(new MessageOptions().setPrompt("Count from 1 to 5, separated by commas.")).get(60, TimeUnit.SECONDS); List types = events.stream().map(AbstractSessionEvent::getType).toList(); // Should have streaming deltas before the final message List deltaEvents = events.stream() .filter(e -> e instanceof AssistantMessageDeltaEvent).map(e -> (AssistantMessageDeltaEvent) e) .toList(); assertFalse(deltaEvents.isEmpty(), "Should have received delta events when streaming is enabled"); // Deltas should have content for (AssistantMessageDeltaEvent delta : deltaEvents) { assertFalse(delta.getData().deltaContent() == null || delta.getData().deltaContent().isEmpty(), "Delta event should have content"); } // Should still have a final assistant.message assertTrue(types.contains("assistant.message"), "Should have a final assistant.message event"); // Deltas should come before the final message int firstDeltaIdx = types.indexOf("assistant.message_delta"); int lastAssistantIdx = types.lastIndexOf("assistant.message"); assertTrue(firstDeltaIdx < lastAssistantIdx, "Delta events should come before the final assistant.message"); session.close(); } } /** * Verifies that no delta events are produced when streaming is disabled. * * @see Snapshot: * streaming_fidelity/should_not_produce_deltas_when_streaming_is_disabled */ @Test void testShouldNotProduceDeltasWhenStreamingIsDisabled() throws Exception { ctx.configureForTest("streaming_fidelity", "should_not_produce_deltas_when_streaming_is_disabled"); try (CopilotClient client = ctx.createClient()) { CopilotSession session = client.createSession( new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setStreaming(false)) .get(); List events = new ArrayList<>(); session.on(events::add); session.sendAndWait(new MessageOptions().setPrompt("Say 'hello world'.")).get(60, TimeUnit.SECONDS); List deltaEvents = events.stream() .filter(e -> e instanceof AssistantMessageDeltaEvent).map(e -> (AssistantMessageDeltaEvent) e) .toList(); // No deltas when streaming is off assertTrue(deltaEvents.isEmpty(), "Should not receive delta events when streaming is disabled"); // But should still have a final assistant.message List assistantEvents = events.stream() .filter(e -> e instanceof AssistantMessageEvent).map(e -> (AssistantMessageEvent) e).toList(); assertFalse(assistantEvents.isEmpty(), "Should still have a final assistant.message when streaming is disabled"); session.close(); } } /** * Verifies that delta events are produced after resuming a session with * streaming enabled. * * @see Snapshot: streaming_fidelity/should_produce_deltas_after_session_resume */ @Test void testShouldProduceDeltasAfterSessionResume() throws Exception { ctx.configureForTest("streaming_fidelity", "should_produce_deltas_after_session_resume"); try (CopilotClient client = ctx.createClient()) { // Create a non-streaming session and send an initial message CopilotSession session = client.createSession( new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setStreaming(false)) .get(); session.sendAndWait(new MessageOptions().setPrompt("What is 3 + 6?")).get(60, TimeUnit.SECONDS); String sessionId = session.getSessionId(); session.close(); // Resume using a new client with streaming enabled try (CopilotClient newClient = ctx.createClient()) { CopilotSession session2 = newClient.resumeSession(sessionId, new ResumeSessionConfig() .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setStreaming(true)).get(); List events = new ArrayList<>(); session2.on(events::add); AssistantMessageEvent answer = session2 .sendAndWait(new MessageOptions().setPrompt("Now if you double that, what do you get?")) .get(60, TimeUnit.SECONDS); assertNotNull(answer); assertTrue(answer.getData().content().contains("18"), "Follow-up response should contain 18: " + answer.getData().content()); // Should have streaming deltas before the final message List deltaEvents = events.stream() .filter(e -> e instanceof AssistantMessageDeltaEvent).map(e -> (AssistantMessageDeltaEvent) e) .toList(); assertFalse(deltaEvents.isEmpty(), "Should have received delta events after session resume"); // Deltas should have content for (AssistantMessageDeltaEvent delta : deltaEvents) { assertFalse(delta.getData().deltaContent() == null || delta.getData().deltaContent().isEmpty(), "Delta event should have content"); } session2.close(); } } } }