Forge
csharpdeeb25a2
1using System.Text.Json;
2using CascadeIDE.Features.WebAiPortal.Application;
3using CascadeIDE.Services;
4using Xunit;
5using TestContext = Xunit.TestContext;
6
7namespace CascadeIDE.Tests;
8
9public sealed class WebAiPortalCommandBridgeTests
10{
11 [Fact]
12 public async Task Execute_rejects_when_bridge_not_armed()
13 {
14 var fake = new FakeActions();
15 var bridge = new WebAiPortalCommandBridge(fake);
16 bridge.BridgeConsented = true;
17 bridge.BridgeArmed = false;
18 var r = await bridge.ExecuteFromWebJsonAsync(
19 """{"command_id":"codebase_index_search","args":{"query":"x"}}""",
20 TestContext.Current.CancellationToken);
21 Assert.Contains("not armed", r, StringComparison.OrdinalIgnoreCase);
22 }
23
24 [Fact]
25 public async Task Execute_rejects_unknown_command_id()
26 {
27 var fake = new FakeActions();
28 var bridge = new WebAiPortalCommandBridge(fake)
29 {
30 BridgeConsented = true,
31 BridgeArmed = true,
32 };
33 var r = await bridge.ExecuteFromWebJsonAsync(
34 """{"command_id":"open_file"}""",
35 TestContext.Current.CancellationToken);
36 Assert.Contains("not allowed", r, StringComparison.OrdinalIgnoreCase);
37 }
38
39 [Fact]
40 public async Task Execute_forwards_whitelisted_command()
41 {
42 var fake = new FakeActions();
43 var bridge = new WebAiPortalCommandBridge(fake)
44 {
45 BridgeConsented = true,
46 BridgeArmed = true,
47 };
48 var r = await bridge.ExecuteFromWebJsonAsync(
49 """{"command_id":"codebase_index_search","args":{"query":"test"}}""",
50 TestContext.Current.CancellationToken);
51 Assert.Equal("{\"hits\":[]}", r);
52 Assert.Equal(IdeCommands.CodebaseIndexSearch, fake.LastCommandId);
53 }
54
55 private sealed class FakeActions : IIdeMcpActions
56 {
57 public string? LastCommandId { get; private set; }
58
59 public Task<string> ExecuteCommandAsync(
60 string commandId,
61 IReadOnlyDictionary<string, JsonElement>? args,
62 CancellationToken cancellationToken = default)
63 {
64 LastCommandId = commandId;
65 if (commandId == IdeCommands.CodebaseIndexSearch)
66 return Task.FromResult("{\"hits\":[]}");
67 return Task.FromResult("OK");
68 }
69
70 public Task<string> RequestConfirmationAsync(string message, CancellationToken cancellationToken = default) =>
71 Task.FromResult(ConfirmationResponses.Ok);
72
73 public void OpenFile(string path) => throw new NotImplementedException();
74 public void LoadSolution(string path) => throw new NotImplementedException();
75
76 public Task<string> LoadSolutionAndWaitAsync(string path, CancellationToken cancellationToken = default) =>
77 throw new NotImplementedException();
78 public void SelectInEditor(string? filePath, int startLine, int startColumn, int endLine, int endColumn) => throw new NotImplementedException();
79 public Task<string> GetEditorStateAsync(int? maxPreviewChars = null) => throw new NotImplementedException();
80 public Task<string> GetEditorContentRangeAsync(int startLine, int endLine) => throw new NotImplementedException();
81 public Task<string> GetOpenDocumentTextAsync(string? filePath, int? maxChars) => throw new NotImplementedException();
82 public Task<string> ApplyEditAsync(string filePath, int startLine, int startColumn, int endLine, int endColumn, string newText) =>
83 throw new NotImplementedException();
84
85 public Task<string> ReadWorkspaceFileAsync(string filePath, int? offset, int? limit, int? maxChars) =>
86 throw new NotImplementedException();
87
88 public Task<string> SaveDocumentAsync(string? filePath, string? content) =>
89 throw new NotImplementedException();
90 public void GoToPosition(string? filePath, int line, int column, int? endLine = null, int? endColumn = null) => throw new NotImplementedException();
91 public void RevealEditorRange(string? filePath, int startLine, int endLine, int? durationMs) => throw new NotImplementedException();
92 public string GetSolutionInfo() => throw new NotImplementedException();
93 public Task<string> GetSolutionFilesAsync() => throw new NotImplementedException();
94 public Task<string> SearchWorkspaceTextAsync(string pattern, string? subPath, bool fixedString, string? glob, int maxMatches, string? rgPath) => throw new NotImplementedException();
95 public Task<string> SearchWebPublicQueryAsync(string query, CancellationToken cancellationToken = default) => throw new NotImplementedException();
96 public Task<string> FetchWebPublicUrlAsync(string url, int? maxChars, CancellationToken cancellationToken = default) => throw new NotImplementedException();
97 public Task<string> GetCurrentFileDiagnosticsAsync() => throw new NotImplementedException();
98 public Task<string> GetCodeNavigationContextAsync(string mode, string? filePath, int? line, int? column, int? maxRelated, int? maxNodes, int? maxEdges, string? preset, IReadOnlyList<string>? includeKinds, IReadOnlyList<string>? excludeKinds, string? level) => throw new NotImplementedException();
99 public Task<string> BuildAsync() => throw new NotImplementedException();
100 public Task<string> BuildStructuredAsync() => throw new NotImplementedException();
101 public Task<string> RunTestsAsync() => throw new NotImplementedException();
102 public Task<string> RunAffectedTestsAsync(IReadOnlyList<string>? changedPaths = null) => throw new NotImplementedException();
103 public Task<string> RunCodeCleanupAsync(string? includePath = null) => throw new NotImplementedException();
104 public Task<string> GetCodeMetricsAsync(string? scope = null, string? path = null) => throw new NotImplementedException();
105 public Task<string> GetIdeStateAsync() => throw new NotImplementedException();
106 public Task<string> GetCockpitSurfaceAsync() => throw new NotImplementedException();
107 public Task<string> GetUiModesDiagnosticsAsync() => throw new NotImplementedException();
108 public Task<string> GitStatusAsync() => throw new NotImplementedException();
109 public Task<string> GitDiffAsync(string? path = null, bool staged = false) => throw new NotImplementedException();
110 public Task<string> GitCommitAsync(string message, IReadOnlyList<string>? paths = null) => throw new NotImplementedException();
111 public Task<string> GitPushAsync(string? remote = null, string? branch = null, bool dryRun = false) => throw new NotImplementedException();
112 public Task<string> GitLogAsync(int n = 20) => throw new NotImplementedException();
113 public Task<string> GitFetchAsync(string? remote = null, bool all = false, bool prune = false, bool dryRun = false) => throw new NotImplementedException();
114 public Task<string> GitPullAsync(string? remote = null, string? branch = null, bool ffOnly = true, bool dryRun = false) => throw new NotImplementedException();
115 public Task<string> GitBranchAsync(string? action = null, string? name = null, string? startPoint = null, bool force = false) => throw new NotImplementedException();
116 public Task<string> GitShowAsync(string rev, string? path = null, bool statOnly = false) => throw new NotImplementedException();
117 public Task<string> GitSubmoduleAsync(string? action = null, string? path = null, bool recursive = true) => throw new NotImplementedException();
118 public Task<string> GitPreflightAsync(bool staged = false, bool includeUntracked = true, bool includePatches = true) => throw new NotImplementedException();
119 public Task<string> GitPreflightFixSafeAsync(bool includePatches = true) => throw new NotImplementedException();
120 public string GetBuildOutput() => throw new NotImplementedException();
121 public void SetBreakpoint(string filePath, int line, string? condition = null) => throw new NotImplementedException();
122 public void RemoveBreakpoint(string filePath, int line) => throw new NotImplementedException();
123 public void ShowPreview(string title, string content) => throw new NotImplementedException();
124 public void ShowEditorPreview() => throw new NotImplementedException();
125 public void FocusEditor() => throw new NotImplementedException();
126 public string GetUiTheme() => throw new NotImplementedException();
127 public Task<string> SetUiThemeAsync(string themeJson) => throw new NotImplementedException();
128 public Task<string> GetUiLayoutAsync() => throw new NotImplementedException();
129 public Task<string> GetColorsUnderCursorAsync() => throw new NotImplementedException();
130 public Task<string> GetControlAppearanceAsync(string? name) => throw new NotImplementedException();
131 public Task<string> SetControlLayoutAsync(string controlName, string layoutJson) => throw new NotImplementedException();
132 public Task<string> AddControlAsync(string parentName, string controlType, string? content, string? name) => throw new NotImplementedException();
133 public Task<string> SetControlTextAsync(string controlName, string text) => throw new NotImplementedException();
134 public Task<string> ClickControlAsync(string? controlName) => throw new NotImplementedException();
135 public Task<string> SendKeysAsync(string? controlName, string keys) => throw new NotImplementedException();
136 public Task<string> SelectChatMessageAsync(int index) => throw new NotImplementedException();
137 public Task<string> SelectChatMessageByOrdinalAsync(int ordinal, int endOrdinal) => throw new NotImplementedException();
138 public Task<string> GetSelectedChatMessageAsync() => throw new NotImplementedException();
139 public Task<string> FindIntercomMessagesForCodeAsync(IReadOnlyDictionary<string, JsonElement>? args) =>
140 throw new NotImplementedException();
141 public Task<string> RelateIntercomMessageRangeToCodeAsync(IReadOnlyDictionary<string, JsonElement>? args) =>
142 throw new NotImplementedException();
143 public Task<string> EditChatAssistantMessageAsync(string messageId, string newContent, string? reason = null) => throw new NotImplementedException();
144 public Task<string> ExportChatReadableAsync(bool writeFile = false, string? fileName = null) => throw new NotImplementedException();
145 public Task<string> SetFocusAsync(string? controlName) => throw new NotImplementedException();
146 public Task<string> HighlightControlAsync(string? controlName) => throw new NotImplementedException();
147 public Task<string> SetPanelSizeAsync(string panel, double? width, double? height) => throw new NotImplementedException();
148 public string GetSupportedEditorLanguages() => throw new NotImplementedException();
149 public Task<string> GetDebugSnapshotAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException();
150 public Task<string> WriteAgentNotesAsync(string content, CancellationToken cancellationToken = default) => throw new NotImplementedException();
151 public Task<string> ReadAgentNotesAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException();
152 public Task<string> AppendAgentNotesAsync(string content, CancellationToken cancellationToken = default) => throw new NotImplementedException();
153 public Task<string> ListAgentNotesRevisionsAsync(int? limit = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
154 public Task<string> RollbackAgentNotesAsync(string? revisionFile = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
155 public Task<string> ReadHotContextAsync(string? activeScope = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
156 public Task<string> RouteContextAsync(string query, string? activeScope = null, int? maxSections = null, int? maxChars = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
157 public Task<string> MemoryHealthAsync(string? activeScope = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
158 public Task<string> CompactHotContextAsync(bool apply = false, CancellationToken cancellationToken = default) => throw new NotImplementedException();
159 public Task<string> ExtractFromArchiveAsync(string query, string? revisionFile = null, int? headLimit = null, int? contextLines = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
160 public Task<string> UpsertAgentNotesSectionAsync(string sectionId, string content, CancellationToken cancellationToken = default) => throw new NotImplementedException();
161 public Task<string> SearchAgentNotesAsync(string query, int? headLimit = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
162 public Task<string> ReadKnowledgeFileAsync(string filePath, int? offset = null, int? limit = null, string? knowledgeRootId = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
163 public Task<string> ListKnowledgeFilesAsync(string? subdir = null, string? knowledgeRootId = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
164 public Task<string> WriteKnowledgeFileAsync(string filePath, string content, string? knowledgePath = null, bool saveRevision = true, string? knowledgeRootId = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
165 public Task<string> AppendKnowledgeFileAsync(string filePath, string content, string? knowledgePath = null, bool saveRevision = true, string? knowledgeRootId = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
166 public Task<string> UpsertKnowledgeSectionAsync(string filePath, string sectionId, string content, string? knowledgePath = null, bool saveRevision = true, string? knowledgeRootId = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
167 public Task<string> DeleteKnowledgeFileAsync(string filePath, string? knowledgePath = null, string? knowledgeRootId = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
168 public Task<string> DeleteKnowledgeSectionAsync(string filePath, string sectionId, string? knowledgePath = null, string? knowledgeRootId = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
169 public Task<string> CodebaseIndexStatusAsync(string? workspacePath, string? solutionPath, CancellationToken cancellationToken = default) => throw new NotImplementedException();
170 public Task<string> CodebaseIndexSearchAsync(string? workspacePath, string? solutionPath, string query, int topN, string? pathPrefix, IReadOnlyList<string>? excludePathPrefixes, IReadOnlyList<string>? extensions, bool semantic, double alpha, double beta, int vecTopK, CancellationToken cancellationToken = default) => throw new NotImplementedException();
171 public Task<string> CodebaseIndexExplainAsync(string? workspacePath, string? solutionPath, long hitId, CancellationToken cancellationToken = default) => throw new NotImplementedException();
172 public Task<string> CodebaseIndexReindexAsync(string? workspacePath, string? solutionPath, bool fullRebuild, CancellationToken cancellationToken = default) => throw new NotImplementedException();
173 public Task<string> IdeAgentVerifyAsync(string? policy, string? sandboxProfile, string? solutionPath, CancellationToken cancellationToken = default) => throw new NotImplementedException();
174 public Task<string> IdeAgentVerifyBatchAsync(string? policy, string? sandboxProfile, string? solutionPath, bool useWorktree, CancellationToken cancellationToken = default) => throw new NotImplementedException();
175 public Task<string> IdeAgentCancelAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException();
176 public Task<string> IdeAgentStatusAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException();
177 public Task<string> IdeAgentLastAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException();
178 public Task<string> IdeAgentSandboxPrepareAsync(string? profile, string? workspaceRoot, CancellationToken cancellationToken = default) => throw new NotImplementedException();
179 }
180}
181
View only · write via MCP/CIDE