diff --git a/src/test/java/com/github/copilot/sdk/AgentInfoTest.java b/src/test/java/com/github/copilot/sdk/AgentInfoTest.java new file mode 100644 index 000000000..0893773e7 --- /dev/null +++ b/src/test/java/com/github/copilot/sdk/AgentInfoTest.java @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * 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.github.copilot.sdk.json.AgentInfo; + +/** + * Unit tests for {@link AgentInfo} getters, setters, and fluent chaining. + */ +class AgentInfoTest { + + @Test + void defaultValuesAreNull() { + var agent = new AgentInfo(); + assertNull(agent.getName()); + assertNull(agent.getDisplayName()); + assertNull(agent.getDescription()); + } + + @Test + void nameGetterSetter() { + var agent = new AgentInfo(); + agent.setName("coder"); + assertEquals("coder", agent.getName()); + } + + @Test + void displayNameGetterSetter() { + var agent = new AgentInfo(); + agent.setDisplayName("Code Assistant"); + assertEquals("Code Assistant", agent.getDisplayName()); + } + + @Test + void descriptionGetterSetter() { + var agent = new AgentInfo(); + agent.setDescription("Helps with coding tasks"); + assertEquals("Helps with coding tasks", agent.getDescription()); + } + + @Test + void fluentChainingReturnsThis() { + var agent = new AgentInfo().setName("coder").setDisplayName("Code Assistant") + .setDescription("Helps with coding tasks"); + + assertEquals("coder", agent.getName()); + assertEquals("Code Assistant", agent.getDisplayName()); + assertEquals("Helps with coding tasks", agent.getDescription()); + } + + @Test + void fluentChainingReturnsSameInstance() { + var agent = new AgentInfo(); + assertSame(agent, agent.setName("test")); + assertSame(agent, agent.setDisplayName("Test")); + assertSame(agent, agent.setDescription("A test agent")); + } +} diff --git a/src/test/java/com/github/copilot/sdk/CliServerManagerTest.java b/src/test/java/com/github/copilot/sdk/CliServerManagerTest.java index f17201583..32257b0a5 100644 --- a/src/test/java/com/github/copilot/sdk/CliServerManagerTest.java +++ b/src/test/java/com/github/copilot/sdk/CliServerManagerTest.java @@ -13,6 +13,7 @@ import org.junit.jupiter.api.Test; import com.github.copilot.sdk.json.CopilotClientOptions; +import com.github.copilot.sdk.json.TelemetryConfig; /** * Unit tests for {@link CliServerManager} covering parseCliUrl, @@ -212,4 +213,30 @@ void startCliServerWithNullCliPath() throws Exception { assertNotNull(e); } } + + @Test + void startCliServerWithTelemetryAllOptions() throws Exception { + // The telemetry env vars are applied before ProcessBuilder.start() + // so even with a nonexistent CLI path, the telemetry code path is exercised + var telemetry = new TelemetryConfig().setOtlpEndpoint("http://localhost:4318").setFilePath("/tmp/telemetry.log") + .setExporterType("otlp-http").setSourceName("test-app").setCaptureContent(true); + var options = new CopilotClientOptions().setCliPath("/nonexistent/copilot").setTelemetry(telemetry) + .setUseStdio(true); + var manager = new CliServerManager(options); + + var ex = assertThrows(IOException.class, () -> manager.startCliServer()); + assertNotNull(ex); + } + + @Test + void startCliServerWithTelemetryCaptureContentFalse() throws Exception { + // Test the false branch of getCaptureContent() + var telemetry = new TelemetryConfig().setCaptureContent(false); + var options = new CopilotClientOptions().setCliPath("/nonexistent/copilot").setTelemetry(telemetry) + .setUseStdio(true); + var manager = new CliServerManager(options); + + var ex = assertThrows(IOException.class, () -> manager.startCliServer()); + assertNotNull(ex); + } } diff --git a/src/test/java/com/github/copilot/sdk/CommandsTest.java b/src/test/java/com/github/copilot/sdk/CommandsTest.java index dad26afbb..baf26b39b 100644 --- a/src/test/java/com/github/copilot/sdk/CommandsTest.java +++ b/src/test/java/com/github/copilot/sdk/CommandsTest.java @@ -135,4 +135,22 @@ void commandWireDefinitionNullDescriptionAllowed() { assertEquals("rollback", wire.getName()); assertNull(wire.getDescription()); } + + @Test + void commandWireDefinitionFluentSetters() { + var wire = new CommandWireDefinition(); + wire.setName("status"); + wire.setDescription("Show deployment status"); + + assertEquals("status", wire.getName()); + assertEquals("Show deployment status", wire.getDescription()); + } + + @Test + void commandWireDefinitionFluentSettersChaining() { + var wire = new CommandWireDefinition().setName("logs").setDescription("View application logs"); + + assertEquals("logs", wire.getName()); + assertEquals("View application logs", wire.getDescription()); + } } diff --git a/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java b/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java index bf4881d5c..f3787705f 100644 --- a/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java +++ b/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java @@ -17,10 +17,13 @@ import com.github.copilot.sdk.events.AbstractSessionEvent; import com.github.copilot.sdk.json.CopilotClientOptions; +import com.github.copilot.sdk.json.InfiniteSessionConfig; import com.github.copilot.sdk.json.MessageOptions; import com.github.copilot.sdk.json.ModelInfo; import com.github.copilot.sdk.json.ResumeSessionConfig; import com.github.copilot.sdk.json.SessionConfig; +import com.github.copilot.sdk.json.SystemMessageConfig; +import com.github.copilot.sdk.json.TelemetryConfig; class ConfigCloneTest { @@ -193,4 +196,97 @@ void clonePreservesNullFields() { MessageOptions msgClone = msg.clone(); assertNull(msgClone.getMode()); } + + @Test + @SuppressWarnings("deprecation") + void copilotClientOptionsDeprecatedAutoRestart() { + CopilotClientOptions opts = new CopilotClientOptions(); + assertFalse(opts.isAutoRestart()); + opts.setAutoRestart(true); + assertTrue(opts.isAutoRestart()); + } + + @Test + void copilotClientOptionsSetCliArgsNullClearsExisting() { + CopilotClientOptions opts = new CopilotClientOptions(); + opts.setCliArgs(new String[]{"--flag1"}); + assertNotNull(opts.getCliArgs()); + + // Setting null should clear the existing array + opts.setCliArgs(null); + assertNotNull(opts.getCliArgs()); + assertEquals(0, opts.getCliArgs().length); + } + + @Test + void copilotClientOptionsSetEnvironmentNullClearsExisting() { + CopilotClientOptions opts = new CopilotClientOptions(); + opts.setEnvironment(Map.of("KEY", "VALUE")); + assertNotNull(opts.getEnvironment()); + + // Setting null should clear the existing map (clears in-place → returns empty + // map) + opts.setEnvironment(null); + var env = opts.getEnvironment(); + assertTrue(env == null || env.isEmpty()); + } + + @Test + @SuppressWarnings("deprecation") + void copilotClientOptionsDeprecatedGithubToken() { + CopilotClientOptions opts = new CopilotClientOptions(); + opts.setGithubToken("ghp_deprecated_token"); + assertEquals("ghp_deprecated_token", opts.getGithubToken()); + assertEquals("ghp_deprecated_token", opts.getGitHubToken()); + } + + @Test + void copilotClientOptionsSetTelemetry() { + var telemetry = new TelemetryConfig().setOtlpEndpoint("http://localhost:4318"); + var opts = new CopilotClientOptions(); + opts.setTelemetry(telemetry); + assertSame(telemetry, opts.getTelemetry()); + } + + @Test + void copilotClientOptionsSetUseLoggedInUserNull() { + var opts = new CopilotClientOptions(); + opts.setUseLoggedInUser(null); + // null → Boolean.FALSE + assertEquals(Boolean.FALSE, opts.getUseLoggedInUser()); + } + + @Test + void resumeSessionConfigAllSetters() { + var config = new ResumeSessionConfig(); + + var sysMsg = new SystemMessageConfig(); + config.setSystemMessage(sysMsg); + assertSame(sysMsg, config.getSystemMessage()); + + config.setAvailableTools(List.of("bash", "read_file")); + assertEquals(List.of("bash", "read_file"), config.getAvailableTools()); + + config.setExcludedTools(List.of("write_file")); + assertEquals(List.of("write_file"), config.getExcludedTools()); + + config.setReasoningEffort("high"); + assertEquals("high", config.getReasoningEffort()); + + config.setWorkingDirectory("/project/src"); + assertEquals("/project/src", config.getWorkingDirectory()); + + config.setConfigDir("/home/user/.config/copilot"); + assertEquals("/home/user/.config/copilot", config.getConfigDir()); + + config.setSkillDirectories(List.of("/skills/custom")); + assertEquals(List.of("/skills/custom"), config.getSkillDirectories()); + + config.setDisabledSkills(List.of("some-skill")); + assertEquals(List.of("some-skill"), config.getDisabledSkills()); + + var infiniteConfig = new InfiniteSessionConfig().setEnabled(true); + config.setInfiniteSessions(infiniteConfig); + assertSame(infiniteConfig, config.getInfiniteSessions()); + } } diff --git a/src/test/java/com/github/copilot/sdk/DataObjectCoverageTest.java b/src/test/java/com/github/copilot/sdk/DataObjectCoverageTest.java new file mode 100644 index 000000000..203f5faed --- /dev/null +++ b/src/test/java/com/github/copilot/sdk/DataObjectCoverageTest.java @@ -0,0 +1,172 @@ +/*--------------------------------------------------------------------------------------------- + * 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.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.copilot.sdk.json.GetForegroundSessionResponse; +import com.github.copilot.sdk.json.PermissionRequest; +import com.github.copilot.sdk.json.PermissionRequestResult; +import com.github.copilot.sdk.json.PostToolUseHookInput; +import com.github.copilot.sdk.json.PostToolUseHookOutput; +import com.github.copilot.sdk.json.PreToolUseHookInput; +import com.github.copilot.sdk.json.PreToolUseHookOutput; +import com.github.copilot.sdk.json.SectionOverride; +import com.github.copilot.sdk.json.SetForegroundSessionResponse; +import com.github.copilot.sdk.json.ToolBinaryResult; +import com.github.copilot.sdk.json.ToolResultObject; + +/** + * Unit tests for various data transfer objects and record types that were + * missing coverage, including hook output factory methods, record constructors, + * and getters for hook inputs. + */ +class DataObjectCoverageTest { + + // ===== PreToolUseHookOutput factory methods ===== + + @Test + void preToolUseHookOutputDenyWithReason() { + var output = PreToolUseHookOutput.deny("Security policy violation"); + assertEquals("deny", output.permissionDecision()); + assertEquals("Security policy violation", output.permissionDecisionReason()); + assertNull(output.modifiedArgs()); + } + + @Test + void preToolUseHookOutputAsk() { + var output = PreToolUseHookOutput.ask(); + assertEquals("ask", output.permissionDecision()); + assertNull(output.permissionDecisionReason()); + } + + @Test + void preToolUseHookOutputWithModifiedArgs() { + ObjectNode args = JsonNodeFactory.instance.objectNode(); + args.put("path", "/safe/path"); + + var output = PreToolUseHookOutput.withModifiedArgs("allow", args); + assertEquals("allow", output.permissionDecision()); + assertEquals(args, output.modifiedArgs()); + } + + // ===== PostToolUseHookOutput record ===== + + @Test + void postToolUseHookOutputRecord() { + var output = new PostToolUseHookOutput(null, "Extra context", false); + assertNull(output.modifiedResult()); + assertEquals("Extra context", output.additionalContext()); + assertFalse(output.suppressOutput()); + } + + // ===== ToolBinaryResult record ===== + + @Test + void toolBinaryResultRecord() { + var result = new ToolBinaryResult("base64data==", "image/png", "image", "A chart"); + assertEquals("base64data==", result.data()); + assertEquals("image/png", result.mimeType()); + assertEquals("image", result.type()); + assertEquals("A chart", result.description()); + } + + // ===== GetForegroundSessionResponse record ===== + + @Test + void getForegroundSessionResponseRecord() { + var response = new GetForegroundSessionResponse("session-123", "/home/user/project"); + assertEquals("session-123", response.sessionId()); + assertEquals("/home/user/project", response.workspacePath()); + } + + // ===== SetForegroundSessionResponse record ===== + + @Test + void setForegroundSessionResponseRecord() { + var successResponse = new SetForegroundSessionResponse(true, null); + assertTrue(successResponse.success()); + assertNull(successResponse.error()); + + var errorResponse = new SetForegroundSessionResponse(false, "Session not found"); + assertFalse(errorResponse.success()); + assertEquals("Session not found", errorResponse.error()); + } + + // ===== ToolResultObject factory methods ===== + + @Test + void toolResultObjectErrorWithTextAndError() { + var result = ToolResultObject.error("partial output", "File not found"); + assertEquals("error", result.resultType()); + assertEquals("partial output", result.textResultForLlm()); + assertEquals("File not found", result.error()); + } + + @Test + void toolResultObjectFailure() { + var result = ToolResultObject.failure("Tool unavailable", "Unknown tool"); + assertEquals("failure", result.resultType()); + assertEquals("Tool unavailable", result.textResultForLlm()); + assertEquals("Unknown tool", result.error()); + } + + // ===== PermissionRequest additional setters ===== + + @Test + void permissionRequestSetExtensionData() { + var req = new PermissionRequest(); + req.setExtensionData(java.util.Map.of("key", "value")); + assertEquals("value", req.getExtensionData().get("key")); + } + + // ===== SectionOverride setContent ===== + + @Test + void sectionOverrideSetContent() { + var override = new SectionOverride(); + override.setContent("Custom content"); + assertEquals("Custom content", override.getContent()); + } + + // ===== PreToolUseHookInput getters ===== + + @Test + void preToolUseHookInputGetters() { + var input = new PreToolUseHookInput(); + // Default values + assertEquals(0L, input.getTimestamp()); + assertNull(input.getCwd()); + assertNull(input.getToolArgs()); + } + + // ===== PostToolUseHookInput getters ===== + + @Test + void postToolUseHookInputGetters() { + var input = new PostToolUseHookInput(); + // Default values + assertEquals(0L, input.getTimestamp()); + assertNull(input.getCwd()); + assertNull(input.getToolArgs()); + } + + // ===== PermissionRequestResult setRules ===== + + @Test + void permissionRequestResultSetRules() { + var result = new PermissionRequestResult().setKind("allow"); + var rules = new java.util.ArrayList(); + rules.add("bash:read"); + rules.add("bash:write"); + result.setRules(rules); + assertEquals(2, result.getRules().size()); + assertEquals("bash:read", result.getRules().get(0)); + } +} diff --git a/src/test/java/com/github/copilot/sdk/ModelInfoTest.java b/src/test/java/com/github/copilot/sdk/ModelInfoTest.java new file mode 100644 index 000000000..f36d0c4bd --- /dev/null +++ b/src/test/java/com/github/copilot/sdk/ModelInfoTest.java @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.sdk; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import com.github.copilot.sdk.json.ModelInfo; +import com.github.copilot.sdk.json.ModelSupports; +import com.github.copilot.sdk.json.SessionMetadata; + +/** + * Unit tests for {@link ModelInfo}, {@link ModelSupports}, and + * {@link SessionMetadata} getters and setters. + */ +class ModelInfoTest { + + @Test + void modelSupportsReasoningEffortGetterSetter() { + var supports = new ModelSupports(); + assertFalse(supports.isReasoningEffort()); + + supports.setReasoningEffort(true); + assertTrue(supports.isReasoningEffort()); + } + + @Test + void modelSupportsFluentChaining() { + var supports = new ModelSupports().setVision(true).setReasoningEffort(true); + assertTrue(supports.isVision()); + assertTrue(supports.isReasoningEffort()); + } + + @Test + void modelInfoSupportedReasoningEffortsGetterSetter() { + var model = new ModelInfo(); + assertNull(model.getSupportedReasoningEfforts()); + + model.setSupportedReasoningEfforts(List.of("low", "medium", "high")); + assertEquals(List.of("low", "medium", "high"), model.getSupportedReasoningEfforts()); + } + + @Test + void modelInfoDefaultReasoningEffortGetterSetter() { + var model = new ModelInfo(); + assertNull(model.getDefaultReasoningEffort()); + + model.setDefaultReasoningEffort("medium"); + assertEquals("medium", model.getDefaultReasoningEffort()); + } + + @Test + void sessionMetadataGettersAndSetters() { + var meta = new SessionMetadata(); + assertNull(meta.getStartTime()); + assertNull(meta.getModifiedTime()); + assertNull(meta.getSummary()); + assertFalse(meta.isRemote()); + + meta.setRemote(true); + assertTrue(meta.isRemote()); + } +} diff --git a/src/test/java/com/github/copilot/sdk/RpcHandlerDispatcherTest.java b/src/test/java/com/github/copilot/sdk/RpcHandlerDispatcherTest.java index 79f5d7c7e..315a38b90 100644 --- a/src/test/java/com/github/copilot/sdk/RpcHandlerDispatcherTest.java +++ b/src/test/java/com/github/copilot/sdk/RpcHandlerDispatcherTest.java @@ -542,4 +542,52 @@ void hooksInvokeWithNoHooksRegistered() throws Exception { JsonNode output = response.get("result").get("output"); assertTrue(output == null || output.isNull(), "Output should be null when no hooks registered"); } + + // ===== systemMessage.transform tests ===== + + @Test + void systemMessageTransformWithUnknownSession() throws Exception { + ObjectNode params = MAPPER.createObjectNode(); + params.put("sessionId", "nonexistent"); + params.putObject("sections"); + + invokeHandler("systemMessage.transform", "40", params); + + JsonNode response = readResponse(); + assertNotNull(response.get("error")); + assertEquals(-32602, response.get("error").get("code").asInt()); + } + + @Test + void systemMessageTransformWithNullSessionId() throws Exception { + ObjectNode params = MAPPER.createObjectNode(); + // sessionId omitted → null → session lookup returns null → error + params.putObject("sections"); + + invokeHandler("systemMessage.transform", "41", params); + + JsonNode response = readResponse(); + assertNotNull(response.get("error")); + assertEquals(-32602, response.get("error").get("code").asInt()); + } + + @Test + void systemMessageTransformWithKnownSessionNoCallbacks() throws Exception { + // Session without transform callbacks returns the sections unchanged + createSession("s1"); + + ObjectNode params = MAPPER.createObjectNode(); + params.put("sessionId", "s1"); + ObjectNode sections = params.putObject("sections"); + ObjectNode sectionData = sections.putObject("identity"); + sectionData.put("content", "Original content"); + + invokeHandler("systemMessage.transform", "42", params); + + JsonNode response = readResponse(); + assertNotNull(response.get("result")); + JsonNode resultSections = response.get("result").get("sections"); + assertNotNull(resultSections); + assertEquals("Original content", resultSections.get("identity").get("content").asText()); + } } diff --git a/src/test/java/com/github/copilot/sdk/SessionEventParserTest.java b/src/test/java/com/github/copilot/sdk/SessionEventParserTest.java index 5898d5301..4a63bb243 100644 --- a/src/test/java/com/github/copilot/sdk/SessionEventParserTest.java +++ b/src/test/java/com/github/copilot/sdk/SessionEventParserTest.java @@ -2385,4 +2385,188 @@ void testParseSystemNotificationEvent() throws Exception { assertNotNull(event.getData()); assertTrue(event.getData().content().contains("Agent completed")); } + + @Test + void testParseCapabilitiesChangedEvent() throws Exception { + String json = """ + { + "type": "capabilities.changed", + "data": { + "ui": { + "elicitation": true + } + } + } + """; + + AbstractSessionEvent event = parseJson(json); + assertNotNull(event); + assertInstanceOf(CapabilitiesChangedEvent.class, event); + assertEquals("capabilities.changed", event.getType()); + + var castedEvent = (CapabilitiesChangedEvent) event; + assertNotNull(castedEvent.getData()); + assertNotNull(castedEvent.getData().ui()); + assertTrue(castedEvent.getData().ui().elicitation()); + + // Verify setData round-trip + var newData = new CapabilitiesChangedEvent.CapabilitiesChangedData( + new CapabilitiesChangedEvent.CapabilitiesChangedUi(false)); + castedEvent.setData(newData); + assertFalse(castedEvent.getData().ui().elicitation()); + } + + @Test + void testParseCommandExecuteEvent() throws Exception { + String json = """ + { + "type": "command.execute", + "data": { + "requestId": "req-001", + "command": "/deploy production", + "commandName": "deploy", + "args": "production" + } + } + """; + + AbstractSessionEvent event = parseJson(json); + assertNotNull(event); + assertInstanceOf(CommandExecuteEvent.class, event); + assertEquals("command.execute", event.getType()); + + var castedEvent = (CommandExecuteEvent) event; + assertNotNull(castedEvent.getData()); + assertEquals("req-001", castedEvent.getData().requestId()); + assertEquals("/deploy production", castedEvent.getData().command()); + assertEquals("deploy", castedEvent.getData().commandName()); + assertEquals("production", castedEvent.getData().args()); + + // Verify setData round-trip + castedEvent.setData(new CommandExecuteEvent.CommandExecuteData("req-002", "/rollback", "rollback", null)); + assertEquals("req-002", castedEvent.getData().requestId()); + } + + @Test + void testParseElicitationRequestedEvent() throws Exception { + String json = """ + { + "type": "elicitation.requested", + "data": { + "requestId": "elix-001", + "toolCallId": "tc-123", + "elicitationSource": "mcp_tool", + "message": "Please provide your name", + "mode": "form", + "requestedSchema": { + "type": "object", + "properties": { + "name": {"type": "string"} + }, + "required": ["name"] + }, + "url": null + } + } + """; + + AbstractSessionEvent event = parseJson(json); + assertNotNull(event); + assertInstanceOf(ElicitationRequestedEvent.class, event); + assertEquals("elicitation.requested", event.getType()); + + var castedEvent = (ElicitationRequestedEvent) event; + assertNotNull(castedEvent.getData()); + assertEquals("elix-001", castedEvent.getData().requestId()); + assertEquals("tc-123", castedEvent.getData().toolCallId()); + assertEquals("mcp_tool", castedEvent.getData().elicitationSource()); + assertEquals("Please provide your name", castedEvent.getData().message()); + assertEquals("form", castedEvent.getData().mode()); + assertNotNull(castedEvent.getData().requestedSchema()); + assertEquals("object", castedEvent.getData().requestedSchema().type()); + assertNotNull(castedEvent.getData().requestedSchema().properties()); + assertNotNull(castedEvent.getData().requestedSchema().required()); + assertTrue(castedEvent.getData().requestedSchema().required().contains("name")); + + // Verify setData round-trip + castedEvent.setData(new ElicitationRequestedEvent.ElicitationRequestedData("elix-002", null, null, "Enter URL", + "url", null, "https://example.com")); + assertEquals("elix-002", castedEvent.getData().requestId()); + assertEquals("url", castedEvent.getData().mode()); + } + + @Test + void testParseSessionContextChangedEvent() throws Exception { + String json = """ + { + "type": "session.context_changed", + "data": { + "cwd": "/home/user/project", + "gitRoot": "/home/user/project", + "repository": "my-repo", + "branch": "main" + } + } + """; + + AbstractSessionEvent event = parseJson(json); + assertNotNull(event); + assertInstanceOf(SessionContextChangedEvent.class, event); + assertEquals("session.context_changed", event.getType()); + + var castedEvent = (SessionContextChangedEvent) event; + assertNotNull(castedEvent.getData()); + assertEquals("/home/user/project", castedEvent.getData().getCwd()); + + // Verify setData round-trip + castedEvent.setData(null); + assertNull(castedEvent.getData()); + } + + @Test + void testParseSessionTaskCompleteEvent() throws Exception { + String json = """ + { + "type": "session.task_complete", + "data": { + "summary": "Task completed successfully" + } + } + """; + + AbstractSessionEvent event = parseJson(json); + assertNotNull(event); + assertInstanceOf(SessionTaskCompleteEvent.class, event); + assertEquals("session.task_complete", event.getType()); + + var castedEvent = (SessionTaskCompleteEvent) event; + assertNotNull(castedEvent.getData()); + assertEquals("Task completed successfully", castedEvent.getData().summary()); + + // Verify setData round-trip + castedEvent.setData(new SessionTaskCompleteEvent.SessionTaskCompleteData("New summary")); + assertEquals("New summary", castedEvent.getData().summary()); + } + + @Test + void testParseSubagentDeselectedEvent() throws Exception { + String json = """ + { + "type": "subagent.deselected", + "data": {} + } + """; + + AbstractSessionEvent event = parseJson(json); + assertNotNull(event); + assertInstanceOf(SubagentDeselectedEvent.class, event); + assertEquals("subagent.deselected", event.getType()); + + var castedEvent = (SubagentDeselectedEvent) event; + assertNotNull(castedEvent.getData()); + + // Verify setData round-trip + castedEvent.setData(new SubagentDeselectedEvent.SubagentDeselectedData()); + assertNotNull(castedEvent.getData()); + } } diff --git a/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java b/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java index 1a64b7534..75457583e 100644 --- a/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java +++ b/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java @@ -13,6 +13,9 @@ import org.junit.jupiter.api.Test; import com.github.copilot.sdk.json.CreateSessionRequest; +import com.github.copilot.sdk.json.ElicitationHandler; +import com.github.copilot.sdk.json.ElicitationResult; +import com.github.copilot.sdk.json.ElicitationResultAction; import com.github.copilot.sdk.json.ResumeSessionConfig; import com.github.copilot.sdk.json.ResumeSessionRequest; import com.github.copilot.sdk.json.SessionConfig; @@ -305,4 +308,96 @@ void extractTransformCallbacks_customizeModeWithTransform_extractsCallbacks() { assertEquals(com.github.copilot.sdk.json.SectionOverrideAction.TRANSFORM, wireSection.getAction()); assertNull(wireSection.getTransform()); } + + @Test + @SuppressWarnings("deprecation") + void buildCreateRequestWithSessionId_usesProvidedSessionId() { + var config = new SessionConfig(); + config.setSessionId("my-session-id"); + + // The deprecated single-arg overload uses the sessionId from config when set + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config); + + assertEquals("my-session-id", request.getSessionId()); + } + + @Test + void configureSessionWithNullConfig_returnsEarly() { + // configureSession with null config should return without error + CopilotSession session = new CopilotSession("session-1", null); + // Covers the null config early-return branch (L219-220) + assertDoesNotThrow(() -> SessionRequestBuilder.configureSession(session, (SessionConfig) null)); + } + + @Test + void configureSessionWithCommands_registersCommands() { + CopilotSession session = new CopilotSession("session-1", null); + + var cmd = new com.github.copilot.sdk.json.CommandDefinition().setName("deploy") + .setHandler(ctx -> CompletableFuture.completedFuture(null)); + var config = new SessionConfig().setCommands(List.of(cmd)); + + // Covers config.getCommands() != null branch (L235-236) + SessionRequestBuilder.configureSession(session, config); + // If no exception thrown, the branch was covered + } + + @Test + void configureSessionWithElicitationHandler_registersHandler() { + CopilotSession session = new CopilotSession("session-1", null); + + ElicitationHandler handler = (context) -> CompletableFuture + .completedFuture(new ElicitationResult().setAction(ElicitationResultAction.CANCEL)); + var config = new SessionConfig().setOnElicitationRequest(handler); + + // Covers config.getOnElicitationRequest() != null branch (L238-239) + SessionRequestBuilder.configureSession(session, config); + } + + @Test + void configureSessionWithOnEvent_registersEventHandler() { + CopilotSession session = new CopilotSession("session-1", null); + + var config = new SessionConfig().setOnEvent(event -> { + }); + + // Covers config.getOnEvent() != null branch (L241-242) + SessionRequestBuilder.configureSession(session, config); + } + + @Test + void configureResumedSessionWithCommands_registersCommands() { + CopilotSession session = new CopilotSession("session-1", null); + + var cmd = new com.github.copilot.sdk.json.CommandDefinition().setName("rollback") + .setHandler(ctx -> CompletableFuture.completedFuture(null)); + var config = new ResumeSessionConfig().setCommands(List.of(cmd)); + + // Covers ResumeSessionConfig.getCommands() != null branch (L271-272) + SessionRequestBuilder.configureSession(session, config); + } + + @Test + void configureResumedSessionWithElicitationHandler_registersHandler() { + CopilotSession session = new CopilotSession("session-1", null); + + ElicitationHandler handler = (context) -> CompletableFuture + .completedFuture(new ElicitationResult().setAction(ElicitationResultAction.CANCEL)); + var config = new ResumeSessionConfig().setOnElicitationRequest(handler); + + // Covers ResumeSessionConfig.getOnElicitationRequest() != null branch + // (L274-275) + SessionRequestBuilder.configureSession(session, config); + } + + @Test + void configureResumedSessionWithOnEvent_registersEventHandler() { + CopilotSession session = new CopilotSession("session-1", null); + + var config = new ResumeSessionConfig().setOnEvent(event -> { + }); + + // Covers ResumeSessionConfig.getOnEvent() != null branch (L277-278) + SessionRequestBuilder.configureSession(session, config); + } } diff --git a/src/test/java/com/github/copilot/sdk/TelemetryConfigTest.java b/src/test/java/com/github/copilot/sdk/TelemetryConfigTest.java new file mode 100644 index 000000000..99b360d2d --- /dev/null +++ b/src/test/java/com/github/copilot/sdk/TelemetryConfigTest.java @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * 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.github.copilot.sdk.json.TelemetryConfig; + +/** + * Unit tests for {@link TelemetryConfig} getters, setters, and fluent chaining. + */ +class TelemetryConfigTest { + + @Test + void defaultValuesAreNull() { + var config = new TelemetryConfig(); + assertNull(config.getOtlpEndpoint()); + assertNull(config.getFilePath()); + assertNull(config.getExporterType()); + assertNull(config.getSourceName()); + assertNull(config.getCaptureContent()); + } + + @Test + void otlpEndpointGetterSetter() { + var config = new TelemetryConfig(); + config.setOtlpEndpoint("http://localhost:4318"); + assertEquals("http://localhost:4318", config.getOtlpEndpoint()); + } + + @Test + void filePathGetterSetter() { + var config = new TelemetryConfig(); + config.setFilePath("/tmp/telemetry.log"); + assertEquals("/tmp/telemetry.log", config.getFilePath()); + } + + @Test + void exporterTypeGetterSetter() { + var config = new TelemetryConfig(); + config.setExporterType("otlp-http"); + assertEquals("otlp-http", config.getExporterType()); + } + + @Test + void sourceNameGetterSetter() { + var config = new TelemetryConfig(); + config.setSourceName("my-app"); + assertEquals("my-app", config.getSourceName()); + } + + @Test + void captureContentGetterSetter() { + var config = new TelemetryConfig(); + config.setCaptureContent(true); + assertTrue(config.getCaptureContent()); + + config.setCaptureContent(false); + assertFalse(config.getCaptureContent()); + } + + @Test + void fluentChainingReturnsThis() { + var config = new TelemetryConfig().setOtlpEndpoint("http://localhost:4318").setFilePath("/tmp/spans.json") + .setExporterType("file").setSourceName("sdk-test").setCaptureContent(true); + + assertEquals("http://localhost:4318", config.getOtlpEndpoint()); + assertEquals("/tmp/spans.json", config.getFilePath()); + assertEquals("file", config.getExporterType()); + assertEquals("sdk-test", config.getSourceName()); + assertTrue(config.getCaptureContent()); + } +}