Files
ClaudeDo/tests/ClaudeDo.Ui.Tests/ViewModels/SshSettingsTabViewModelTests.cs
T

97 lines
3.1 KiB
C#

using ClaudeDo.Data.Models;
using ClaudeDo.Ui.ViewModels.Modals.Settings;
namespace ClaudeDo.Ui.Tests.ViewModels;
public sealed class SshSettingsTabViewModelTests
{
private sealed class Worker : StubWorkerClient
{
public List<SshWorkspace> Stored = new();
public override Task<List<SshWorkspace>> GetSshWorkspacesAsync() => Task.FromResult(Stored.ToList());
public override Task SetSshWorkspacesAsync(List<SshWorkspace> workspaces) { Stored = workspaces; return Task.CompletedTask; }
}
[Fact]
public void ParseHosts_SkipsWildcardsAndComments_SplitsMultiAlias()
{
var text = """
# comment
Host *
User root
Host vps vps-alt
HostName 1.2.3.4
host gitea
Host *.internal
Match host foo
""";
Assert.Equal(["vps", "vps-alt", "gitea"], SshSettingsTabViewModel.ParseHosts(text));
}
[Fact]
public async Task LoadAddSave_RoundTripsRows()
{
var worker = new Worker { Stored = [new SshWorkspace("w1", "VPS", "vps", "/srv/app")] };
var vm = new SshSettingsTabViewModel(worker, () => "Host vps");
await vm.LoadAsync();
Assert.Single(vm.Workspaces);
Assert.Equal(["vps"], vm.Hosts);
vm.AddCommand.Execute(null);
vm.Workspaces[1].Name = "Box";
vm.Workspaces[1].Host = "box";
vm.Workspaces[1].RemotePath = "~/code";
Assert.Null(vm.Validate());
await vm.SaveAsync();
Assert.Equal(2, worker.Stored.Count);
Assert.Equal("w1", worker.Stored[0].Id); // existing id preserved
Assert.False(string.IsNullOrEmpty(worker.Stored[1].Id)); // new row got an id
}
[Fact]
public async Task Validate_ReportsInvalidRow_AndRemoveDropsIt()
{
var vm = new SshSettingsTabViewModel(new Worker(), () => "");
await vm.LoadAsync();
vm.AddCommand.Execute(null);
vm.Workspaces[0].Name = "Bad";
vm.Workspaces[0].Host = "-oProxyCommand=calc";
vm.Workspaces[0].RemotePath = "/x";
Assert.NotNull(vm.Validate());
vm.Workspaces[0].RemoveCommand.Execute(null);
Assert.Empty(vm.Workspaces);
Assert.Null(vm.Validate());
}
[Fact]
public async Task MissingSshConfig_YieldsNoHosts()
{
var vm = new SshSettingsTabViewModel(new Worker(), () => throw new FileNotFoundException());
await vm.LoadAsync();
Assert.Empty(vm.Hosts);
}
private sealed class OfflineWorker : StubWorkerClient
{
public bool SetCalled;
public override Task<List<SshWorkspace>> GetSshWorkspacesAsync() => throw new InvalidOperationException("offline");
public override Task SetSshWorkspacesAsync(List<SshWorkspace> workspaces) { SetCalled = true; return Task.CompletedTask; }
}
[Fact]
public async Task WorkerOffline_LoadDoesNotThrow_AndSaveDoesNotWipe()
{
var worker = new OfflineWorker();
var vm = new SshSettingsTabViewModel(worker, () => "");
await vm.LoadAsync();
await vm.SaveAsync();
Assert.False(worker.SetCalled);
}
}