The signup/cancel endpoints were passing the Keycloak sub claim (external UUID)
directly as MemberId, but ShiftSignup.MemberId references the internal Member.Id.
Now ShiftService resolves ExternalUserId to the internal Member.Id before creating
the signup record. Integration tests updated to seed proper Member entities.
Stabilize test harness across full stack:
Backend integration tests:
- Fix Auth/Club/Migration/RLS/Member/Tenant/RLS Isolation/Shift/Task test suites
- Add AssemblyInfo.cs for test configuration
- Enhance CustomWebApplicationFactory + TestAuthHandler for stable test environment
- Expand RlsIsolationTests with comprehensive multi-tenant RLS verification
Frontend test harness:
- Align vitest.config.ts with backend API changes
- Add bunfig.toml for bun test environment stability
- Enhance api.test.ts with proper test setup integration
- Expand test/setup.ts with fixture initialization
All tests now passing: backend 12/12 unit + 63/63 integration, frontend 45/45
- Add path exemption in TenantValidationMiddleware for /api/clubs/me
- Change authorization policy from RequireMember to RequireViewer
- Fix KEYCLOAK_CLIENT_ID in docker-compose.yml (workclub-app not workclub-api)
- Endpoint now works without X-Tenant-Id header as intended
- Other endpoints still protected by tenant validation
This fixes the chicken-and-egg problem where frontend needs to call
/api/clubs/me to discover available clubs before selecting a tenant.
Implement Task 16: Club + Member API endpoints with MemberSyncService
Services:
- ClubService: GetMyClubsAsync (user's clubs), GetCurrentClubAsync (tenant club)
- MemberService: GetMembersAsync (list), GetMemberByIdAsync, GetCurrentMemberAsync
- MemberSyncService: Auto-creates Member records from JWT on first request
Middleware:
- MemberSyncMiddleware: Runs after auth, calls MemberSyncService
Endpoints:
- GET /api/clubs/me (list user's clubs)
- GET /api/clubs/current (current tenant's club)
- GET /api/members (list members, RLS filtered)
- GET /api/members/{id} (member detail)
- GET /api/members/me (current user's membership)
Tests: 14 integration tests (6 club + 8 member)
- Club filtering by user membership
- Multi-tenant isolation via RLS
- Member auto-sync on first request
- Cross-tenant access blocked
- Role-based authorization
Build: 0 errors, all tests compile
Pattern: TypedResults, RequireAuthorization policies, TDD approach