Forge
csharpdeeb25a2
1using CascadeIDE.Models.Editor;
2using Xunit;
3
4namespace CascadeIDE.Tests;
5
6public sealed class ParametricIntentMelodyTests
7{
8 [Theory]
9 [InlineData("wai:", true, "")]
10 [InlineData("wai:google.com", true, "google.com")]
11 [InlineData("wai:https://host/p", true, "https://host/p")]
12 [InlineData("wa", false, null)]
13 [InlineData("wai", true, "")]
14 public void TryParseWebAiPortalMelodyTail_PrefixAndPayload(string tail, bool expectedOk, string? expectedPayload)
15 {
16 var ok = ParametricIntentMelody.TryParseWebAiPortalMelodyTail(tail, out var slugOut, out var payload);
17 Assert.Equal(expectedOk, ok);
18 Assert.Equal(expectedPayload, ok ? payload : null);
19 if (ok)
20 Assert.Equal("wai", slugOut);
21 }
22
23 [Fact]
24 public void TryParseLineRangeTail_Parses_EditorLineAliases()
25 {
26 Assert.True(ParametricIntentMelody.TryParseLineRangeTail("els:5:15", out var parsed));
27 Assert.NotNull(parsed);
28 Assert.Equal("els", parsed!.Alias);
29 Assert.Equal(5, parsed.Lines.Start.Value);
30 Assert.Equal(15, parsed.Lines.End.Value);
31 }
32
33 [Fact]
34 public void TryParseLineRangeTail_Normalizes_space_separated_three_tokens()
35 {
36 Assert.True(ParametricIntentMelody.TryParseLineRangeTail("els 5 15", out var parsed));
37 Assert.NotNull(parsed);
38 Assert.Equal("els", parsed!.Alias);
39 Assert.Equal("els:5:15", parsed.DisplayTail);
40 Assert.Equal(5, parsed.Lines.Start.Value);
41 Assert.Equal(15, parsed.Lines.End.Value);
42 }
43
44 [Fact]
45 public void TryBuildExecutionArgs_ForSelect_UsesFullLineColumns()
46 {
47 var parsed = ParsedLineRange("els", "els:2:3", 2, 3);
48 var text = "one\ntwo\nthree\nfour";
49
50 var ok = ParametricIntentMelody.TryBuildExecutionArgs(
51 parsed,
52 @"D:\repo\file.txt",
53 text,
54 out var commandId,
55 out var argsJson,
56 out var error);
57
58 Assert.True(ok, error);
59 Assert.Equal(IdeCommands.Select, commandId);
60 Assert.Contains(@"""start_line"":2", argsJson, StringComparison.Ordinal);
61 Assert.Contains(@"""start_column"":1", argsJson, StringComparison.Ordinal);
62 Assert.Contains(@"""end_line"":3", argsJson, StringComparison.Ordinal);
63 Assert.Contains(@"""end_column"":6", argsJson, StringComparison.Ordinal);
64 }
65
66 [Fact]
67 public void TryBuildExecutionArgs_ForDelete_RemovesWholeLines()
68 {
69 var parsed = ParsedLineRange("eld", "eld:2:3", 2, 3);
70 var text = "one\ntwo\nthree\nfour";
71
72 var ok = ParametricIntentMelody.TryBuildExecutionArgs(
73 parsed,
74 @"D:\repo\file.txt",
75 text,
76 out var commandId,
77 out var argsJson,
78 out var error);
79
80 Assert.True(ok, error);
81 Assert.Equal(IdeCommands.ApplyEdit, commandId);
82 Assert.Contains(@"""start_line"":2", argsJson, StringComparison.Ordinal);
83 Assert.Contains(@"""start_column"":1", argsJson, StringComparison.Ordinal);
84 Assert.Contains(@"""end_line"":4", argsJson, StringComparison.Ordinal);
85 Assert.Contains(@"""end_column"":1", argsJson, StringComparison.Ordinal);
86 Assert.Contains(@"""new_text"":""""", argsJson, StringComparison.Ordinal);
87 }
88
89 [Fact]
90 public void TryParseLineRangeTail_Canonicalizes_ReversedBounds_ToSameRange()
91 {
92 Assert.True(ParametricIntentMelody.TryParseLineRangeTail("els:7:3", out var reversed));
93 Assert.True(ParametricIntentMelody.TryParseLineRangeTail("els:3:7", out var forward));
94 Assert.NotNull(reversed);
95 Assert.NotNull(forward);
96 Assert.Equal(forward!.Lines, reversed!.Lines);
97 Assert.Equal(3, reversed.Lines.Start.Value);
98 Assert.Equal(7, reversed.Lines.End.Value);
99 }
100
101 [Theory]
102 [InlineData("els;7")]
103 [InlineData("els:7")]
104 [InlineData("els 7")]
105 [InlineData("eld;7")]
106 [InlineData("eld:7")]
107 [InlineData("eld 7")]
108 public void TryParseLineRangeTail_SingleLineNumber_IsInclusiveOneLineRange(string tail)
109 {
110 Assert.True(ParametricIntentMelody.TryParseLineRangeTail(tail, out var parsed), tail);
111 Assert.NotNull(parsed);
112 Assert.Equal(7, parsed!.Lines.Start.Value);
113 Assert.Equal(7, parsed.Lines.End.Value);
114 }
115
116 [Theory]
117 [InlineData("esc:", true, "")]
118 [InlineData("esc:[m:run]", true, "[m:run]")]
119 [InlineData("erc:[f:foo.cs; m:bar]", true, "[f:foo.cs; m:bar]")]
120 [InlineData("er", false, null)]
121 public void TryParseBracketCodeRefMelodyTail_PrefixAndPayload(string tail, bool expectedOk, string? expectedPayload)
122 {
123 var ok = ParametricIntentMelody.TryParseBracketCodeRefMelodyTail(tail, out var slugOut, out var payload);
124 Assert.Equal(expectedOk, ok);
125 Assert.Equal(expectedPayload, ok ? payload : null);
126 if (ok)
127 {
128 var expectedSlug = tail.StartsWith("erc", StringComparison.Ordinal) ? "erc" : "esc";
129 Assert.Equal(expectedSlug, slugOut);
130 }
131 }
132
133 [Fact]
134 public void TryResolveParametricExecution_BracketSelect_BuildsEditorSelectCodeArgs()
135 {
136 var ok = ParametricIntentMelody.TryResolveParametricExecution(
137 "esc:[M:Run]",
138 @"D:\repo\src\Foo.cs",
139 "",
140 out var commandId,
141 out var argsJson,
142 out _);
143
144 Assert.True(ok);
145 Assert.Equal(IdeCommands.EditorSelectCode, commandId);
146 Assert.Contains(@"""code_ref"":""[M:Run]""", argsJson, StringComparison.Ordinal);
147 Assert.Contains("Foo.cs", argsJson!, StringComparison.Ordinal);
148 }
149
150 [Theory]
151 [InlineData("e", true)]
152 [InlineData("es", true)]
153 [InlineData("esc", true)]
154 [InlineData("esc:", true)]
155 [InlineData("esc:[", true)]
156 [InlineData("erc:[m:run", true)]
157 [InlineData("ex", false)]
158 public void IsParametricChordTailPrefix_MatchesBracketMelodyTails(string tail, bool expected) =>
159 Assert.Equal(expected, ParametricIntentMelody.IsParametricChordTailPrefix(tail));
160
161 [Theory]
162 [InlineData("w", true)]
163 [InlineData("wa", true)]
164 [InlineData("wai", true)]
165 [InlineData("wai:", true)]
166 [InlineData("wai:g", true)]
167 [InlineData("wag", false)]
168 [InlineData("el", true)]
169 [InlineData("els", true)]
170 [InlineData("els:", true)]
171 [InlineData("els;", true)]
172 [InlineData("els:5", true)]
173 [InlineData("els;5", true)]
174 [InlineData("els 7", true)]
175 [InlineData("els:5:", true)]
176 [InlineData("els;5;", true)]
177 [InlineData("els:5:10", true)]
178 [InlineData("els;5;10", true)]
179 [InlineData("els:5:x", false)]
180 public void IsParametricChordTailPrefix_MatchesChordBuildingTails(string tail, bool expected) =>
181 Assert.Equal(expected, ParametricIntentMelody.IsParametricChordTailPrefix(tail));
182
183 [Theory]
184 [InlineData("wai", true)]
185 [InlineData("els", true)]
186 [InlineData("eld", true)]
187 [InlineData("so", false)]
188 [InlineData("gs", false)]
189 public void IsParametricMelodyBaseAlias_RecognizesParametricRoots(string alias, bool expected) =>
190 Assert.Equal(expected, ParametricIntentMelody.IsParametricMelodyBaseAlias(alias));
191
192 [Fact]
193 public void BuildAliasUsageHint_reads_palette_usage_hint_from_catalog()
194 {
195 var h = ParametricIntentMelody.BuildAliasUsageHint("els");
196 Assert.Contains("els", h, StringComparison.Ordinal);
197 Assert.Contains(";", h, StringComparison.Ordinal);
198 }
199
200 [Fact]
201 public void BuildAliasUsageCategory_resolves_palette_hint_slug_to_root()
202 {
203 Assert.Equal("Web AI Portal", ParametricIntentMelody.BuildAliasUsageCategory("wai-url"));
204 Assert.Equal("Web AI Portal", ParametricIntentMelody.BuildAliasUsageCategory("wai"));
205 }
206
207 [Fact]
208 public void BuildAliasUsageHint_unknown_slug_falls_back_to_template() =>
209 Assert.Equal("c:zzz:<start>:<end>", ParametricIntentMelody.BuildAliasUsageHint("zzz"));
210
211 private static ParametricIntentMelody.ParsedLineRange ParsedLineRange(string alias, string display, int start, int end)
212 {
213 Assert.True(LineNumber.TryCreate(start, out var s));
214 Assert.True(LineNumber.TryCreate(end, out var e));
215 Assert.True(LineRange.TryCreate(s, e, out var lines));
216 return new ParametricIntentMelody.ParsedLineRange(alias, display, lines);
217 }
218}
219
View only · write via MCP/CIDE