Forge
csharpdeeb25a2
1using System.Collections.ObjectModel;
2using System.Text.Json;
3using CascadeIDE.Cockpit.Cds;
4using CascadeIDE.Features.IdeMcp.Application;
5using CascadeIDE.Models;
6using CascadeIDE.Services;
7using CascadeIDE.ViewModels;
8using Xunit;
9
10namespace CascadeIDE.Tests;
11
12public sealed class IdeMcpOrchestratorThinningTests
13{
14 [Fact]
15 public void BuildGetOpenDocumentTextResponse_no_path_returns_error()
16 {
17 var json = IdeMcpEditorOrchestrator.BuildGetOpenDocumentTextResponse(null, null, [], null);
18 Assert.Contains("no_path", json, StringComparison.Ordinal);
19 }
20
21 [Fact]
22 public void BuildGetOpenDocumentTextResponse_not_open_tab_returns_error()
23 {
24 var tabs = new[]
25 {
26 new IdeMcpEditorOrchestrator.OpenDocumentTabSnapshot(@"C:\proj\a.cs", "x", false),
27 };
28 var json = IdeMcpEditorOrchestrator.BuildGetOpenDocumentTextResponse(@"C:\proj\b.cs", null, tabs, null);
29 Assert.Contains("not_open", json, StringComparison.Ordinal);
30 }
31
32 [Fact]
33 public void BuildGetOpenDocumentTextResponse_matches_tab_by_path()
34 {
35 var path = @"C:\proj\match.cs";
36 var tabs = new[] { new IdeMcpEditorOrchestrator.OpenDocumentTabSnapshot(path, "body", true) };
37 var json = IdeMcpEditorOrchestrator.BuildGetOpenDocumentTextResponse(path, null, tabs, null);
38 Assert.Contains("\"body\"", json, StringComparison.Ordinal);
39 Assert.Contains("is_dirty", json, StringComparison.Ordinal);
40 }
41
42 [Fact]
43 public void BuildSolutionFilesJson_empty_collection_has_empty_arrays()
44 {
45 var json = IdeMcpBuildTestOrchestrator.BuildSolutionFilesJson(null, new ObservableCollection<SolutionItem>());
46 Assert.Contains("\"file_entries\":[]", json, StringComparison.Ordinal);
47 Assert.Contains("\"solution_tree\":[]", json, StringComparison.Ordinal);
48 }
49
50 [Fact]
51 public void BuildIdeStatePayload_maps_capture_and_nested_cockpit_surface()
52 {
53 var vm = new MainWindowViewModel();
54 var cds = CockpitSurfaceSnapshotBuilder.Build(vm);
55 var capture = new IdeMcpIdeStateUiCapture(
56 @"C:\sol\a.sln",
57 @"C:\sol\File.cs",
58 SelectedSolutionPath: null,
59 EditorTextLength: 42,
60 SelectionStart: 1,
61 SelectionLength: 5,
62 CurrentFileBreakpoints: new[] { 10, 20 },
63 DebugSnapshot: DebugSessionSnapshot.Empty,
64 IsBuildOutputVisible: true,
65 BuildOutputPreview: "build preview",
66 BinlogPath: @"C:\logs\a.binlog",
67 IsTerminalVisible: false,
68 UiMode: "Power",
69 IsPfdRegionExpanded: true,
70 IsMfdRegionExpanded: false,
71 IsGitPanelVisible: true,
72 IsInstrumentationDockVisible: false,
73 SafetyLevel: "A",
74 EditorGroupCount: 2,
75 AgentTraceStepCount: 3,
76 IsAutonomousRunning: false,
77 CockpitSurface: cds);
78 var diag = JsonSerializer.SerializeToElement(Array.Empty<object>());
79 var json = JsonSerializer.Serialize(IdeMcpWorkspaceOrchestrator.BuildIdeStatePayload(capture, diag));
80 using var doc = JsonDocument.Parse(json);
81 var root = doc.RootElement;
82 Assert.Equal(@"C:\sol\a.sln", root.GetProperty("solution_path").GetString());
83 Assert.Equal(42, root.GetProperty("editor").GetProperty("content_length").GetInt32());
84 Assert.False(string.IsNullOrEmpty(root.GetProperty("cockpit_surface").GetProperty("schema_version").GetString()));
85 }
86
87 [Fact]
88 public void BuildDapSnapshotUiPlan_not_stopped_resets_mfd_prim()
89 {
90 var s = DebugSessionSnapshot.Empty with { HasActiveSession = true, IsExecutionStopped = false };
91 var p = IdeMcpDebugOrchestrator.BuildDapSnapshotUiPlan(s, mfdDebugPagePrimedForCurrentStop: true);
92 Assert.False(p.MfdPrimedForCurrentStopNext);
93 Assert.False(p.ActivateInstrumentationDockAndDebugStack);
94 Assert.Equal(-1, p.DebugStackSelectedIndex);
95 }
96
97 [Fact]
98 public void BuildDapSnapshotUiPlan_first_stop_activates_dock_and_prim()
99 {
100 var s = DebugSessionSnapshot.Empty with
101 {
102 HasActiveSession = true,
103 IsExecutionStopped = true,
104 StoppedFile = @"C:\proj\Stopped.cs",
105 StoppedLine = 9,
106 StackFrames = [("Main", @"C:\proj\Stopped.cs", 9)],
107 VariablesFrameIndex = 0,
108 };
109 var p = IdeMcpDebugOrchestrator.BuildDapSnapshotUiPlan(s, false);
110 Assert.True(p.MfdPrimedForCurrentStopNext);
111 Assert.True(p.ActivateInstrumentationDockAndDebugStack);
112 Assert.True(p.ShouldAttemptOpenStoppedSource);
113 Assert.Equal(@"C:\proj\Stopped.cs", p.StoppedSourcePathForOpenAttempt);
114 Assert.Equal(0, p.DebugStackSelectedIndex);
115 }
116
117 [Fact]
118 public void BuildDapSnapshotUiPlan_subsequent_stop_does_not_activate_dock()
119 {
120 var s = DebugSessionSnapshot.Empty with
121 {
122 HasActiveSession = true,
123 IsExecutionStopped = true,
124 StoppedFile = @"C:\a.cs",
125 StackFrames = [("F", @"C:\a.cs", 1)],
126 VariablesFrameIndex = 0,
127 };
128 var p = IdeMcpDebugOrchestrator.BuildDapSnapshotUiPlan(s, mfdDebugPagePrimedForCurrentStop: true);
129 Assert.True(p.MfdPrimedForCurrentStopNext);
130 Assert.False(p.ActivateInstrumentationDockAndDebugStack);
131 }
132
133 [Fact]
134 public void BuildMissingSolutionPanelSurface_aligns_reply_with_panel_wrap()
135 {
136 var s = IdeMcpBuildTestOrchestrator.BuildMissingSolutionPanelSurface();
137 Assert.Equal(IdeMcpBuildTestOrchestrator.MissingSolutionMessage(), s.McpReplyText);
138 Assert.StartsWith(s.McpReplyText + "\r\n", s.BuildOutputPanelFullText, StringComparison.Ordinal);
139 }
140
141 [Fact]
142 public void FailedBuildPanelSurface_echoes_error_prefix()
143 {
144 var s = IdeMcpBuildTestOrchestrator.FailedBuildPanelSurface("boom");
145 Assert.Equal("Error: boom", s.McpReplyText);
146 Assert.Contains("boom", s.BuildOutputPanelFullText, StringComparison.Ordinal);
147 }
148
149 [Fact]
150 public void PingJson_contains_expected_kind_and_ok()
151 {
152 using var doc = JsonDocument.Parse(IdeMcpHostOrchestrator.PingJson());
153 Assert.True(doc.RootElement.GetProperty("ok").GetBoolean());
154 Assert.Equal("cascade_ide_mcp_host", doc.RootElement.GetProperty("kind").GetString());
155 Assert.True(doc.RootElement.GetProperty("pid").TryGetInt32(out var pid));
156 Assert.True(pid > 0);
157 }
158
159 [Fact]
160 public void HybridCodebaseIndexOrchestrator_errors_are_stable_literals()
161 {
162 Assert.Contains("missing_query", IdeMcpHybridCodebaseIndexOrchestrator.MissingQueryJson(), StringComparison.Ordinal);
163 Assert.Contains("invalid_hit_id", IdeMcpHybridCodebaseIndexOrchestrator.InvalidHitIdJson(), StringComparison.Ordinal);
164 var fail = IdeMcpHybridCodebaseIndexOrchestrator.SerializeReindexFailed("x");
165 Assert.Contains("reindex_failed", fail, StringComparison.Ordinal);
166 Assert.Contains("\"detail\":\"x\"", fail, StringComparison.Ordinal);
167 }
168}
169
View only · write via MCP/CIDE