Forge
csharp3407750f
1using Microsoft.CodeAnalysis;
2using Microsoft.CodeAnalysis.Text;
3using Xunit;
4
5namespace AgentForge.PluginAnalyzers.Tests;
6
7public sealed class ForgePluginAnalyzerTests
8{
9 [Fact]
10 public async Task InternalsVisibleTo_reports_FORGE001()
11 {
12 const string source = """
13 [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("AgentForge.Plugin.Issue")]
14 namespace AgentForge.Plugin.Test;
15 """;
16
17 var diagnostics = await AnalyzerTestHelper.GetDiagnosticsAsync(source);
18 Assert.True(AnalyzerTestHelper.HasDiagnostic(diagnostics, ForgePluginAnalyzer.InternalsVisibleToId, DiagnosticSeverity.Error));
19 }
20
21 [Fact]
22 public async Task DuplicateDoi_reports_FORGE002()
23 {
24 const string source = """
25 using AgentForge.Abstractions;
26 namespace AgentForge.Plugin.Test;
27 public static class Commands
28 {
29 public static ForgeCommandDescriptor[] All =>
30 [
31 new ForgeCommandDescriptor { Domain = "forge", Object = "issue", Intent = "open", Help = "h", Category = "c", Surfaces = ["global"] },
32 new ForgeCommandDescriptor { Domain = "forge", Object = "issue", Intent = "open", Help = "h2", Category = "c2", Surfaces = ["global"] },
33 ];
34 }
35 """;
36
37 var diagnostics = await AnalyzerTestHelper.GetDiagnosticsAsync(
38 source,
39 extraReferences: [AnalyzerTestHelper.SdkReference]);
40
41 Assert.True(AnalyzerTestHelper.HasDiagnostic(diagnostics, ForgePluginAnalyzer.DuplicateDoiId, DiagnosticSeverity.Error));
42 }
43
44 [Fact]
45 public async Task MapGetCreateRoute_reports_FORGE004()
46 {
47 const string source = """
48 namespace AgentForge.Plugin.Test;
49 public static class Routes
50 {
51 public static void Map(object app) => app.MapGet("/view/issues/new", () => "");
52 }
53 public static class AppExtensions
54 {
55 public static object MapGet(this object app, string route, System.Func<object> handler) => app;
56 }
57 """;
58
59 var diagnostics = await AnalyzerTestHelper.GetDiagnosticsAsync(source);
60 Assert.True(AnalyzerTestHelper.HasDiagnostic(diagnostics, ForgePluginAnalyzer.AntiJiraCreateRouteId, DiagnosticSeverity.Error));
61 }
62
63 [Fact]
64 public async Task MapGetCiNewRoute_in_PluginCi_skips_FORGE004()
65 {
66 const string source = """
67 namespace AgentForge.Plugin.Ci;
68 public static class Routes
69 {
70 public static void Map(object app) => app.MapGet("/view/repos/{name}/ci/new", () => "");
71 }
72 public static class AppExtensions
73 {
74 public static object MapGet(this object app, string route, System.Func<object> handler) => app;
75 }
76 """;
77
78 var diagnostics = await AnalyzerTestHelper.GetDiagnosticsAsync(
79 source,
80 assemblyName: "AgentForge.Plugin.Ci");
81
82 Assert.False(AnalyzerTestHelper.HasDiagnostic(diagnostics, ForgePluginAnalyzer.AntiJiraCreateRouteId));
83 }
84
85 [Fact]
86 public async Task EmbeddedSlashScript_reports_FORGE007()
87 {
88 const string source = """
89 namespace AgentForge.Plugin.Test;
90 public static class Plugin;
91 """;
92
93 var additional = new AdditionalTextImplementation("wwwroot/forge-slash.js", "console.log('x');");
94 var diagnostics = await AnalyzerTestHelper.GetDiagnosticsAsync(
95 source,
96 additionalFiles: [additional]);
97
98 Assert.True(AnalyzerTestHelper.HasDiagnostic(diagnostics, ForgePluginAnalyzer.EmbeddedSlashScriptId, DiagnosticSeverity.Error));
99 }
100
101 [Fact]
102 public async Task ViewShell_skips_FORGE007_for_slash_script()
103 {
104 const string source = """
105 namespace AgentForge.Plugin.Test;
106 public static class Plugin;
107 """;
108
109 var additional = new AdditionalTextImplementation("wwwroot/forge-slash.js", "console.log('x');");
110 var diagnostics = await AnalyzerTestHelper.GetDiagnosticsAsync(
111 source,
112 assemblyName: "AgentForge.Plugin.ViewShell",
113 additionalFiles: [additional]);
114
115 Assert.False(AnalyzerTestHelper.HasDiagnostic(diagnostics, ForgePluginAnalyzer.EmbeddedSlashScriptId));
116 }
117
118 [Fact]
119 public async Task MissingHelp_reports_FORGE008()
120 {
121 const string source = """
122 using AgentForge.Abstractions;
123 namespace AgentForge.Plugin.Test;
124 public static class Commands
125 {
126 public static ForgeCommandDescriptor One =>
127 new ForgeCommandDescriptor { Domain = "forge", Object = "issue", Intent = "open", Surfaces = ["global"] };
128 }
129 """;
130
131 var diagnostics = await AnalyzerTestHelper.GetDiagnosticsAsync(
132 source,
133 extraReferences: [AnalyzerTestHelper.SdkReference]);
134
135 Assert.True(AnalyzerTestHelper.HasDiagnostic(diagnostics, ForgePluginAnalyzer.MissingCommandHelpId, DiagnosticSeverity.Info));
136 }
137
138 private sealed class AdditionalTextImplementation(string path, string content) : AdditionalText
139 {
140 public override SourceText? GetText(CancellationToken cancellationToken = default) =>
141 SourceText.From(content);
142
143 public override string Path { get; } = path;
144 }
145}
146
View only · write via MCP/CIDE