From 7efa2cd1a14dff733e9f17abffa52531b7a60c78 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Apr 2026 16:39:11 +0000 Subject: [PATCH 1/6] Initial plan From 78aff883237b0d807be72d1663b711034f2e383b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Apr 2026 17:02:13 +0000 Subject: [PATCH 2/6] Add comprehensive test coverage for generated RPC code Agent-Logs-Url: https://github.com/github/copilot-sdk-java/sessions/bb9210b4-2aa3-475c-9c4f-5b2318a436f5 Co-authored-by: edburns <75821+edburns@users.noreply.github.com> --- .../sdk/GeneratedEventTypesCoverageTest.java | 732 +++++++++++ .../sdk/GeneratedRpcApiCoverageTest.java | 727 +++++++++++ .../sdk/GeneratedRpcRecordsCoverageTest.java | 1160 +++++++++++++++++ 3 files changed, 2619 insertions(+) create mode 100644 src/test/java/com/github/copilot/sdk/GeneratedEventTypesCoverageTest.java create mode 100644 src/test/java/com/github/copilot/sdk/GeneratedRpcApiCoverageTest.java create mode 100644 src/test/java/com/github/copilot/sdk/GeneratedRpcRecordsCoverageTest.java diff --git a/src/test/java/com/github/copilot/sdk/GeneratedEventTypesCoverageTest.java b/src/test/java/com/github/copilot/sdk/GeneratedEventTypesCoverageTest.java new file mode 100644 index 000000000..5cbc6d1a1 --- /dev/null +++ b/src/test/java/com/github/copilot/sdk/GeneratedEventTypesCoverageTest.java @@ -0,0 +1,732 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.sdk; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import com.github.copilot.sdk.generated.*; + +/** + * Deserialization tests for generated session event types that are not covered + * in {@link SessionEventDeserializationTest}. Verifies that each event + * deserializes correctly from JSON and that the {@code type} discriminator and + * {@code data} fields are accessible. + */ +public class GeneratedEventTypesCoverageTest { + + private static final ObjectMapper MAPPER = JsonRpcClient.getObjectMapper(); + + private static SessionEvent parse(String json) throws Exception { + return MAPPER.readValue(json, SessionEvent.class); + } + + // ── AssistantStreamingDeltaEvent ─────────────────────────────────────── + + @Test + void testParseAssistantStreamingDeltaEvent() throws Exception { + var event = parse(""" + {"type":"assistant.streaming_delta","data":{"totalResponseSizeBytes":1024.0}} + """); + assertInstanceOf(AssistantStreamingDeltaEvent.class, event); + assertEquals("assistant.streaming_delta", event.getType()); + var typed = (AssistantStreamingDeltaEvent) event; + assertEquals(1024.0, typed.getData().totalResponseSizeBytes()); + } + + // ── CapabilitiesChangedEvent ─────────────────────────────────────────── + + @Test + void testParseCapabilitiesChangedEvent() throws Exception { + var event = parse(""" + {"type":"capabilities.changed","data":{"ui":{"elicitation":true}}} + """); + assertInstanceOf(CapabilitiesChangedEvent.class, event); + assertEquals("capabilities.changed", event.getType()); + var typed = (CapabilitiesChangedEvent) event; + assertNotNull(typed.getData()); + assertTrue(typed.getData().ui().elicitation()); + } + + @Test + void testParseCapabilitiesChangedEventNoData() throws Exception { + var event = parse(""" + {"type":"capabilities.changed"} + """); + assertInstanceOf(CapabilitiesChangedEvent.class, event); + } + + // ── CommandQueuedEvent ───────────────────────────────────────────────── + + @Test + void testParseCommandQueuedEvent() throws Exception { + var event = parse(""" + {"type":"command.queued","data":{"requestId":"req-001","command":"/deploy"}} + """); + assertInstanceOf(CommandQueuedEvent.class, event); + assertEquals("command.queued", event.getType()); + var typed = (CommandQueuedEvent) event; + assertEquals("req-001", typed.getData().requestId()); + assertEquals("/deploy", typed.getData().command()); + } + + // ── CommandExecuteEvent ──────────────────────────────────────────────── + + @Test + void testParseCommandExecuteEvent() throws Exception { + var event = parse( + """ + {"type":"command.execute","data":{"requestId":"req-002","command":"/help me","commandName":"help","args":"me"}} + """); + assertInstanceOf(CommandExecuteEvent.class, event); + assertEquals("command.execute", event.getType()); + var typed = (CommandExecuteEvent) event; + assertEquals("req-002", typed.getData().requestId()); + assertEquals("help", typed.getData().commandName()); + assertEquals("me", typed.getData().args()); + } + + // ── CommandCompletedEvent ────────────────────────────────────────────── + + @Test + void testParseCommandCompletedEvent() throws Exception { + var event = parse(""" + {"type":"command.completed","data":{"requestId":"req-003"}} + """); + assertInstanceOf(CommandCompletedEvent.class, event); + assertEquals("command.completed", event.getType()); + var typed = (CommandCompletedEvent) event; + assertEquals("req-003", typed.getData().requestId()); + } + + // ── CommandsChangedEvent ─────────────────────────────────────────────── + + @Test + void testParseCommandsChangedEvent() throws Exception { + var event = parse(""" + {"type":"commands.changed","data":{"commands":[{"name":"deploy","description":"Deploy to prod"}]}} + """); + assertInstanceOf(CommandsChangedEvent.class, event); + assertEquals("commands.changed", event.getType()); + var typed = (CommandsChangedEvent) event; + assertNotNull(typed.getData().commands()); + assertEquals(1, typed.getData().commands().size()); + assertEquals("deploy", typed.getData().commands().get(0).name()); + assertEquals("Deploy to prod", typed.getData().commands().get(0).description()); + } + + @Test + void testParseCommandsChangedEventEmpty() throws Exception { + var event = parse(""" + {"type":"commands.changed","data":{"commands":[]}} + """); + assertInstanceOf(CommandsChangedEvent.class, event); + var typed = (CommandsChangedEvent) event; + assertNotNull(typed.getData().commands()); + assertEquals(0, typed.getData().commands().size()); + } + + // ── ElicitationRequestedEvent ────────────────────────────────────────── + + @Test + void testParseElicitationRequestedEvent() throws Exception { + var event = parse( + """ + {"type":"elicitation.requested","data":{"requestId":"elicit-1","message":"Please enter your name","mode":"form"}} + """); + assertInstanceOf(ElicitationRequestedEvent.class, event); + assertEquals("elicitation.requested", event.getType()); + var typed = (ElicitationRequestedEvent) event; + assertEquals("elicit-1", typed.getData().requestId()); + assertEquals("Please enter your name", typed.getData().message()); + assertEquals(ElicitationRequestedEvent.ElicitationRequestedEventData.ElicitationRequestedEventDataMode.FORM, + typed.getData().mode()); + } + + @Test + void testParseElicitationRequestedEventUrlMode() throws Exception { + var event = parse( + """ + {"type":"elicitation.requested","data":{"requestId":"elicit-2","message":"Open browser","mode":"url","url":"https://example.com"}} + """); + assertInstanceOf(ElicitationRequestedEvent.class, event); + var typed = (ElicitationRequestedEvent) event; + assertEquals(ElicitationRequestedEvent.ElicitationRequestedEventData.ElicitationRequestedEventDataMode.URL, + typed.getData().mode()); + assertEquals("https://example.com", typed.getData().url()); + } + + // ── ElicitationCompletedEvent ────────────────────────────────────────── + + @Test + void testParseElicitationCompletedEvent() throws Exception { + var event = parse( + """ + {"type":"elicitation.completed","data":{"requestId":"elicit-1","action":"accept","content":{"name":"Alice"}}} + """); + assertInstanceOf(ElicitationCompletedEvent.class, event); + assertEquals("elicitation.completed", event.getType()); + var typed = (ElicitationCompletedEvent) event; + assertEquals("elicit-1", typed.getData().requestId()); + assertEquals(ElicitationCompletedEvent.ElicitationCompletedEventData.ElicitationCompletedEventDataAction.ACCEPT, + typed.getData().action()); + assertEquals("Alice", typed.getData().content().get("name")); + } + + @Test + void testParseElicitationCompletedEventDecline() throws Exception { + var event = parse(""" + {"type":"elicitation.completed","data":{"requestId":"elicit-2","action":"decline"}} + """); + assertInstanceOf(ElicitationCompletedEvent.class, event); + var typed = (ElicitationCompletedEvent) event; + assertEquals( + ElicitationCompletedEvent.ElicitationCompletedEventData.ElicitationCompletedEventDataAction.DECLINE, + typed.getData().action()); + } + + @Test + void testParseElicitationCompletedEventCancel() throws Exception { + var event = parse(""" + {"type":"elicitation.completed","data":{"requestId":"elicit-3","action":"cancel"}} + """); + assertInstanceOf(ElicitationCompletedEvent.class, event); + var typed = (ElicitationCompletedEvent) event; + assertEquals(ElicitationCompletedEvent.ElicitationCompletedEventData.ElicitationCompletedEventDataAction.CANCEL, + typed.getData().action()); + } + + // ── ExitPlanModeRequestedEvent ───────────────────────────────────────── + + @Test + void testParseExitPlanModeRequestedEvent() throws Exception { + var event = parse( + """ + {"type":"exit_plan_mode.requested","data":{"requestId":"epm-1","summary":"Implement login","planContent":"# Plan\\n1. Create login","actions":["approve","edit","reject"],"recommendedAction":"approve"}} + """); + assertInstanceOf(ExitPlanModeRequestedEvent.class, event); + assertEquals("exit_plan_mode.requested", event.getType()); + var typed = (ExitPlanModeRequestedEvent) event; + assertEquals("epm-1", typed.getData().requestId()); + assertEquals("Implement login", typed.getData().summary()); + assertEquals("approve", typed.getData().recommendedAction()); + assertEquals(3, typed.getData().actions().size()); + } + + // ── ExitPlanModeCompletedEvent ───────────────────────────────────────── + + @Test + void testParseExitPlanModeCompletedEvent() throws Exception { + var event = parse(""" + {"type":"exit_plan_mode.completed","data":{"requestId":"epm-1","action":"approve"}} + """); + assertInstanceOf(ExitPlanModeCompletedEvent.class, event); + assertEquals("exit_plan_mode.completed", event.getType()); + var typed = (ExitPlanModeCompletedEvent) event; + assertNotNull(typed.getData()); + } + + // ── ExternalToolRequestedEvent ───────────────────────────────────────── + + @Test + void testParseExternalToolRequestedEvent() throws Exception { + var event = parse( + """ + {"type":"external_tool.requested","data":{"requestId":"ext-1","sessionId":"sess-abc","toolCallId":"tc-1","toolName":"myTool","arguments":{"key":"value"}}} + """); + assertInstanceOf(ExternalToolRequestedEvent.class, event); + assertEquals("external_tool.requested", event.getType()); + var typed = (ExternalToolRequestedEvent) event; + assertEquals("ext-1", typed.getData().requestId()); + assertEquals("sess-abc", typed.getData().sessionId()); + assertEquals("myTool", typed.getData().toolName()); + } + + // ── ExternalToolCompletedEvent ───────────────────────────────────────── + + @Test + void testParseExternalToolCompletedEvent() throws Exception { + var event = parse(""" + {"type":"external_tool.completed","data":{"requestId":"ext-1"}} + """); + assertInstanceOf(ExternalToolCompletedEvent.class, event); + assertEquals("external_tool.completed", event.getType()); + var typed = (ExternalToolCompletedEvent) event; + assertEquals("ext-1", typed.getData().requestId()); + } + + // ── McpOauthRequiredEvent ────────────────────────────────────────────── + + @Test + void testParseMcpOauthRequiredEvent() throws Exception { + var event = parse( + """ + {"type":"mcp.oauth_required","data":{"requestId":"mcp-oauth-1","serverName":"my-mcp","serverUrl":"https://mcp.example.com"}} + """); + assertInstanceOf(McpOauthRequiredEvent.class, event); + assertEquals("mcp.oauth_required", event.getType()); + var typed = (McpOauthRequiredEvent) event; + assertEquals("mcp-oauth-1", typed.getData().requestId()); + assertEquals("my-mcp", typed.getData().serverName()); + assertEquals("https://mcp.example.com", typed.getData().serverUrl()); + } + + @Test + void testParseMcpOauthRequiredEventWithStaticConfig() throws Exception { + var event = parse( + """ + {"type":"mcp.oauth_required","data":{"requestId":"mcp-oauth-2","serverName":"s","serverUrl":"https://s.com","staticClientConfig":{"clientId":"cid-123","publicClient":true}}} + """); + assertInstanceOf(McpOauthRequiredEvent.class, event); + var typed = (McpOauthRequiredEvent) event; + assertEquals("cid-123", typed.getData().staticClientConfig().clientId()); + assertTrue(typed.getData().staticClientConfig().publicClient()); + } + + // ── McpOauthCompletedEvent ───────────────────────────────────────────── + + @Test + void testParseMcpOauthCompletedEvent() throws Exception { + var event = parse(""" + {"type":"mcp.oauth_completed","data":{"requestId":"mcp-oauth-1"}} + """); + assertInstanceOf(McpOauthCompletedEvent.class, event); + assertEquals("mcp.oauth_completed", event.getType()); + var typed = (McpOauthCompletedEvent) event; + assertEquals("mcp-oauth-1", typed.getData().requestId()); + } + + // ── PermissionRequestedEvent ─────────────────────────────────────────── + + @Test + void testParsePermissionRequestedEvent() throws Exception { + var event = parse( + """ + {"type":"permission.requested","data":{"requestId":"perm-1","permissionRequest":{"tool":"bash"},"resolvedByHook":false}} + """); + assertInstanceOf(PermissionRequestedEvent.class, event); + assertEquals("permission.requested", event.getType()); + var typed = (PermissionRequestedEvent) event; + assertEquals("perm-1", typed.getData().requestId()); + assertNotNull(typed.getData().permissionRequest()); + assertFalse(typed.getData().resolvedByHook()); + } + + @Test + void testParsePermissionRequestedEventResolvedByHook() throws Exception { + var event = parse(""" + {"type":"permission.requested","data":{"requestId":"perm-2","resolvedByHook":true}} + """); + assertInstanceOf(PermissionRequestedEvent.class, event); + var typed = (PermissionRequestedEvent) event; + assertTrue(typed.getData().resolvedByHook()); + } + + // ── PermissionCompletedEvent ─────────────────────────────────────────── + + @Test + void testParsePermissionCompletedEvent() throws Exception { + var event = parse(""" + {"type":"permission.completed","data":{"requestId":"perm-1","decision":"allow"}} + """); + assertInstanceOf(PermissionCompletedEvent.class, event); + assertEquals("permission.completed", event.getType()); + var typed = (PermissionCompletedEvent) event; + assertNotNull(typed.getData()); + } + + // ── SamplingRequestedEvent ───────────────────────────────────────────── + + @Test + void testParseSamplingRequestedEvent() throws Exception { + var event = parse(""" + {"type":"sampling.requested","data":{"requestId":"samp-1","serverName":"my-mcp","mcpRequestId":42}} + """); + assertInstanceOf(SamplingRequestedEvent.class, event); + assertEquals("sampling.requested", event.getType()); + var typed = (SamplingRequestedEvent) event; + assertEquals("samp-1", typed.getData().requestId()); + assertEquals("my-mcp", typed.getData().serverName()); + assertNotNull(typed.getData().mcpRequestId()); + } + + // ── SamplingCompletedEvent ───────────────────────────────────────────── + + @Test + void testParseSamplingCompletedEvent() throws Exception { + var event = parse(""" + {"type":"sampling.completed","data":{"requestId":"samp-1"}} + """); + assertInstanceOf(SamplingCompletedEvent.class, event); + assertEquals("sampling.completed", event.getType()); + var typed = (SamplingCompletedEvent) event; + assertNotNull(typed.getData()); + } + + // ── SessionBackgroundTasksChangedEvent ───────────────────────────────── + + @Test + void testParseSessionBackgroundTasksChangedEvent() throws Exception { + var event = parse(""" + {"type":"session.background_tasks_changed","data":{}} + """); + assertInstanceOf(SessionBackgroundTasksChangedEvent.class, event); + assertEquals("session.background_tasks_changed", event.getType()); + assertNotNull(((SessionBackgroundTasksChangedEvent) event).getData()); + } + + // ── SessionContextChangedEvent ───────────────────────────────────────── + + @Test + void testParseSessionContextChangedEvent() throws Exception { + var event = parse( + """ + {"type":"session.context_changed","data":{"cwd":"/workspace","gitRoot":"/workspace","repository":"myorg/myrepo","hostType":"github","branch":"main","headCommit":"abc123","baseCommit":"def456"}} + """); + assertInstanceOf(SessionContextChangedEvent.class, event); + assertEquals("session.context_changed", event.getType()); + var typed = (SessionContextChangedEvent) event; + assertEquals("/workspace", typed.getData().cwd()); + assertEquals("myorg/myrepo", typed.getData().repository()); + assertEquals( + SessionContextChangedEvent.SessionContextChangedEventData.SessionContextChangedEventDataHostType.GITHUB, + typed.getData().hostType()); + assertEquals("main", typed.getData().branch()); + } + + @Test + void testParseSessionContextChangedEventAdoHostType() throws Exception { + var event = parse(""" + {"type":"session.context_changed","data":{"hostType":"ado"}} + """); + assertInstanceOf(SessionContextChangedEvent.class, event); + var typed = (SessionContextChangedEvent) event; + assertEquals( + SessionContextChangedEvent.SessionContextChangedEventData.SessionContextChangedEventDataHostType.ADO, + typed.getData().hostType()); + } + + // ── SessionCustomAgentsUpdatedEvent ──────────────────────────────────── + + @Test + void testParseSessionCustomAgentsUpdatedEvent() throws Exception { + var event = parse( + """ + {"type":"session.custom_agents_updated","data":{"agents":[{"name":"my-agent","displayName":"My Agent","description":"Does stuff"}]}} + """); + assertInstanceOf(SessionCustomAgentsUpdatedEvent.class, event); + assertEquals("session.custom_agents_updated", event.getType()); + var typed = (SessionCustomAgentsUpdatedEvent) event; + assertNotNull(typed.getData().agents()); + assertEquals(1, typed.getData().agents().size()); + assertEquals("my-agent", typed.getData().agents().get(0).name()); + } + + // ── SessionExtensionsLoadedEvent ─────────────────────────────────────── + + @Test + void testParseSessionExtensionsLoadedEvent() throws Exception { + var event = parse( + """ + {"type":"session.extensions_loaded","data":{"extensions":[{"id":"ext-1","name":"My Extension","enabled":true}]}} + """); + assertInstanceOf(SessionExtensionsLoadedEvent.class, event); + assertEquals("session.extensions_loaded", event.getType()); + var typed = (SessionExtensionsLoadedEvent) event; + assertNotNull(typed.getData().extensions()); + assertEquals(1, typed.getData().extensions().size()); + assertEquals("ext-1", typed.getData().extensions().get(0).id()); + } + + @Test + void testParseSessionExtensionsLoadedEventEmpty() throws Exception { + var event = parse(""" + {"type":"session.extensions_loaded","data":{"extensions":[]}} + """); + assertInstanceOf(SessionExtensionsLoadedEvent.class, event); + } + + // ── SessionMcpServersLoadedEvent ─────────────────────────────────────── + + @Test + void testParseSessionMcpServersLoadedEvent() throws Exception { + var event = parse( + """ + {"type":"session.mcp_servers_loaded","data":{"servers":[{"name":"mcp1","status":"connected","source":"user"}]}} + """); + assertInstanceOf(SessionMcpServersLoadedEvent.class, event); + assertEquals("session.mcp_servers_loaded", event.getType()); + var typed = (SessionMcpServersLoadedEvent) event; + assertNotNull(typed.getData().servers()); + assertEquals(1, typed.getData().servers().size()); + assertEquals("mcp1", typed.getData().servers().get(0).name()); + assertEquals( + SessionMcpServersLoadedEvent.SessionMcpServersLoadedEventData.SessionMcpServersLoadedEventDataServersItem.SessionMcpServersLoadedEventDataServersItemStatus.CONNECTED, + typed.getData().servers().get(0).status()); + } + + @Test + void testParseSessionMcpServersLoadedEventAllStatuses() throws Exception { + // Verify all enum variants are parseable + for (var status : new String[]{"connected", "failed", "needs-auth", "pending", "disabled", "not_configured"}) { + var event = parse( + "{\"type\":\"session.mcp_servers_loaded\",\"data\":{\"servers\":[{\"name\":\"s\",\"status\":\"" + + status + "\"}]}}"); + assertInstanceOf(SessionMcpServersLoadedEvent.class, event); + } + } + + // ── SessionMcpServerStatusChangedEvent ───────────────────────────────── + + @Test + void testParseSessionMcpServerStatusChangedEvent() throws Exception { + var event = parse(""" + {"type":"session.mcp_server_status_changed","data":{"name":"mcp1","status":"connected"}} + """); + assertInstanceOf(SessionMcpServerStatusChangedEvent.class, event); + assertEquals("session.mcp_server_status_changed", event.getType()); + var typed = (SessionMcpServerStatusChangedEvent) event; + assertNotNull(typed.getData()); + } + + // ── SessionRemoteSteerableChangedEvent ───────────────────────────────── + + @Test + void testParseSessionRemoteSteerableChangedEvent() throws Exception { + var event = parse(""" + {"type":"session.remote_steerable_changed","data":{"remoteSteerable":true}} + """); + assertInstanceOf(SessionRemoteSteerableChangedEvent.class, event); + assertEquals("session.remote_steerable_changed", event.getType()); + var typed = (SessionRemoteSteerableChangedEvent) event; + assertTrue(typed.getData().remoteSteerable()); + } + + @Test + void testParseSessionRemoteSteerableChangedEventFalse() throws Exception { + var event = parse(""" + {"type":"session.remote_steerable_changed","data":{"remoteSteerable":false}} + """); + assertInstanceOf(SessionRemoteSteerableChangedEvent.class, event); + var typed = (SessionRemoteSteerableChangedEvent) event; + assertFalse(typed.getData().remoteSteerable()); + } + + // ── SessionSkillsLoadedEvent ─────────────────────────────────────────── + + @Test + void testParseSessionSkillsLoadedEvent() throws Exception { + var event = parse( + """ + {"type":"session.skills_loaded","data":{"skills":[{"name":"deploy","description":"Deploy app","source":"project","userInvocable":true,"enabled":true,"path":"/skills/deploy.md"}]}} + """); + assertInstanceOf(SessionSkillsLoadedEvent.class, event); + assertEquals("session.skills_loaded", event.getType()); + var typed = (SessionSkillsLoadedEvent) event; + assertNotNull(typed.getData().skills()); + assertEquals(1, typed.getData().skills().size()); + var skill = typed.getData().skills().get(0); + assertEquals("deploy", skill.name()); + assertEquals("project", skill.source()); + assertTrue(skill.userInvocable()); + assertTrue(skill.enabled()); + } + + // ── SessionTaskCompleteEvent ─────────────────────────────────────────── + + @Test + void testParseSessionTaskCompleteEvent() throws Exception { + var event = parse(""" + {"type":"session.task_complete","data":{"summary":"All tests pass","success":true}} + """); + assertInstanceOf(SessionTaskCompleteEvent.class, event); + assertEquals("session.task_complete", event.getType()); + var typed = (SessionTaskCompleteEvent) event; + assertEquals("All tests pass", typed.getData().summary()); + assertTrue(typed.getData().success()); + } + + @Test + void testParseSessionTaskCompleteEventFailure() throws Exception { + var event = parse(""" + {"type":"session.task_complete","data":{"summary":"Build failed","success":false}} + """); + assertInstanceOf(SessionTaskCompleteEvent.class, event); + var typed = (SessionTaskCompleteEvent) event; + assertFalse(typed.getData().success()); + } + + // ── SessionTitleChangedEvent ─────────────────────────────────────────── + + @Test + void testParseSessionTitleChangedEvent() throws Exception { + var event = parse(""" + {"type":"session.title_changed","data":{"title":"My new session title"}} + """); + assertInstanceOf(SessionTitleChangedEvent.class, event); + assertEquals("session.title_changed", event.getType()); + var typed = (SessionTitleChangedEvent) event; + assertEquals("My new session title", typed.getData().title()); + } + + // ── SessionToolsUpdatedEvent ─────────────────────────────────────────── + + @Test + void testParseSessionToolsUpdatedEvent() throws Exception { + var event = parse(""" + {"type":"session.tools_updated","data":{"model":"gpt-5"}} + """); + assertInstanceOf(SessionToolsUpdatedEvent.class, event); + assertEquals("session.tools_updated", event.getType()); + var typed = (SessionToolsUpdatedEvent) event; + assertEquals("gpt-5", typed.getData().model()); + } + + // ── SessionWarningEvent ──────────────────────────────────────────────── + + @Test + void testParseSessionWarningEvent() throws Exception { + var event = parse( + """ + {"type":"session.warning","data":{"warningType":"subscription","message":"Quota at 90%","url":"https://github.com/billing"}} + """); + assertInstanceOf(SessionWarningEvent.class, event); + assertEquals("session.warning", event.getType()); + var typed = (SessionWarningEvent) event; + assertEquals("subscription", typed.getData().warningType()); + assertEquals("Quota at 90%", typed.getData().message()); + assertEquals("https://github.com/billing", typed.getData().url()); + } + + // ── SubagentDeselectedEvent ──────────────────────────────────────────── + + @Test + void testParseSubagentDeselectedEvent() throws Exception { + var event = parse(""" + {"type":"subagent.deselected","data":{}} + """); + assertInstanceOf(SubagentDeselectedEvent.class, event); + assertEquals("subagent.deselected", event.getType()); + assertNotNull(((SubagentDeselectedEvent) event).getData()); + } + + // ── SystemNotificationEvent ──────────────────────────────────────────── + + @Test + void testParseSystemNotificationEvent() throws Exception { + var event = parse(""" + {"type":"system.notification","data":{"message":"Update available","level":"info"}} + """); + assertInstanceOf(SystemNotificationEvent.class, event); + assertEquals("system.notification", event.getType()); + var typed = (SystemNotificationEvent) event; + assertNotNull(typed.getData()); + } + + // ── UserInputRequestedEvent ──────────────────────────────────────────── + + @Test + void testParseUserInputRequestedEvent() throws Exception { + var event = parse( + """ + {"type":"user_input.requested","data":{"requestId":"ui-1","question":"What is your name?","choices":["Alice","Bob"],"allowFreeform":true,"toolCallId":"tc-ui-1"}} + """); + assertInstanceOf(UserInputRequestedEvent.class, event); + assertEquals("user_input.requested", event.getType()); + var typed = (UserInputRequestedEvent) event; + assertEquals("ui-1", typed.getData().requestId()); + assertEquals("What is your name?", typed.getData().question()); + assertEquals(2, typed.getData().choices().size()); + assertTrue(typed.getData().allowFreeform()); + assertEquals("tc-ui-1", typed.getData().toolCallId()); + } + + // ── UserInputCompletedEvent ──────────────────────────────────────────── + + @Test + void testParseUserInputCompletedEvent() throws Exception { + var event = parse(""" + {"type":"user_input.completed","data":{"requestId":"ui-1","answer":"Alice","wasFreeform":false}} + """); + assertInstanceOf(UserInputCompletedEvent.class, event); + assertEquals("user_input.completed", event.getType()); + var typed = (UserInputCompletedEvent) event; + assertEquals("ui-1", typed.getData().requestId()); + assertEquals("Alice", typed.getData().answer()); + assertFalse(typed.getData().wasFreeform()); + } + + @Test + void testParseUserInputCompletedEventFreeform() throws Exception { + var event = parse( + """ + {"type":"user_input.completed","data":{"requestId":"ui-2","answer":"Custom response","wasFreeform":true}} + """); + assertInstanceOf(UserInputCompletedEvent.class, event); + var typed = (UserInputCompletedEvent) event; + assertTrue(typed.getData().wasFreeform()); + } + + // ── Enum round-trip tests ────────────────────────────────────────────── + + @Test + void testElicitationRequestedEventDataModeEnumValues() { + assertEquals("form", + ElicitationRequestedEvent.ElicitationRequestedEventData.ElicitationRequestedEventDataMode.FORM + .getValue()); + assertEquals("url", + ElicitationRequestedEvent.ElicitationRequestedEventData.ElicitationRequestedEventDataMode.URL + .getValue()); + } + + @Test + void testElicitationRequestedEventDataModeEnumFromValue() { + assertEquals(ElicitationRequestedEvent.ElicitationRequestedEventData.ElicitationRequestedEventDataMode.FORM, + ElicitationRequestedEvent.ElicitationRequestedEventData.ElicitationRequestedEventDataMode + .fromValue("form")); + assertThrows(IllegalArgumentException.class, + () -> ElicitationRequestedEvent.ElicitationRequestedEventData.ElicitationRequestedEventDataMode + .fromValue("unknown")); + } + + @Test + void testElicitationCompletedEventActionEnumValues() { + assertEquals("accept", + ElicitationCompletedEvent.ElicitationCompletedEventData.ElicitationCompletedEventDataAction.ACCEPT + .getValue()); + assertEquals("decline", + ElicitationCompletedEvent.ElicitationCompletedEventData.ElicitationCompletedEventDataAction.DECLINE + .getValue()); + assertEquals("cancel", + ElicitationCompletedEvent.ElicitationCompletedEventData.ElicitationCompletedEventDataAction.CANCEL + .getValue()); + } + + @Test + void testSessionContextChangedHostTypeEnumFromValue() { + assertEquals( + SessionContextChangedEvent.SessionContextChangedEventData.SessionContextChangedEventDataHostType.GITHUB, + SessionContextChangedEvent.SessionContextChangedEventData.SessionContextChangedEventDataHostType + .fromValue("github")); + assertEquals( + SessionContextChangedEvent.SessionContextChangedEventData.SessionContextChangedEventDataHostType.ADO, + SessionContextChangedEvent.SessionContextChangedEventData.SessionContextChangedEventDataHostType + .fromValue("ado")); + assertThrows(IllegalArgumentException.class, + () -> SessionContextChangedEvent.SessionContextChangedEventData.SessionContextChangedEventDataHostType + .fromValue("unknown")); + } + + @Test + void testSessionMcpServersLoadedStatusEnumFromValue() { + var s = SessionMcpServersLoadedEvent.SessionMcpServersLoadedEventData.SessionMcpServersLoadedEventDataServersItem.SessionMcpServersLoadedEventDataServersItemStatus.class; + assertThrows(IllegalArgumentException.class, + () -> SessionMcpServersLoadedEvent.SessionMcpServersLoadedEventData.SessionMcpServersLoadedEventDataServersItem.SessionMcpServersLoadedEventDataServersItemStatus + .fromValue("unknown")); + } +} diff --git a/src/test/java/com/github/copilot/sdk/GeneratedRpcApiCoverageTest.java b/src/test/java/com/github/copilot/sdk/GeneratedRpcApiCoverageTest.java new file mode 100644 index 000000000..e6a6bd72a --- /dev/null +++ b/src/test/java/com/github/copilot/sdk/GeneratedRpcApiCoverageTest.java @@ -0,0 +1,727 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.sdk; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.junit.jupiter.api.Test; + +import com.github.copilot.sdk.generated.rpc.McpConfigRemoveParams; +import com.github.copilot.sdk.generated.rpc.McpConfigUpdateParams; +import com.github.copilot.sdk.generated.rpc.RpcCaller; +import com.github.copilot.sdk.generated.rpc.ServerRpc; +import com.github.copilot.sdk.generated.rpc.SessionAgentDeselectResult; +import com.github.copilot.sdk.generated.rpc.SessionCommandsHandlePendingCommandParams; +import com.github.copilot.sdk.generated.rpc.SessionExtensionsDisableParams; +import com.github.copilot.sdk.generated.rpc.SessionExtensionsEnableParams; +import com.github.copilot.sdk.generated.rpc.SessionFleetStartParams; +import com.github.copilot.sdk.generated.rpc.SessionFsSetProviderParams; +import com.github.copilot.sdk.generated.rpc.SessionHistoryTruncateParams; +import com.github.copilot.sdk.generated.rpc.SessionLogParams; +import com.github.copilot.sdk.generated.rpc.SessionMcpDisableParams; +import com.github.copilot.sdk.generated.rpc.SessionMcpEnableParams; +import com.github.copilot.sdk.generated.rpc.SessionModeSetParams; +import com.github.copilot.sdk.generated.rpc.SessionPermissionsHandlePendingPermissionRequestParams; +import com.github.copilot.sdk.generated.rpc.SessionPlanUpdateParams; +import com.github.copilot.sdk.generated.rpc.SessionRpc; +import com.github.copilot.sdk.generated.rpc.SessionShellExecParams; +import com.github.copilot.sdk.generated.rpc.SessionShellKillParams; +import com.github.copilot.sdk.generated.rpc.SessionSkillsDisableParams; +import com.github.copilot.sdk.generated.rpc.SessionSkillsEnableParams; +import com.github.copilot.sdk.generated.rpc.SessionToolsHandlePendingToolCallParams; +import com.github.copilot.sdk.generated.rpc.SessionUiElicitationParams; +import com.github.copilot.sdk.generated.rpc.SessionUiHandlePendingElicitationParams; +import com.github.copilot.sdk.generated.rpc.SessionWorkspaceCreateFileParams; +import com.github.copilot.sdk.generated.rpc.SessionWorkspaceReadFileParams; +import com.github.copilot.sdk.generated.rpc.SessionsForkParams; +import com.github.copilot.sdk.generated.rpc.ToolsListParams; + +/** + * Coverage tests for generated RPC API classes that are not exercised in + * {@link RpcWrappersTest}. Uses the same {@link StubCaller} pattern to verify + * that each API method dispatches the correct RPC method name and passes + * parameters correctly. + */ +class GeneratedRpcApiCoverageTest { + + /** A simple stub {@link RpcCaller} that records every call made to it. */ + private static final class StubCaller implements RpcCaller { + + record Call(String method, Object params) { + } + + final List calls = new ArrayList<>(); + Object nextResult = null; + + @Override + @SuppressWarnings("unchecked") + public CompletableFuture invoke(String method, Object params, Class resultType) { + calls.add(new Call(method, params)); + return CompletableFuture.completedFuture((T) nextResult); + } + } + + // ── ServerRpc additional methods ─────────────────────────────────────── + + @Test + void serverRpc_tools_list_invokes_correct_method() { + var stub = new StubCaller(); + var server = new ServerRpc(stub); + + var params = new ToolsListParams("gpt-5"); + server.tools.list(params); + + assertEquals(1, stub.calls.size()); + assertEquals("tools.list", stub.calls.get(0).method()); + assertSame(params, stub.calls.get(0).params()); + } + + @Test + void serverRpc_sessionFs_setProvider_invokes_correct_method() { + var stub = new StubCaller(); + var server = new ServerRpc(stub); + + var params = new SessionFsSetProviderParams("/workspace", "/state", null); + server.sessionFs.setProvider(params); + + assertEquals(1, stub.calls.size()); + assertEquals("sessionFs.setProvider", stub.calls.get(0).method()); + assertSame(params, stub.calls.get(0).params()); + } + + @Test + void serverRpc_sessions_fork_invokes_correct_method() { + var stub = new StubCaller(); + var server = new ServerRpc(stub); + + var params = new SessionsForkParams("parent-session-id", null); + server.sessions.fork(params); + + assertEquals(1, stub.calls.size()); + assertEquals("sessions.fork", stub.calls.get(0).method()); + assertSame(params, stub.calls.get(0).params()); + } + + @Test + void serverRpc_mcp_config_update_invokes_correct_method() { + var stub = new StubCaller(); + var server = new ServerRpc(stub); + + var params = new McpConfigUpdateParams("myServer", "new-config"); + server.mcp.config.update(params); + + assertEquals(1, stub.calls.size()); + assertEquals("mcp.config.update", stub.calls.get(0).method()); + assertSame(params, stub.calls.get(0).params()); + } + + @Test + void serverRpc_mcp_config_remove_invokes_correct_method() { + var stub = new StubCaller(); + var server = new ServerRpc(stub); + + var params = new McpConfigRemoveParams("myServer"); + server.mcp.config.remove(params); + + assertEquals(1, stub.calls.size()); + assertEquals("mcp.config.remove", stub.calls.get(0).method()); + assertSame(params, stub.calls.get(0).params()); + } + + // ── SessionRpc.mode ──────────────────────────────────────────────────── + + @Test + void sessionRpc_mode_get_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-mode"); + + session.mode.get(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.mode.get", stub.calls.get(0).method()); + var params = stub.calls.get(0).params(); + assertInstanceOf(Map.class, params); + assertEquals("sess-mode", ((Map) params).get("sessionId")); + } + + @Test + void sessionRpc_mode_set_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-mode-set"); + + var modeParams = new SessionModeSetParams(null, null); + session.mode.set(modeParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.mode.set", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-mode-set", params.get("sessionId").asText()); + } + + // ── SessionRpc.plan ──────────────────────────────────────────────────── + + @Test + void sessionRpc_plan_read_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-plan"); + + session.plan.read(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.plan.read", stub.calls.get(0).method()); + var params = (Map) stub.calls.get(0).params(); + assertEquals("sess-plan", params.get("sessionId")); + } + + @Test + void sessionRpc_plan_update_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-plan-upd"); + + var planParams = new SessionPlanUpdateParams(null, "# My Plan"); + session.plan.update(planParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.plan.update", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-plan-upd", params.get("sessionId").asText()); + } + + @Test + void sessionRpc_plan_delete_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-plan-del"); + + session.plan.delete(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.plan.delete", stub.calls.get(0).method()); + var params = (Map) stub.calls.get(0).params(); + assertEquals("sess-plan-del", params.get("sessionId")); + } + + // ── SessionRpc.workspace ─────────────────────────────────────────────── + + @Test + void sessionRpc_workspace_listFiles_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-ws"); + + session.workspace.listFiles(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.workspace.listFiles", stub.calls.get(0).method()); + var params = (Map) stub.calls.get(0).params(); + assertEquals("sess-ws", params.get("sessionId")); + } + + @Test + void sessionRpc_workspace_readFile_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-ws-rf"); + + var rfParams = new SessionWorkspaceReadFileParams(null, "/src/Main.java"); + session.workspace.readFile(rfParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.workspace.readFile", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-ws-rf", params.get("sessionId").asText()); + assertEquals("/src/Main.java", params.get("path").asText()); + } + + @Test + void sessionRpc_workspace_createFile_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-ws-cf"); + + var cfParams = new SessionWorkspaceCreateFileParams(null, "/new/file.txt", "content"); + session.workspace.createFile(cfParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.workspace.createFile", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-ws-cf", params.get("sessionId").asText()); + } + + // ── SessionRpc.fleet ─────────────────────────────────────────────────── + + @Test + void sessionRpc_fleet_start_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-fleet"); + + var fleetParams = new SessionFleetStartParams(null, "fix all bugs"); + session.fleet.start(fleetParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.fleet.start", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-fleet", params.get("sessionId").asText()); + assertEquals("fix all bugs", params.get("prompt").asText()); + } + + // ── SessionRpc.skills ────────────────────────────────────────────────── + + @Test + void sessionRpc_skills_list_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-skills"); + + session.skills.list(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.skills.list", stub.calls.get(0).method()); + var params = (Map) stub.calls.get(0).params(); + assertEquals("sess-skills", params.get("sessionId")); + } + + @Test + void sessionRpc_skills_enable_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-skills-en"); + + var enableParams = new SessionSkillsEnableParams(null, "my-skill"); + session.skills.enable(enableParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.skills.enable", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-skills-en", params.get("sessionId").asText()); + assertEquals("my-skill", params.get("name").asText()); + } + + @Test + void sessionRpc_skills_disable_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-skills-dis"); + + var disableParams = new SessionSkillsDisableParams(null, "my-skill"); + session.skills.disable(disableParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.skills.disable", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-skills-dis", params.get("sessionId").asText()); + } + + @Test + void sessionRpc_skills_reload_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-skills-rel"); + + session.skills.reload(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.skills.reload", stub.calls.get(0).method()); + var params = (Map) stub.calls.get(0).params(); + assertEquals("sess-skills-rel", params.get("sessionId")); + } + + // ── SessionRpc.mcp ───────────────────────────────────────────────────── + + @Test + void sessionRpc_mcp_list_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-mcp"); + + session.mcp.list(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.mcp.list", stub.calls.get(0).method()); + var params = (Map) stub.calls.get(0).params(); + assertEquals("sess-mcp", params.get("sessionId")); + } + + @Test + void sessionRpc_mcp_enable_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-mcp-en"); + + var enableParams = new SessionMcpEnableParams(null, "my-mcp-server"); + session.mcp.enable(enableParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.mcp.enable", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-mcp-en", params.get("sessionId").asText()); + assertEquals("my-mcp-server", params.get("serverName").asText()); + } + + @Test + void sessionRpc_mcp_disable_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-mcp-dis"); + + var disableParams = new SessionMcpDisableParams(null, "my-mcp-server"); + session.mcp.disable(disableParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.mcp.disable", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-mcp-dis", params.get("sessionId").asText()); + assertEquals("my-mcp-server", params.get("serverName").asText()); + } + + @Test + void sessionRpc_mcp_reload_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-mcp-rel"); + + session.mcp.reload(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.mcp.reload", stub.calls.get(0).method()); + var params = (Map) stub.calls.get(0).params(); + assertEquals("sess-mcp-rel", params.get("sessionId")); + } + + // ── SessionRpc.plugins ───────────────────────────────────────────────── + + @Test + void sessionRpc_plugins_list_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-plugins"); + + session.plugins.list(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.plugins.list", stub.calls.get(0).method()); + var params = (Map) stub.calls.get(0).params(); + assertEquals("sess-plugins", params.get("sessionId")); + } + + // ── SessionRpc.extensions ────────────────────────────────────────────── + + @Test + void sessionRpc_extensions_list_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-ext"); + + session.extensions.list(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.extensions.list", stub.calls.get(0).method()); + var params = (Map) stub.calls.get(0).params(); + assertEquals("sess-ext", params.get("sessionId")); + } + + @Test + void sessionRpc_extensions_enable_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-ext-en"); + + var enableParams = new SessionExtensionsEnableParams(null, "github.ext-id"); + session.extensions.enable(enableParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.extensions.enable", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-ext-en", params.get("sessionId").asText()); + assertEquals("github.ext-id", params.get("id").asText()); + } + + @Test + void sessionRpc_extensions_disable_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-ext-dis"); + + var disableParams = new SessionExtensionsDisableParams(null, "github.ext-id"); + session.extensions.disable(disableParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.extensions.disable", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-ext-dis", params.get("sessionId").asText()); + } + + @Test + void sessionRpc_extensions_reload_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-ext-rel"); + + session.extensions.reload(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.extensions.reload", stub.calls.get(0).method()); + var params = (Map) stub.calls.get(0).params(); + assertEquals("sess-ext-rel", params.get("sessionId")); + } + + // ── SessionRpc.tools ─────────────────────────────────────────────────── + + @Test + void sessionRpc_tools_handlePendingToolCall_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-tools"); + + var toolParams = new SessionToolsHandlePendingToolCallParams(null, "req-123", "ok", null); + session.tools.handlePendingToolCall(toolParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.tools.handlePendingToolCall", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-tools", params.get("sessionId").asText()); + assertEquals("req-123", params.get("requestId").asText()); + } + + // ── SessionRpc.commands ──────────────────────────────────────────────── + + @Test + void sessionRpc_commands_handlePendingCommand_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-cmds"); + + var cmdParams = new SessionCommandsHandlePendingCommandParams(null, "req-cmd-456", null); + session.commands.handlePendingCommand(cmdParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.commands.handlePendingCommand", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-cmds", params.get("sessionId").asText()); + assertEquals("req-cmd-456", params.get("requestId").asText()); + } + + // ── SessionRpc.ui ────────────────────────────────────────────────────── + + @Test + void sessionRpc_ui_elicitation_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-ui"); + + var uiParams = new SessionUiElicitationParams(null, "Please provide info", null); + session.ui.elicitation(uiParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.ui.elicitation", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-ui", params.get("sessionId").asText()); + assertEquals("Please provide info", params.get("message").asText()); + } + + @Test + void sessionRpc_ui_handlePendingElicitation_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-ui-elicit"); + + var elicitParams = new SessionUiHandlePendingElicitationParams(null, "req-elicit-789", null); + session.ui.handlePendingElicitation(elicitParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.ui.handlePendingElicitation", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-ui-elicit", params.get("sessionId").asText()); + assertEquals("req-elicit-789", params.get("requestId").asText()); + } + + // ── SessionRpc.permissions ───────────────────────────────────────────── + + @Test + void sessionRpc_permissions_handlePendingPermissionRequest_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-perm"); + + var permParams = new SessionPermissionsHandlePendingPermissionRequestParams(null, "req-perm-1", "allow"); + session.permissions.handlePendingPermissionRequest(permParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.permissions.handlePendingPermissionRequest", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-perm", params.get("sessionId").asText()); + assertEquals("req-perm-1", params.get("requestId").asText()); + } + + // ── SessionRpc.shell ─────────────────────────────────────────────────── + + @Test + void sessionRpc_shell_exec_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-shell"); + + var shellParams = new SessionShellExecParams(null, "ls -la", "/workspace", null); + session.shell.exec(shellParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.shell.exec", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-shell", params.get("sessionId").asText()); + assertEquals("ls -la", params.get("command").asText()); + } + + @Test + void sessionRpc_shell_kill_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-shell-kill"); + + var killParams = new SessionShellKillParams(null, "proc-123", null); + session.shell.kill(killParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.shell.kill", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-shell-kill", params.get("sessionId").asText()); + assertEquals("proc-123", params.get("processId").asText()); + } + + // ── SessionRpc.history ───────────────────────────────────────────────── + + @Test + void sessionRpc_history_compact_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-hist"); + + session.history.compact(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.history.compact", stub.calls.get(0).method()); + var params = (Map) stub.calls.get(0).params(); + assertEquals("sess-hist", params.get("sessionId")); + } + + @Test + void sessionRpc_history_truncate_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-hist-trunc"); + + var truncParams = new SessionHistoryTruncateParams(null, "event-id-abc"); + session.history.truncate(truncParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.history.truncate", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-hist-trunc", params.get("sessionId").asText()); + assertEquals("event-id-abc", params.get("eventId").asText()); + } + + // ── SessionRpc.usage ─────────────────────────────────────────────────── + + @Test + void sessionRpc_usage_getMetrics_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-usage"); + + session.usage.getMetrics(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.usage.getMetrics", stub.calls.get(0).method()); + var params = (Map) stub.calls.get(0).params(); + assertEquals("sess-usage", params.get("sessionId")); + } + + // ── SessionRpc.agent additional methods ──────────────────────────────── + + @Test + void sessionRpc_agent_getCurrent_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-agent-gc"); + + session.agent.getCurrent(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.agent.getCurrent", stub.calls.get(0).method()); + var params = (Map) stub.calls.get(0).params(); + assertEquals("sess-agent-gc", params.get("sessionId")); + } + + @Test + void sessionRpc_agent_deselect_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-agent-des"); + + session.agent.deselect(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.agent.deselect", stub.calls.get(0).method()); + var params = (Map) stub.calls.get(0).params(); + assertEquals("sess-agent-des", params.get("sessionId")); + } + + @Test + void sessionRpc_agent_reload_injects_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-agent-rel"); + + session.agent.reload(); + + assertEquals(1, stub.calls.size()); + assertEquals("session.agent.reload", stub.calls.get(0).method()); + var params = (Map) stub.calls.get(0).params(); + assertEquals("sess-agent-rel", params.get("sessionId")); + } + + // ── SessionRpc.log (top-level) ───────────────────────────────────────── + + @Test + void sessionRpc_log_merges_sessionId() { + var stub = new StubCaller(); + var session = new SessionRpc(stub, "sess-log"); + + var logParams = new SessionLogParams(null, "Hello from test", null, null, null); + session.log(logParams); + + assertEquals(1, stub.calls.size()); + assertEquals("session.log", stub.calls.get(0).method()); + var params = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(0).params(); + assertEquals("sess-log", params.get("sessionId").asText()); + assertEquals("Hello from test", params.get("message").asText()); + } + + // ── SessionFs server-side methods (via SessionRpc) ───────────────────── + // SessionFs methods are accessed via ServerRpc.sessionFs; these tests + // cover the remaining SessionFs param records used server-side. + + @Test + void serverRpc_sessionFs_setProvider_params_record() { + var params = new SessionFsSetProviderParams("/workspace", "/state", null); + assertEquals("/workspace", params.initialCwd()); + assertEquals("/state", params.sessionStatePath()); + assertNull(params.conventions()); + } + + @Test + void sessionsForkParams_record() { + var params = new SessionsForkParams("parent-id", "event-123"); + assertEquals("parent-id", params.sessionId()); + assertEquals("event-123", params.toEventId()); + } + + // ── SessionAgentDeselectResult (empty record) ────────────────────────── + + @Test + void sessionAgentDeselectResult_empty_record() { + var result = new SessionAgentDeselectResult(); + assertNotNull(result); + } + + // ── SessionLogParams enum ────────────────────────────────────────────── + + @Test + void sessionLogParams_level_enum_values() { + assertEquals("info", SessionLogParams.SessionLogParamsLevel.INFO.getValue()); + assertEquals("warning", SessionLogParams.SessionLogParamsLevel.WARNING.getValue()); + assertEquals("error", SessionLogParams.SessionLogParamsLevel.ERROR.getValue()); + } + + @Test + void sessionLogParams_level_enum_fromValue() { + assertEquals(SessionLogParams.SessionLogParamsLevel.INFO, + SessionLogParams.SessionLogParamsLevel.fromValue("info")); + assertEquals(SessionLogParams.SessionLogParamsLevel.WARNING, + SessionLogParams.SessionLogParamsLevel.fromValue("warning")); + assertEquals(SessionLogParams.SessionLogParamsLevel.ERROR, + SessionLogParams.SessionLogParamsLevel.fromValue("error")); + } + + @Test + void sessionLogParams_level_enum_fromValue_unknown_throws() { + assertThrows(IllegalArgumentException.class, + () -> SessionLogParams.SessionLogParamsLevel.fromValue("unknown-level")); + } +} diff --git a/src/test/java/com/github/copilot/sdk/GeneratedRpcRecordsCoverageTest.java b/src/test/java/com/github/copilot/sdk/GeneratedRpcRecordsCoverageTest.java new file mode 100644 index 000000000..d083b8237 --- /dev/null +++ b/src/test/java/com/github/copilot/sdk/GeneratedRpcRecordsCoverageTest.java @@ -0,0 +1,1160 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.sdk; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.copilot.sdk.generated.rpc.AccountGetQuotaResult; +import com.github.copilot.sdk.generated.rpc.McpConfigListResult; +import com.github.copilot.sdk.generated.rpc.McpConfigRemoveParams; +import com.github.copilot.sdk.generated.rpc.McpConfigUpdateParams; +import com.github.copilot.sdk.generated.rpc.McpDiscoverParams; +import com.github.copilot.sdk.generated.rpc.McpDiscoverResult; +import com.github.copilot.sdk.generated.rpc.ModelsListResult; +import com.github.copilot.sdk.generated.rpc.PingParams; +import com.github.copilot.sdk.generated.rpc.PingResult; +import com.github.copilot.sdk.generated.rpc.SessionAgentDeselectParams; +import com.github.copilot.sdk.generated.rpc.SessionAgentDeselectResult; +import com.github.copilot.sdk.generated.rpc.SessionAgentGetCurrentParams; +import com.github.copilot.sdk.generated.rpc.SessionAgentGetCurrentResult; +import com.github.copilot.sdk.generated.rpc.SessionAgentListParams; +import com.github.copilot.sdk.generated.rpc.SessionAgentListResult; +import com.github.copilot.sdk.generated.rpc.SessionAgentReloadParams; +import com.github.copilot.sdk.generated.rpc.SessionAgentReloadResult; +import com.github.copilot.sdk.generated.rpc.SessionAgentSelectParams; +import com.github.copilot.sdk.generated.rpc.SessionAgentSelectResult; +import com.github.copilot.sdk.generated.rpc.SessionCommandsHandlePendingCommandParams; +import com.github.copilot.sdk.generated.rpc.SessionCommandsHandlePendingCommandResult; +import com.github.copilot.sdk.generated.rpc.SessionExtensionsDisableParams; +import com.github.copilot.sdk.generated.rpc.SessionExtensionsDisableResult; +import com.github.copilot.sdk.generated.rpc.SessionExtensionsEnableParams; +import com.github.copilot.sdk.generated.rpc.SessionExtensionsEnableResult; +import com.github.copilot.sdk.generated.rpc.SessionExtensionsListParams; +import com.github.copilot.sdk.generated.rpc.SessionExtensionsListResult; +import com.github.copilot.sdk.generated.rpc.SessionExtensionsReloadParams; +import com.github.copilot.sdk.generated.rpc.SessionExtensionsReloadResult; +import com.github.copilot.sdk.generated.rpc.SessionFleetStartParams; +import com.github.copilot.sdk.generated.rpc.SessionFleetStartResult; +import com.github.copilot.sdk.generated.rpc.SessionFsAppendFileParams; +import com.github.copilot.sdk.generated.rpc.SessionFsExistsParams; +import com.github.copilot.sdk.generated.rpc.SessionFsExistsResult; +import com.github.copilot.sdk.generated.rpc.SessionFsMkdirParams; +import com.github.copilot.sdk.generated.rpc.SessionFsReadFileParams; +import com.github.copilot.sdk.generated.rpc.SessionFsReadFileResult; +import com.github.copilot.sdk.generated.rpc.SessionFsReaddirParams; +import com.github.copilot.sdk.generated.rpc.SessionFsReaddirResult; +import com.github.copilot.sdk.generated.rpc.SessionFsReaddirWithTypesParams; +import com.github.copilot.sdk.generated.rpc.SessionFsReaddirWithTypesResult; +import com.github.copilot.sdk.generated.rpc.SessionFsRenameParams; +import com.github.copilot.sdk.generated.rpc.SessionFsRmParams; +import com.github.copilot.sdk.generated.rpc.SessionFsSetProviderParams; +import com.github.copilot.sdk.generated.rpc.SessionFsSetProviderResult; +import com.github.copilot.sdk.generated.rpc.SessionFsStatParams; +import com.github.copilot.sdk.generated.rpc.SessionFsStatResult; +import com.github.copilot.sdk.generated.rpc.SessionFsWriteFileParams; +import com.github.copilot.sdk.generated.rpc.SessionHistoryCompactParams; +import com.github.copilot.sdk.generated.rpc.SessionHistoryCompactResult; +import com.github.copilot.sdk.generated.rpc.SessionHistoryTruncateParams; +import com.github.copilot.sdk.generated.rpc.SessionHistoryTruncateResult; +import com.github.copilot.sdk.generated.rpc.SessionLogParams; +import com.github.copilot.sdk.generated.rpc.SessionLogResult; +import com.github.copilot.sdk.generated.rpc.SessionMcpDisableParams; +import com.github.copilot.sdk.generated.rpc.SessionMcpDisableResult; +import com.github.copilot.sdk.generated.rpc.SessionMcpEnableParams; +import com.github.copilot.sdk.generated.rpc.SessionMcpEnableResult; +import com.github.copilot.sdk.generated.rpc.SessionMcpListParams; +import com.github.copilot.sdk.generated.rpc.SessionMcpListResult; +import com.github.copilot.sdk.generated.rpc.SessionMcpReloadParams; +import com.github.copilot.sdk.generated.rpc.SessionMcpReloadResult; +import com.github.copilot.sdk.generated.rpc.SessionModeGetParams; +import com.github.copilot.sdk.generated.rpc.SessionModeGetResult; +import com.github.copilot.sdk.generated.rpc.SessionModeSetParams; +import com.github.copilot.sdk.generated.rpc.SessionModeSetResult; +import com.github.copilot.sdk.generated.rpc.SessionModelGetCurrentParams; +import com.github.copilot.sdk.generated.rpc.SessionModelGetCurrentResult; +import com.github.copilot.sdk.generated.rpc.SessionModelSwitchToParams; +import com.github.copilot.sdk.generated.rpc.SessionModelSwitchToResult; +import com.github.copilot.sdk.generated.rpc.SessionPermissionsHandlePendingPermissionRequestParams; +import com.github.copilot.sdk.generated.rpc.SessionPermissionsHandlePendingPermissionRequestResult; +import com.github.copilot.sdk.generated.rpc.SessionPlanDeleteParams; +import com.github.copilot.sdk.generated.rpc.SessionPlanDeleteResult; +import com.github.copilot.sdk.generated.rpc.SessionPlanReadParams; +import com.github.copilot.sdk.generated.rpc.SessionPlanReadResult; +import com.github.copilot.sdk.generated.rpc.SessionPlanUpdateParams; +import com.github.copilot.sdk.generated.rpc.SessionPlanUpdateResult; +import com.github.copilot.sdk.generated.rpc.SessionPluginsListParams; +import com.github.copilot.sdk.generated.rpc.SessionPluginsListResult; +import com.github.copilot.sdk.generated.rpc.SessionShellExecParams; +import com.github.copilot.sdk.generated.rpc.SessionShellExecResult; +import com.github.copilot.sdk.generated.rpc.SessionShellKillParams; +import com.github.copilot.sdk.generated.rpc.SessionShellKillResult; +import com.github.copilot.sdk.generated.rpc.SessionSkillsDisableParams; +import com.github.copilot.sdk.generated.rpc.SessionSkillsDisableResult; +import com.github.copilot.sdk.generated.rpc.SessionSkillsEnableParams; +import com.github.copilot.sdk.generated.rpc.SessionSkillsEnableResult; +import com.github.copilot.sdk.generated.rpc.SessionSkillsListParams; +import com.github.copilot.sdk.generated.rpc.SessionSkillsListResult; +import com.github.copilot.sdk.generated.rpc.SessionSkillsReloadParams; +import com.github.copilot.sdk.generated.rpc.SessionSkillsReloadResult; +import com.github.copilot.sdk.generated.rpc.SessionToolsHandlePendingToolCallParams; +import com.github.copilot.sdk.generated.rpc.SessionToolsHandlePendingToolCallResult; +import com.github.copilot.sdk.generated.rpc.SessionUiElicitationParams; +import com.github.copilot.sdk.generated.rpc.SessionUiElicitationResult; +import com.github.copilot.sdk.generated.rpc.SessionUiHandlePendingElicitationParams; +import com.github.copilot.sdk.generated.rpc.SessionUiHandlePendingElicitationResult; +import com.github.copilot.sdk.generated.rpc.SessionUsageGetMetricsParams; +import com.github.copilot.sdk.generated.rpc.SessionUsageGetMetricsResult; +import com.github.copilot.sdk.generated.rpc.SessionWorkspaceCreateFileParams; +import com.github.copilot.sdk.generated.rpc.SessionWorkspaceCreateFileResult; +import com.github.copilot.sdk.generated.rpc.SessionWorkspaceListFilesParams; +import com.github.copilot.sdk.generated.rpc.SessionWorkspaceListFilesResult; +import com.github.copilot.sdk.generated.rpc.SessionWorkspaceReadFileParams; +import com.github.copilot.sdk.generated.rpc.SessionWorkspaceReadFileResult; +import com.github.copilot.sdk.generated.rpc.SessionsForkParams; +import com.github.copilot.sdk.generated.rpc.SessionsForkResult; +import com.github.copilot.sdk.generated.rpc.ToolsListParams; +import com.github.copilot.sdk.generated.rpc.ToolsListResult; + +/** + * Tests for generated RPC param and result record types. Exercises + * constructors, field accessors, and enum variants to provide JaCoCo coverage + * of the generated code without requiring network access. + */ +class GeneratedRpcRecordsCoverageTest { + + private static final ObjectMapper MAPPER = JsonRpcClient.getObjectMapper(); + + // ── Params records ───────────────────────────────────────────────────── + + @Test + void pingParams_record() { + var params = new PingParams("hello"); + assertEquals("hello", params.message()); + assertNull(new PingParams(null).message()); + } + + @Test + void pingResult_record() { + var result = new PingResult("pong", 1234.0, 2.0); + assertEquals("pong", result.message()); + assertEquals(1234.0, result.timestamp()); + assertEquals(2.0, result.protocolVersion()); + } + + @Test + void mcpDiscoverParams_record() { + var params = new McpDiscoverParams("/workspace"); + assertEquals("/workspace", params.workingDirectory()); + assertNull(new McpDiscoverParams(null).workingDirectory()); + } + + @Test + void mcpConfigRemoveParams_record() { + var params = new McpConfigRemoveParams("old-server"); + assertEquals("old-server", params.name()); + } + + @Test + void mcpConfigUpdateParams_record() { + var params = new McpConfigUpdateParams("my-server", Map.of("key", "val")); + assertEquals("my-server", params.name()); + assertNotNull(params.config()); + } + + @Test + void toolsListParams_record() { + var params = new ToolsListParams("gpt-5"); + assertEquals("gpt-5", params.model()); + assertNull(new ToolsListParams(null).model()); + } + + @Test + void sessionsForkParams_record() { + var params = new SessionsForkParams("sess-1", "event-abc"); + assertEquals("sess-1", params.sessionId()); + assertEquals("event-abc", params.toEventId()); + } + + @Test + void sessionAgentDeselectParams_record() { + var params = new SessionAgentDeselectParams("sess-1"); + assertEquals("sess-1", params.sessionId()); + } + + @Test + void sessionAgentGetCurrentParams_record() { + var params = new SessionAgentGetCurrentParams("sess-2"); + assertEquals("sess-2", params.sessionId()); + } + + @Test + void sessionAgentListParams_record() { + var params = new SessionAgentListParams("sess-3"); + assertEquals("sess-3", params.sessionId()); + } + + @Test + void sessionAgentReloadParams_record() { + var params = new SessionAgentReloadParams("sess-4"); + assertEquals("sess-4", params.sessionId()); + } + + @Test + void sessionAgentSelectParams_record() { + var params = new SessionAgentSelectParams("sess-5", "my-agent"); + assertEquals("sess-5", params.sessionId()); + assertEquals("my-agent", params.name()); + } + + @Test + void sessionCommandsHandlePendingCommandParams_record() { + var params = new SessionCommandsHandlePendingCommandParams("sess-6", "req-cmd", "error msg"); + assertEquals("sess-6", params.sessionId()); + assertEquals("req-cmd", params.requestId()); + assertEquals("error msg", params.error()); + } + + @Test + void sessionExtensionsDisableParams_record() { + var params = new SessionExtensionsDisableParams("sess-7", "ext-id-1"); + assertEquals("sess-7", params.sessionId()); + assertEquals("ext-id-1", params.id()); + } + + @Test + void sessionExtensionsEnableParams_record() { + var params = new SessionExtensionsEnableParams("sess-8", "ext-id-2"); + assertEquals("sess-8", params.sessionId()); + assertEquals("ext-id-2", params.id()); + } + + @Test + void sessionExtensionsListParams_record() { + var params = new SessionExtensionsListParams("sess-9"); + assertEquals("sess-9", params.sessionId()); + } + + @Test + void sessionExtensionsReloadParams_record() { + var params = new SessionExtensionsReloadParams("sess-10"); + assertEquals("sess-10", params.sessionId()); + } + + @Test + void sessionFleetStartParams_record() { + var params = new SessionFleetStartParams("sess-11", "fix all bugs"); + assertEquals("sess-11", params.sessionId()); + assertEquals("fix all bugs", params.prompt()); + } + + @Test + void sessionFsAppendFileParams_record() { + var params = new SessionFsAppendFileParams("sess-12", "/tmp/log.txt", "new line\n", null); + assertEquals("sess-12", params.sessionId()); + assertEquals("/tmp/log.txt", params.path()); + assertEquals("new line\n", params.content()); + assertNull(params.mode()); + } + + @Test + void sessionFsExistsParams_record() { + var params = new SessionFsExistsParams("sess-13", "/tmp/file.txt"); + assertEquals("sess-13", params.sessionId()); + assertEquals("/tmp/file.txt", params.path()); + } + + @Test + void sessionFsMkdirParams_record() { + var params = new SessionFsMkdirParams("sess-14", "/tmp/newdir", true, null); + assertEquals("sess-14", params.sessionId()); + assertEquals("/tmp/newdir", params.path()); + assertTrue(params.recursive()); + assertNull(params.mode()); + } + + @Test + void sessionFsReadFileParams_record() { + var params = new SessionFsReadFileParams("sess-15", "/src/Main.java"); + assertEquals("sess-15", params.sessionId()); + assertEquals("/src/Main.java", params.path()); + } + + @Test + void sessionFsReaddirParams_record() { + var params = new SessionFsReaddirParams("sess-16", "/src"); + assertEquals("sess-16", params.sessionId()); + assertEquals("/src", params.path()); + } + + @Test + void sessionFsReaddirWithTypesParams_record() { + var params = new SessionFsReaddirWithTypesParams("sess-17", "/src"); + assertEquals("sess-17", params.sessionId()); + assertEquals("/src", params.path()); + } + + @Test + void sessionFsRenameParams_record() { + var params = new SessionFsRenameParams("sess-18", "/old.txt", "/new.txt"); + assertEquals("sess-18", params.sessionId()); + assertEquals("/old.txt", params.src()); + assertEquals("/new.txt", params.dest()); + } + + @Test + void sessionFsRmParams_record() { + var params = new SessionFsRmParams("sess-19", "/tmp/file.txt", false, true); + assertEquals("sess-19", params.sessionId()); + assertEquals("/tmp/file.txt", params.path()); + assertFalse(params.recursive()); + assertTrue(params.force()); + } + + @Test + void sessionFsSetProviderParams_conventions_enum() { + assertEquals("windows", SessionFsSetProviderParams.SessionFsSetProviderParamsConventions.WINDOWS.getValue()); + assertEquals("posix", SessionFsSetProviderParams.SessionFsSetProviderParamsConventions.POSIX.getValue()); + assertEquals(SessionFsSetProviderParams.SessionFsSetProviderParamsConventions.POSIX, + SessionFsSetProviderParams.SessionFsSetProviderParamsConventions.fromValue("posix")); + assertThrows(IllegalArgumentException.class, + () -> SessionFsSetProviderParams.SessionFsSetProviderParamsConventions.fromValue("unknown")); + } + + @Test + void sessionFsStatParams_record() { + var params = new SessionFsStatParams("sess-20", "/etc/hosts"); + assertEquals("sess-20", params.sessionId()); + assertEquals("/etc/hosts", params.path()); + } + + @Test + void sessionFsWriteFileParams_record() { + var params = new SessionFsWriteFileParams("sess-21", "/tmp/out.txt", "content here", 0644.0); + assertEquals("sess-21", params.sessionId()); + assertEquals("/tmp/out.txt", params.path()); + assertEquals("content here", params.content()); + assertEquals(0644.0, params.mode()); + } + + @Test + void sessionHistoryCompactParams_record() { + var params = new SessionHistoryCompactParams("sess-22"); + assertEquals("sess-22", params.sessionId()); + } + + @Test + void sessionHistoryTruncateParams_record() { + var params = new SessionHistoryTruncateParams("sess-23", "event-id-xyz"); + assertEquals("sess-23", params.sessionId()); + assertEquals("event-id-xyz", params.eventId()); + } + + @Test + void sessionLogParams_record() { + var params = new SessionLogParams("sess-24", "test message", SessionLogParams.SessionLogParamsLevel.INFO, false, + null); + assertEquals("sess-24", params.sessionId()); + assertEquals("test message", params.message()); + assertEquals(SessionLogParams.SessionLogParamsLevel.INFO, params.level()); + assertFalse(params.ephemeral()); + assertNull(params.url()); + } + + @Test + void sessionLogParams_level_enum_all_values() { + for (var level : SessionLogParams.SessionLogParamsLevel.values()) { + assertNotNull(level.getValue()); + assertEquals(level, SessionLogParams.SessionLogParamsLevel.fromValue(level.getValue())); + } + } + + @Test + void sessionMcpDisableParams_record() { + var params = new SessionMcpDisableParams("sess-25", "mcp-server-1"); + assertEquals("sess-25", params.sessionId()); + assertEquals("mcp-server-1", params.serverName()); + } + + @Test + void sessionMcpEnableParams_record() { + var params = new SessionMcpEnableParams("sess-26", "mcp-server-2"); + assertEquals("sess-26", params.sessionId()); + assertEquals("mcp-server-2", params.serverName()); + } + + @Test + void sessionMcpListParams_record() { + var params = new SessionMcpListParams("sess-27"); + assertEquals("sess-27", params.sessionId()); + } + + @Test + void sessionMcpReloadParams_record() { + var params = new SessionMcpReloadParams("sess-28"); + assertEquals("sess-28", params.sessionId()); + } + + @Test + void sessionModeGetParams_record() { + var params = new SessionModeGetParams("sess-29"); + assertEquals("sess-29", params.sessionId()); + } + + @Test + void sessionModeSetParams_record() { + var params = new SessionModeSetParams("sess-30", SessionModeSetParams.SessionModeSetParamsMode.PLAN); + assertEquals("sess-30", params.sessionId()); + assertEquals(SessionModeSetParams.SessionModeSetParamsMode.PLAN, params.mode()); + } + + @Test + void sessionModeSetParams_mode_enum() { + assertEquals("interactive", SessionModeSetParams.SessionModeSetParamsMode.INTERACTIVE.getValue()); + assertEquals("plan", SessionModeSetParams.SessionModeSetParamsMode.PLAN.getValue()); + assertEquals("autopilot", SessionModeSetParams.SessionModeSetParamsMode.AUTOPILOT.getValue()); + for (var mode : SessionModeSetParams.SessionModeSetParamsMode.values()) { + assertEquals(mode, SessionModeSetParams.SessionModeSetParamsMode.fromValue(mode.getValue())); + } + assertThrows(IllegalArgumentException.class, + () -> SessionModeSetParams.SessionModeSetParamsMode.fromValue("unknown-mode")); + } + + @Test + void sessionModelGetCurrentParams_record() { + var params = new SessionModelGetCurrentParams("sess-31"); + assertEquals("sess-31", params.sessionId()); + } + + @Test + void sessionModelSwitchToParams_record() { + var params = new SessionModelSwitchToParams("sess-32", "claude-sonnet-4.5", "high", null); + assertEquals("sess-32", params.sessionId()); + assertEquals("claude-sonnet-4.5", params.modelId()); + assertEquals("high", params.reasoningEffort()); + assertNull(params.modelCapabilities()); + } + + @Test + void sessionPermissionsHandlePendingPermissionRequestParams_record() { + var params = new SessionPermissionsHandlePendingPermissionRequestParams("sess-33", "req-1", "allow"); + assertEquals("sess-33", params.sessionId()); + assertEquals("req-1", params.requestId()); + assertEquals("allow", params.result()); + } + + @Test + void sessionPlanDeleteParams_record() { + var params = new SessionPlanDeleteParams("sess-34"); + assertEquals("sess-34", params.sessionId()); + } + + @Test + void sessionPlanReadParams_record() { + var params = new SessionPlanReadParams("sess-35"); + assertEquals("sess-35", params.sessionId()); + } + + @Test + void sessionPlanUpdateParams_record() { + var params = new SessionPlanUpdateParams("sess-36", "# My Plan\n1. Do stuff"); + assertEquals("sess-36", params.sessionId()); + assertEquals("# My Plan\n1. Do stuff", params.content()); + } + + @Test + void sessionPluginsListParams_record() { + var params = new SessionPluginsListParams("sess-37"); + assertEquals("sess-37", params.sessionId()); + } + + @Test + void sessionShellExecParams_record() { + var params = new SessionShellExecParams("sess-38", "ls -la", "/workspace", 5000.0); + assertEquals("sess-38", params.sessionId()); + assertEquals("ls -la", params.command()); + assertEquals("/workspace", params.cwd()); + assertEquals(5000.0, params.timeout()); + } + + @Test + void sessionShellKillParams_record() { + var params = new SessionShellKillParams("sess-39", "proc-abc", + SessionShellKillParams.SessionShellKillParamsSignal.SIGTERM); + assertEquals("sess-39", params.sessionId()); + assertEquals("proc-abc", params.processId()); + assertEquals(SessionShellKillParams.SessionShellKillParamsSignal.SIGTERM, params.signal()); + } + + @Test + void sessionShellKillParams_signal_enum() { + assertEquals("SIGTERM", SessionShellKillParams.SessionShellKillParamsSignal.SIGTERM.getValue()); + assertEquals("SIGKILL", SessionShellKillParams.SessionShellKillParamsSignal.SIGKILL.getValue()); + assertEquals("SIGINT", SessionShellKillParams.SessionShellKillParamsSignal.SIGINT.getValue()); + for (var sig : SessionShellKillParams.SessionShellKillParamsSignal.values()) { + assertEquals(sig, SessionShellKillParams.SessionShellKillParamsSignal.fromValue(sig.getValue())); + } + assertThrows(IllegalArgumentException.class, + () -> SessionShellKillParams.SessionShellKillParamsSignal.fromValue("SIGHUP")); + } + + @Test + void sessionSkillsDisableParams_record() { + var params = new SessionSkillsDisableParams("sess-40", "my-skill"); + assertEquals("sess-40", params.sessionId()); + assertEquals("my-skill", params.name()); + } + + @Test + void sessionSkillsEnableParams_record() { + var params = new SessionSkillsEnableParams("sess-41", "another-skill"); + assertEquals("sess-41", params.sessionId()); + assertEquals("another-skill", params.name()); + } + + @Test + void sessionSkillsListParams_record() { + var params = new SessionSkillsListParams("sess-42"); + assertEquals("sess-42", params.sessionId()); + } + + @Test + void sessionSkillsReloadParams_record() { + var params = new SessionSkillsReloadParams("sess-43"); + assertEquals("sess-43", params.sessionId()); + } + + @Test + void sessionToolsHandlePendingToolCallParams_record() { + var params = new SessionToolsHandlePendingToolCallParams("sess-44", "req-tool-1", "result data", null); + assertEquals("sess-44", params.sessionId()); + assertEquals("req-tool-1", params.requestId()); + assertEquals("result data", params.result()); + assertNull(params.error()); + } + + @Test + void sessionUiElicitationParams_record() { + var params = new SessionUiElicitationParams("sess-45", "What is your name?", null); + assertEquals("sess-45", params.sessionId()); + assertEquals("What is your name?", params.message()); + assertNull(params.requestedSchema()); + } + + @Test + void sessionUiHandlePendingElicitationParams_record() { + var params = new SessionUiHandlePendingElicitationParams("sess-46", "req-elicit-1", null); + assertEquals("sess-46", params.sessionId()); + assertEquals("req-elicit-1", params.requestId()); + assertNull(params.result()); + } + + @Test + void sessionUsageGetMetricsParams_record() { + var params = new SessionUsageGetMetricsParams("sess-47"); + assertEquals("sess-47", params.sessionId()); + } + + @Test + void sessionWorkspaceCreateFileParams_record() { + var params = new SessionWorkspaceCreateFileParams("sess-48", "README.md", "# Hello"); + assertEquals("sess-48", params.sessionId()); + assertEquals("README.md", params.path()); + assertEquals("# Hello", params.content()); + } + + @Test + void sessionWorkspaceListFilesParams_record() { + var params = new SessionWorkspaceListFilesParams("sess-49"); + assertEquals("sess-49", params.sessionId()); + } + + @Test + void sessionWorkspaceReadFileParams_record() { + var params = new SessionWorkspaceReadFileParams("sess-50", "src/Main.java"); + assertEquals("sess-50", params.sessionId()); + assertEquals("src/Main.java", params.path()); + } + + // ── Result records ───────────────────────────────────────────────────── + + @Test + void pingResult_fields() { + var result = new PingResult("pong", 9999.0, 1.0); + assertEquals("pong", result.message()); + assertEquals(9999.0, result.timestamp()); + assertEquals(1.0, result.protocolVersion()); + } + + @Test + void sessionAgentDeselectResult_empty() { + assertNotNull(new SessionAgentDeselectResult()); + } + + @Test + void sessionAgentListResult_with_items() { + var item = new SessionAgentListResult.SessionAgentListResultAgentsItem("name1", "Name One", "Desc 1"); + var result = new SessionAgentListResult(List.of(item)); + assertEquals(1, result.agents().size()); + assertEquals("name1", result.agents().get(0).name()); + assertEquals("Name One", result.agents().get(0).displayName()); + assertEquals("Desc 1", result.agents().get(0).description()); + } + + @Test + void sessionAgentGetCurrentResult_nested() { + var agent = new SessionAgentGetCurrentResult.SessionAgentGetCurrentResultAgent("agent-1", "Agent One", + "Does things"); + var result = new SessionAgentGetCurrentResult(agent); + assertEquals("agent-1", result.agent().name()); + assertEquals("Agent One", result.agent().displayName()); + assertEquals("Does things", result.agent().description()); + } + + @Test + void sessionAgentGetCurrentResult_null_agent() { + var result = new SessionAgentGetCurrentResult(null); + assertNull(result.agent()); + } + + @Test + void sessionAgentReloadResult_with_items() { + var item = new SessionAgentReloadResult.SessionAgentReloadResultAgentsItem("a", "A", "Desc"); + var result = new SessionAgentReloadResult(List.of(item)); + assertEquals(1, result.agents().size()); + assertEquals("a", result.agents().get(0).name()); + } + + @Test + void sessionAgentSelectResult_nested() { + var agent = new SessionAgentSelectResult.SessionAgentSelectResultAgent("selected", "Selected", + "The selected agent"); + var result = new SessionAgentSelectResult(agent); + assertEquals("selected", result.agent().name()); + } + + @Test + void sessionCommandsHandlePendingCommandResult_record() { + var result = new SessionCommandsHandlePendingCommandResult(true); + assertTrue(result.success()); + assertFalse(new SessionCommandsHandlePendingCommandResult(false).success()); + } + + @Test + void sessionExtensionsDisableResult_empty() { + assertNotNull(new SessionExtensionsDisableResult()); + } + + @Test + void sessionExtensionsEnableResult_empty() { + assertNotNull(new SessionExtensionsEnableResult()); + } + + @Test + void sessionExtensionsListResult_nested() { + var ext = new SessionExtensionsListResult.SessionExtensionsListResultExtensionsItem("ext-1", "My Extension", + SessionExtensionsListResult.SessionExtensionsListResultExtensionsItem.SessionExtensionsListResultExtensionsItemSource.PROJECT, + SessionExtensionsListResult.SessionExtensionsListResultExtensionsItem.SessionExtensionsListResultExtensionsItemStatus.RUNNING, + 1234L); + var result = new SessionExtensionsListResult(List.of(ext)); + assertEquals(1, result.extensions().size()); + assertEquals("ext-1", result.extensions().get(0).id()); + assertEquals("My Extension", result.extensions().get(0).name()); + assertEquals( + SessionExtensionsListResult.SessionExtensionsListResultExtensionsItem.SessionExtensionsListResultExtensionsItemSource.PROJECT, + result.extensions().get(0).source()); + assertEquals( + SessionExtensionsListResult.SessionExtensionsListResultExtensionsItem.SessionExtensionsListResultExtensionsItemStatus.RUNNING, + result.extensions().get(0).status()); + assertEquals(1234L, result.extensions().get(0).pid()); + } + + @Test + void sessionExtensionsListResult_enums() { + for (var src : SessionExtensionsListResult.SessionExtensionsListResultExtensionsItem.SessionExtensionsListResultExtensionsItemSource + .values()) { + assertNotNull(src.getValue()); + assertEquals(src, + SessionExtensionsListResult.SessionExtensionsListResultExtensionsItem.SessionExtensionsListResultExtensionsItemSource + .fromValue(src.getValue())); + } + for (var status : SessionExtensionsListResult.SessionExtensionsListResultExtensionsItem.SessionExtensionsListResultExtensionsItemStatus + .values()) { + assertNotNull(status.getValue()); + assertEquals(status, + SessionExtensionsListResult.SessionExtensionsListResultExtensionsItem.SessionExtensionsListResultExtensionsItemStatus + .fromValue(status.getValue())); + } + assertThrows(IllegalArgumentException.class, + () -> SessionExtensionsListResult.SessionExtensionsListResultExtensionsItem.SessionExtensionsListResultExtensionsItemSource + .fromValue("unknown")); + assertThrows(IllegalArgumentException.class, + () -> SessionExtensionsListResult.SessionExtensionsListResultExtensionsItem.SessionExtensionsListResultExtensionsItemStatus + .fromValue("unknown")); + } + + @Test + void sessionExtensionsReloadResult_empty() { + assertNotNull(new SessionExtensionsReloadResult()); + } + + @Test + void sessionFleetStartResult_record() { + var result = new SessionFleetStartResult(true); + assertTrue(result.started()); + assertFalse(new SessionFleetStartResult(false).started()); + } + + @Test + void sessionFsExistsResult_record() { + var result = new SessionFsExistsResult(true); + assertTrue(result.exists()); + assertFalse(new SessionFsExistsResult(false).exists()); + } + + @Test + void sessionFsReadFileResult_record() { + var result = new SessionFsReadFileResult("file content here"); + assertEquals("file content here", result.content()); + } + + @Test + void sessionFsReaddirResult_record() { + var result = new SessionFsReaddirResult(List.of("file1.txt", "file2.txt")); + assertEquals(2, result.entries().size()); + assertEquals("file1.txt", result.entries().get(0)); + } + + @Test + void sessionFsReaddirWithTypesResult_nested() { + var entry = new SessionFsReaddirWithTypesResult.SessionFsReaddirWithTypesResultEntriesItem("myfile.txt", + SessionFsReaddirWithTypesResult.SessionFsReaddirWithTypesResultEntriesItem.SessionFsReaddirWithTypesResultEntriesItemType.FILE); + var result = new SessionFsReaddirWithTypesResult(List.of(entry)); + assertEquals(1, result.entries().size()); + assertEquals("myfile.txt", result.entries().get(0).name()); + assertEquals( + SessionFsReaddirWithTypesResult.SessionFsReaddirWithTypesResultEntriesItem.SessionFsReaddirWithTypesResultEntriesItemType.FILE, + result.entries().get(0).type()); + assertEquals("file", result.entries().get(0).type().getValue()); + } + + @Test + void sessionFsReaddirWithTypesResult_type_enum() { + for (var t : SessionFsReaddirWithTypesResult.SessionFsReaddirWithTypesResultEntriesItem.SessionFsReaddirWithTypesResultEntriesItemType + .values()) { + assertNotNull(t.getValue()); + assertEquals(t, + SessionFsReaddirWithTypesResult.SessionFsReaddirWithTypesResultEntriesItem.SessionFsReaddirWithTypesResultEntriesItemType + .fromValue(t.getValue())); + } + assertThrows(IllegalArgumentException.class, + () -> SessionFsReaddirWithTypesResult.SessionFsReaddirWithTypesResultEntriesItem.SessionFsReaddirWithTypesResultEntriesItemType + .fromValue("symlink")); + } + + @Test + void sessionFsSetProviderResult_record() { + var result = new SessionFsSetProviderResult(true); + assertTrue(result.success()); + assertFalse(new SessionFsSetProviderResult(false).success()); + } + + @Test + void sessionFsStatResult_record() { + var result = new SessionFsStatResult(true, false, 1024.0, "2026-01-01T00:00:00Z", "2025-12-01T00:00:00Z"); + assertTrue(result.isFile()); + assertFalse(result.isDirectory()); + assertEquals(1024.0, result.size()); + assertEquals("2026-01-01T00:00:00Z", result.mtime()); + assertEquals("2025-12-01T00:00:00Z", result.birthtime()); + } + + @Test + void sessionHistoryCompactResult_nested() { + var ctx = new SessionHistoryCompactResult.SessionHistoryCompactResultContextWindow(100000.0, 5000.0, 20.0, + 1000.0, 3000.0, 500.0); + var result = new SessionHistoryCompactResult(true, 2000.0, 5.0, ctx); + assertTrue(result.success()); + assertEquals(2000.0, result.tokensRemoved()); + assertEquals(5.0, result.messagesRemoved()); + assertNotNull(result.contextWindow()); + assertEquals(100000.0, result.contextWindow().tokenLimit()); + assertEquals(5000.0, result.contextWindow().currentTokens()); + } + + @Test + void sessionHistoryTruncateResult_record() { + var result = new SessionHistoryTruncateResult(3.0); + assertEquals(3.0, result.eventsRemoved()); + } + + @Test + void sessionLogResult_record() { + var id = UUID.randomUUID(); + var result = new SessionLogResult(id); + assertEquals(id, result.eventId()); + } + + @Test + void sessionMcpDisableResult_empty() { + assertNotNull(new SessionMcpDisableResult()); + } + + @Test + void sessionMcpEnableResult_empty() { + assertNotNull(new SessionMcpEnableResult()); + } + + @Test + void sessionMcpListResult_nested() { + var server = new SessionMcpListResult.SessionMcpListResultServersItem("my-mcp", + SessionMcpListResult.SessionMcpListResultServersItem.SessionMcpListResultServersItemStatus.CONNECTED, + "user", null); + var result = new SessionMcpListResult(List.of(server)); + assertEquals(1, result.servers().size()); + assertEquals("my-mcp", result.servers().get(0).name()); + assertEquals( + SessionMcpListResult.SessionMcpListResultServersItem.SessionMcpListResultServersItemStatus.CONNECTED, + result.servers().get(0).status()); + assertEquals("user", result.servers().get(0).source()); + } + + @Test + void sessionMcpListResult_status_enum_all_values() { + for (var status : SessionMcpListResult.SessionMcpListResultServersItem.SessionMcpListResultServersItemStatus + .values()) { + assertNotNull(status.getValue()); + assertEquals(status, + SessionMcpListResult.SessionMcpListResultServersItem.SessionMcpListResultServersItemStatus + .fromValue(status.getValue())); + } + assertThrows(IllegalArgumentException.class, + () -> SessionMcpListResult.SessionMcpListResultServersItem.SessionMcpListResultServersItemStatus + .fromValue("unknown-status")); + } + + @Test + void sessionMcpReloadResult_empty() { + assertNotNull(new SessionMcpReloadResult()); + } + + @Test + void sessionModeGetResult_enum() { + var result = new SessionModeGetResult(SessionModeGetResult.SessionModeGetResultMode.INTERACTIVE); + assertEquals(SessionModeGetResult.SessionModeGetResultMode.INTERACTIVE, result.mode()); + assertEquals("interactive", result.mode().getValue()); + for (var mode : SessionModeGetResult.SessionModeGetResultMode.values()) { + assertEquals(mode, SessionModeGetResult.SessionModeGetResultMode.fromValue(mode.getValue())); + } + assertThrows(IllegalArgumentException.class, + () -> SessionModeGetResult.SessionModeGetResultMode.fromValue("unknown")); + } + + @Test + void sessionModeSetResult_enum() { + var result = new SessionModeSetResult(SessionModeSetResult.SessionModeSetResultMode.AUTOPILOT); + assertEquals(SessionModeSetResult.SessionModeSetResultMode.AUTOPILOT, result.mode()); + assertEquals("autopilot", result.mode().getValue()); + } + + @Test + void sessionModelGetCurrentResult_record() { + var result = new SessionModelGetCurrentResult("claude-sonnet-4.5"); + assertEquals("claude-sonnet-4.5", result.modelId()); + } + + @Test + void sessionModelSwitchToResult_record() { + var result = new SessionModelSwitchToResult("gpt-5"); + assertEquals("gpt-5", result.modelId()); + } + + @Test + void sessionPermissionsHandlePendingPermissionRequestResult_record() { + var result = new SessionPermissionsHandlePendingPermissionRequestResult(true); + assertTrue(result.success()); + assertFalse(new SessionPermissionsHandlePendingPermissionRequestResult(false).success()); + } + + @Test + void sessionPlanDeleteResult_empty() { + assertNotNull(new SessionPlanDeleteResult()); + } + + @Test + void sessionPlanReadResult_record() { + var result = new SessionPlanReadResult(true, "# Plan\n1. Do stuff", "/workspace/.plan"); + assertTrue(result.exists()); + assertEquals("# Plan\n1. Do stuff", result.content()); + assertEquals("/workspace/.plan", result.path()); + } + + @Test + void sessionPlanUpdateResult_empty() { + assertNotNull(new SessionPlanUpdateResult()); + } + + @Test + void sessionPluginsListResult_nested() { + var plugin = new SessionPluginsListResult.SessionPluginsListResultPluginsItem("my-plugin", "marketplace-x", + "1.2.3", true); + var result = new SessionPluginsListResult(List.of(plugin)); + assertEquals(1, result.plugins().size()); + assertEquals("my-plugin", result.plugins().get(0).name()); + assertEquals("marketplace-x", result.plugins().get(0).marketplace()); + assertEquals("1.2.3", result.plugins().get(0).version()); + assertTrue(result.plugins().get(0).enabled()); + } + + @Test + void sessionShellExecResult_record() { + var result = new SessionShellExecResult("proc-id-123"); + assertEquals("proc-id-123", result.processId()); + } + + @Test + void sessionShellKillResult_record() { + var result = new SessionShellKillResult(true); + assertTrue(result.killed()); + assertFalse(new SessionShellKillResult(false).killed()); + } + + @Test + void sessionSkillsDisableResult_empty() { + assertNotNull(new SessionSkillsDisableResult()); + } + + @Test + void sessionSkillsEnableResult_empty() { + assertNotNull(new SessionSkillsEnableResult()); + } + + @Test + void sessionSkillsListResult_nested() { + var item = new SessionSkillsListResult.SessionSkillsListResultSkillsItem("deploy", "Deploy the app", "project", + true, true, "/skills/deploy.md"); + var result = new SessionSkillsListResult(List.of(item)); + assertEquals(1, result.skills().size()); + assertEquals("deploy", result.skills().get(0).name()); + assertEquals("project", result.skills().get(0).source()); + assertTrue(result.skills().get(0).enabled()); + } + + @Test + void sessionSkillsReloadResult_empty() { + assertNotNull(new SessionSkillsReloadResult()); + } + + @Test + void sessionToolsHandlePendingToolCallResult_record() { + var result = new SessionToolsHandlePendingToolCallResult(true); + assertTrue(result.success()); + assertFalse(new SessionToolsHandlePendingToolCallResult(false).success()); + } + + @Test + void sessionUiElicitationResult_accept() { + var result = new SessionUiElicitationResult(SessionUiElicitationResult.SessionUiElicitationResultAction.ACCEPT, + Map.of("name", "Alice")); + assertEquals(SessionUiElicitationResult.SessionUiElicitationResultAction.ACCEPT, result.action()); + assertEquals("Alice", result.content().get("name")); + } + + @Test + void sessionUiElicitationResult_action_enum() { + assertEquals("accept", SessionUiElicitationResult.SessionUiElicitationResultAction.ACCEPT.getValue()); + assertEquals("decline", SessionUiElicitationResult.SessionUiElicitationResultAction.DECLINE.getValue()); + assertEquals("cancel", SessionUiElicitationResult.SessionUiElicitationResultAction.CANCEL.getValue()); + for (var a : SessionUiElicitationResult.SessionUiElicitationResultAction.values()) { + assertEquals(a, SessionUiElicitationResult.SessionUiElicitationResultAction.fromValue(a.getValue())); + } + assertThrows(IllegalArgumentException.class, + () -> SessionUiElicitationResult.SessionUiElicitationResultAction.fromValue("unknown")); + } + + @Test + void sessionUiHandlePendingElicitationResult_record() { + var result = new SessionUiHandlePendingElicitationResult(true); + assertTrue(result.success()); + } + + @Test + void sessionUsageGetMetricsResult_nested() { + var changes = new SessionUsageGetMetricsResult.SessionUsageGetMetricsResultCodeChanges(100L, 50L, 5L); + var result = new SessionUsageGetMetricsResult(0.5, 10L, 2000.0, 1700000000000L, changes, null, "gpt-5", 1000L, + 500L); + assertEquals(0.5, result.totalPremiumRequestCost()); + assertEquals(10L, result.totalUserRequests()); + assertNotNull(result.codeChanges()); + assertEquals(100L, result.codeChanges().linesAdded()); + assertEquals(50L, result.codeChanges().linesRemoved()); + assertEquals(5L, result.codeChanges().filesModifiedCount()); + assertEquals("gpt-5", result.currentModel()); + } + + @Test + void sessionWorkspaceCreateFileResult_empty() { + assertNotNull(new SessionWorkspaceCreateFileResult()); + } + + @Test + void sessionWorkspaceListFilesResult_record() { + var result = new SessionWorkspaceListFilesResult(List.of("src/Main.java", "README.md")); + assertEquals(2, result.files().size()); + assertEquals("src/Main.java", result.files().get(0)); + } + + @Test + void sessionWorkspaceReadFileResult_record() { + var result = new SessionWorkspaceReadFileResult("public class Main {}"); + assertEquals("public class Main {}", result.content()); + } + + @Test + void sessionsForkResult_record() { + var result = new SessionsForkResult("forked-sess-id"); + assertEquals("forked-sess-id", result.sessionId()); + } + + // ── Complex nested result records ────────────────────────────────────── + + @Test + void accountGetQuotaResult_nested() { + var snapshot = new AccountGetQuotaResult.AccountGetQuotaResultQuotaSnapshotsValue(100.0, 40.0, 60.0, 5.0, true, + "2026-05-01"); + var result = new AccountGetQuotaResult(Map.of("chat", snapshot)); + assertEquals(1, result.quotaSnapshots().size()); + var s = result.quotaSnapshots().get("chat"); + assertEquals(100.0, s.entitlementRequests()); + assertEquals(40.0, s.usedRequests()); + assertEquals(60.0, s.remainingPercentage()); + assertEquals(5.0, s.overage()); + assertTrue(s.overageAllowedWithExhaustedQuota()); + assertEquals("2026-05-01", s.resetDate()); + } + + @Test + void mcpConfigListResult_record() { + var result = new McpConfigListResult(Map.of("server1", "config1")); + assertEquals(1, result.servers().size()); + assertEquals("config1", result.servers().get("server1")); + } + + @Test + void mcpDiscoverResult_nested() { + var server = new McpDiscoverResult.McpDiscoverResultServersItem("discovered-server", "local", + McpDiscoverResult.McpDiscoverResultServersItem.McpDiscoverResultServersItemSource.USER, true); + var result = new McpDiscoverResult(List.of(server)); + assertEquals(1, result.servers().size()); + assertEquals("discovered-server", result.servers().get(0).name()); + assertEquals("local", result.servers().get(0).type()); + assertEquals(McpDiscoverResult.McpDiscoverResultServersItem.McpDiscoverResultServersItemSource.USER, + result.servers().get(0).source()); + assertTrue(result.servers().get(0).enabled()); + } + + @Test + void mcpDiscoverResult_source_enum_all_values() { + for (var src : McpDiscoverResult.McpDiscoverResultServersItem.McpDiscoverResultServersItemSource.values()) { + assertNotNull(src.getValue()); + assertEquals(src, McpDiscoverResult.McpDiscoverResultServersItem.McpDiscoverResultServersItemSource + .fromValue(src.getValue())); + } + assertThrows(IllegalArgumentException.class, + () -> McpDiscoverResult.McpDiscoverResultServersItem.McpDiscoverResultServersItemSource + .fromValue("unknown-source")); + } + + @Test + void modelsListResult_nested() { + var supports = new ModelsListResult.ModelsListResultModelsItem.ModelsListResultModelsItemCapabilities.ModelsListResultModelsItemCapabilitiesSupports( + true, false); + var limits = new ModelsListResult.ModelsListResultModelsItem.ModelsListResultModelsItemCapabilities.ModelsListResultModelsItemCapabilitiesLimits( + 100000.0, 8192.0, 128000.0, null); + var capabilities = new ModelsListResult.ModelsListResultModelsItem.ModelsListResultModelsItemCapabilities( + supports, limits); + var policy = new ModelsListResult.ModelsListResultModelsItem.ModelsListResultModelsItemPolicy("active", null); + var billing = new ModelsListResult.ModelsListResultModelsItem.ModelsListResultModelsItemBilling(1.0); + var modelItem = new ModelsListResult.ModelsListResultModelsItem("gpt-5", "GPT-5", capabilities, policy, billing, + null, null); + var result = new ModelsListResult(List.of(modelItem)); + + assertEquals(1, result.models().size()); + assertEquals("gpt-5", result.models().get(0).id()); + assertEquals("GPT-5", result.models().get(0).name()); + assertTrue(result.models().get(0).capabilities().supports().vision()); + assertFalse(result.models().get(0).capabilities().supports().reasoningEffort()); + assertEquals(100000.0, result.models().get(0).capabilities().limits().maxPromptTokens()); + assertEquals("active", result.models().get(0).policy().state()); + assertEquals(1.0, result.models().get(0).billing().multiplier()); + } + + @Test + void toolsListResult_nested() { + var tool = new ToolsListResult.ToolsListResultToolsItem("bash", "bash", "Run shell commands", + Map.of("type", "object"), "Use for shell commands"); + var result = new ToolsListResult(List.of(tool)); + assertEquals(1, result.tools().size()); + assertEquals("bash", result.tools().get(0).name()); + assertEquals("bash", result.tools().get(0).namespacedName()); + assertEquals("Run shell commands", result.tools().get(0).description()); + assertEquals("Use for shell commands", result.tools().get(0).instructions()); + } + + // ── SessionModelSwitchToParams nested records ────────────────────────── + + @Test + void sessionModelSwitchToParams_nested_records() { + var limitsVision = new SessionModelSwitchToParams.SessionModelSwitchToParamsModelCapabilities.SessionModelSwitchToParamsModelCapabilitiesLimits.SessionModelSwitchToParamsModelCapabilitiesLimitsVision( + List.of("image/png", "image/jpeg"), 10.0, 5000000.0); + var limits = new SessionModelSwitchToParams.SessionModelSwitchToParamsModelCapabilities.SessionModelSwitchToParamsModelCapabilitiesLimits( + 100000.0, 8192.0, 128000.0, limitsVision); + var supports = new SessionModelSwitchToParams.SessionModelSwitchToParamsModelCapabilities.SessionModelSwitchToParamsModelCapabilitiesSupports( + true, true); + var capabilities = new SessionModelSwitchToParams.SessionModelSwitchToParamsModelCapabilities(supports, limits); + var params = new SessionModelSwitchToParams("sess-m", "gpt-5", null, capabilities); + + assertEquals("gpt-5", params.modelId()); + assertNotNull(params.modelCapabilities()); + assertTrue(params.modelCapabilities().supports().vision()); + assertTrue(params.modelCapabilities().supports().reasoningEffort()); + assertEquals(100000.0, params.modelCapabilities().limits().maxPromptTokens()); + assertEquals(2, params.modelCapabilities().limits().vision().supportedMediaTypes().size()); + } + + // ── SessionUiElicitationParams nested record ─────────────────────────── + + @Test + void sessionUiElicitationParams_nested_schema() { + var schema = new SessionUiElicitationParams.SessionUiElicitationParamsRequestedSchema("object", + Map.of("name", Map.of("type", "string")), List.of("name")); + var params = new SessionUiElicitationParams("sess-elicit", "Please fill form", schema); + assertEquals("sess-elicit", params.sessionId()); + assertEquals("object", params.requestedSchema().type()); + assertTrue(params.requestedSchema().required().contains("name")); + } + + // ── SessionUiHandlePendingElicitationParams nested enum ──────────────── + + @Test + void sessionUiHandlePendingElicitationParamsResult_action_enum() { + for (var action : SessionUiHandlePendingElicitationParams.SessionUiHandlePendingElicitationParamsResult.SessionUiHandlePendingElicitationParamsResultAction + .values()) { + assertNotNull(action.getValue()); + assertEquals(action, + SessionUiHandlePendingElicitationParams.SessionUiHandlePendingElicitationParamsResult.SessionUiHandlePendingElicitationParamsResultAction + .fromValue(action.getValue())); + } + assertThrows(IllegalArgumentException.class, + () -> SessionUiHandlePendingElicitationParams.SessionUiHandlePendingElicitationParamsResult.SessionUiHandlePendingElicitationParamsResultAction + .fromValue("unknown")); + } +} From 99a8145ac235c4aeaa5071b519a2ae505fe1a0e6 Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Mon, 20 Apr 2026 13:24:15 -0400 Subject: [PATCH 3/6] Potential fix for pull request finding 'Unread local variable' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> --- .../com/github/copilot/sdk/GeneratedEventTypesCoverageTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/com/github/copilot/sdk/GeneratedEventTypesCoverageTest.java b/src/test/java/com/github/copilot/sdk/GeneratedEventTypesCoverageTest.java index 5cbc6d1a1..f749236d9 100644 --- a/src/test/java/com/github/copilot/sdk/GeneratedEventTypesCoverageTest.java +++ b/src/test/java/com/github/copilot/sdk/GeneratedEventTypesCoverageTest.java @@ -724,7 +724,6 @@ void testSessionContextChangedHostTypeEnumFromValue() { @Test void testSessionMcpServersLoadedStatusEnumFromValue() { - var s = SessionMcpServersLoadedEvent.SessionMcpServersLoadedEventData.SessionMcpServersLoadedEventDataServersItem.SessionMcpServersLoadedEventDataServersItemStatus.class; assertThrows(IllegalArgumentException.class, () -> SessionMcpServersLoadedEvent.SessionMcpServersLoadedEventData.SessionMcpServersLoadedEventDataServersItem.SessionMcpServersLoadedEventDataServersItemStatus .fromValue("unknown")); From 863eba8581932af4fdfd4363851e02ba3cb49a9e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Apr 2026 18:15:16 +0000 Subject: [PATCH 4/6] Move test files to match generated package structure Agent-Logs-Url: https://github.com/github/copilot-sdk-java/sessions/0c4c84ed-1cb5-4e79-954f-633c61e99a87 Co-authored-by: edburns <75821+edburns@users.noreply.github.com> --- .../GeneratedEventTypesCoverageTest.java | 23 +++- .../rpc}/GeneratedRpcApiCoverageTest.java | 32 +---- .../rpc}/GeneratedRpcRecordsCoverageTest.java | 120 +----------------- 3 files changed, 20 insertions(+), 155 deletions(-) rename src/test/java/com/github/copilot/sdk/{ => generated}/GeneratedEventTypesCoverageTest.java (97%) rename src/test/java/com/github/copilot/sdk/{ => generated/rpc}/GeneratedRpcApiCoverageTest.java (93%) rename src/test/java/com/github/copilot/sdk/{ => generated/rpc}/GeneratedRpcRecordsCoverageTest.java (84%) diff --git a/src/test/java/com/github/copilot/sdk/GeneratedEventTypesCoverageTest.java b/src/test/java/com/github/copilot/sdk/generated/GeneratedEventTypesCoverageTest.java similarity index 97% rename from src/test/java/com/github/copilot/sdk/GeneratedEventTypesCoverageTest.java rename to src/test/java/com/github/copilot/sdk/generated/GeneratedEventTypesCoverageTest.java index f749236d9..72642cd74 100644 --- a/src/test/java/com/github/copilot/sdk/GeneratedEventTypesCoverageTest.java +++ b/src/test/java/com/github/copilot/sdk/generated/GeneratedEventTypesCoverageTest.java @@ -2,25 +2,34 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ -package com.github.copilot.sdk; +package com.github.copilot.sdk.generated; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; - -import com.github.copilot.sdk.generated.*; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; /** * Deserialization tests for generated session event types that are not covered - * in {@link SessionEventDeserializationTest}. Verifies that each event - * deserializes correctly from JSON and that the {@code type} discriminator and - * {@code data} fields are accessible. + * in {@link com.github.copilot.sdk.SessionEventDeserializationTest}. Verifies + * that each event deserializes correctly from JSON and that the {@code type} + * discriminator and {@code data} fields are accessible. */ public class GeneratedEventTypesCoverageTest { - private static final ObjectMapper MAPPER = JsonRpcClient.getObjectMapper(); + private static final ObjectMapper MAPPER = createMapper(); + + private static ObjectMapper createMapper() { + var mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + return mapper; + } private static SessionEvent parse(String json) throws Exception { return MAPPER.readValue(json, SessionEvent.class); diff --git a/src/test/java/com/github/copilot/sdk/GeneratedRpcApiCoverageTest.java b/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcApiCoverageTest.java similarity index 93% rename from src/test/java/com/github/copilot/sdk/GeneratedRpcApiCoverageTest.java rename to src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcApiCoverageTest.java index e6a6bd72a..e7f8d55d4 100644 --- a/src/test/java/com/github/copilot/sdk/GeneratedRpcApiCoverageTest.java +++ b/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcApiCoverageTest.java @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ -package com.github.copilot.sdk; +package com.github.copilot.sdk.generated.rpc; import static org.junit.jupiter.api.Assertions.*; @@ -13,36 +13,6 @@ import org.junit.jupiter.api.Test; -import com.github.copilot.sdk.generated.rpc.McpConfigRemoveParams; -import com.github.copilot.sdk.generated.rpc.McpConfigUpdateParams; -import com.github.copilot.sdk.generated.rpc.RpcCaller; -import com.github.copilot.sdk.generated.rpc.ServerRpc; -import com.github.copilot.sdk.generated.rpc.SessionAgentDeselectResult; -import com.github.copilot.sdk.generated.rpc.SessionCommandsHandlePendingCommandParams; -import com.github.copilot.sdk.generated.rpc.SessionExtensionsDisableParams; -import com.github.copilot.sdk.generated.rpc.SessionExtensionsEnableParams; -import com.github.copilot.sdk.generated.rpc.SessionFleetStartParams; -import com.github.copilot.sdk.generated.rpc.SessionFsSetProviderParams; -import com.github.copilot.sdk.generated.rpc.SessionHistoryTruncateParams; -import com.github.copilot.sdk.generated.rpc.SessionLogParams; -import com.github.copilot.sdk.generated.rpc.SessionMcpDisableParams; -import com.github.copilot.sdk.generated.rpc.SessionMcpEnableParams; -import com.github.copilot.sdk.generated.rpc.SessionModeSetParams; -import com.github.copilot.sdk.generated.rpc.SessionPermissionsHandlePendingPermissionRequestParams; -import com.github.copilot.sdk.generated.rpc.SessionPlanUpdateParams; -import com.github.copilot.sdk.generated.rpc.SessionRpc; -import com.github.copilot.sdk.generated.rpc.SessionShellExecParams; -import com.github.copilot.sdk.generated.rpc.SessionShellKillParams; -import com.github.copilot.sdk.generated.rpc.SessionSkillsDisableParams; -import com.github.copilot.sdk.generated.rpc.SessionSkillsEnableParams; -import com.github.copilot.sdk.generated.rpc.SessionToolsHandlePendingToolCallParams; -import com.github.copilot.sdk.generated.rpc.SessionUiElicitationParams; -import com.github.copilot.sdk.generated.rpc.SessionUiHandlePendingElicitationParams; -import com.github.copilot.sdk.generated.rpc.SessionWorkspaceCreateFileParams; -import com.github.copilot.sdk.generated.rpc.SessionWorkspaceReadFileParams; -import com.github.copilot.sdk.generated.rpc.SessionsForkParams; -import com.github.copilot.sdk.generated.rpc.ToolsListParams; - /** * Coverage tests for generated RPC API classes that are not exercised in * {@link RpcWrappersTest}. Uses the same {@link StubCaller} pattern to verify diff --git a/src/test/java/com/github/copilot/sdk/GeneratedRpcRecordsCoverageTest.java b/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcRecordsCoverageTest.java similarity index 84% rename from src/test/java/com/github/copilot/sdk/GeneratedRpcRecordsCoverageTest.java rename to src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcRecordsCoverageTest.java index d083b8237..65ca4260d 100644 --- a/src/test/java/com/github/copilot/sdk/GeneratedRpcRecordsCoverageTest.java +++ b/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcRecordsCoverageTest.java @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ -package com.github.copilot.sdk; +package com.github.copilot.sdk.generated.rpc; import static org.junit.jupiter.api.Assertions.*; @@ -12,118 +12,6 @@ import org.junit.jupiter.api.Test; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.copilot.sdk.generated.rpc.AccountGetQuotaResult; -import com.github.copilot.sdk.generated.rpc.McpConfigListResult; -import com.github.copilot.sdk.generated.rpc.McpConfigRemoveParams; -import com.github.copilot.sdk.generated.rpc.McpConfigUpdateParams; -import com.github.copilot.sdk.generated.rpc.McpDiscoverParams; -import com.github.copilot.sdk.generated.rpc.McpDiscoverResult; -import com.github.copilot.sdk.generated.rpc.ModelsListResult; -import com.github.copilot.sdk.generated.rpc.PingParams; -import com.github.copilot.sdk.generated.rpc.PingResult; -import com.github.copilot.sdk.generated.rpc.SessionAgentDeselectParams; -import com.github.copilot.sdk.generated.rpc.SessionAgentDeselectResult; -import com.github.copilot.sdk.generated.rpc.SessionAgentGetCurrentParams; -import com.github.copilot.sdk.generated.rpc.SessionAgentGetCurrentResult; -import com.github.copilot.sdk.generated.rpc.SessionAgentListParams; -import com.github.copilot.sdk.generated.rpc.SessionAgentListResult; -import com.github.copilot.sdk.generated.rpc.SessionAgentReloadParams; -import com.github.copilot.sdk.generated.rpc.SessionAgentReloadResult; -import com.github.copilot.sdk.generated.rpc.SessionAgentSelectParams; -import com.github.copilot.sdk.generated.rpc.SessionAgentSelectResult; -import com.github.copilot.sdk.generated.rpc.SessionCommandsHandlePendingCommandParams; -import com.github.copilot.sdk.generated.rpc.SessionCommandsHandlePendingCommandResult; -import com.github.copilot.sdk.generated.rpc.SessionExtensionsDisableParams; -import com.github.copilot.sdk.generated.rpc.SessionExtensionsDisableResult; -import com.github.copilot.sdk.generated.rpc.SessionExtensionsEnableParams; -import com.github.copilot.sdk.generated.rpc.SessionExtensionsEnableResult; -import com.github.copilot.sdk.generated.rpc.SessionExtensionsListParams; -import com.github.copilot.sdk.generated.rpc.SessionExtensionsListResult; -import com.github.copilot.sdk.generated.rpc.SessionExtensionsReloadParams; -import com.github.copilot.sdk.generated.rpc.SessionExtensionsReloadResult; -import com.github.copilot.sdk.generated.rpc.SessionFleetStartParams; -import com.github.copilot.sdk.generated.rpc.SessionFleetStartResult; -import com.github.copilot.sdk.generated.rpc.SessionFsAppendFileParams; -import com.github.copilot.sdk.generated.rpc.SessionFsExistsParams; -import com.github.copilot.sdk.generated.rpc.SessionFsExistsResult; -import com.github.copilot.sdk.generated.rpc.SessionFsMkdirParams; -import com.github.copilot.sdk.generated.rpc.SessionFsReadFileParams; -import com.github.copilot.sdk.generated.rpc.SessionFsReadFileResult; -import com.github.copilot.sdk.generated.rpc.SessionFsReaddirParams; -import com.github.copilot.sdk.generated.rpc.SessionFsReaddirResult; -import com.github.copilot.sdk.generated.rpc.SessionFsReaddirWithTypesParams; -import com.github.copilot.sdk.generated.rpc.SessionFsReaddirWithTypesResult; -import com.github.copilot.sdk.generated.rpc.SessionFsRenameParams; -import com.github.copilot.sdk.generated.rpc.SessionFsRmParams; -import com.github.copilot.sdk.generated.rpc.SessionFsSetProviderParams; -import com.github.copilot.sdk.generated.rpc.SessionFsSetProviderResult; -import com.github.copilot.sdk.generated.rpc.SessionFsStatParams; -import com.github.copilot.sdk.generated.rpc.SessionFsStatResult; -import com.github.copilot.sdk.generated.rpc.SessionFsWriteFileParams; -import com.github.copilot.sdk.generated.rpc.SessionHistoryCompactParams; -import com.github.copilot.sdk.generated.rpc.SessionHistoryCompactResult; -import com.github.copilot.sdk.generated.rpc.SessionHistoryTruncateParams; -import com.github.copilot.sdk.generated.rpc.SessionHistoryTruncateResult; -import com.github.copilot.sdk.generated.rpc.SessionLogParams; -import com.github.copilot.sdk.generated.rpc.SessionLogResult; -import com.github.copilot.sdk.generated.rpc.SessionMcpDisableParams; -import com.github.copilot.sdk.generated.rpc.SessionMcpDisableResult; -import com.github.copilot.sdk.generated.rpc.SessionMcpEnableParams; -import com.github.copilot.sdk.generated.rpc.SessionMcpEnableResult; -import com.github.copilot.sdk.generated.rpc.SessionMcpListParams; -import com.github.copilot.sdk.generated.rpc.SessionMcpListResult; -import com.github.copilot.sdk.generated.rpc.SessionMcpReloadParams; -import com.github.copilot.sdk.generated.rpc.SessionMcpReloadResult; -import com.github.copilot.sdk.generated.rpc.SessionModeGetParams; -import com.github.copilot.sdk.generated.rpc.SessionModeGetResult; -import com.github.copilot.sdk.generated.rpc.SessionModeSetParams; -import com.github.copilot.sdk.generated.rpc.SessionModeSetResult; -import com.github.copilot.sdk.generated.rpc.SessionModelGetCurrentParams; -import com.github.copilot.sdk.generated.rpc.SessionModelGetCurrentResult; -import com.github.copilot.sdk.generated.rpc.SessionModelSwitchToParams; -import com.github.copilot.sdk.generated.rpc.SessionModelSwitchToResult; -import com.github.copilot.sdk.generated.rpc.SessionPermissionsHandlePendingPermissionRequestParams; -import com.github.copilot.sdk.generated.rpc.SessionPermissionsHandlePendingPermissionRequestResult; -import com.github.copilot.sdk.generated.rpc.SessionPlanDeleteParams; -import com.github.copilot.sdk.generated.rpc.SessionPlanDeleteResult; -import com.github.copilot.sdk.generated.rpc.SessionPlanReadParams; -import com.github.copilot.sdk.generated.rpc.SessionPlanReadResult; -import com.github.copilot.sdk.generated.rpc.SessionPlanUpdateParams; -import com.github.copilot.sdk.generated.rpc.SessionPlanUpdateResult; -import com.github.copilot.sdk.generated.rpc.SessionPluginsListParams; -import com.github.copilot.sdk.generated.rpc.SessionPluginsListResult; -import com.github.copilot.sdk.generated.rpc.SessionShellExecParams; -import com.github.copilot.sdk.generated.rpc.SessionShellExecResult; -import com.github.copilot.sdk.generated.rpc.SessionShellKillParams; -import com.github.copilot.sdk.generated.rpc.SessionShellKillResult; -import com.github.copilot.sdk.generated.rpc.SessionSkillsDisableParams; -import com.github.copilot.sdk.generated.rpc.SessionSkillsDisableResult; -import com.github.copilot.sdk.generated.rpc.SessionSkillsEnableParams; -import com.github.copilot.sdk.generated.rpc.SessionSkillsEnableResult; -import com.github.copilot.sdk.generated.rpc.SessionSkillsListParams; -import com.github.copilot.sdk.generated.rpc.SessionSkillsListResult; -import com.github.copilot.sdk.generated.rpc.SessionSkillsReloadParams; -import com.github.copilot.sdk.generated.rpc.SessionSkillsReloadResult; -import com.github.copilot.sdk.generated.rpc.SessionToolsHandlePendingToolCallParams; -import com.github.copilot.sdk.generated.rpc.SessionToolsHandlePendingToolCallResult; -import com.github.copilot.sdk.generated.rpc.SessionUiElicitationParams; -import com.github.copilot.sdk.generated.rpc.SessionUiElicitationResult; -import com.github.copilot.sdk.generated.rpc.SessionUiHandlePendingElicitationParams; -import com.github.copilot.sdk.generated.rpc.SessionUiHandlePendingElicitationResult; -import com.github.copilot.sdk.generated.rpc.SessionUsageGetMetricsParams; -import com.github.copilot.sdk.generated.rpc.SessionUsageGetMetricsResult; -import com.github.copilot.sdk.generated.rpc.SessionWorkspaceCreateFileParams; -import com.github.copilot.sdk.generated.rpc.SessionWorkspaceCreateFileResult; -import com.github.copilot.sdk.generated.rpc.SessionWorkspaceListFilesParams; -import com.github.copilot.sdk.generated.rpc.SessionWorkspaceListFilesResult; -import com.github.copilot.sdk.generated.rpc.SessionWorkspaceReadFileParams; -import com.github.copilot.sdk.generated.rpc.SessionWorkspaceReadFileResult; -import com.github.copilot.sdk.generated.rpc.SessionsForkParams; -import com.github.copilot.sdk.generated.rpc.SessionsForkResult; -import com.github.copilot.sdk.generated.rpc.ToolsListParams; -import com.github.copilot.sdk.generated.rpc.ToolsListResult; - /** * Tests for generated RPC param and result record types. Exercises * constructors, field accessors, and enum variants to provide JaCoCo coverage @@ -131,8 +19,6 @@ */ class GeneratedRpcRecordsCoverageTest { - private static final ObjectMapper MAPPER = JsonRpcClient.getObjectMapper(); - // ── Params records ───────────────────────────────────────────────────── @Test @@ -338,11 +224,11 @@ void sessionFsStatParams_record() { @Test void sessionFsWriteFileParams_record() { - var params = new SessionFsWriteFileParams("sess-21", "/tmp/out.txt", "content here", 0644.0); + var params = new SessionFsWriteFileParams("sess-21", "/tmp/out.txt", "content here", 644.0); assertEquals("sess-21", params.sessionId()); assertEquals("/tmp/out.txt", params.path()); assertEquals("content here", params.content()); - assertEquals(0644.0, params.mode()); + assertEquals(644.0, params.mode()); } @Test From ed3307fd3d29ff1337ce0491c2a4e4168003ec2a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Apr 2026 18:45:24 +0000 Subject: [PATCH 5/6] Fix POSIXisms: use TestUtil.tempPath() for cross-platform temp paths Agent-Logs-Url: https://github.com/github/copilot-sdk-java/sessions/b7de171b-6625-4ab4-b222-4858b0cacfc2 Co-authored-by: edburns <75821+edburns@users.noreply.github.com> --- .../java/com/github/copilot/sdk/TestUtil.java | 18 ++++++++++++-- .../rpc/GeneratedRpcRecordsCoverageTest.java | 24 ++++++++++--------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/test/java/com/github/copilot/sdk/TestUtil.java b/src/test/java/com/github/copilot/sdk/TestUtil.java index 72f214a8b..d9462af87 100644 --- a/src/test/java/com/github/copilot/sdk/TestUtil.java +++ b/src/test/java/com/github/copilot/sdk/TestUtil.java @@ -10,13 +10,27 @@ import java.nio.file.Paths; /** - * Shared test utilities for locating the Copilot CLI binary. + * Shared test utilities for locating the Copilot CLI binary and other + * cross-platform test helpers. */ -final class TestUtil { +public final class TestUtil { private TestUtil() { } + /** + * Returns a platform-independent path string for a file inside the system + * temporary directory. Uses {@code java.io.tmpdir} so tests run correctly on + * both POSIX and Windows. + * + * @param filename + * the file name (no directory separator required) + * @return absolute path string in the system temp directory + */ + public static String tempPath(String filename) { + return Path.of(System.getProperty("java.io.tmpdir"), filename).toString(); + } + /** * Locates a launchable Copilot CLI executable. *

diff --git a/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcRecordsCoverageTest.java b/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcRecordsCoverageTest.java index 65ca4260d..e2bf423ff 100644 --- a/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcRecordsCoverageTest.java +++ b/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcRecordsCoverageTest.java @@ -12,6 +12,8 @@ import org.junit.jupiter.api.Test; +import com.github.copilot.sdk.TestUtil; + /** * Tests for generated RPC param and result record types. Exercises * constructors, field accessors, and enum variants to provide JaCoCo coverage @@ -144,25 +146,25 @@ void sessionFleetStartParams_record() { @Test void sessionFsAppendFileParams_record() { - var params = new SessionFsAppendFileParams("sess-12", "/tmp/log.txt", "new line\n", null); + var params = new SessionFsAppendFileParams("sess-12", TestUtil.tempPath("log.txt"), "new line\n", null); assertEquals("sess-12", params.sessionId()); - assertEquals("/tmp/log.txt", params.path()); + assertEquals(TestUtil.tempPath("log.txt"), params.path()); assertEquals("new line\n", params.content()); assertNull(params.mode()); } @Test void sessionFsExistsParams_record() { - var params = new SessionFsExistsParams("sess-13", "/tmp/file.txt"); + var params = new SessionFsExistsParams("sess-13", TestUtil.tempPath("file.txt")); assertEquals("sess-13", params.sessionId()); - assertEquals("/tmp/file.txt", params.path()); + assertEquals(TestUtil.tempPath("file.txt"), params.path()); } @Test void sessionFsMkdirParams_record() { - var params = new SessionFsMkdirParams("sess-14", "/tmp/newdir", true, null); + var params = new SessionFsMkdirParams("sess-14", TestUtil.tempPath("newdir"), true, null); assertEquals("sess-14", params.sessionId()); - assertEquals("/tmp/newdir", params.path()); + assertEquals(TestUtil.tempPath("newdir"), params.path()); assertTrue(params.recursive()); assertNull(params.mode()); } @@ -198,9 +200,9 @@ void sessionFsRenameParams_record() { @Test void sessionFsRmParams_record() { - var params = new SessionFsRmParams("sess-19", "/tmp/file.txt", false, true); + var params = new SessionFsRmParams("sess-19", TestUtil.tempPath("file.txt"), false, true); assertEquals("sess-19", params.sessionId()); - assertEquals("/tmp/file.txt", params.path()); + assertEquals(TestUtil.tempPath("file.txt"), params.path()); assertFalse(params.recursive()); assertTrue(params.force()); } @@ -224,11 +226,11 @@ void sessionFsStatParams_record() { @Test void sessionFsWriteFileParams_record() { - var params = new SessionFsWriteFileParams("sess-21", "/tmp/out.txt", "content here", 644.0); + var params = new SessionFsWriteFileParams("sess-21", TestUtil.tempPath("out.txt"), "content here", null); assertEquals("sess-21", params.sessionId()); - assertEquals("/tmp/out.txt", params.path()); + assertEquals(TestUtil.tempPath("out.txt"), params.path()); assertEquals("content here", params.content()); - assertEquals(644.0, params.mode()); + assertNull(params.mode()); } @Test From 4b19e4bc78b505b6ea0ce7c89f9de777fab5fe6a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Apr 2026 19:56:50 +0000 Subject: [PATCH 6/6] Fix race condition in sendAndWait: use whenCompleteAsync to prevent test-thread wakeup mid-dispatch Agent-Logs-Url: https://github.com/github/copilot-sdk-java/sessions/e7c09bc7-4fc9-4619-a0d9-d1bed96104b9 Co-authored-by: edburns <75821+edburns@users.noreply.github.com> --- .../com/github/copilot/sdk/CopilotSession.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/copilot/sdk/CopilotSession.java b/src/main/java/com/github/copilot/sdk/CopilotSession.java index a58356c2a..aaf2f4345 100644 --- a/src/main/java/com/github/copilot/sdk/CopilotSession.java +++ b/src/main/java/com/github/copilot/sdk/CopilotSession.java @@ -542,9 +542,17 @@ public CompletableFuture sendAndWait(MessageOptions optio } } - // When inner future completes, run cleanup and propagate to result + // When inner future completes, run cleanup and propagate to result. + // Use whenCompleteAsync so that result.complete(r) is not called + // synchronously on the event-dispatch thread while dispatchEvent() is + // still iterating over handlers. Without async dispatch, a caller that + // registered its own session.on() listener before calling sendAndWait() + // could see its listener invoked *after* result.get() returned, because + // sendAndWait's internal handler would complete the future mid-loop. By + // submitting the completion to timeoutScheduler we allow the current + // dispatch loop to finish calling all other handlers first. final ScheduledFuture taskToCancel = timeoutTask; - future.whenComplete((r, ex) -> { + future.whenCompleteAsync((r, ex) -> { try { subscription.close(); } catch (IOException e) { @@ -560,7 +568,7 @@ public CompletableFuture sendAndWait(MessageOptions optio result.complete(r); } } - }); + }, timeoutScheduler); // When result is cancelled externally, cancel inner future to trigger cleanup result.whenComplete((v, ex) -> {