From 9e190e3d3da5bafbd01718ad59046fa980572f71 Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Sun, 21 Jun 2026 03:03:56 +0100 Subject: [PATCH] postit: point Register button at /Account/Register /signin only renders the local-account sign-in form on Yavsc.Org; new accounts live at /Account/Register. Update RegisterUrl in LoginPageViewModel and the matching test accordingly. --- src/PostIt.Tests/LoginPageViewModelTests.cs | 2 +- src/PostIt.Tests/SettingsLoadTests.cs | 36 +++++++++++ src/PostIt/PostIt/PostIt.csproj | 5 ++ src/PostIt/PostIt/Settings/Settings.cs | 64 ++++++++++++++++--- .../PostIt/ViewModels/LoginPageViewModel.cs | 4 +- 5 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 src/PostIt.Tests/SettingsLoadTests.cs diff --git a/src/PostIt.Tests/LoginPageViewModelTests.cs b/src/PostIt.Tests/LoginPageViewModelTests.cs index 38f4c76e..abd0916e 100644 --- a/src/PostIt.Tests/LoginPageViewModelTests.cs +++ b/src/PostIt.Tests/LoginPageViewModelTests.cs @@ -60,7 +60,7 @@ public class LoginPageViewModelTests // Trailing slash on Authority is normalised away. Assert.Equal( - "https://yavsc.example.com/signin?ReturnUrl=~%2F&AllowRememberLogin=true", + "https://yavsc.example.com/Account/Register", vm.RegisterUrl); Assert.Equal( "https://yavsc.example.com/Account/ForgotPassword", diff --git a/src/PostIt.Tests/SettingsLoadTests.cs b/src/PostIt.Tests/SettingsLoadTests.cs new file mode 100644 index 00000000..7e9f396c --- /dev/null +++ b/src/PostIt.Tests/SettingsLoadTests.cs @@ -0,0 +1,36 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Xunit; + +namespace PostIt.Tests; + +public class SettingsLoadTests +{ + /// + /// On the dev machine, the user-level settings file + /// (~/.config/PostIt/postit-settings.json) does not exist, so Load() + /// must fall back to the embedded default resource shipped inside + /// PostIt.dll. + /// + [Fact] + public async Task Load_falls_back_to_embedded_resource_when_user_file_missing() + { + // Skip if a user-level file exists (CI / different dev machines). + var userConfigPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "PostIt", + "postit-settings.json"); + if (File.Exists(userConfigPath)) + { + return; // nothing to assert: user file wins. + } + + var settings = new PostIt.Settings(); + await settings.Load(); + + // The bundled postit-settings.json points at yavsc.pschneider.fr. + Assert.False(string.IsNullOrWhiteSpace(settings.Authentication?.Authority)); + Assert.Equal("postit", settings.Authentication.ClientId); + } +} \ No newline at end of file diff --git a/src/PostIt/PostIt/PostIt.csproj b/src/PostIt/PostIt/PostIt.csproj index 8cf9465c..13645f88 100644 --- a/src/PostIt/PostIt/PostIt.csproj +++ b/src/PostIt/PostIt/PostIt.csproj @@ -26,4 +26,9 @@ PreserveNewest + + + PostIt.postit-settings.json + + diff --git a/src/PostIt/PostIt/Settings/Settings.cs b/src/PostIt/PostIt/Settings/Settings.cs index b17c341e..1d8e59c2 100644 --- a/src/PostIt/PostIt/Settings/Settings.cs +++ b/src/PostIt/PostIt/Settings/Settings.cs @@ -1,3 +1,5 @@ +using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; using Avalonia; using Avalonia.Controls; using Avalonia.Platform.Storage; @@ -9,6 +11,8 @@ using System.IO; using System.Text.Json; using System.Threading.Tasks; +[assembly: InternalsVisibleTo("PostIt.Tests")] + namespace PostIt; public partial class Settings : ObservableObject @@ -87,7 +91,16 @@ public partial class Settings : ObservableObject if (!configFileInfo.Exists) { Console.Error.WriteLine($"🩎 Settings file not found at {configFileInfo.FullName}"); - return; // no settings file + // Only fall back to the embedded default when the in-memory + // settings haven't been populated yet. This protects callers + // (notably tests) that pre-load Settings with explicit values + // from being silently overwritten by the bundled default. + if (string.IsNullOrWhiteSpace(this.Authentication?.Authority) + && !TryLoadEmbeddedFallback()) + { + Console.Error.WriteLine("🩎 No embedded default settings; running with empty configuration."); + } + return; // no user settings file } Console.WriteLine($"🔎 Loading settings from {configFileInfo.FullName}"); @@ -97,17 +110,49 @@ public partial class Settings : ObservableObject using var stream = configFileInfo.OpenRead(); using var reader = new StreamReader(stream); var json = await reader.ReadToEndAsync(); - if (string.IsNullOrWhiteSpace(json)) - { - Console.Error.WriteLine("🩎 Settings file is empty."); - return; - } + ApplyJson(json, $"user file {configFileInfo.FullName}"); + } + catch (Exception ex) + { + Console.Error.WriteLine($"🩎 Error loading settings: {ex.Message}"); + } + } + private bool TryLoadEmbeddedFallback() + { + const string ResourceName = "PostIt.postit-settings.json"; + var assembly = typeof(Settings).Assembly; + using var stream = assembly.GetManifestResourceStream(ResourceName); + if (stream is null) + { + Console.Error.WriteLine($"🩎 Embedded resource {ResourceName} not found."); + return false; + } + using var reader = new StreamReader(stream); + var json = reader.ReadToEnd(); + if (string.IsNullOrWhiteSpace(json)) + { + Console.Error.WriteLine("🩎 Embedded settings resource is empty."); + return false; + } + Console.WriteLine($"🔎 Loading embedded default settings ({ResourceName})."); + ApplyJson(json, $"embedded resource {ResourceName}"); + return true; + } + + private void ApplyJson(string json, string source) + { + if (string.IsNullOrWhiteSpace(json)) + { + Console.Error.WriteLine($"🩎 Settings payload is empty (source: {source})."); + return; + } + try + { var settings = JsonSerializer.Deserialize(json); - if (settings is null) { - Console.Error.WriteLine("🩎 Settings file is invalid."); + Console.Error.WriteLine($"🩎 Settings payload is invalid (source: {source})."); return; } this.Authentication = settings.Authentication; @@ -115,11 +160,10 @@ public partial class Settings : ObservableObject this.ApiUrl = settings.ApiUrl; this.RedirectUri = string.IsNullOrWhiteSpace(settings.RedirectUri) ? DefaultLoopbackRedirectUri : settings.RedirectUri; this.Scopes = settings.Scopes; - } catch (Exception ex) { - Console.Error.WriteLine($"🩎 Error loading settings: {ex.Message}"); + Console.Error.WriteLine($"🩎 Error applying settings from {source}: {ex.Message}"); } } } \ No newline at end of file diff --git a/src/PostIt/PostIt/ViewModels/LoginPageViewModel.cs b/src/PostIt/PostIt/ViewModels/LoginPageViewModel.cs index 6a22cbe7..70877733 100644 --- a/src/PostIt/PostIt/ViewModels/LoginPageViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/LoginPageViewModel.cs @@ -13,12 +13,12 @@ public partial class LoginPageViewModel : ViewModelBase public string Password { get; set; } /// - /// URL of the Yavsc.Org register/sign-in page for new users. + /// URL of the Yavsc.Org account-registration page. /// Derived from 's Authority. /// Empty when the authority is not configured. /// public string RegisterUrl => - BuildExternalUrl("/signin?ReturnUrl=~%2F&AllowRememberLogin=true"); + BuildExternalUrl("/Account/Register"); /// /// URL of the Yavsc.Org password-reset page (open to anonymous users).