From 506dd9241f910e0ef68c61bdf0e66f38398b1661 Mon Sep 17 00:00:00 2001 From: Stone_Red <56473591+Stone-Red-Code@users.noreply.github.com> Date: Tue, 3 Nov 2026 21:37:52 +0100 Subject: [PATCH] fix: save refreshed tokens for server URLs with a trailing slash --- src/EchoHub.Client/AppOrchestrator.cs | 3 +- src/EchoHub.Client/Config/ClientConfig.cs | 8 +++++ .../Services/ConnectionManager.cs | 8 ++--- src/EchoHub.Tests/ClientConfigTests.cs | 30 +++++++++++++++++++ 4 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 src/EchoHub.Tests/ClientConfigTests.cs diff --git a/src/EchoHub.Client/AppOrchestrator.cs b/src/EchoHub.Client/AppOrchestrator.cs index 7769d0a..20d5e9e 100644 --- a/src/EchoHub.Client/AppOrchestrator.cs +++ b/src/EchoHub.Client/AppOrchestrator.cs @@ -2232,8 +2232,7 @@ public sealed class AppOrchestrator : IDisposable if (url is null) return; var config = ConfigManager.Load(); - var server = config.SavedServers.FirstOrDefault(s => - string.Equals(s.Url, url, StringComparison.OrdinalIgnoreCase)); + var server = config.FindServer(url); if (server is null) return; mutate(server); diff --git a/src/EchoHub.Client/Config/ClientConfig.cs b/src/EchoHub.Client/Config/ClientConfig.cs index 6fa2d91..0a69ab9 100644 --- a/src/EchoHub.Client/Config/ClientConfig.cs +++ b/src/EchoHub.Client/Config/ClientConfig.cs @@ -18,6 +18,14 @@ public class ClientConfig /// Applies to copy-paste/drag-drop attachments, which have no per-file size flag. /// public string DefaultAsciiSize { get; set; } = "m"; + + /// + /// Finds a saved server by URL. A trailing slash is ignored: entries keep the URL as the + /// user typed it, while has the slash removed. + /// + public SavedServer? FindServer(string url) => + SavedServers.FirstOrDefault(s => + string.Equals(s.Url.TrimEnd('/'), url.TrimEnd('/'), StringComparison.OrdinalIgnoreCase)); } public class NotificationConfig diff --git a/src/EchoHub.Client/Services/ConnectionManager.cs b/src/EchoHub.Client/Services/ConnectionManager.cs index 40cd701..b4f5004 100644 --- a/src/EchoHub.Client/Services/ConnectionManager.cs +++ b/src/EchoHub.Client/Services/ConnectionManager.cs @@ -152,7 +152,7 @@ internal sealed class ConnectionManager : IAsyncDisposable // all of them, not just channels opened this session. Channels the user left // with /leave stay out until rejoined; protected channels we can't enter // silently (no cached membership) are skipped, never prompted for. - var leftChannels = FindServer(ConfigManager.Load(), info.ServerUrl)?.LeftChannels ?? []; + var leftChannels = ConfigManager.Load().FindServer(info.ServerUrl)?.LeftChannels ?? []; foreach (var channel in channels) { if (channel.Name.Equals(HubConstants.DefaultChannel, StringComparison.OrdinalIgnoreCase)) @@ -332,7 +332,7 @@ internal sealed class ConnectionManager : IAsyncDisposable { if (_apiClient?.RefreshToken is null) return; var config = ConfigManager.Load(); - var server = FindServer(config, _apiClient.BaseUrl); + var server = config.FindServer(_apiClient.BaseUrl); if (server is not null && server.RememberMe) { server.RefreshToken = _apiClient.RefreshToken; @@ -340,10 +340,6 @@ internal sealed class ConnectionManager : IAsyncDisposable } } - private static SavedServer? FindServer(ClientConfig config, string url) => - config.SavedServers.FirstOrDefault(s => - string.Equals(s.Url, url, StringComparison.OrdinalIgnoreCase)); - // ── Dispose ─────────────────────────────────────────────────────────── public async ValueTask DisposeAsync() diff --git a/src/EchoHub.Tests/ClientConfigTests.cs b/src/EchoHub.Tests/ClientConfigTests.cs new file mode 100644 index 0000000..a3bac43 --- /dev/null +++ b/src/EchoHub.Tests/ClientConfigTests.cs @@ -0,0 +1,30 @@ +using EchoHub.Client.Config; +using Xunit; + +namespace EchoHub.Tests; + +public class ClientConfigTests +{ + [Theory] + [InlineData("https://echo.example.com/", "https://echo.example.com")] + [InlineData("https://echo.example.com", "https://echo.example.com/")] + [InlineData("https://echo.example.com", "https://echo.example.com")] + [InlineData("https://ECHO.example.com/", "https://echo.example.com")] + public void FindServer_IgnoresTrailingSlashAndCase(string savedUrl, string lookupUrl) + { + var config = new ClientConfig(); + var server = new SavedServer { Name = "echo", Url = savedUrl }; + config.SavedServers.Add(server); + + Assert.Same(server, config.FindServer(lookupUrl)); + } + + [Fact] + public void FindServer_OtherServer_ReturnsNull() + { + var config = new ClientConfig(); + config.SavedServers.Add(new SavedServer { Name = "echo", Url = "https://echo.example.com/" }); + + Assert.Null(config.FindServer("https://other.example.com")); + } +}