using ClaudeDo.Worker.Config; using ClaudeDo.Worker.Tickets; namespace ClaudeDo.Worker.Tests.Tickets; public sealed class TicketSystemConfigTests : IDisposable { private readonly string _dir = Path.Combine(Path.GetTempPath(), "cdo-tcfg-" + Guid.NewGuid().ToString("N")); private TicketSystemConfig Make(string? baseUrl, string? pat) { Directory.CreateDirectory(_dir); var store = new DpapiTokenStore(Path.Combine(_dir, "ticket.pat")); if (pat is not null) store.Save(pat); var worker = new WorkerConfig { TicketApiBaseUrl = baseUrl }; return new TicketSystemConfig(worker, new TicketPatStore(store)); } [Fact] public void Not_configured_without_base_url() => Assert.False(Make(null, "tsp_x").IsConfigured); [Fact] public void Not_configured_without_pat() => Assert.False(Make("http://api.local", null).IsConfigured); [Fact] public void Configured_with_both() => Assert.True(Make("http://api.local", "tsp_x").IsConfigured); [Fact] public void Blank_base_url_counts_as_missing() => Assert.False(Make(" ", "tsp_x").IsConfigured); [Fact] public void Base_url_trailing_slash_is_trimmed() => Assert.Equal("http://api.local", Make("http://api.local/", "tsp_x").BaseUrl); // Ein blanker Hostname ist das, was man in ein Feld namens "API base URL" tippt. Ohne Schema // ist das eine RELATIVE URI, und HttpClient lehnt sie mit InvalidOperationException ab — // genau daran ist die erste Einrichtung gescheitert. [Theory] [InlineData("bandelapis.fb-tuning.local", "http://bandelapis.fb-tuning.local")] [InlineData("bandelapis.fb-tuning.local/", "http://bandelapis.fb-tuning.local")] [InlineData(" bandelapis.fb-tuning.local ", "http://bandelapis.fb-tuning.local")] [InlineData("api.local:8080", "http://api.local:8080")] public void Missing_scheme_defaults_to_http(string entered, string expected) => Assert.Equal(expected, Make(entered, "tsp_x").BaseUrl); [Theory] [InlineData("https://api.local")] [InlineData("http://api.local")] public void Explicit_scheme_is_left_alone(string entered) => Assert.Equal(entered, Make(entered, "tsp_x").BaseUrl); public void Dispose() { if (Directory.Exists(_dir)) Directory.Delete(_dir, recursive: true); } }