# ColaFlow Identity Module - Integration Tests Professional .NET Integration Test project for Day 5 Refresh Token and RBAC functionality. ## Project Overview This test project provides comprehensive integration testing for: - **Phase 1**: Refresh Token functionality (token refresh, rotation, revocation) - **Phase 2**: Role-Based Access Control (RBAC) (role assignment, JWT claims, role persistence) - **Day 4 Regression**: Authentication basics (registration, login, password hashing) ## Project Structure ``` ColaFlow.Modules.Identity.IntegrationTests/ ├── Infrastructure/ │ ├── ColaFlowWebApplicationFactory.cs # Custom WebApplicationFactory │ ├── DatabaseFixture.cs # In-Memory database fixture │ ├── RealDatabaseFixture.cs # PostgreSQL database fixture │ └── TestAuthHelper.cs # Authentication test utilities ├── Identity/ │ ├── AuthenticationTests.cs # Day 4 regression tests │ ├── RefreshTokenTests.cs # Day 5 Phase 1 tests │ └── RbacTests.cs # Day 5 Phase 2 tests ├── appsettings.Testing.json # Test configuration └── ColaFlow.Modules.Identity.IntegrationTests.csproj ``` ## Test Categories ### 1. Authentication Tests (Day 4 Regression) - RegisterTenant with valid/invalid data - Login with correct/incorrect credentials - Protected endpoint access with/without token - JWT token claims validation - Password hashing verification - Complete auth flow (register → login → access) **Total Tests**: 10 ### 2. Refresh Token Tests (Day 5 Phase 1) - RegisterTenant returns access + refresh tokens - Login returns access + refresh tokens - RefreshToken returns new token pair - Old refresh token cannot be reused (token rotation) - Invalid refresh token fails - Logout revokes refresh token - Refresh token maintains user identity - Multiple refresh operations work - Expired refresh token fails **Total Tests**: 9 ### 3. RBAC Tests (Day 5 Phase 2) - RegisterTenant assigns TenantOwner role - JWT contains role claims - Login preserves role - RefreshToken preserves role - /api/auth/me returns user role - JWT contains all required role claims - Multiple token refresh maintains role - Protected endpoint access with valid role succeeds - Protected endpoint access without token fails - Protected endpoint access with invalid token fails - Role consistency across all authentication flows **Total Tests**: 11 **Grand Total**: **30 Integration Tests** ## Test Infrastructure ### WebApplicationFactory The `ColaFlowWebApplicationFactory` supports two database modes: #### 1. In-Memory Database (Default) - Fast, isolated tests - No external dependencies - Each test class gets its own database instance - **Recommended for CI/CD pipelines** ```csharp var factory = new ColaFlowWebApplicationFactory(useInMemoryDatabase: true); ``` #### 2. Real PostgreSQL Database - Tests actual database behavior - Verifies migrations and real database constraints - Requires PostgreSQL running on localhost - **Recommended for local testing** ```csharp var factory = new ColaFlowWebApplicationFactory(useInMemoryDatabase: false); ``` ### Database Fixtures #### DatabaseFixture (In-Memory) - Implements `IClassFixture` - Provides isolated database per test class - Automatic cleanup after tests #### RealDatabaseFixture (PostgreSQL) - Implements `IClassFixture` - Creates unique test database per test run - Automatic cleanup (database deletion) after tests ## NuGet Packages | Package | Version | Purpose | |---------|---------|---------| | `xunit` | 2.9.2 | Test framework | | `xunit.runner.visualstudio` | 2.8.2 | Visual Studio test runner | | `Microsoft.AspNetCore.Mvc.Testing` | 9.0.0 | WebApplicationFactory | | `Microsoft.EntityFrameworkCore.InMemory` | 9.0.0 | In-Memory database | | `Npgsql.EntityFrameworkCore.PostgreSQL` | 9.0.4 | PostgreSQL provider | | `FluentAssertions` | 7.0.0 | Fluent assertion library | | `System.IdentityModel.Tokens.Jwt` | 8.14.0 | JWT token parsing | ## Running Tests ### Prerequisites **For In-Memory Tests** (No external dependencies): - .NET 9.0 SDK installed **For PostgreSQL Tests**: - PostgreSQL running on `localhost:5432` - Username: `postgres` - Password: `postgres` - Database: `colaflow_test` (auto-created) ### Command Line #### Run All Tests ```bash cd c:\Users\yaoji\git\ColaCoder\product-master\colaflow-api dotnet test tests/Modules/Identity/ColaFlow.Modules.Identity.IntegrationTests ``` #### Run Specific Test Class ```bash # Refresh Token Tests only dotnet test --filter "FullyQualifiedName~RefreshTokenTests" # RBAC Tests only dotnet test --filter "FullyQualifiedName~RbacTests" # Authentication Tests only dotnet test --filter "FullyQualifiedName~AuthenticationTests" ``` #### Run Specific Test Method ```bash dotnet test --filter "FullyQualifiedName~RefreshToken_ShouldReturnNewTokenPair" ``` #### Verbose Output ```bash dotnet test --logger "console;verbosity=detailed" ``` #### Generate Coverage Report ```bash dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov /p:CoverletOutput=./coverage.lcov ``` ### Visual Studio / Rider 1. **Visual Studio**: - Open Test Explorer (Test → Test Explorer) - Right-click project → Run Tests - Or right-click individual test → Run Test 2. **JetBrains Rider**: - Open Unit Tests window (View → Tool Windows → Unit Tests) - Right-click project → Run Unit Tests - Or use `Ctrl+U, Ctrl+R` shortcut ### Parallel Execution By default, xUnit runs test classes in parallel but tests within a class sequentially. This is perfect for integration tests because: - Each test class uses its own `DatabaseFixture` (isolated database) - Tests within a class share the same database (sequential execution prevents conflicts) To disable parallelization (for debugging): ```csharp [assembly: CollectionBehavior(DisableTestParallelization = true)] ``` ## Test Configuration ### appsettings.Testing.json ```json { "ConnectionStrings": { "IdentityConnection": "Host=localhost;Port=5432;Database=colaflow_test;Username=postgres;Password=postgres" }, "Jwt": { "SecretKey": "test-secret-key-min-32-characters-long-12345678901234567890", "Issuer": "ColaFlow.API.Test", "Audience": "ColaFlow.Web.Test", "ExpirationMinutes": "15", "RefreshTokenExpirationDays": "7" }, "Logging": { "LogLevel": { "Default": "Warning" } } } ``` ### Override Configuration You can override configuration in tests: ```csharp builder.ConfigureAppConfiguration((context, config) => { config.AddInMemoryCollection(new Dictionary { ["Jwt:ExpirationMinutes"] = "5", ["Jwt:RefreshTokenExpirationDays"] = "1" }); }); ``` ## Test Helpers ### TestAuthHelper Provides convenient methods for common test scenarios: ```csharp // Register and get tokens var (accessToken, refreshToken) = await TestAuthHelper.RegisterAndGetTokensAsync(client); // Login and get tokens var (accessToken, refreshToken) = await TestAuthHelper.LoginAndGetTokensAsync( client, "tenant-slug", "email@test.com", "password"); // Parse JWT token var claims = TestAuthHelper.ParseJwtToken(accessToken); // Get specific claim var userId = TestAuthHelper.GetClaimValue(accessToken, "user_id"); // Check role bool isOwner = TestAuthHelper.HasRole(accessToken, "TenantOwner"); ``` ## CI/CD Integration ### GitHub Actions ```yaml name: Integration Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup .NET uses: actions/setup-dotnet@v3 with: dotnet-version: 9.0.x - name: Restore dependencies run: dotnet restore - name: Build run: dotnet build --no-restore - name: Run Integration Tests run: dotnet test tests/Modules/Identity/ColaFlow.Modules.Identity.IntegrationTests --no-build --verbosity normal ``` ### Azure DevOps ```yaml trigger: - main pool: vmImage: 'ubuntu-latest' steps: - task: UseDotNet@2 inputs: version: '9.0.x' - task: DotNetCoreCLI@2 displayName: 'Restore' inputs: command: 'restore' - task: DotNetCoreCLI@2 displayName: 'Build' inputs: command: 'build' - task: DotNetCoreCLI@2 displayName: 'Test' inputs: command: 'test' projects: '**/ColaFlow.Modules.Identity.IntegrationTests.csproj' ``` ## Test Coverage Goals - **Line Coverage**: ≥ 80% - **Branch Coverage**: ≥ 70% - **Critical Paths**: 100% coverage for: - Token generation and refresh - Role assignment and persistence - Authentication flows ## Troubleshooting ### Test Failures #### "Database connection failed" - Ensure PostgreSQL is running (`RealDatabaseFixture` only) - Check connection string in `appsettings.Testing.json` - Use In-Memory database for tests that don't need real database #### "Token validation failed" - Verify `Jwt:SecretKey` matches between test config and API config - Check token expiration time is sufficient - Ensure clock skew tolerance is configured #### "Test isolation issues" - Ensure each test class uses `IClassFixture` - Verify tests don't share global state - Use unique tenant slugs and emails (`Guid.NewGuid()`) #### "Port already in use" - The test server uses a random port by default - No need to stop the real API server - If issues persist, use `_factory.Server` instead of `_factory.CreateClient()` ### Debug Tips #### Enable Detailed Logging ```csharp builder.ConfigureLogging(logging => { logging.ClearProviders(); logging.AddConsole(); logging.SetMinimumLevel(LogLevel.Debug); }); ``` #### Inspect Database State ```csharp using var scope = Factory.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var users = db.Users.ToList(); // Inspect users... ``` #### Pause Test Execution ```csharp await Task.Delay(TimeSpan.FromSeconds(30)); // Inspect state manually ``` ## Best Practices 1. **Use In-Memory Database for most tests**: Faster, no dependencies 2. **Use Real Database for critical paths**: Migrations, constraints, transactions 3. **Isolate tests**: Each test should be independent 4. **Use unique identifiers**: `Guid.NewGuid()` for slugs, emails 5. **Clean up after tests**: Use `IDisposable` fixtures 6. **Use FluentAssertions**: More readable assertions 7. **Test happy paths AND error cases**: Both success and failure scenarios 8. **Use descriptive test names**: `MethodName_Scenario_ExpectedResult` ## Future Enhancements - [ ] Add Testcontainers for PostgreSQL (no manual setup required) - [ ] Add performance benchmarks - [ ] Add load testing (k6 integration) - [ ] Add Swagger/OpenAPI contract tests - [ ] Add mutation testing (Stryker.NET) - [ ] Add E2E tests with Playwright ## Contributing When adding new tests: 1. Follow existing test structure and naming conventions 2. Use `TestAuthHelper` for common operations 3. Ensure tests are isolated and don't depend on execution order 4. Add test documentation in this README 5. Verify tests pass with both In-Memory and Real database ## License This test project is part of ColaFlow and follows the same license. --- **Questions?** Contact the QA team or refer to the main ColaFlow documentation.