Forge
csharp4405de34
1#nullable enable
2
3using System.Collections.Immutable;
4using Basic.Reference.Assemblies;
5using Microsoft.CodeAnalysis;
6using Microsoft.CodeAnalysis.CSharp;
7using Microsoft.CodeAnalysis.Diagnostics;
8using Xunit;
9
10namespace CascadeIDE.ArchitectureAnalyzers.Tests;
11
12public sealed class LayerRoleConsistencyAnalyzerTests
13{
14 private static async Task<ImmutableArray<Diagnostic>> RunAnalyzerAsync(params (string Path, string Text)[] files)
15 {
16 var trees = new List<SyntaxTree>(files.Length);
17 foreach (var (path, text) in files)
18 trees.Add(CSharpSyntaxTree.ParseText(text, path: path));
19
20 var compilation = CSharpCompilation.Create(
21 "TestAssembly",
22 trees,
23 Net80.References.All,
24 new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
25
26 var analyzer = new LayerRoleConsistencyAnalyzer();
27 return await compilation
28 .WithAnalyzers(ImmutableArray.Create<DiagnosticAnalyzer>(analyzer))
29 .GetAnalyzerDiagnosticsAsync();
30 }
31
32 [Fact]
33 public async Task CASCOPE032_OrchestratorWithoutAttribute_Reports()
34 {
35 var diags = await RunAnalyzerAsync((
36 @"D:\repo\Features\Chat\Application\FooOrchestrator.cs",
37 """
38 namespace CascadeIDE.Features.Chat.Application;
39 public static class FooOrchestrator { }
40 """));
41
42 Assert.Contains(
43 diags,
44 d => d.Id == LayerRoleConsistencyAnalyzer.MissingApplicationOrchestratorAttributeId);
45 }
46
47 [Fact]
48 public async Task CASCOPE033_CcuWithoutAttribute_Reports()
49 {
50 var diags = await RunAnalyzerAsync(
51 (
52 @"D:\repo\Cockpit\ComputingUnits\Foo\BarUnit.cs",
53 """
54 namespace CascadeIDE.Cockpit.ComputingUnits.Foo;
55 public sealed class BarUnit : ICockpitComputeUnit { }
56 """),
57 (
58 @"D:\repo\Cockpit\ComputingUnits\ICockpitComputeUnit.cs",
59 """
60 namespace CascadeIDE.Cockpit.ComputingUnits;
61 public interface ICockpitComputeUnit { }
62 """));
63
64 Assert.Contains(
65 diags,
66 d => d.Id == LayerRoleConsistencyAnalyzer.MissingComputingUnitAttributeId);
67 }
68
69 [Fact]
70 public async Task CASCOPE036_ComputingUnitOnOrchestratorName_Reports()
71 {
72 var diags = await RunAnalyzerAsync((
73 @"D:\repo\Features\X\Application\BadOrchestrator.cs",
74 """
75 namespace CascadeIDE.Contracts;
76 [System.AttributeUsage(System.AttributeTargets.Class)]
77 sealed class ComputingUnitAttribute : System.Attribute { }
78
79 namespace CascadeIDE.Features.X.Application;
80 [ComputingUnit]
81 public static class BadOrchestrator { }
82 """));
83
84 Assert.Contains(
85 diags,
86 d => d.Id == LayerRoleConsistencyAnalyzer.ComputingUnitOnOrchestratorNameId);
87 }
88
89 [Fact]
90 public async Task CASCOPE040_PresentationProjection_FileIo_Reports()
91 {
92 var diags = await RunAnalyzerAsync((
93 @"D:\repo\Features\Shell\Application\BadProjection.cs",
94 """
95 namespace CascadeIDE.Contracts;
96 [System.AttributeUsage(System.AttributeTargets.Class)]
97 sealed class PresentationProjectionAttribute : System.Attribute { }
98
99 namespace CascadeIDE.Features.Shell.Application;
100 [PresentationProjection]
101 public static class BadProjection
102 {
103 public static bool Exists(string path) => System.IO.File.Exists(path);
104 }
105 """));
106
107 Assert.Contains(
108 diags,
109 d => d.Id == LayerRoleConsistencyAnalyzer.PresentationProjectionForbiddenIoId);
110 }
111
112 [Fact]
113 public async Task MarkedOrchestrator_NoCasope032()
114 {
115 var diags = await RunAnalyzerAsync((
116 @"D:\repo\Features\X\Application\GoodOrchestrator.cs",
117 """
118 namespace CascadeIDE.Contracts;
119 [System.AttributeUsage(System.AttributeTargets.Class)]
120 sealed class ApplicationOrchestratorAttribute : System.Attribute { }
121
122 namespace CascadeIDE.Features.X.Application;
123 [ApplicationOrchestrator]
124 public static class GoodOrchestrator
125 {
126 public static System.Threading.Tasks.Task RunAsync() => System.Threading.Tasks.Task.CompletedTask;
127 }
128 """));
129
130 Assert.DoesNotContain(
131 diags,
132 d => d.Id == LayerRoleConsistencyAnalyzer.MissingApplicationOrchestratorAttributeId);
133 }
134
135 [Fact]
136 public async Task CASCOPE039_McpServiceFacade_WithApplicationOrchestrator_NoReport()
137 {
138 var diags = await RunAnalyzerAsync((
139 @"D:\repo\Features\IdeMcp\Application\NotesOrchestrator.cs",
140 """
141 namespace CascadeIDE.Contracts;
142 [System.AttributeUsage(System.AttributeTargets.Class)]
143 sealed class ApplicationOrchestratorAttribute : System.Attribute { }
144
145 namespace CascadeIDE.Features.IdeMcp.Application;
146 sealed class McpAgentNotesService
147 {
148 public string Write(string path, string content) => content;
149 }
150
151 [ApplicationOrchestrator]
152 public static class NotesOrchestrator
153 {
154 public static string Write(McpAgentNotesService svc, string content) =>
155 svc.Write("x", content);
156 }
157 """));
158
159 Assert.DoesNotContain(
160 diags,
161 d => d.Id == LayerRoleConsistencyAnalyzer.OrchestratorLooksLikeProjectionId);
162 }
163
164 [Fact]
165 public async Task CASCOPE039_PureNormalizeOrchestrator_Reports()
166 {
167 var diags = await RunAnalyzerAsync((
168 @"D:\repo\Features\Shell\Application\NormalizeOrchestrator.cs",
169 """
170 namespace CascadeIDE.Contracts;
171 [System.AttributeUsage(System.AttributeTargets.Class)]
172 sealed class ApplicationOrchestratorAttribute : System.Attribute { }
173
174 namespace CascadeIDE.Features.Shell.Application;
175 [ApplicationOrchestrator]
176 public static class NormalizeOrchestrator
177 {
178 public static string Normalize(string? value) => value ?? "";
179 }
180 """));
181
182 Assert.Contains(
183 diags,
184 d => d.Id == LayerRoleConsistencyAnalyzer.OrchestratorLooksLikeProjectionId);
185 }
186
187 [Fact]
188 public async Task CASCOPE038_MarkedComputingUnit_StaticHelper_NoReport()
189 {
190 var diags = await RunAnalyzerAsync((
191 @"D:\repo\Features\Shell\Application\MarkedHelper.cs",
192 """
193 namespace CascadeIDE.Contracts;
194 [System.AttributeUsage(System.AttributeTargets.Class)]
195 sealed class ComputingUnitAttribute : System.Attribute { }
196
197 namespace CascadeIDE.Features.Shell.Application;
198 [ComputingUnit]
199 public static class IntercomFeedProjector
200 {
201 public static string Format(string s) => s;
202 }
203 """));
204
205 Assert.DoesNotContain(
206 diags,
207 d => d.Id == LayerRoleConsistencyAnalyzer.StaticHelperSuggestProjectionOrCuId);
208 }
209}
210
View only · write via MCP/CIDE