using System; using System.IO; using System.Text.Json; using StellaOps.Cli.Configuration; using Xunit; namespace StellaOps.Cli.Tests.Configuration; public sealed class CliBootstrapperTests : IDisposable { private readonly string _originalDirectory = Directory.GetCurrentDirectory(); private readonly string _tempDirectory = Path.Combine(Path.GetTempPath(), $"stellaops-cli-tests-{Guid.NewGuid():N}"); public CliBootstrapperTests() { Directory.CreateDirectory(_tempDirectory); Directory.SetCurrentDirectory(_tempDirectory); } [Fact] public void Build_UsesEnvironmentVariablesWhenPresent() { Environment.SetEnvironmentVariable("API_KEY", "env-key"); Environment.SetEnvironmentVariable("STELLAOPS_BACKEND_URL", "https://env-backend.example"); Environment.SetEnvironmentVariable("STELLAOPS_AUTHORITY_URL", "https://authority.env"); Environment.SetEnvironmentVariable("STELLAOPS_AUTHORITY_CLIENT_ID", "cli-env"); Environment.SetEnvironmentVariable("STELLAOPS_AUTHORITY_SCOPE", "concelier.jobs.trigger"); Environment.SetEnvironmentVariable("STELLAOPS_AUTHORITY_ENABLE_RETRIES", "false"); Environment.SetEnvironmentVariable("STELLAOPS_AUTHORITY_RETRY_DELAYS", "00:00:02,00:00:05"); Environment.SetEnvironmentVariable("STELLAOPS_AUTHORITY_ALLOW_OFFLINE_CACHE_FALLBACK", "false"); Environment.SetEnvironmentVariable("STELLAOPS_AUTHORITY_OFFLINE_CACHE_TOLERANCE", "00:20:00"); try { var (options, _) = CliBootstrapper.Build(Array.Empty()); Assert.Equal("env-key", options.ApiKey); Assert.Equal("https://env-backend.example", options.BackendUrl); Assert.Equal("https://authority.env", options.Authority.Url); Assert.Equal("cli-env", options.Authority.ClientId); Assert.Equal("concelier.jobs.trigger", options.Authority.Scope); Assert.NotNull(options.Authority.Resilience); Assert.False(options.Authority.Resilience.EnableRetries); Assert.Equal(new[] { TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(5) }, options.Authority.Resilience.RetryDelays); Assert.False(options.Authority.Resilience.AllowOfflineCacheFallback); Assert.Equal(TimeSpan.FromMinutes(20), options.Authority.Resilience.OfflineCacheTolerance); } finally { Environment.SetEnvironmentVariable("API_KEY", null); Environment.SetEnvironmentVariable("STELLAOPS_BACKEND_URL", null); Environment.SetEnvironmentVariable("STELLAOPS_AUTHORITY_URL", null); Environment.SetEnvironmentVariable("STELLAOPS_AUTHORITY_CLIENT_ID", null); Environment.SetEnvironmentVariable("STELLAOPS_AUTHORITY_SCOPE", null); Environment.SetEnvironmentVariable("STELLAOPS_AUTHORITY_ENABLE_RETRIES", null); Environment.SetEnvironmentVariable("STELLAOPS_AUTHORITY_RETRY_DELAYS", null); Environment.SetEnvironmentVariable("STELLAOPS_AUTHORITY_ALLOW_OFFLINE_CACHE_FALLBACK", null); Environment.SetEnvironmentVariable("STELLAOPS_AUTHORITY_OFFLINE_CACHE_TOLERANCE", null); } } [Fact] public void Build_FallsBackToAppSettings() { WriteAppSettings(new { StellaOps = new { ApiKey = "file-key", BackendUrl = "https://file-backend.example", Authority = new { Url = "https://authority.file", ClientId = "cli-file", Scope = "concelier.jobs.trigger" } } }); var (options, _) = CliBootstrapper.Build(Array.Empty()); Assert.Equal("file-key", options.ApiKey); Assert.Equal("https://file-backend.example", options.BackendUrl); Assert.Equal("https://authority.file", options.Authority.Url); Assert.Equal("cli-file", options.Authority.ClientId); } public void Dispose() { Directory.SetCurrentDirectory(_originalDirectory); if (Directory.Exists(_tempDirectory)) { try { Directory.Delete(_tempDirectory, recursive: true); } catch { // Ignored. } } } private static void WriteAppSettings(T payload) { var json = JsonSerializer.Serialize(payload, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText("appsettings.json", json); } }