Unit 1 of the slpsoftware-api feature (FR-1/FR-2/FR-3): a new Client project in the Clients solution folder, intended to eventually become the deployed API for test.slpsoftware.nl/slpsoftware.nl, hosting the same four modules as SlpModularCms.Api plus a future Offerings module. - Extracts SlpModularCms.Api/Program.cs's hosting-pipeline composition into SlpModularCms.Core.Hosting.CmsHost (ConfigureServices/ConfigurePipeline), shared by both Client projects so they cannot drift apart - Moves StaticContentExtensions.cs + WebsitePlaceholder.html from Api into Core, since CmsHost cannot live in Api but Core cannot depend on Api - Adds SlpModularCms.Api.SlpSoftware with its own isolated local dev database and dev ports (5286/7223, distinct from Api's and Api.Slave's) - Adds SlpModularCms.Api.Tests with WebApplicationFactory-based pipeline regression tests (security headers, health check, SPA fallback, rate limiting), scoped to Api per NFR Design - Adds a frontend dev:slpsoftware pnpm script mirroring dev:slave - Fixes GlobalExceptionHandler logging routine 401s (e.g. an expired/missing refresh token) as unhandled errors -- pre-existing, unrelated to this feature's own scope, found while testing the new instance Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FWyStNL2ZsjrS7FLd7xvvN
2.8 KiB
NFR Design Patterns — Unit: SlpSoftware Client Setup
Pattern 1: Pipeline Regression Test, Scoped to Api (NFR-CS-01)
Decision (Q1 = C): the new WebApplicationFactory-based integration tests target SlpModularCms.Api only. SlpModularCms.Api.SlpSoftware is not separately pipeline-tested in this unit — it inherits confidence transitively through the shared CmsHost.ConfigureServices/ConfigurePipeline code path that both projects call identically.
Pattern: a single WebApplicationFactory<TEntryPoint>-based test fixture, pointed at Api, asserting on real HTTP responses:
- Security headers present (CSP, HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy) — CSP presence and required directives only, not the exact Umami/Sentry exceptions (those are
Api-specific configuration, unrelated toCmsHostcorrectness). /healthreturns success.- A non-file
/admin/*route resolves to the admin SPA'sindex.html. - A burst of requests against a rate-limited route eventually receives a
429.
Accepted trade-off: if a future change to CmsHost behaves differently under Api.SlpSoftware's specific module composition (Offerings module present) than under Api's, this test suite alone would not catch it. This is an accepted gap per the user's decision, not an oversight — full end-to-end coverage of Api.SlpSoftware itself is covered later by the feature-wide Build and Test phase once Unit 2 exists.
Pattern 2: CmsHostOptions as an Empty Extension Point (NFR-CS-02)
Decision (Q2 = B): CmsHostOptions is introduced as a genuinely empty class (or, in C# terms, a class with zero properties, or internal sealed record CmsHostOptions; — exact syntax decided at Code Generation) — a placeholder in the method signature, not a placeholder-with-a-guess-field.
Pattern: standard Options Object pattern, sized for its current job (nothing) rather than a speculative future job. Both Api and Api.SlpSoftware construct new CmsHostOptions() and pass it to CmsHost.ConfigureServices(builder, options) / ConfigurePipeline(app, orchestrator, options). When a real per-project difference appears later, a property is added to this one class rather than changing either method's signature again.
Constraint carried forward (from NFR-CS-02 / tech-stack-decisions.md): whatever is eventually added to CmsHostOptions, it must never affect DataProtectionExtensions.ApplicationDiscriminator — that stays the hardcoded "SlpModularCms" constant regardless.
Pattern 3: Security — Verification, Not New Design (NFR-CS-03)
No new security pattern is introduced by this unit. The applicable pattern is "prove equivalence," fully covered by Pattern 1's regression tests. No additional logical components (WAF, extra middleware, etc.) are needed.