Commit graph

5 commits

Author SHA1 Message Date
0adc60d5ed simpler 2026-07-08 23:16:16 +01:00
e9df13a477 test(postit): pin blogs scope on bearer, fix post-refactor tests
Two intertwined jobs here:

1. Diagnostic test for the production 401 we see when PostIt
   talks to Yavsc.Blogs. The hypothesis this test isolates:
   the access token sent on the wire is missing the 'blogs'
   scope that Yavsc.Blogs' BlogScope policy requires (see
   Yavsc.Blogs/Program.cs: RequireClaim(JwtClaimTypes.Scope,
   "blogs")). The test fakes a single HttpMessageHandler,
   captures the outbound bearer, decodes the JWT, and asserts
   the 'scope' claim contains 'blogs'. It does not stand up a
   server, an OIDC stub, or any network listener. Result: the
   scope is present in the access_token we construct, so the
   401 is not on the client side — most likely the IdP at
   Yavsc.Org is not issuing 'blogs' as a recognised scope.

2. Mechanical fix of the three test files that broke during
   the Settings model refactor (PostIt.Settings ->
   PostIt.ViewModels.Settings; ApiUrl -> BusinessApiUrl;
   Scopes/RedirectUri moved under Authentication;
   DefaultDesktopRedirectUri is on AuthenticationSettings in
   the global namespace). Also restored the BaseAddress
   setup that BlogApiClient does in production in
   LoginAndPersistAsync / the reloaded-client path of
   YavscApiClientTests, so the two integration tests that
   call CallAsync("posts") directly don't trip on
   'request URI must be absolute or BaseAddress must be set'.

Test status: 45 / 45 passing in PostIt.Tests.
2026-07-08 19:36:40 +01:00
Lum
0617fc6bda postit: make Settings thread-safe and route PropertyChanged through UI dispatcher
The postit://callback re-launch crashed Avalonia inside
DataValidationErrors.SetErrors with 'The calling thread cannot
access this object because a different thread owns it'. Two
Settings instances raced on PropertyChanged: one was the DI
singleton registered by App.OnFrameworkInitializationCompleted,
the other was a freshly-constructed fallback in
LoginPageViewModel() and LoginPage.axaml.cs's DataContext-null
branch. Avalonia's binding sink caught the cross-thread
notification and crashed before the LoginPage could render.

Fix at three layers:

1. Settings: lock the mutation gate so concurrent Load() /
   ApplyJson() callers cannot tear reads; override
   OnPropertyChanged to marshal every notification onto the
   Avalonia UI thread via a new UiDispatcher helper (no more
   cross-thread SetErrors). Add BindToServiceProvider /
   RequireCurrent so production code paths cannot silently
   allocate a second instance.

2. LoginPageViewModel(): resolve the canonical Settings from
   the DI container (Settings.RequireCurrent) instead of
   new Settings(). The cross-thread crash is now caught loudly
   with a clear 'Settings.Current is not bound' error if
   something instantiates the VM outside a bound App.

3. HomePage.axaml.cs and LoginPage.axaml.cs: resolve the
   next view-model and BlogApiClient through App.Services
   instead of constructing them with 'new'. Same instance
   tree as the rest of the app; the postit://callback race
   disappears by construction.

MainPageViewModel and HomePageViewModel keep their existing
'?? new Settings()' fallback for test friendliness, but the
fallback is now harmless because Settings itself is
thread-safe.

Adds two regression tests in SettingsLoadTests covering
concurrent Load+mutate and concurrent idempotent Load.
2026-06-28 13:33:06 +01:00
f3a3b63595 WIP PostIt login 2026-06-25 00:08:25 +01:00
9e190e3d3d 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.
2026-06-21 03:03:56 +01:00