Set up YesChef.Api.UnitTests and YesChef.Api.IntegrationTests projects running on TUnit + Microsoft.Testing.Platform. Integration tests use a single Postgres 17 Testcontainer per session and clone a migrated template database per test (`CREATE DATABASE … TEMPLATE …`) so tests remain fully isolated and run in parallel without replaying migrations each time. Test-author DX is built around fluent entity builders, a TestDataFactory for common scenarios, and a two-level base hierarchy (IntegrationTest / AuthenticatedIntegrationTest) whose `[Before(Test)]` hooks stand up the per-test database, app factory, default user, and authenticated HttpClient — leaving each test body focused on the action under test. Adds src/backend/global.json to opt `dotnet test` into MTP mode on the .NET 10 SDK, and updates CLAUDE.md with how to run the tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6.2 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
YesChef is a family shopping list and recipe app. Single-tenant per deployment, gated by a shared FAMILY_CODE invite phrase at registration. Real-time list collaboration is delivered over SignalR.
The repo is a small monorepo with two deployables plus an e2e harness at the root:
src/backend/YesChef.Api/— ASP.NET Core (net10.0) minimal API + EF Core (Npgsql) + SignalRsrc/frontend/— SvelteKit 2 (Svelte 5 runes) + TypeScript + Tailwind v4, deployed via@sveltejs/adapter-nodedocker-compose.yml(root) — production-style stack: Postgres 17 + backend + frontend, fronted by Traefik (TLS via Let's Encrypt). Frontend and backend share the sameDOMAIN; Traefik routes/api,/hubs, and/healthto the backend, everything else to the frontend.test-e2e.mjs/test-e2e-multiuser.mjs— Playwright scripts run againsthttp://localhost:5173(Vite dev) hitting a local backend.
Common commands
Frontend (run from src/frontend, use npm --prefix src/frontend <cmd> to avoid cd):
npm --prefix src/frontend install
npm --prefix src/frontend run dev # Vite dev server on :5173
npm --prefix src/frontend run build # adapter-node build → src/frontend/build
npm --prefix src/frontend run check # svelte-kit sync + svelte-check (type/lint check)
Backend (use dotnet --project to avoid cd):
dotnet run --project src/backend/YesChef.Api # listens per launchSettings.json
dotnet build src/backend/YesChef.Api/YesChef.Api.csproj
dotnet ef migrations add <Name> --project src/backend/YesChef.Api
dotnet ef database update --project src/backend/YesChef.Api
Note: Program.cs calls db.Database.MigrateAsync() on startup, so running the API auto-applies pending migrations against the configured ConnectionStrings:DefaultConnection.
Backend tests (TUnit on Microsoft.Testing.Platform; integration tests use Testcontainers + Postgres). The src/backend/global.json opts dotnet test into MTP mode, so use --solution / --project flags:
dotnet test --solution src/backend/YesChef.slnx # all tests
dotnet test --project src/backend/YesChef.Api.UnitTests/YesChef.Api.UnitTests.csproj
dotnet test --project src/backend/YesChef.Api.IntegrationTests/YesChef.Api.IntegrationTests.csproj
Integration tests start a single Postgres 17 container per test session, create a migrated yeschef_template database, then clone a fresh DB per test via CREATE DATABASE … TEMPLATE …. Tests run in parallel (capped by IntegrationTestParallelLimit) and require Docker (Rancher Desktop or Docker Desktop).
Full stack via Docker (requires .env populated from .env.example):
docker compose up -d --build
docker compose logs -f backend
E2E (requires frontend running on :5173 and backend reachable):
node test-e2e.mjs
node test-e2e-multiuser.mjs
There is no backend test project and no test runner configured in the root package.json — npm test is a no-op stub.
Architecture
Backend — feature-folder minimal API
Program.cs is the composition root. It wires EF Core (YesChefDb), JWT bearer auth, SignalR, runs migrations, then maps endpoints by feature folder:
Auth/—/api/authregister/login/me. Registration requires theFamilyCodeconfig value (envFamilyCode). Passwords usePasswordHasher<User>. JWTs are signed with HS256 fromJwt:Secret; issuer/audience are NOT validated. TheOnMessageReceivedevent also accepts?access_token=for the/hubs/*SignalR paths.Features/Stores/,Features/ShoppingLists/,Features/Recipes/— each exposes a staticMap…Endpoints(this RouteGroupBuilder)extension. All are mapped underRequireAuthorization().Features/ShoppingLists/ShoppingListHub.cs— SignalR hub at/hubs/shopping-listfor live list updates.Data/YesChefDb.cs— singleDbContext. Notable model rules:User.NameandStore.Nameare unique;ShoppingList → Itemscascade-deletes;ShoppingListItem.CheckedByUserandShoppingListItem.Recipeset null on delete;Recipe → Ingredientscascades.Auth/ClaimsPrincipalExtensions.cs—GetUserId()/GetUserName()helpers used by endpoints to scope writes.Migrations/— EF Core migrations. Add new ones withdotnet ef migrations add(see commands above).
When adding a feature: create Features/<Name>/<Name>Endpoints.cs with a Map<Name>Endpoints extension, register the group in Program.cs, and add any new entities to Entities/ plus configuration in YesChefDb.OnModelCreating.
Frontend — SvelteKit with runes
svelte.config.jsforcesrunes: truefor all non-node_modulesfiles. Use Svelte 5 runes ($state,$derived,$effect); do not use legacy reactive$:syntax.src/lib/api.ts—api<T>(path, opts)helper. Stores the JWT inlocalStorageundertoken, attachesAuthorization: Bearer …, and on 401 it clears the token andgoto('/login').src/lib/auth.svelte.ts— runes-based auth state.src/lib/signalr.ts— SignalR client wiring (passes the JWT via the?access_token=query string the backend'sOnMessageReceivedhandler expects).src/routes/— file-based routes:/login,/lists,/lists/[id],/recipes,/recipes/new,/recipes/[id],/stores.src/service-worker.ts— PWA service worker.$libis aliased tosrc/lib(seesvelte.config.js).- The frontend calls relative paths like
/api/...; in production Traefik routes those to the backend on the same host. For local dev against a backend on a different port, configure a Vite proxy or run the backend behind the same origin.
Configuration & secrets
.env.example documents the four required environment variables for the Docker stack: POSTGRES_PASSWORD, JWT_SECRET, FAMILY_CODE, DOMAIN. The backend reads Jwt:Secret, FamilyCode, and ConnectionStrings:DefaultConnection via IConfiguration — these can come from appsettings.Development.json for local runs or from environment variables (double-underscore form, e.g. ConnectionStrings__DefaultConnection) in containers.