using Kopaya.AgentHost.Services; namespace Kopaya.AgentHost.Tests; public sealed class CopilotCliPathResolverTests { [Fact] public void Resolve_UsesCopilotFromPath() { string copilotDirectory = @"C:\tools\copilot"; HashSet existingFiles = new(StringComparer.OrdinalIgnoreCase) { Path.Combine(copilotDirectory, "copilot.exe"), }; string? cliPath = CopilotCliPathResolver.Resolve( pathValue: $"C:\\other;\"{copilotDirectory}\"", pathExtValue: ".COM;.EXE;.BAT;.CMD", isWindows: true, fileExists: existingFiles.Contains); Assert.Equal(Path.Combine(copilotDirectory, "copilot.exe"), cliPath, ignoreCase: true); } [Fact] public void Resolve_UsesDefaultWindowsExtensionsWhenPathExtMissing() { string copilotDirectory = @"C:\tools\copilot"; HashSet existingFiles = new(StringComparer.OrdinalIgnoreCase) { Path.Combine(copilotDirectory, "copilot.cmd"), }; string? cliPath = CopilotCliPathResolver.Resolve( pathValue: $"C:\\other;\"{copilotDirectory}\"", pathExtValue: null, isWindows: true, fileExists: existingFiles.Contains); Assert.Equal(Path.Combine(copilotDirectory, "copilot.cmd"), cliPath, ignoreCase: true); } [Fact] public void Resolve_ReturnsNullWhenPathDoesNotContainCopilot() { string? cliPath = CopilotCliPathResolver.Resolve( pathValue: @"C:\tools;C:\other", pathExtValue: ".COM;.EXE;.BAT;.CMD", isWindows: true, fileExists: _ => false); Assert.Null(cliPath); } [Fact] public void ResolveCliEnvironment_RemovesRuntimeSpecificPrefixes() { IReadOnlyDictionary environment = CopilotCliPathResolver.ResolveCliEnvironment( [ new KeyValuePair("PATH", @"C:\tools"), new KeyValuePair("APPDATA", @"C:\Users\mail\AppData\Roaming"), new KeyValuePair("COPILOT_CLI", "1"), new KeyValuePair("NODE_OPTIONS", "--no-warnings"), new KeyValuePair("electron_run_as_node", "1"), new KeyValuePair("BUN_FAKE_FLAG", "1"), new KeyValuePair("npm_config_user_agent", "bun/1.3.6"), ]); Assert.Equal( new Dictionary(StringComparer.OrdinalIgnoreCase) { ["PATH"] = @"C:\tools", ["APPDATA"] = @"C:\Users\mail\AppData\Roaming", }, environment); } [Fact] public void ResolveCliEnvironment_PreservesUnrelatedVariables() { IReadOnlyDictionary environment = CopilotCliPathResolver.ResolveCliEnvironment( [ new KeyValuePair("PATH", @"C:\tools"), new KeyValuePair("HOME", @"C:\Users\mail"), new KeyValuePair("HTTPS_PROXY", "http://proxy.local:8080"), new KeyValuePair("FORCE_COLOR", "1"), ]); Assert.Equal( new Dictionary(StringComparer.OrdinalIgnoreCase) { ["PATH"] = @"C:\tools", ["HOME"] = @"C:\Users\mail", ["HTTPS_PROXY"] = "http://proxy.local:8080", ["FORCE_COLOR"] = "1", }, environment); } [Fact] public void ResolveCliLaunch_UsesCommandProcessorWrapperOnWindows() { CopilotCliLaunch launch = CopilotCliPathResolver.ResolveCliLaunch( cliPath: @"C:\Tools With Spaces\copilot.exe", isWindows: true, commandProcessorPath: @"C:\Windows\System32\cmd.exe"); Assert.Equal(@"C:\Windows\System32\cmd.exe", launch.Path); Assert.Equal( ["/d", "/s", "/c", "copilot"], launch.Args); } [Fact] public void ResolveCliLaunch_UsesCliDirectlyOutsideWindows() { CopilotCliLaunch launch = CopilotCliPathResolver.ResolveCliLaunch( cliPath: "/usr/local/bin/copilot", isWindows: false, commandProcessorPath: null); Assert.Equal("/usr/local/bin/copilot", launch.Path); Assert.Empty(launch.Args); } }