Forge
csharpdeeb25a2
1using System.Text.Json;
2using CascadeIDE.Models.Editor;
3using CascadeIDE.Services;
4using Xunit;
5
6namespace CascadeIDE.Tests;
7
8public sealed class EditorMcpSpansTests
9{
10 [Fact]
11 public void EditorTextSpan_TryParse_AcceptsValidRectangularSpan()
12 {
13 var fp = CanonicalFilePath.Normalize(Path.Combine(Path.GetTempPath(), nameof(EditorMcpSpansTests), "sample.txt"));
14 var args = Args(
15 ("file_path", JsonSerializer.SerializeToElement(fp)),
16 ("start_line", JsonSerializer.SerializeToElement(2)),
17 ("start_column", JsonSerializer.SerializeToElement(1)),
18 ("end_line", JsonSerializer.SerializeToElement(4)),
19 ("end_column", JsonSerializer.SerializeToElement(10)));
20
21 Assert.True(EditorTextSpan.TryParse(args, out var span, out var error), error);
22 Assert.Equal(fp, span.File.Value, ignoreCase: true);
23 Assert.Equal(2, span.StartLine.Value);
24 Assert.Equal(1, span.StartColumn.Value);
25 Assert.Equal(4, span.EndLine.Value);
26 Assert.Equal(10, span.EndColumn.Value);
27 }
28
29 [Fact]
30 public void EditorTextSpan_TryParse_RejectsZeroColumn()
31 {
32 var fp = @"D:\a\b\c.txt";
33 var args = Args(
34 ("file_path", JsonSerializer.SerializeToElement(fp)),
35 ("start_line", JsonSerializer.SerializeToElement(1)),
36 ("start_column", JsonSerializer.SerializeToElement(0)),
37 ("end_line", JsonSerializer.SerializeToElement(1)),
38 ("end_column", JsonSerializer.SerializeToElement(5)));
39
40 Assert.False(EditorTextSpan.TryParse(args, out _, out var err));
41 Assert.Contains("start_column", err, StringComparison.Ordinal);
42 }
43
44 [Fact]
45 public void EditorTextSpan_TryParse_RejectsInvertedColumnsOnSameLine()
46 {
47 var fp = @"D:\a\b\c.txt";
48 var args = Args(
49 ("file_path", JsonSerializer.SerializeToElement(fp)),
50 ("start_line", JsonSerializer.SerializeToElement(2)),
51 ("start_column", JsonSerializer.SerializeToElement(8)),
52 ("end_line", JsonSerializer.SerializeToElement(2)),
53 ("end_column", JsonSerializer.SerializeToElement(3)));
54
55 Assert.False(EditorTextSpan.TryParse(args, out _, out var err));
56 Assert.Contains("end_column", err, StringComparison.Ordinal);
57 }
58
59 [Fact]
60 public void EditorTextSpan_TryParse_RejectsEndLineBeforeStartLine()
61 {
62 var fp = @"D:\a\b\c.txt";
63 var args = Args(
64 ("file_path", JsonSerializer.SerializeToElement(fp)),
65 ("start_line", JsonSerializer.SerializeToElement(5)),
66 ("start_column", JsonSerializer.SerializeToElement(1)),
67 ("end_line", JsonSerializer.SerializeToElement(2)),
68 ("end_column", JsonSerializer.SerializeToElement(5)));
69
70 Assert.False(EditorTextSpan.TryParse(args, out _, out var err));
71 Assert.Contains("end_line не может быть меньше start_line", err, StringComparison.Ordinal);
72 }
73
74 [Fact]
75 public void EditorContentLineRange_TryParse_DefaultsToSingleLineOne()
76 {
77 Assert.True(EditorContentLineRangeMcpArgs.TryParse(null, out var lines, out var err), err);
78 Assert.Equal(1, lines.Start.Value);
79 Assert.Equal(1, lines.End.Value);
80 }
81
82 [Fact]
83 public void EditorContentLineRange_TryParse_RejectsInvertedRangeWhenExplicit()
84 {
85 var args = Args(
86 ("start_line", JsonSerializer.SerializeToElement(8)),
87 ("end_line", JsonSerializer.SerializeToElement(3)));
88 Assert.False(EditorContentLineRangeMcpArgs.TryParse(args, out _, out var err));
89 Assert.Contains("end_line не может быть меньше", err, StringComparison.Ordinal);
90 }
91
92 [Fact]
93 public void EditorRevealRange_TryParse_RequiresFileAndLines()
94 {
95 var fp = @"D:\p\f.cs";
96 var args = Args(
97 ("file_path", JsonSerializer.SerializeToElement(fp)),
98 ("start_line", JsonSerializer.SerializeToElement(10)),
99 ("end_line", JsonSerializer.SerializeToElement(25)),
100 ("duration_ms", JsonSerializer.SerializeToElement(5000)));
101
102 Assert.True(EditorRevealRangeMcpArgs.TryParse(args, out var req, out var err), err);
103 Assert.Equal(CanonicalFilePath.Normalize(fp), req.File.Value, ignoreCase: true);
104 Assert.NotNull(req.Lines);
105 Assert.Equal(10, req.Lines!.Value.Start.Value);
106 Assert.Equal(25, req.Lines!.Value.End.Value);
107 Assert.Equal(5000, req.DurationMs);
108 }
109
110 [Fact]
111 public void EditorRevealRange_TryParse_AcceptsMemberKeyWithoutLines()
112 {
113 var fp = @"D:\p\f.cs";
114 var args = Args(
115 ("file_path", JsonSerializer.SerializeToElement(fp)),
116 ("member_key", JsonSerializer.SerializeToElement("DoWork")));
117
118 Assert.True(EditorRevealRangeMcpArgs.TryParse(args, out var req, out var err), err);
119 Assert.Equal("DoWork", req.MemberKey);
120 Assert.Null(req.Lines);
121 }
122
123 [Fact]
124 public void EditorGoToPosition_TryParse_OptionalEndColumns()
125 {
126 var fp = @"D:\p\f.cs";
127 var args = Args(
128 ("file_path", JsonSerializer.SerializeToElement(fp)),
129 ("line", JsonSerializer.SerializeToElement(2)),
130 ("column", JsonSerializer.SerializeToElement(12)),
131 ("end_column", JsonSerializer.SerializeToElement(18)));
132
133 Assert.True(EditorGoToPositionMcpArgs.TryParse(args, out var doc, out var line, out var col, out var el, out var ec, out var err), err);
134 Assert.Equal(CanonicalFilePath.Normalize(fp), doc.Value, ignoreCase: true);
135 Assert.Equal(2, line.Value);
136 Assert.Equal(12, col.Value);
137 Assert.Null(el);
138 Assert.NotNull(ec);
139 Assert.Equal(18, ec!.Value.Value);
140 }
141
142 private static Dictionary<string, JsonElement> Args(params (string key, JsonElement el)[] pairs)
143 {
144 var d = new Dictionary<string, JsonElement>(StringComparer.Ordinal);
145 foreach (var (key, el) in pairs)
146 d[key] = el;
147 return d;
148 }
149}
150
View only · write via MCP/CIDE