# Code Generation Plan — Unit 2: slave-availability-extension ## Scope **Modify**: `src/SlpModularCms.Modules.Availability/` (existing project) **Create**: `src/SlpModularCms.Modules.Availability.Master.Tests/` (new test project) **Update**: `SlpModularCms.sln` (add new test project) **Update**: existing `AvailabilityMiddlewareTests.cs` in `Availability.Tests` (new 3-param signature) --- ## Steps ### Part A — Production Code (Availability module) - [x] **Step 1** — `SlpModularCms.Modules.Availability.csproj` — add `InternalsVisibleTo` for new test project - [x] **Step 2** — `Data/Entities/MasterRegistration.cs` — entity with singleton Id constant - [x] **Step 3** — `Data/AvailabilityDbContext.cs` — DbContext with MasterRegistrations DbSet + OnModelCreating - [x] **Step 4** — `Repositories/IMasterRegistrationRepository.cs` — `GetAsync`, `AddAsync`, `Update`, `SaveChangesAsync` - [x] **Step 5** — `Repositories/MasterRegistrationRepository.cs` — EF Core implementation - [x] **Step 6** — `Services/IMasterApiKeyProtector.cs` — `Protect` + `Unprotect` (null on crypto failure) - [x] **Step 7** — `Services/MasterApiKeyProtector.cs` — wraps `IDataProtectionProvider`; purpose `"SlpModularCms.Availability.MasterApiKey"` - [x] **Step 8** — `Services/MasterGateStatus.cs` — `record MasterGateStatus(bool IsAvailable, string? DisableMessage)` - [x] **Step 9** — `Services/MasterAvailabilityServiceDependencies.cs` — record with repo, protector, logger - [x] **Step 10** — `Services/IMasterAvailabilityService.cs` — `RegisterAsync(bool)`, `PushStatusAsync(bool)`, `GetRegisteredUrlAsync(string?)`, `GetMasterStatus()` - [x] **Step 11** — `Services/MasterAvailabilityService.cs` — volatile static fields + implementation of all 4 methods - [x] **Step 12** — `Models/RegisterMasterRequest.cs` + `Models/PushStatusRequest.cs` — `[ExcludeFromCodeCoverage]` records - [x] **Step 13** — `Controllers/MasterController.cs` — 3 endpoints; header extraction; delegate to service; Unauthorized() on false/null - [x] **Step 14** — `Middleware/AvailabilityMiddleware.cs` — extend: add `/api/v1/master/` bypass; move admin check before local gate; add master gate between admin check and local gate - [x] **Step 15** — `AvailabilityModule.cs` — add DbContext, protector, repo, deps, service registrations; add `MigrateAsync` in UseModule ### Part B — Test Project (new) - [x] **Step 16** — `SlpModularCms.Modules.Availability.Master.Tests.csproj` — new project; copy packages from Master.Tests; add `Microsoft.IdentityModel.Tokens` for JWT tests - [x] **Step 17** — `Repositories/MasterRegistrationRepositoryTests.cs` — EF InMemory; GetAsync (null + found); AddAsync; Update; SaveChanges - [x] **Step 18** — `Services/MasterApiKeyProtectorTests.cs` — `EphemeralDataProtectionProvider`; round-trip; invalid ciphertext returns null - [x] **Step 19** — `Services/MasterAvailabilityServiceTests.cs` — NSubstitute; all 4 public methods; key mismatch; first registration; re-registration; static cache state - [x] **Step 20** — `Controllers/MasterControllerTests.cs` — NSubstitute; missing header → 401; success → 200; service returns false/null → 401 - [x] **Step 21** — `Middleware/AvailabilityMiddlewareMasterGateTests.cs` — NSubstitute; master gate blocks → 503; master gate passes → local gate evaluated; `/api/v1/master/` bypasses; master gate + admin JWT → pass through ### Part C — Cross-cutting - [x] **Step 22** — `SlpModularCms.sln` — add `Availability.Master.Tests` project with new GUID - [x] **Step 23** — `src/SlpModularCms.Modules.Availability.Tests/AvailabilityMiddlewareTests.cs` — update all `InvokeAsync(context, _service)` calls to `InvokeAsync(context, _service, masterSvc)` (masterSvc stub returning `IsAvailable=true`) --- ## Key Design Notes | Concern | Decision | |---------|----------| | Singleton Id | `public static readonly Guid SingletonId = new("00000000-0000-0000-0000-000000000001")` | | IMasterAvailabilityService.RegisterAsync | Returns `bool` (true=ok, false=key mismatch) | | IMasterAvailabilityService.PushStatusAsync | Returns `bool` (true=ok, false=key mismatch) | | IMasterAvailabilityService.GetRegisteredUrlAsync | Returns `string?` (null=unauthorized) | | Unprotect null guard | `storedKey is null \|\| storedKey != incoming` → treat as mismatch | | Middleware InvokeAsync order | bypass paths → admin JWT → master gate → local gate | | Existing AvailabilityMiddlewareTests | Add stub `IMasterAvailabilityService` returning `IsAvailable=true` as 3rd param | --- *Artifact path*: `aidlc-docs/features/master-cms-module/construction/plans/slave-availability-extension-code-generation-plan.md`