--- type: Runbook title: Operations, Environment Setup & Testing Guidance description: Operational guide for local development setup, .NET Aspire startup, EF Core PostgreSQL migrations, JWT configuration, and testing verification scenarios. tags: [operations, runbook, aspire, postgresql, migrations, testing, xunit, signalr] openwiki: roles: [operations, testing] change_kinds: [lifecycle, public-api] source_paths: [SlipItIn.AppHost/AppHost.cs, SlipItIn.Server/Program.cs, SlipItIn/MauiProgram.cs, SlipItIn.Server.Tests/TestDbHelper.cs] symbols: [AppHost, Program, MauiProgram, TestDbHelper] validation_commands: ["dotnet build SlipItIn.slnx", "dotnet test SlipItIn.Server.Tests"] verified: - by: openwiki/0.5.0 at: 2026-09-07T15:04:46.288Z --- # Operations, Environment Setup & Testing Guidance This runbook provides actionable instructions for local development setup, starting services via .NET Aspire, executing Entity Framework Core migrations, configuring environment keys, running client/server applications, and executing targeted test scenarios. This guide [configures environment parameters for](/openwiki/architecture/overview.md) the backend and client architecture, [manages database migrations for entities in](/openwiki/domain/game-mechanics.md) the domain model, [verifies real-time event flows defined in](/openwiki/workflows/slip-and-challenge.md) the workflow guide, and [references source entrypoints cataloged in](/openwiki/source-map.md) the source map. --- ## 1. Local Development Environment Setup ### Prerequisites * **.NET 10 SDK** (Installed and verified via `dotnet --version`). * **Docker Desktop** or **Podman** (Required by .NET Aspire to run the PostgreSQL container). * **Workloads**: .NET Aspire workload and .NET MAUI workload (`dotnet workload install aspire maui`). ### Starting the Distributed Application with Aspire Run the Aspire orchestrator project: ```bash dotnet run --project SlipItIn.AppHost/SlipItIn.AppHost.csproj ``` **What happens during launch:** 1. Aspire launches a PostgreSQL container (`pgsql`/`postgresdb`). 2. Aspire builds and starts `SlipItIn.Server`. 3. `Program.cs` automatically executes EF Core migrations (`db.Database.Migrate()`), creating database tables if they do not exist. 4. `DbSeeder.SeedAsync(db)` populates initial users and phrases when the database is empty. 5. Aspire Dashboard opens in your web browser, displaying live metrics, OpenTelemetry traces, and structured logs for all services. --- ## 2. Configuration & Secrets Management Configuration settings are loaded from `appsettings.json`, `appsettings.Development.json`, and environment variables. ### Key Configuration Keys (`SlipItIn.Server`) | Key | Default Value | Description | |---|---|---| | `Jwt:Key` | _(none in production; dev fallback: `DevOnlyKey-NotForProduction-ChangeMe-0123456789abcdef`)_ | Secret signing key for JWT tokens (Must be >= 32 characters / >= 256 bits). In non-development environments, a missing or short key causes a startup exception. | | `Jwt:Issuer` | `SlipItInServer` | Token issuer claim. | | `Jwt:Audience` | `SlipItInClient` | Token audience claim. | | `Jwt:ExpirationMinutes` | `1440` (24 hours) | JWT token lifespan. | | `ConnectionStrings:postgresdb` | Configured via Aspire | PostgreSQL connection string injected by the Aspire orchestrator. | ### Production Configuration Security In production deployment, override `Jwt:Key` using environment variables or user secrets (`dotnet user-secrets`): ```bash dotnet user-secrets set "Jwt:Key" "YOUR_HIGH_ENTROPY_PRODUCTION_SECRET_KEY_HERE" --project SlipItIn.Server ``` --- ## 3. Entity Framework Core Migrations When altering models in `SlipItIn.Shared/Models/` or `SlipItInDbContext.cs`: ### Adding a New Migration Run the EF Core CLI from the repository root: ```bash dotnet ef migrations add --project SlipItIn.Server --startup-project SlipItIn.Server ``` ### Applying Migrations Manually While `Program.cs` applies migrations at startup (`db.Database.Migrate()`), migrations can also be manually applied via command line: ```bash dotnet ef database update --project SlipItIn.Server --startup-project SlipItIn.Server ``` --- ## 4. Test Project Structure The `SlipItIn.Server.Tests` project contains all server-side automated tests. It targets `net10.0` and uses **xUnit** as the test framework, **Moq** for mocking dependencies, and **EF Core InMemory** (`Microsoft.EntityFrameworkCore.InMemory`) for isolated database contexts. ### Test Infrastructure **`TestDbHelper.cs`** — Static helper class that creates isolated test databases and seeds test data: - `CreateFactory()` — Returns an `IDbContextFactory` backed by an EF Core InMemory database with a unique name per test, ensuring complete test isolation. - `SeedUser(db, username, email, isActive)` — Inserts a minimal `User` entity and returns it. - `SeedPhrases(db, count)` — Inserts a set of `Phrase` entities (with an auto-created creator user) for card-dealing tests. - `SeedGame(db, host, status)` — Creates a `Game` with a host `Player` attached, returning both for test assertions. ### Test Files | File | Class Under Test | What It Covers | |---|---|---| | `GameServiceTests.cs` | `GameService` | Business logic: game creation, joining, starting, player ready, card dealing, slip submission, challenge creation/resolution, game state queries with round time computation. | | `GameHubTests.cs` | `GameHub` | SignalR hub behavior: lobby creation/join, player ready, game start, slip submission, challenge submission/resolution, authorization enforcement. Uses Moq to mock `IGameService`, `IHubCallerClients`, `IGroupManager`, and `ISingleClientProxy`. InMemory DB is used only for the hub's own player-access validation. | | `AuthControllerTests.cs` | `AuthController` | Registration (token generation, input validation, case-insensitive duplicate detection), login (credential verification, missing-field handling), and the `/auth/me` claims endpoint. | | `DbSeederTests.cs` | `DbSeeder` | Seed idempotency: verifies `SeedAsync` creates users and phrases when the database is empty, but does nothing when data already exists, and is safe to call repeatedly. | ### Running Tests ```bash dotnet test SlipItIn.Server.Tests/SlipItIn.Server.Tests.csproj ``` For CI-matching behavior (Release configuration): ```bash dotnet test SlipItIn.Server.Tests/SlipItIn.Server.Tests.csproj --configuration Release ``` --- ## 5. Testing Guidance & Verification Scenarios When developing or extending SlipItIn features, verify the core architecture through these targeted test scenarios: ### 1. JWT Authentication & Claims Tests * **Test Objective**: Verify `GameHub` rejects unauthenticated WebSocket connections or missing token query parameters. * **Verification**: Connect to `/hubs/game` without `?access_token=...` or with an expired token. Confirm SignalR connection is terminated with 401 Unauthorized. ### 2. Player Access Authorization Tests * **Test Objective**: Verify Player A cannot manipulate Player B's cards or state. * **Verification**: Authenticate as User A and attempt to call `GameHub.SubmitSlip(gameId, playerBId, cardId)`. Verify that `ValidatePlayerAccessAsync` throws `UnauthorizedAccessException` and returns an error response. ### 3. Client MVVM Decoupled Messaging Tests * **Test Objective**: Confirm `GameStateService` translates SignalR events into `WeakReferenceMessenger` messages without memory leak risk. * **Verification**: Trigger a `GameStateUpdated` event on `SignalRService`. Confirm that `LobbyViewModel` and `GameBoardViewModel` receive `GameStateChangedMessage` and update their observable collections cleanly. ### 4. Offline Action Queueing & Resync Verification * **Test Objective**: Verify actions taken while disconnected are stored locally and replayed upon reconnection. * **Verification**: 1. Disconnect network or stop `SignalRService`. 2. Invoke `GameBoardViewModel.SubmitSlipAsync(card)`. Confirm `QueuedGameAction` is written to `Preferences`. 3. Re-establish connection. Confirm `GameStateService.ResyncAsync()` calls `RequestGameStateAsync()` and replays the queued action via `SendQueuedActionAsync()`. ### 5. Dual-Layer Local Storage Verification * **Test Objective**: Confirm sensitive tokens are isolated in `SecureStorage` while non-sensitive state stays in `Preferences`. * **Verification**: Inspect local device storage after login. Verify `auth_token` is stored via platform `SecureStorage` and `game_state` / `queued_actions` are saved in `Preferences`. ### 6. Concurrency & Race Condition Tests * **Test Objective**: Confirm `IDbContextFactory` handles simultaneous WebSocket calls without thread collision. * **Verification**: Simulate 5 parallel calls to `GameHub.ChallengeSlip()` or `SubmitSlip()` across multiple clients. Verify that all calls complete cleanly without `InvalidOperationException` from DbContext. --- ## 6. Guidance for Future Agents & Developers - **When to Consult**: Refer to this runbook for local environment setup, Aspire startup commands, EF Core migration commands, and test verification procedures. - **Invariants**: - Always run migrations using `SlipItIn.Server` as both project and startup-project. - Test offline queue flushing before submitting client-side real-time changes. - **CI Build Commands** (from `.github/workflows/build.yml`): - Server stack: `dotnet build SlipItIn.Shared/SlipItIn.Shared.csproj --no-restore --configuration Release` (and similarly for `ServiceDefaults`, `Server`, `Server.Tests`, `AppHost`). - MAUI client: `dotnet build SlipItIn/SlipItIn.csproj --no-restore --configuration Debug`. - Server tests: `dotnet test SlipItIn.Server.Tests/SlipItIn.Server.Tests.csproj --no-restore --configuration Release`. - **Minimal Validation Command**: ```bash dotnet build SlipItIn.slnx dotnet test SlipItIn.Server.Tests ```