| 1 | using CascadeIDE.Cockpit.DataBus; |
| 2 | using CascadeIDE.Features.Agent.Environment; |
| 3 | using CascadeIDE.Models; |
| 4 | using Xunit; |
| 5 | |
| 6 | namespace CascadeIDE.Tests; |
| 7 | |
| 8 | public sealed class AgentEnvironmentServiceTests |
| 9 | { |
| 10 | private static AgentEnvironmentService CreateService(InMemoryDataBus? bus = null) => |
| 11 | new(bus ?? new InMemoryDataBus(), new AgentEnvironmentSettings()); |
| 12 | |
| 13 | [Fact] |
| 14 | public void StartVerify_WithoutSolution_ReturnsError() |
| 15 | { |
| 16 | var result = CreateService().StartVerify("", AgentVerifyPolicy.Standard); |
| 17 | |
| 18 | Assert.False(result.Accepted); |
| 19 | Assert.NotNull(result.Error); |
| 20 | } |
| 21 | |
| 22 | [Fact] |
| 23 | public void StartVerifyBatch_WithoutSolution_ReturnsError() |
| 24 | { |
| 25 | var result = CreateService().StartVerifyBatch(new AgentVerifyBatchRequest( |
| 26 | AgentVerifyPolicy.Standard, |
| 27 | AgentSandboxProfile.AgentEphemeral, |
| 28 | null)); |
| 29 | |
| 30 | Assert.False(result.Accepted); |
| 31 | Assert.Contains("solution_path", result.Error, StringComparison.OrdinalIgnoreCase); |
| 32 | } |
| 33 | |
| 34 | [Fact] |
| 35 | public void ParseBatchVerify_UseWorktree_SetsFlag() |
| 36 | { |
| 37 | var req = AgentEnvironmentNativeTools.ParseBatchVerify(new Dictionary<string, string?> |
| 38 | { |
| 39 | ["policy"] = "standard", |
| 40 | ["use_worktree"] = "true", |
| 41 | }); |
| 42 | |
| 43 | Assert.True(req.UseWorktree); |
| 44 | } |
| 45 | |
| 46 | [Fact] |
| 47 | public void SubstrateEnvironment_IncludesIntercomDataDirectory() |
| 48 | { |
| 49 | var dir = Path.Combine(Path.GetTempPath(), "cide-sub-" + Guid.NewGuid().ToString("N")); |
| 50 | Directory.CreateDirectory(dir); |
| 51 | try |
| 52 | { |
| 53 | var bundle = AgentSandboxSubstrate.Allocate(dir); |
| 54 | var env = AgentSandboxProcessEnvironmentKeys.ForBundle(bundle); |
| 55 | Assert.True(env.ContainsKey(AgentSandboxProcessEnvironmentKeys.IntercomDataDirectory)); |
| 56 | Assert.Equal(bundle.SubstrateDirectory, env[AgentSandboxProcessEnvironmentKeys.IntercomDataDirectory]); |
| 57 | } |
| 58 | finally |
| 59 | { |
| 60 | try { Directory.Delete(dir, recursive: true); } catch { /* best-effort */ } |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | [Fact] |
| 65 | public void PolicyParser_AcceptsAliases() |
| 66 | { |
| 67 | Assert.True(AgentVerifyPolicyParser.TryParse("quick", out var p)); |
| 68 | Assert.Equal(AgentVerifyPolicy.Minimal, p); |
| 69 | } |
| 70 | |
| 71 | [Fact] |
| 72 | public void SandboxProfileParser_AcceptsEphemeral() |
| 73 | { |
| 74 | Assert.True(AgentSandboxProfileParser.TryParse("ephemeral", out var p)); |
| 75 | Assert.Equal(AgentSandboxProfile.AgentEphemeral, p); |
| 76 | } |
| 77 | |
| 78 | [Fact] |
| 79 | public void PrepareSandbox_CreatesRunDirectory() |
| 80 | { |
| 81 | var root = Path.Combine(Path.GetTempPath(), "cide-aee-" + Guid.NewGuid().ToString("N")); |
| 82 | var manager = new AgentSandboxManager(Path.Combine(root, "runs")); |
| 83 | var lease = manager.Prepare("test-run", AgentSandboxProfile.AgentEphemeral); |
| 84 | Assert.True(Directory.Exists(lease.RunDirectory)); |
| 85 | try |
| 86 | { |
| 87 | Directory.Delete(root, recursive: true); |
| 88 | } |
| 89 | catch |
| 90 | { |
| 91 | // best-effort cleanup |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | [Fact] |
| 96 | public void EnvironmentTaskDedup_CoalescesWithinWindow() |
| 97 | { |
| 98 | var dedup = new EnvironmentTaskDedup(1500); |
| 99 | Assert.False(dedup.ShouldCoalesce("k")); |
| 100 | Assert.True(dedup.ShouldCoalesce("k")); |
| 101 | } |
| 102 | |
| 103 | [Fact] |
| 104 | public void SettingsToml_DeserializesAgentEnvironment() |
| 105 | { |
| 106 | const string toml = """ |
| 107 | [agent.environment] |
| 108 | default_verify_policy = "strict" |
| 109 | coalesce_window_ms = 2000 |
| 110 | shell_escape_tier = "tests_only" |
| 111 | |
| 112 | [agent.environment.ladder] |
| 113 | diagnose_files_enabled = false |
| 114 | """; |
| 115 | var s = CascadeIDE.Services.CascadeTomlSerializer.Deserialize<CascadeIdeSettings>(toml)!; |
| 116 | Assert.Equal("strict", s.Agent.Environment.DefaultVerifyPolicy); |
| 117 | Assert.Equal(2000, s.Agent.Environment.CoalesceWindowMs); |
| 118 | Assert.Equal("tests_only", s.Agent.Environment.ShellEscapeTier); |
| 119 | Assert.False(s.Agent.Environment.Ladder.DiagnoseFilesEnabled); |
| 120 | } |
| 121 | |
| 122 | [Fact] |
| 123 | public void SettingsToml_DeserializesDiagnoseFilesCsScopeAndCap() |
| 124 | { |
| 125 | const string toml = """ |
| 126 | [agent.environment.ladder] |
| 127 | diagnose_files_cs_scope = "open_tabs" |
| 128 | diagnose_files_git_dirty_max_files = 12 |
| 129 | """; |
| 130 | var s = CascadeIDE.Services.CascadeTomlSerializer.Deserialize<CascadeIdeSettings>(toml)!; |
| 131 | Assert.Equal("open_tabs", s.Agent.Environment.Ladder.DiagnoseFilesCsScope); |
| 132 | Assert.Equal(12, s.Agent.Environment.Ladder.DiagnoseFilesGitDirtyMaxFiles); |
| 133 | } |
| 134 | } |
| 135 | |