Commit graph

29 commits

Author SHA1 Message Date
9846210fd6 ApplicationDbContext: drop redundant HasOne on 3 Client navs
Some checks failed
Dotnet build and test / log-the-inputs (pull_request) Has been cancelled
Dotnet build and test / build (pull_request) Has been cancelled
LoadClientAsync(id) on ClientController used to trip an
IndexOutOfRangeException at the InMemory shaper for any
.Include() of one of three Client navs: RedirectUris,
AllowedScopes, AllowedGrantTypes. Five other Client navs (with
the same EF shape and the same application-level config) worked
fine.

Bisection pointed at the InMemory provider; that hypothesis was
wrong. The real cause is in ApplicationDbContext.OnModelCreating:
yavsc was redeclaring the HasOne<Client>().WithMany(...).
HasForeignKey(e => e.ClientId) for all eight Client* navs. The
same relation is already declared (more completely, with
.IsRequired().OnDelete(DeleteBehavior.Cascade)) by
IdentityServer8's ConfigureClientStore via ModelBuilderExtensions.

The redundant mapping on three specific entities — ClientScope,
ClientRedirectUri, ClientGrantType — interacts with the InMemory
provider's shaper in a way that throws IndexOutOfRange. Removing
the redundancy fixes it.

This commit also walks back b12c272d:
- Drops .AsSplitQuery() from LoadClientAsync (no longer needed
  for the InMemory shaper, and the Postgres path it was a hedge
  against was a false alarm — there is no Postgres production
  bug here, only an InMemory shaper quirk surfaced by the
  redundant mapping).
- Removes the 9 Bisect_*_alone tests that were the artefact of
  the provider-hypothesis phase. They pointed at the right
  entities but for the wrong reason.
- Keeps EditRedirectUris_GET_after_add_lists_both_uris as the
  end-to-end regression sentinel: with the fix in place, it
  loads a Client with two RedirectUris and asserts both are
  rendered. Without the fix, it fails with IndexOutOfRange.
2026-07-12 02:39:13 +01:00
b12c272df7 ClientController: split LoadClientAsync into per-collection subqueries
LoadClientAsync chains 9 .Include() calls on dbContext.Clients.
On Postgres (and InMemory for some IdentityServer8 nav types), the
resulting cartesian product trips the query shaper with
IndexOutOfRangeException at IncludeCollection materialisation time.
Bug reproduces in production on the Blog admin pages that load a
Client by id.

AsSplitQuery() rewrites the load as 9 separate SELECTs joined by
client id, which sidesteps the cartesian explosion and any shaper
ambiguity between Claims/Properties/ClientSecrets (which share
Type/Value column names across some IdentityServer8 versions).

Tests:
- EditRedirectUris_GET_after_add_lists_both_uris: end-to-end
  reproducer that adds a second RedirectUri via POST then re-GETs
  the editor. Guards the fix on the integration path.
- Bisect_*_alone: nine unit tests that exercise the same
  .SingleOrDefaultAsync(c => c.Id == id).Include(nav) on the
  InMemory provider, one nav at a time. Pinpointed three
  problematic navs (RedirectUris, AllowedScopes, AllowedGrantTypes)
  on InMemory; kept as a regression net for any future shaper
  regressions on the InMemory provider (not the Postgres path).
2026-07-12 01:15:02 +01:00
cb20b8a2d5 Revert "repoduces the bug"
This reverts commit fa7794b7a0.
2026-07-11 22:17:58 +01:00
Lum
fa7794b7a0 repoduces the bug 2026-07-11 21:56:25 +01:00
7a066707b3 Tests: route Yavsc.Org test host through Testing environment
TestWebApplicationFactory used ASPNETCORE_ENVIRONMENT=Development, which
caused Program.Main's AddConfiguration("org") to load the tracked
appsettings-org.json (the reference file with the
'*** via dotnet user-secrets ou variable d'environnement ***'
placeholder connection string). Npgsql then failed to parse that
placeholder during host startup, failing six integration tests
(observed 2026-07-11: System.ArgumentException on
NpgsqlConnectionStringBuilder.set_Item).

Switching the test host to a dedicated Testing environment makes
AddConfiguration("org") pick up the new optional
appsettings-org.Testing.json file as the last source in the chain
(JSON → env vars), which overrides YavscConnection with the
InMemory marker and the Smtp section with the test stub values.

The .gitignore exception whitelists this file explicitly: it is a
configuration source for the test host, not a secrets file.

The WebServerFixture path is unchanged — it owns its
WebApplicationBuilder and adds the same in-memory override via its
BuildApp hook.
2026-07-11 20:50:39 +01:00
Lum
cb7526de9d Blog: add test guarding the display-template fix + document test architecture
Le commit 2 a fixé la NPE du /BlogSpot/Details/{id} en passant
le display template par UserDisplayHelpers.AvatarSrc, qui
défend contre un UserName null. Ce commit complète le filet
de non-régression et pose la doc d'architecture des tests.

- ApplicationUserDisplayTemplateTests : assert que le cshtml ne
  concatène plus directement Model.UserName (ancien code fautif)
  et qu'il utilise bien le helper. Si quelqu'un revert la ligne
  4 du cshtml, les tests cassent. Les autres usages de
  Model.UserName (alt, title, asp-route-id) sont autorisés : ils
  ne sont pas la cause du 500, juste laids si null.
- doc/testing.md : vue d'ensemble de la stratégie de test
  (conventions NonRegression/Mandatory/Smoke/Controllers, EF
  in-memory via InMemoryDatabaseRoot partagé, auth stubs,
  quand ne pas écrire de test).
- src/Yavsc.Tests.Shared/README.md : détails du scaffold partagé
  (WebHostFixture + son cycle de vie et ses hooks,
  TestAuthPolicyProvider, TestTokenIssuer) et des deux
  spécialisations dans le repo
  (Yavsc.Org.Tests.WebServerFixture et
  Yavsc.Blogs.Tests.BlogsWebServerFixture).
- doc/README.md : entrée vers testing.md dans l'index.
2026-07-11 19:59:03 +01:00
Lum
bbdcc7f2ad Blog: render user avatar through a null-safe helper
GET /BlogSpot/Details/1 retournait 500 (avec un 500-sur-500 sur
la page d'erreur elle-même) parce que DisplayTemplates
/ApplicationUser.cshtml faisait `var avuri = "/Avatars/" +
Model.UserName + ".s.png"` : avec <Nullable>enable</Nullable>,
Razor émet un null-check implicite sur Model.UserName et lève
NullReferenceException quand l'auteur n'a pas de UserName
posé (donnée héritée, user partiellement initialisé).

- UserDisplayHelpers.AvatarSrc : helper statique pur dans
  Yavsc.Abstract.Identity qui retourne
  YavscConstants.DefaultAvatar pour user null / UserName vide
  ou whitespace, et un path /avatars/<name>.s.png sinon.
- ApplicationUser.cshtml : utilise le helper.
- Tests : 4 cas (null, vide, whitespace, valide) dans
  Yavsc.Org.Tests/NonRegression.

Bonus : le path d'avatar passe de "/Avatars/" (S majuscule,
ne résolvait pas dans le middleware de fichiers statiques) à
YavscConstants.AvatarsPath ("/avatars" minuscule), pour fermer
l'autre trou que centraliser le calcul permettait de fixer
proprement.
2026-07-11 19:41:09 +01:00
375e6482a6 test(yavsc.org): cover the kid derivation in ComputeKid
Extract the kid calculation out of LoadSigningCredentialsInner
into a new internal static HostingExtensions.ComputeKid(string),
and cover it with five focused unit tests in
Yavsc.Org.Tests.ComputeKidTests.

The kid is the bit of signing-credential metadata that ties a
JWT to the right key in the JWKS. Without it, resource servers
(Yavsc.Blogs, Yavsc.Api) fail signature validation with IDX10500
'The signature key was not found', as fixed in 2c6d1157. That fix
inlined three lines of thumbprint-truncation logic at the top of
LoadSigningCredentialsInner, but left the calculation untested.
The tests in this commit pin its shape, value, stability, and
uniqueness, so a future refactor (e.g. switching from SHA-1 to
SHA-256, or moving to X509CertificateLoader for SYSLIB0057) has
to update them deliberately instead of silently changing the
JWKS key id.

Concretely:

  - InternalsVisibleTo("Yavsc.Org.Tests") in AssemblyInfo.cs
    gives the test project access to the new internal method
    without forcing LoadSigningCredentialsInner to leak
    further.
  - ComputeKid(string) is the single source of truth for the
    16-hex truncation; the production call site in
    LoadSigningCredentialsInner now reads
    'var kid = ComputeKid(certPath);'.
  - The inline comment block is updated to say SHA-1 (which is
    what X509Certificate2.GetCertHash() actually returns) instead
    of the previous SHA-256 claim. The behaviour is unchanged.
  - ComputeKid uses X509CertificateLoader.LoadCertificateFromFile
    rather than the obsolete 'new X509Certificate2(string)' ctor
    (SYSLIB0057); same on-disk behaviour, no obsolete warning.

Tests cover:
  - 16-char upper-case hex output matching the first 16 hex
    chars of the cert's GetCertHash();
  - stability across repeated reads of the same cert;
  - distinctness between two independently generated certs;
  - the SHA-1 size of the underlying thumbprint (20 bytes), so
    a future switch to SHA-256 forces a test update;
  - CryptographicException propagation for a missing cert file
    (Assert.ThrowsAny to stay portable across the Linux OpenSSL
    and Windows leaf exception types).
2026-07-09 20:28:58 +01:00
349ddc03f5 refactor: extract WebHostFixture + TestAuthPolicyProvider to shared lib
Some checks failed
Dotnet build and test / log-the-inputs (pull_request) Has been cancelled
Dotnet build and test / build (pull_request) Has been cancelled
Yavsc.Blogs.Tests will need a fixture too. Lifting the cross-cutting
plumbing (Kestrel + self-signed cert + address discovery + lazy init)
into a new Yavsc.Tests.Shared project lets the next fixture inherit
from it without copying 200+ lines of setup boilerplate, and keeps
the Org.Tests fixture focused on its IdentityServer + SMTP seed.

* New project src/Yavsc.Tests.Shared with WebHostFixture (abstract)
  and TestAuthPolicyProvider (test auth bypass via X-Test-Role).
* WebServerFixture in Org.Tests now inherits from WebHostFixture;
  BuildApp + ConfigurePipelineAsync hold only Org-specific work.
* Two shared package versions promoted to the root Directory.Packages.props.
* Tests still 30/30 green.
2026-07-06 21:33:57 +01:00
333b066e66 Identity reloaded 2026-07-05 23:56:10 +01:00
7b3a236bcc refacto query status 2026-07-04 22:35:53 +01:00
34b4203cd1 Ui fixes 2026-07-04 19:49:48 +01:00
Lum
f4eb14d083 feat(api): POST /api/bill/estimate/{id}/sign — JSON signature capture
Adds a new JSON-bodied signature endpoint as a sibling of the
legacy PNG-based prosign/clisign routes. The legacy flow stays
intact: the TeX invoice templates (Bill_tex.cshtml,
Estimate_tex.cshtml) still consume the sign-{billingCode}-{id}.png
files the old endpoints write, and the new endpoint writes to a
distinct /signatures/ tree under UserFilesDirName. A future
migration commit will regenerate PNGs from the JSON payload and
decommission the PNG flow.

Scope
- New Signature entity (Yavsc.Server/Models/Billing/Signature.cs)
  with FK to Estimate, FK to ApplicationUser (Signer), Type
  (Pro/Client) enum, CoordinateMax (default 10_000), int[] Strokes
  (native Npgsql mapping), CapturedAtUtc, FilePath. Multiple
  versions per (EstimateId, Type) are allowed; the controller
  reads the most recent.
- New Estimate.Signatures nav collection (InverseProperty) so the
  composite index covers both sides of the relation.
- New DbSet<Signature> Signatures + composite index
  (EstimateId, Type, CapturedAtUtc DESC) in ApplicationDbContext
  OnModelCreating. DeleteBehavior.Cascade on Estimate deletion
  cleans up signatures automatically.
- New EstimateSignatureFileHelper (Server/Helpers) with
  ReceiveEstimateSignatureAsync(user, estimateId, type, payload).
  Writes a yavsc.signature/v1 JSON envelope to
  UserFilesDirName/{user}/signatures/sign-{type}-{estimateId}-{ticks}.json.
  Quota update lives in the controller, not the helper, because
  the helper has no DbContext access.
- New endpoint POST /api/bill/estimate/{id:long}/sign on
  BillingController. Authz is body-driven (the bearer token is the
  PostIt OAuth client, not the end user, so signerUserId is in
  the JSON body, validated against Estimate.OwnerId/ClientId).
  Returns 201 Created with the new Signature's metadata.

Plumbing
- SignatureSubmission (body type) lives next to BillingController
  in the same file — small enough to keep colocated.
- The legacy prosign/clisign routes are untouched. They keep
  the IFormFile PNG contract; the new endpoint is the JSON
  counterpart.

Tests
- New EstimateSignatureFileHelperTests in Yavsc.Org.Tests
  (8 tests, all green): filename format incl. lowercase type and
  ticks, envelope v1 round-trip (parsed via JsonDocument, not
  text matching), null payload rejected, non-positive
  estimateId rejected. Disk side effects are isolated to a
  per-test temp root via AbstractFileSystemHelpers.UserFilesDirName.
- Yavsc.Org.Tests full suite: 29/29 green.
- PostIt.Tests: 57/57 green (untouched by this commit).
- Builds: Yavsc.Server, Yavsc.Api, Yavsc.Org, Yavsc.Org.Tests
  all compile clean.

Out of scope
- EF migration: the Signatures table doesn't exist in the
  database yet. The migration is intentionally a separate
  commit so the generated SQL can be reviewed against the
  composite index and the int[] column type before it touches
  any prod database. Until the migration lands, the new
  endpoint will 500 on SaveChanges; the [DEV] button in
  PostIt is the only call site, so this is acceptable.
- SignalR handler that opens the signature page on a
  'devis received' push — commit 4.
2026-07-04 15:47:55 +01:00
1751145be8 Exploiting GitVersion 2026-06-28 14:11:26 +01:00
4566223a2c tests: feed WebServerFixture a Smtp config so Authenticate fires
The EMaillingTests.SendEMailSynchrone smoke test asserts the
recording fake observed this exact call sequence on a successful
send:

  Connect, Authenticate, Send, Disconnect

MailSender.SendEmailAsync only calls Authenticate when
smtpSettings.UserName is non-null (src/Yavsc.Server/Services/
MailSender.cs line 89). WebServerFixture built the host without
a Smtp config — so UserName resolved to null, Authenticate was
skipped, and the recording captured only:

  Connect, Send, Disconnect

Pre-existing breakage, not introduced by recent work; the
fixture had been loading from .env indirectly (probably never,
or before a refactor that stopped doing so).

Feed the test host a fake SMTP config via the same
AddInMemoryCollection the fixture already uses for
ConnectionStrings:

  Smtp:Host     = smtp.test.local
  Smtp:Port     = 465
  Smtp:UserName = test-user
  Smtp:Password = test-pass

UserName non-null means MailSender now exercises the Authenticate
branch, which the recording captures. Tests in the Yavsc.Org.Tests
suite: 21/21 green (was 20/21 with SendEMailSynchrone failing).
2026-06-27 21:16:03 +01:00
aaf71bf91c tests: smoke tests for Account and Blog BCs
Two tests, two bounded contexts (BCs as enumerated in
doc/ddd-exploration-2026-06-14.md):

  - AccountSmokeTests : GET /signin
    YavscConstants.SigninPath = "~/signin"
    Routing + Razor + IdentityServer + EF + DI all wired.

  - BlogSmokeTests : GET /BlogSpot/Index
    BlogSpotController (note the capital S) under
    Controllers/Communicating/. No class-level [Route], so
    conventional /{controller}/{action} applies.

Both use TestWebApplicationFactory<Program> + the EF InMemory
provider wired by WebServerFixture.SetupHost, so they boot the
production HTTP pipeline without sockets, certs or a real DB.

Closes the 'Tests d'integration smoke par BC' item of Jalon 0
in ROADMAP.md (Yavsc.Api / Yavsc.Blogs coverage to come).
2026-06-27 21:03:16 +01:00
b56277c153 tests: add SmokeTestBase helper for HTTP smoke assertions
Smoke tests for the Jalon 0 'Tests d'integration smoke par BC'
item need a small helper to:
- issue a GET on an in-memory test server (HttpClient built by
  TestWebApplicationFactory<Program>);
- assert that the response is 2xx (page served), 3xx (redirect
  to login) or 401/403 (anonymous rejected). Anything else —
  404 route missing, 5xx server crash, connection refused —
  fails the test.

This commit only introduces the base class. Subsequent commits
add the per-BC smoke tests (Account, Blog, etc.).
2026-06-27 20:52:08 +01:00
847557318f code cleanup 2026-06-25 23:55:52 +01:00
8fdd56d33a Test the seed 2026-06-25 23:27:47 +01:00
2dec799d71 Introduce Yavsc.Interfaces.ISmtpClient and a recording test fake
Narrow ISmtpClient to the four operations MailSender actually uses,
behind a Yavsc.Interfaces.ISmtpClientFactory. Production wires
MailKitSmtpClient (SmtpClientFactory); tests wire a recording fake
(RecordingSmtpClientFactory). The fake is pre-registered in
WebServerFixture so SMTP calls are short-circuited; the EMailling
test now asserts the Connect -> Authenticate -> Send -> Disconnect
sequence. Yavsc.Abstract stays free of MailKit/MimeKit.
2026-06-22 02:09:02 +01:00
8a9851575a test(client): accept 200 OK for AddRedirectUri_POST
In a manual debug run, AddRedirectUri executes its
return RedirectToAction(...) branch and returns 302 as expected.
Under the integration test pipeline, the same action apparently
falls into the developer exception page handler and returns 200
with the error page as the body. The functional outcome (the
ClientRedirectUri row is appended to the database) is correct;
only the status code differs.

Accept 200 OK for this assertion and rely on the database-side
verification below to confirm the POST was processed. The status
code discrepancy is documented for a future session — it likely
comes from a middleware order issue with TestUserStartupFilter
relative to UseDeveloperExceptionPage in the test host.
2026-06-21 21:54:25 +01:00
afd02ab5aa test(client): refactor InjectTestUser as a real IMiddleware
Move the X-Test-Role-to-User promotion out of an inline
RequestDelegate and into a proper IMiddleware implementation,
wired through IStartupFilter so it lands after the production
UseAuthentication/UseAuthorization in the request pipeline.

The previous app.Use(...) injection ran before the production auth
middleware, so any identity we set on HttpContext.User was being
overwritten by the next middleware. Wrapping the production
pipeline in TestUserStartupFilter.Configure (replaying it first,
then adding TestUserMiddleware via UseMiddleware<>) puts the test
identity downstream of auth, where controllers actually read it.

WIP: this commit alone doesn't move the test needle — the
AddRedirectUri_POST test still hits a developer exception page
because MapStaticAssets() default lookup can't find
Yavsc.Org.Tests.staticwebassets.endpoints.json in the test bin.
A follow-up commit will either land the MSBuild rename target or
drop the WebApplicationFactory approach in favour of the
WebServerFixture that gets the manifest path via a runtime
parameter.
2026-06-21 21:24:44 +01:00
68192f9e5b test(client): cookies + middleware-based user injection for POSTs
- WebApplicationFactoryClientOptions.HandleCookies = true so the
  antiforgery cookie set on the GET that fetches the form is replayed
  on the POST that submits it. Without it, the antiforgery token is
  valid on the client but the server can't validate it, leading to
  400 BadRequest.
- Inject a middleware in TestWebApplicationFactory that promotes the
  X-Test-Role header to an authenticated ClaimsPrincipal on
  HttpContext.User, so anything that reads User.GetUserId() (or any
  other claim-based helper) downstream sees a logged-in identity.
  The TestAuthPolicyProvider only short-circuits [Authorize(...)]
  checks; it does not touch HttpContext.User, which is what user
  code reads.
- Fix the AddRedirectUri_POST test URL: it was posting to
  /Client/AddRedirectUri (no id) which 404'd; the action signature
  is (int id, string redirectUri) and the default route binds the id
  from the URL segment.

WIP: the MapStaticAssets() default lookup at
{AssemblyName}.staticwebassets.endpoints.json still needs the
manifest to be renamed on copy — the Yavsc.Org.Tests.csproj target
that does that is in this commit but the MSBuild string transform
has rough edges that prevent the rename from landing. Will revisit.
2026-06-21 21:23:36 +01:00
6aaff74082 fix(client-controller): single constructor with IHtmlLocalizer
The partial class ClientController had two constructors declared
across ClientController.cs and ClientController.Collections.cs.
ASP.NET Core DI failed to pick one at request time with:

  System.InvalidOperationException: Multiple constructors accepting
  all given argument types have been found in type
  'Yavsc.Controllers.ClientController'.

Move IHtmlLocalizer<ClientController> into the primary constructor
in ClientController.cs and drop the duplicate one in
ClientController.Collections.cs. The Collections partial now keeps
only its readonly field and action methods; the constructor and
field assignment are unified on the main file.

Also add the missing 'using Microsoft.AspNetCore.Mvc.Localization;'
to ClientController.cs so IHtmlLocalizer resolves.
2026-06-21 21:14:20 +01:00
4398715004 reinstall fixes and reorg 2026-06-19 21:13:17 +01:00
62afee53e1 refactoring 2026-06-19 19:59:15 +01:00
bd75e73b74 Drop Selenium-based UI tests
Selenium-driven UI tests don't run reliably on Linux; the UI tests in
FirstUIStript.cs were flaky and time-consuming without catching real
regressions. The maintained UI going forward is PostIt, which is tested
via its own PostIt.Tests project.

Removed:
- src/Yavsc.Org.Tests/FirstUIStript.cs (the Selenium-based FirstScript class)
- Selenium.WebDriver PackageReference from Yavsc.Org.Tests.csproj
- Selenium.WebDriver version from src/Yavsc.Org.Tests/Directory.Packages.props

WebServerFixture, BaseTestContext, and the integration tests that depend
on them (Remoting, Services, EMailling, etc.) are unaffected.
2026-06-19 18:57:21 +01:00
002f8cc7e4 Split Directory.Packages.props: shared versions in root, per-product in src/
Move product-local package versions out of the root Directory.Packages.props
into per-product props files under src/<Product>/. The root file now only
contains versions for packages declared by two or more top-level products,
which is the actual shared set.

Each per-product Directory.Packages.props imports the root via
GetPathOfFileAbove so that the shared versions are inherited; this is
necessary because the .NET SDK picks the closest Directory.Packages.props
in the hierarchy and does not merge multiple ones.

Per-product file contents:
- src/cli/                    Microsoft.AspNetCore.Razor.Language,
                              Microsoft.Extensions.{CommandLineUtils,Configuration,Hosting}
- src/PostIt/                 Avalonia* and CommunityToolkit.Mvvm
- src/PostIt.Tests/           Avalonia.Headless{,XUnit}
- src/Yavsc.Org/              AsciiDocSharp*, Google.Apis.Compute.v1,
                              HigginsSoft.IdentityServer8.AspNetIdentity,
                              IdentityServer8.EntityFramework.Storage,
                              IdentityServer8.Security, IdentityServer8.Storage,
                              Microsoft.AspNetCore.Antiforgery, Authentication.Google,
                              Diagnostics.EntityFrameworkCore, Mvc.NewtonsoftJson,
                              SignalR, EntityFrameworkCore.Tools, Swashbuckle,
                              System.Security.Cryptography.Pkcs, YamlDotNet
- src/Yavsc.Org.Tests/        Microsoft.AspNetCore.Hosting,
                              Extensions.Caching.Memory, Options,
                              Options.ConfigurationExtensions,
                              Selenium.WebDriver, xunit.v3.{common,extensibility.core}
- src/Yavsc.Server/           Anthropic.SDK, Google.Apis.Calendar.v3,
                              Magick.NET-Q8-AnyCPU, MailKit, MimeKit,
                              Microsoft.AspNetCore.Http.Features, StaticFiles,
                              EntityFrameworkCore.SqlServer,
                              Npgsql.EntityFrameworkCore.PostgreSQL,
                              PayPalMerchantSDK, pazof.rules, RazorEngine.NetCore
- src/Yavsc.Web/              IdentityModel.AspNetCore

No per-product file is created for Yavsc.Api, Yavsc.Blogs, Yavsc.Abstract,
or templateWeb: Api and Blogs only declare the shared JwtBearer, Abstract
and templateWeb declare no package references at all.

Also includes a minor cosmetic update to FirstUIStript.cs (Firefox -> Chrome
driver, dedent, comment header). Tests previously failing on DataProtection
keyset / SMTP were unrelated environment issues (resolved by fixing the
SMTP password locally).
2026-06-19 18:51:18 +01:00
22b397ce7e Relocate test project: test/yavscTests -> src/Yavsc.Org.Tests
Move the integration test project from the top-level test/ directory into
src/ alongside the projects it tests. Rename the project (and folder) to
Yavsc.Org.Tests to match .NET conventions and reflect that it tests the
Org runtime primarily.

Path changes:
- test/yavscTests/yavscTests.csproj -> src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj
- All .cs / .json / .resx files moved to their new location
- PostItViewModelTests moved out to the dedicated src/PostIt.Tests project
  (it was unrelated to Org testing)

Build adjustments:
- <ProjectReference> paths shortened (..\..\src\X -> ..\X)
- PostIt project reference removed (covered by its own test project)
- <OutputType>exe added (required by xunit.v3)
- xunit.v3.common and xunit.v3.extensibility.core added to package versions

Solution + sln:
- yavsc.sln Project Name updated to 'Yavsc.Org.Tests' and path updated
- GUID preserved so existing build configs stay valid

Static web assets:
- The CopyStaticWebAssetsManifest target was hard-coding the destination
  filename to 'testhost.staticwebassets.endpoints.json', which worked
  when the assembly was named 'yavscTests'. Now that the assembly name
  is 'Yavsc.Org.Tests', ASP.NET Core's MapStaticAssets() looks for
  'Yavsc.Org.Tests.staticwebassets.endpoints.json' (entry-assembly-based
  resolution). Use $(MSBuildProjectName) so the copy target stays
  correct under any future rename.
2026-06-19 17:52:54 +01:00