diff --git a/tests/Vantage.Core.Tests/Configuration/JsonConfigStoreTests.cs b/tests/Vantage.Core.Tests/Configuration/JsonConfigStoreTests.cs new file mode 100644 index 0000000..aef5891 --- /dev/null +++ b/tests/Vantage.Core.Tests/Configuration/JsonConfigStoreTests.cs @@ -0,0 +1,63 @@ +using Vantage.Core.Configuration; +using Vantage.Core.Models; +using Xunit; + +namespace Vantage.Core.Tests.Configuration; + +public class JsonConfigStoreTests : IDisposable +{ + private readonly string tempDir = Path.Combine(Path.GetTempPath(), "vantage-tests-" + Guid.NewGuid().ToString("N")); + + private string ConfigPath => Path.Combine(tempDir, "config.json"); + + [Fact] + public async Task LoadAsync_MissingFileReturnsEmptyConfig() + { + var store = new JsonConfigStore(ConfigPath); + var config = await store.LoadAsync(); + + Assert.Empty(config.Hosts); + Assert.Empty(config.HealthChecks); + } + + [Fact] + public async Task SaveThenLoad_RoundTripsHostsAndChecks() + { + var store = new JsonConfigStore(ConfigPath); + var original = new VantageConfig + { + RefreshIntervalSeconds = 9, + Hosts = { new HostConfig { Name = "edge", Address = "10.0.0.1", Username = "root", AuthMethod = SshAuthMethod.PrivateKey } }, + HealthChecks = { new HealthCheckConfig { Name = "web", Kind = HealthKind.Http, Target = "https://x" } } + }; + + await store.SaveAsync(original); + var loaded = await store.LoadAsync(); + + Assert.Equal(9, loaded.RefreshIntervalSeconds); + Assert.Single(loaded.Hosts); + Assert.Equal("edge", loaded.Hosts[0].Name); + Assert.Equal(SshAuthMethod.PrivateKey, loaded.Hosts[0].AuthMethod); + Assert.Equal(HealthKind.Http, loaded.HealthChecks[0].Kind); + } + + [Fact] + public async Task SaveAsync_WritesEnumsAsStrings() + { + var store = new JsonConfigStore(ConfigPath); + await store.SaveAsync(new VantageConfig + { + Hosts = { new HostConfig { AuthMethod = SshAuthMethod.PrivateKey } } + }); + + var json = await File.ReadAllTextAsync(ConfigPath); + Assert.Contains("privateKey", json); + Assert.DoesNotContain("\"authMethod\": 0", json); + } + + public void Dispose() + { + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, recursive: true); + } +} diff --git a/tests/Vantage.Core.Tests/Docker/DockerTests.cs b/tests/Vantage.Core.Tests/Docker/DockerTests.cs new file mode 100644 index 0000000..230e342 --- /dev/null +++ b/tests/Vantage.Core.Tests/Docker/DockerTests.cs @@ -0,0 +1,68 @@ +using Vantage.Core.Docker; +using Vantage.Core.Models; +using Vantage.Core.Tests.Fakes; +using Xunit; + +namespace Vantage.Core.Tests.Docker; + +public class DockerTests +{ + private const string PsOutput = + "c1a2\tcaddy\tcaddy:2\trunning\tUp 6 days\n" + + "9f8e\tbackup\talpine:3.20\texited\tExited (0) 3 hours ago\n" + + "aa11\tpaused-svc\tredis:7\tpaused\tUp 2 hours (Paused)\n"; + + [Fact] + public void Parser_MapsFieldsAndStates() + { + var containers = DockerContainerParser.Parse(PsOutput); + + Assert.Equal(3, containers.Count); + Assert.Equal("caddy", containers[0].Name); + Assert.Equal(ContainerState.Running, containers[0].State); + Assert.True(containers[0].IsRunning); + Assert.Equal(ContainerState.Exited, containers[1].State); + Assert.Equal(ContainerState.Paused, containers[2].State); + } + + [Fact] + public async Task SshDockerProvider_ListParsesRunnerOutput() + { + var runner = new FakeSshCommandRunner(PsOutput); + var provider = new SshDockerProvider(runner); + + var containers = await provider.ListContainersAsync(new HostConfig(), CancellationToken.None); + + Assert.Equal(3, containers.Count); + Assert.Contains("docker ps -a", runner.Commands.Single()); + } + + [Fact] + public async Task SshDockerProvider_ExecuteBuildsRestartCommand() + { + var runner = new FakeSshCommandRunner(_ => ""); + var provider = new SshDockerProvider(runner); + + await provider.ExecuteAsync(new HostConfig(), DockerAction.Restart, "abc123", CancellationToken.None); + + Assert.Equal("docker restart 'abc123'", runner.Commands.Single()); + } + + [Fact] + public async Task MockDockerProvider_StopThenStartTransitionsState() + { + var provider = new MockDockerProvider(); + var host = new HostConfig { Id = "h1" }; + + var initial = await provider.ListContainersAsync(host, CancellationToken.None); + var running = initial.First(c => c.IsRunning); + + await provider.ExecuteAsync(host, DockerAction.Stop, running.Id, CancellationToken.None); + var afterStop = await provider.ListContainersAsync(host, CancellationToken.None); + Assert.Equal(ContainerState.Exited, afterStop.First(c => c.Id == running.Id).State); + + await provider.ExecuteAsync(host, DockerAction.Start, running.Id, CancellationToken.None); + var afterStart = await provider.ListContainersAsync(host, CancellationToken.None); + Assert.Equal(ContainerState.Running, afterStart.First(c => c.Id == running.Id).State); + } +}