| 1 | using System.Text.Json; |
| 2 | using System.Text.Json.Nodes; |
| 3 | using CascadeIDE.Features.Git.DataAcquisition; |
| 4 | using CascadeIDE.Features.UiChrome; |
| 5 | using CascadeIDE.Services; |
| 6 | using CascadeIDE.Services.AgentContract; |
| 7 | using Xunit; |
| 8 | |
| 9 | namespace CascadeIDE.Tests; |
| 10 | |
| 11 | [Collection("UiModeCatalog")] |
| 12 | public sealed class AgentContractRunnerTests : IDisposable |
| 13 | { |
| 14 | public AgentContractRunnerTests() => |
| 15 | UiModeCatalog.ResetForTests(); |
| 16 | |
| 17 | public void Dispose() => |
| 18 | UiModeCatalog.ResetForTests(); |
| 19 | |
| 20 | [Fact] |
| 21 | public void Get_ui_modes_diagnostics_matches_UiModeCatalog_payload() |
| 22 | { |
| 23 | UiModeCatalog.Initialize(); |
| 24 | var expected = UiModeCatalog.GetDiagnosticsJson(); |
| 25 | var actual = AgentContractRunner.GetContractJson(IdeCommands.GetUiModesDiagnostics); |
| 26 | Assert.Equal(expected, actual); |
| 27 | } |
| 28 | |
| 29 | [Fact] |
| 30 | public void Get_supported_editor_languages_matches_EditorLanguageSupport_payload() |
| 31 | { |
| 32 | var expected = EditorLanguageSupport.GetJson(); |
| 33 | var actual = AgentContractRunner.GetContractJson(IdeCommands.GetSupportedEditorLanguages); |
| 34 | Assert.Equal(expected, actual); |
| 35 | } |
| 36 | |
| 37 | [Fact] |
| 38 | public void Get_ui_modes_diagnostics_normalized_paths_are_stable() |
| 39 | { |
| 40 | UiModeCatalog.Initialize(); |
| 41 | var raw = AgentContractRunner.GetContractJson(IdeCommands.GetUiModesDiagnostics); |
| 42 | var normalized = NormalizeUiModesDiagnosticsPaths(raw); |
| 43 | using var doc = JsonDocument.Parse(normalized); |
| 44 | Assert.Equal("<BASE>", doc.RootElement.GetProperty("app_base_directory").GetString()); |
| 45 | Assert.StartsWith("<BASE>", doc.RootElement.GetProperty("ui_modes_directory").GetString(), StringComparison.Ordinal); |
| 46 | Assert.StartsWith("<BASE>", doc.RootElement.GetProperty("index_toml_path").GetString(), StringComparison.Ordinal); |
| 47 | Assert.True(doc.RootElement.GetProperty("ui_mode_catalog_initialized").GetBoolean()); |
| 48 | } |
| 49 | |
| 50 | /// <summary>Как в ADR 0052: убрать машинно-зависимые префиксы для снапшотов.</summary> |
| 51 | internal static string NormalizeUiModesDiagnosticsPaths(string json) |
| 52 | { |
| 53 | var node = JsonNode.Parse(json)!; |
| 54 | var baseDir = node["app_base_directory"]?.GetValue<string>(); |
| 55 | if (string.IsNullOrEmpty(baseDir)) |
| 56 | return json; |
| 57 | |
| 58 | var normalizedBase = baseDir.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); |
| 59 | node["app_base_directory"] = "<BASE>"; |
| 60 | |
| 61 | foreach (var key in new[] { "ui_modes_directory", "index_toml_path" }) |
| 62 | { |
| 63 | if (node[key]?.GetValue<string>() is not { } p) |
| 64 | continue; |
| 65 | if (p.StartsWith(normalizedBase, StringComparison.OrdinalIgnoreCase)) |
| 66 | { |
| 67 | var tail = p[normalizedBase.Length..].Replace('\\', '/'); |
| 68 | node[key] = "<BASE>" + tail; |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | return node.ToJsonString(new JsonSerializerOptions { WriteIndented = false }); |
| 73 | } |
| 74 | |
| 75 | [Fact] |
| 76 | public async Task Git_status_uses_workspace_and_returns_json_shape() |
| 77 | { |
| 78 | var tmp = Directory.CreateTempSubdirectory("cascade_agent_contract_git_"); |
| 79 | try |
| 80 | { |
| 81 | await RunGitAsync(tmp.FullName, "init"); |
| 82 | File.WriteAllText(Path.Combine(tmp.FullName, "a.txt"), "x"); |
| 83 | await RunGitAsync(tmp.FullName, "add", "a.txt"); |
| 84 | await RunGitAsync(tmp.FullName, "-c", "user.email=t@t", "-c", "user.name=t", "commit", "-m", "init"); |
| 85 | |
| 86 | var json = AgentContractRunner.GetContractJson( |
| 87 | ["--workspace", tmp.FullName, IdeCommands.GitStatus]); |
| 88 | using var doc = JsonDocument.Parse(json); |
| 89 | Assert.True(doc.RootElement.TryGetProperty("success", out var ok)); |
| 90 | Assert.True(ok.GetBoolean()); |
| 91 | Assert.True(doc.RootElement.TryGetProperty("exit_code", out var code)); |
| 92 | Assert.Equal(0, code.GetInt32()); |
| 93 | Assert.True(doc.RootElement.TryGetProperty("output", out var output)); |
| 94 | Assert.False(string.IsNullOrEmpty(output.GetString())); |
| 95 | } |
| 96 | finally |
| 97 | { |
| 98 | try |
| 99 | { |
| 100 | Directory.Delete(tmp.FullName, recursive: true); |
| 101 | } |
| 102 | catch |
| 103 | { |
| 104 | // best effort |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | private static async Task RunGitAsync(string workingDirectory, params string[] args) |
| 110 | { |
| 111 | var git = new GitCommandRunner(); |
| 112 | var (success, exitCode, _) = await git.RunAsync(args, workingDirectory).ConfigureAwait(false); |
| 113 | Assert.True(success); |
| 114 | Assert.Equal(0, exitCode); |
| 115 | } |
| 116 | } |
| 117 | |