Forge
csharp3407750f
1using System.Collections.Frozen;
2using System.Net.Http.Json;
3using System.Text.Json;
4using AgentForge.Abstractions;
5using AgentForge.Tests;
6
7namespace AgentForge.Mcp.Tests;
8
9public sealed class McpToolTests(ForgeWebApplicationFactory factory) : IClassFixture<ForgeWebApplicationFactory>
10{
11 private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true };
12
13 [Fact]
14 public async Task Create_repo_and_list_via_mcp_invoke()
15 {
16 using var http = factory.CreateClient();
17
18 var create = await InvokeAsync(http, "create_repo", FrozenDictionary<string, JsonElement>.Empty
19 .AppendPair("name", JsonSerializer.SerializeToElement("forge-mcp-test"))
20 .AppendPair("description", JsonSerializer.SerializeToElement("MCP integration test")));
21 Assert.True(create.GetProperty("success").GetBoolean());
22
23 var list = await InvokeAsync(http, "list_repos");
24 Assert.True(list.GetProperty("success").GetBoolean());
25 using var listDoc = JsonDocument.Parse(list.GetProperty("body").GetString()!);
26 var repoNames = listDoc.RootElement.GetProperty("repos").EnumerateArray()
27 .Select(r => r.GetProperty("name").GetString())
28 .ToList();
29 Assert.Contains("forge-mcp-test", repoNames);
30 }
31
32 [Fact]
33 public async Task Issue_with_anchors_and_lens_via_mcp_invoke()
34 {
35 await using var lensFactory = new ForgeWebApplicationFactory { PluginBundle = "cide-companion" };
36 using var http = lensFactory.CreateClient();
37
38 await InvokeAsync(http, "create_repo", FrozenDictionary<string, JsonElement>.Empty
39 .AppendPair("name", JsonSerializer.SerializeToElement("mcp-lens")));
40
41 var anchors = JsonSerializer.SerializeToElement(new[]
42 {
43 new { file = "src/Bar.cs", line_start = 7 },
44 });
45
46 var createIssue = await InvokeAsync(http, ForgeMcpToolNames.IssueCreate, FrozenDictionary<string, JsonElement>.Empty
47 .AppendPair("repo", JsonSerializer.SerializeToElement("mcp-lens"))
48 .AppendPair("title", JsonSerializer.SerializeToElement("Anchor test"))
49 .AppendPair("anchors", anchors));
50 Assert.True(createIssue.GetProperty("success").GetBoolean());
51
52 var getIssue = await InvokeAsync(http, ForgeMcpToolNames.IssueOpen, FrozenDictionary<string, JsonElement>.Empty
53 .AppendPair("repo", JsonSerializer.SerializeToElement("mcp-lens"))
54 .AppendPair("number", JsonSerializer.SerializeToElement(1)));
55 Assert.True(getIssue.GetProperty("success").GetBoolean());
56
57 var lens = await InvokeAsync(http, ForgeMcpToolNames.LensQuery, FrozenDictionary<string, JsonElement>.Empty
58 .AppendPair("repo", JsonSerializer.SerializeToElement("mcp-lens"))
59 .AppendPair("file", JsonSerializer.SerializeToElement("src/Bar.cs")));
60 Assert.True(lens.GetProperty("success").GetBoolean());
61 using var lensDoc = JsonDocument.Parse(lens.GetProperty("body").GetString()!);
62 Assert.Equal(1, lensDoc.RootElement.GetProperty("issues").GetArrayLength());
63 }
64
65 [Fact]
66 public async Task Get_capabilities_via_mcp_invoke()
67 {
68 using var http = factory.CreateClient();
69 var result = await InvokeAsync(http, "get_forge_capabilities");
70 Assert.True(result.GetProperty("success").GetBoolean());
71 using var doc = JsonDocument.Parse(result.GetProperty("body").GetString()!);
72 Assert.Equal("standard", doc.RootElement.GetProperty("bundle").GetString());
73 Assert.True(doc.RootElement.GetProperty("tools").GetArrayLength() > 0);
74 }
75
76 [Fact]
77 public async Task Get_repo_returns_not_found_for_missing()
78 {
79 using var http = factory.CreateClient();
80 var result = await InvokeAsync(http, "get_repo", FrozenDictionary<string, JsonElement>.Empty
81 .AppendPair("name", JsonSerializer.SerializeToElement("no-such-repo")));
82 Assert.False(result.GetProperty("success").GetBoolean());
83 Assert.Contains("not found", result.GetProperty("body").GetString()!, StringComparison.OrdinalIgnoreCase);
84 }
85
86 private static async Task<JsonElement> InvokeAsync(
87 HttpClient http,
88 string tool,
89 IReadOnlyDictionary<string, JsonElement>? arguments = null)
90 {
91 var response = await http.PostAsJsonAsync("/api/v1/mcp/invoke", new
92 {
93 tool,
94 arguments = arguments?.Count > 0 ? arguments : null,
95 });
96 var payload = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
97 return payload;
98 }
99}
100
101file static class FrozenDictionaryExtensions
102{
103 internal static FrozenDictionary<string, JsonElement> AppendPair(
104 this FrozenDictionary<string, JsonElement> source,
105 string key,
106 JsonElement value)
107 {
108 var dict = source.ToDictionary(static pair => pair.Key, static pair => pair.Value);
109 dict[key] = value;
110 return dict.ToFrozenDictionary();
111 }
112}
113
View only · write via MCP/CIDE