The new pipeline regression tests boot the real Api host via WebApplicationFactory, which unconditionally runs the startup database migration -- unlike every other test project here, which mocks or uses EF Core InMemory. CI had no MariaDB service at all, so those tests failed on the connection itself. Adds a mariadb service container to the backend-test job plus a ConnectionStrings__DefaultConnection override for that step, which overrides the appsettings.Development.json placeholder via standard config layering without touching any committed appsettings file or affecting local test runs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FWyStNL2ZsjrS7FLd7xvvN
8.4 KiB
Code Generation Summary — Unit: SlpSoftware Client Setup
Moved (brownfield relocation, Step 1)
src/SlpModularCms.Api/Extensions/StaticContentExtensions.cs→src/SlpModularCms.Core/Hosting/StaticContentExtensions.cs(namespaceSlpModularCms.Api.Extensions→SlpModularCms.Core.Hosting; embedded-resource name updated to match)src/SlpModularCms.Api/Extensions/WebsitePlaceholder.html→src/SlpModularCms.Core/Hosting/WebsitePlaceholder.html- Deleted the now-empty
src/SlpModularCms.Api/Extensions/directory
Created
src/SlpModularCms.Core/Hosting/CmsHostOptions.cs— empty extension-point class (NFR Design Pattern 2)src/SlpModularCms.Core/Hosting/CmsHost.cs—ConfigureServices/ConfigurePipeline, the shared composition both Client projects callsrc/SlpModularCms.Api.SlpSoftware/— new Client project:.csproj,Program.cs,appsettings.json,appsettings.Development.json(own isolated local dev database, Infrastructure Design Q1 = B),Properties/launchSettings.json(ports 5286/7222, distinct fromApi's 5284/7221 so both can run simultaneously)src/SlpModularCms.Api.Tests/— new test project:.csproj,PipelineTests.cs(4 tests:/healthreachable, required security headers present on an HTML response with HSTS's Development-only absence explicitly asserted,/adminfallback 404s correctly with no build present, thesentry-tunnelrate limiter engages past its configured permit limit)
Modified
src/SlpModularCms.Api/Program.cs— rewritten to the thin form callingCmsHost.ConfigureServices/ConfigurePipeline; no behavior changesrc/SlpModularCms.Api/SlpModularCms.Api.csproj— removed theWebsitePlaceholder.htmlembedded-resource item (moved toCore)src/SlpModularCms.Core/SlpModularCms.Core.csproj— added theWebsitePlaceholder.htmlembedded-resource itemSlpModularCms.sln— addedSlpModularCms.Api.SlpSoftware(nested underClients) andSlpModularCms.Api.Tests(nested directly underTests, mirroringCore.Tests's placement)README.md— documented the new project,CmsHost, and the local dev database isolation
Not Created (explicit N/A, per the plan)
- No
Program.Coverage.csforApi.SlpSoftware— no pipeline tests target it in this unit (Q1 = C) - No
appsettings.local.jsonforApi.SlpSoftware— git-ignored, personal-per-developer, created locally when needed - No API/Repository layer code, no database migrations — this unit has neither (Unit 2 "Offerings" does)
- No
.gitea/workflows/*.yamlor Gitea Actions variable changes — Operations-phase scope (D-7/D-15)
Post-Completion Fix: Dev Port Collision + Frontend Dev Script
Prompted by a user question about a frontend pnpm dev:slpsoftware script, investigation surfaced a real bug: Api.SlpSoftware's launchSettings.json had been assigned HTTPS port 7222, which is already SlpModularCms.Api.Slave's port (confirmed via frontend/.env.example and Api.Slave/Properties/launchSettings.json). Fixed:
src/SlpModularCms.Api.SlpSoftware/Properties/launchSettings.json— HTTPS port 7222 → 7223 (HTTP 5286 unchanged, already free)src/SlpModularCms.Api.SlpSoftware/appsettings.Development.json—MasterModule:MasterUrlupdated to match (https://localhost:7223)README.md— corrected port note, with an explicit call-out of the 7222 collision reason
Added (frontend, not part of the original per-unit plan, but a direct, symmetrical follow-up to the port fix):
frontend/package.json—dev:slpsoftwarescript (vite --mode slpsoftware --port 5175), mirroringdev:slavefrontend/.env.example— documented the.env.slpsoftware.localpattern (VITE_API_BASE_URL=https://localhost:7223,VITE_APP_TITLE=SlpModularCms (SlpSoftware))
Post-Completion Fix: CORS Origin Mismatch Blocked the Setup Page
Api.SlpSoftware/appsettings.Development.json's Cors:AllowedOrigins had been copied from Api's own file verbatim, listing localhost:5173 — but pnpm dev:slpsoftware serves the frontend on port 5175. The mismatch caused the frontend's /api/v1/Setup/status check to fail on CORS, and router.tsx's InitGuard fails open toward the login page (not setup) on any fetch error — so the setup page never appeared, even against a genuinely empty database (verified directly via the local MariaDB, not assumed). Fixed: Cors:AllowedOrigins now lists localhost:5175.
Post-Completion Fix: GlobalExceptionHandler Logged Expected 401s as Errors
Pre-existing bug, unrelated to this feature's own scope (not introduced by the CmsHost extraction) but surfaced by testing the new instance: src/SlpModularCms.Core/Exceptions/GlobalExceptionHandler.cs logged every exception at LogError before mapping it to a status code, so a routine UnauthorizedException (e.g. an invalid/missing refresh token — the normal state for a fresh, never-logged-in session) was indistinguishable from a genuine unhandled 500 in the console and in Sentry. Fixed: exceptions that map to a status below 500 now log at LogWarning; only genuinely unmapped exceptions (500) log at LogError. Affects every Client project equally (Api, Api.Slave, Api.SlpSoftware), not just this feature. No existing test asserted log level; full suite reran green (196 + 4).
Post-Completion Fix: CI's backend-test Job Had No Database
The Gitea Actions backend-test job (.gitea/workflows/continuous_integration.yaml) just ran dotnet test with no MariaDB service — every pre-existing test project mocks or uses EF Core InMemory, so none of them ever needed one. SlpModularCms.Api.Tests boots the real Api host via WebApplicationFactory, which unconditionally runs the startup migration, so it genuinely needs a reachable database — a design gap in this unit's own NFR Requirements/Design that should have been anticipated when committing to a real pipeline test (Q1 = C) rather than a mocked one. Fixed by adding a mariadb service container to backend-test plus a ConnectionStrings__DefaultConnection environment-variable override for that step only (overrides the placeholder in Api/appsettings.Development.json via standard ASP.NET Core config layering — env vars win over appsettings.*.json). No committed appsettings changed; local dotnet test runs are unaffected (developers' own appsettings.local.json still applies there). Verified the override mechanism directly against the local MariaDB container before trusting it in CI.
Scope note: continuous_integration.yaml is nominally owned by the gitea-deployment-workflow feature. This specific change (making the test gate pass for a test project this feature introduced) was judged in-scope to fix directly as Build-and-Test correctness — distinct from the actual deploy-target retarget (D-15), which remains deferred to this feature's own Operations phase.
Build and Test Verification (Step 13.5)
Two real build fixes were needed and applied during this step (not deviations from the plan — the plan didn't anticipate these, since they only surface once the code actually compiles):
SlpModularCms.Core.csprojwas missing aScalar.AspNetCorepackage reference —CmsHost.ConfigurePipelinecallsMapScalarApiReference(), which moved intoCorealong with the rest of the pipeline composition but the package reference hadn't followed it. Added.CmsHost.csand the movedStaticContentExtensions.cswere missing explicitusing Microsoft.Extensions.Hosting;/using Microsoft.Extensions.DependencyInjection;—Coreis anMicrosoft.NET.Sdkproject (notSdk.Web), so it does not get ASP.NET Core's implicit usings the wayApi(Sdk.Web) did. Added both.
After both fixes:
- Build: ✅ Success (full solution, all 13 projects)
SlpModularCms.Core.Tests: ✅ 196 passed, 0 failed — no regression from the Step 1 file moveSlpModularCms.Api.Tests(new): ✅ 4 passed, 0 failed —/healthreachable, required security headers present on an HTML response (with HSTS's Development-only absence correctly asserted, not overlooked),/adminfallback 404s correctly with no build present, thesentry-tunnelrate limiter engages past its configured permit limit
Local environment note: running the new tests required a local MariaDB (the same one Api itself needs to run, per the README). It was stopped; started it via podman machine start + podman start mariadb for this verification, and left both running afterward for continued local development (including Unit 2 "Offerings").