380 lines
14 KiB
PowerShell
380 lines
14 KiB
PowerShell
# ColaFlow Day 5 QA Integration Test Suite
|
|
# Comprehensive testing for Refresh Token + RBAC
|
|
|
|
$baseUrl = "http://localhost:5167"
|
|
$ErrorActionPreference = "Continue"
|
|
|
|
# Test counters
|
|
$totalTests = 0
|
|
$passedTests = 0
|
|
$failedTests = 0
|
|
$errors = @()
|
|
|
|
function Test-Api {
|
|
param($Name, $ScriptBlock)
|
|
$totalTests++
|
|
Write-Host "`n========================================" -ForegroundColor Cyan
|
|
Write-Host "Test $totalTests : $Name" -ForegroundColor Cyan
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
|
|
try {
|
|
& $ScriptBlock
|
|
$passedTests++
|
|
Write-Host "[PASS] $Name" -ForegroundColor Green
|
|
return $true
|
|
} catch {
|
|
$failedTests++
|
|
$script:errors += @{Name=$Name; Error=$_.Exception.Message}
|
|
Write-Host "[FAIL] $Name" -ForegroundColor Red
|
|
Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
|
|
return $false
|
|
}
|
|
}
|
|
|
|
Write-Host "===================================================" -ForegroundColor Magenta
|
|
Write-Host " ColaFlow Day 5 Integration Test Suite" -ForegroundColor Magenta
|
|
Write-Host " Testing: Refresh Token + RBAC + Regression" -ForegroundColor Magenta
|
|
Write-Host "===================================================" -ForegroundColor Magenta
|
|
|
|
# Wait for API
|
|
Write-Host "`nWaiting for API to be ready..." -ForegroundColor Yellow
|
|
Start-Sleep -Seconds 5
|
|
|
|
# ============================================================================
|
|
# PHASE 1: REFRESH TOKEN TESTS
|
|
# ============================================================================
|
|
|
|
Write-Host "`n" -ForegroundColor Yellow
|
|
Write-Host "=====================================" -ForegroundColor Yellow
|
|
Write-Host " PHASE 1: REFRESH TOKEN TESTS" -ForegroundColor Yellow
|
|
Write-Host "=====================================" -ForegroundColor Yellow
|
|
|
|
# Global variables for tokens
|
|
$script:tenantSlug = ""
|
|
$script:accessToken1 = ""
|
|
$script:refreshToken1 = ""
|
|
$script:accessToken2 = ""
|
|
$script:refreshToken2 = ""
|
|
$script:userId = ""
|
|
|
|
# Test 1: Register and Get Tokens
|
|
Test-Api "Register Tenant - Get Access & Refresh Tokens" {
|
|
$slug = "test-$(Get-Random -Minimum 1000 -Maximum 9999)"
|
|
$body = @{
|
|
tenantName = "Test Corp Day5"
|
|
tenantSlug = $slug
|
|
subscriptionPlan = "Professional"
|
|
adminEmail = "admin@testday5.com"
|
|
adminPassword = "Admin@1234"
|
|
adminFullName = "Test Admin"
|
|
} | ConvertTo-Json
|
|
|
|
$response = Invoke-RestMethod -Uri "$baseUrl/api/tenants/register" `
|
|
-Method Post -ContentType "application/json" -Body $body
|
|
|
|
if (-not $response.accessToken -or -not $response.refreshToken) {
|
|
throw "Missing tokens in response"
|
|
}
|
|
|
|
$script:tenantSlug = $slug
|
|
$script:accessToken1 = $response.accessToken
|
|
$script:refreshToken1 = $response.refreshToken
|
|
$script:userId = $response.user.id
|
|
|
|
Write-Host " Tenant: $slug" -ForegroundColor Gray
|
|
Write-Host " User ID: $($script:userId)" -ForegroundColor Gray
|
|
Write-Host " Access Token: $($script:accessToken1.Substring(0,20))..." -ForegroundColor Gray
|
|
Write-Host " Refresh Token: $($script:refreshToken1.Substring(0,20))..." -ForegroundColor Gray
|
|
}
|
|
|
|
# Test 2: Use Access Token
|
|
Test-Api "Access Protected Endpoint with Access Token" {
|
|
$headers = @{ "Authorization" = "Bearer $($script:accessToken1)" }
|
|
$response = Invoke-RestMethod -Uri "$baseUrl/api/auth/me" -Method Get -Headers $headers
|
|
|
|
if (-not $response.userId) {
|
|
throw "No user data returned"
|
|
}
|
|
|
|
Write-Host " User: $($response.email)" -ForegroundColor Gray
|
|
}
|
|
|
|
# Test 3: Refresh Token
|
|
Test-Api "Refresh Access Token (Token Rotation)" {
|
|
$body = @{ refreshToken = $script:refreshToken1 } | ConvertTo-Json
|
|
$response = Invoke-RestMethod -Uri "$baseUrl/api/auth/refresh" `
|
|
-Method Post -ContentType "application/json" -Body $body
|
|
|
|
if (-not $response.accessToken -or -not $response.refreshToken) {
|
|
throw "Missing tokens in refresh response"
|
|
}
|
|
|
|
if ($response.accessToken -eq $script:accessToken1 -or $response.refreshToken -eq $script:refreshToken1) {
|
|
throw "Tokens were not rotated"
|
|
}
|
|
|
|
$script:accessToken2 = $response.accessToken
|
|
$script:refreshToken2 = $response.refreshToken
|
|
|
|
Write-Host " New Access Token: $($script:accessToken2.Substring(0,20))..." -ForegroundColor Gray
|
|
Write-Host " Tokens rotated successfully" -ForegroundColor Gray
|
|
}
|
|
|
|
# Test 4: Token Reuse Detection
|
|
Test-Api "Token Reuse Detection (Security)" {
|
|
$body = @{ refreshToken = $script:refreshToken1 } | ConvertTo-Json
|
|
|
|
try {
|
|
$response = Invoke-RestMethod -Uri "$baseUrl/api/auth/refresh" `
|
|
-Method Post -ContentType "application/json" -Body $body
|
|
throw "Old refresh token was accepted - SECURITY ISSUE!"
|
|
} catch {
|
|
$statusCode = $_.Exception.Response.StatusCode.value__
|
|
if ($statusCode -ne 401) {
|
|
throw "Expected 401, got $statusCode"
|
|
}
|
|
Write-Host " Old token correctly rejected (401)" -ForegroundColor Gray
|
|
}
|
|
}
|
|
|
|
# Test 5: New Token Works
|
|
Test-Api "New Access Token Works" {
|
|
$headers = @{ "Authorization" = "Bearer $($script:accessToken2)" }
|
|
$response = Invoke-RestMethod -Uri "$baseUrl/api/auth/me" -Method Get -Headers $headers
|
|
|
|
if ($response.userId -ne $script:userId) {
|
|
throw "User ID mismatch"
|
|
}
|
|
|
|
Write-Host " New token validated successfully" -ForegroundColor Gray
|
|
}
|
|
|
|
# Test 6: Logout
|
|
Test-Api "Logout - Revoke Refresh Token" {
|
|
$body = @{ refreshToken = $script:refreshToken2 } | ConvertTo-Json
|
|
$response = Invoke-RestMethod -Uri "$baseUrl/api/auth/logout" `
|
|
-Method Post -ContentType "application/json" -Body $body
|
|
|
|
if (-not ($response.message -like "*success*")) {
|
|
throw "Logout did not return success"
|
|
}
|
|
|
|
Write-Host " Token revoked successfully" -ForegroundColor Gray
|
|
}
|
|
|
|
# Test 7: Revoked Token Rejected
|
|
Test-Api "Revoked Token Cannot Be Used" {
|
|
$body = @{ refreshToken = $script:refreshToken2 } | ConvertTo-Json
|
|
|
|
try {
|
|
$response = Invoke-RestMethod -Uri "$baseUrl/api/auth/refresh" `
|
|
-Method Post -ContentType "application/json" -Body $body
|
|
throw "Revoked token was accepted - SECURITY ISSUE!"
|
|
} catch {
|
|
$statusCode = $_.Exception.Response.StatusCode.value__
|
|
if ($statusCode -ne 401) {
|
|
throw "Expected 401, got $statusCode"
|
|
}
|
|
Write-Host " Revoked token correctly rejected" -ForegroundColor Gray
|
|
}
|
|
}
|
|
|
|
# ============================================================================
|
|
# PHASE 2: RBAC TESTS
|
|
# ============================================================================
|
|
|
|
Write-Host "`n" -ForegroundColor Yellow
|
|
Write-Host "=====================================" -ForegroundColor Yellow
|
|
Write-Host " PHASE 2: RBAC TESTS" -ForegroundColor Yellow
|
|
Write-Host "=====================================" -ForegroundColor Yellow
|
|
|
|
# Global variables for RBAC tests
|
|
$script:rbacAccessToken = ""
|
|
$script:rbacRefreshToken = ""
|
|
$script:rbacTenantSlug = ""
|
|
|
|
# Test 8: Register for RBAC
|
|
Test-Api "Register Tenant for RBAC Testing" {
|
|
$slug = "rbac-$(Get-Random -Minimum 1000 -Maximum 9999)"
|
|
$body = @{
|
|
tenantName = "RBAC Test Corp"
|
|
tenantSlug = $slug
|
|
subscriptionPlan = "Professional"
|
|
adminEmail = "rbac@test.com"
|
|
adminPassword = "Admin@1234"
|
|
adminFullName = "RBAC Admin"
|
|
} | ConvertTo-Json
|
|
|
|
$response = Invoke-RestMethod -Uri "$baseUrl/api/tenants/register" `
|
|
-Method Post -ContentType "application/json" -Body $body
|
|
|
|
$script:rbacAccessToken = $response.accessToken
|
|
$script:rbacRefreshToken = $response.refreshToken
|
|
$script:rbacTenantSlug = $slug
|
|
|
|
Write-Host " Tenant: $slug" -ForegroundColor Gray
|
|
}
|
|
|
|
# Test 9: Verify TenantOwner Role
|
|
Test-Api "Verify TenantOwner Role Assignment" {
|
|
$headers = @{ "Authorization" = "Bearer $($script:rbacAccessToken)" }
|
|
$response = Invoke-RestMethod -Uri "$baseUrl/api/auth/me" -Method Get -Headers $headers
|
|
|
|
if ($response.tenantRole -ne "TenantOwner" -or $response.role -ne "TenantOwner") {
|
|
throw "Expected TenantOwner, got tenantRole=$($response.tenantRole), role=$($response.role)"
|
|
}
|
|
|
|
Write-Host " Role: $($response.tenantRole)" -ForegroundColor Gray
|
|
}
|
|
|
|
# Test 10: Role Persistence
|
|
Test-Api "Role Persistence Across Login" {
|
|
$body = @{
|
|
tenantSlug = $script:rbacTenantSlug
|
|
email = "rbac@test.com"
|
|
password = "Admin@1234"
|
|
} | ConvertTo-Json
|
|
|
|
$response = Invoke-RestMethod -Uri "$baseUrl/api/auth/login" `
|
|
-Method Post -ContentType "application/json" -Body $body
|
|
|
|
$headers = @{ "Authorization" = "Bearer $($response.accessToken)" }
|
|
$meResponse = Invoke-RestMethod -Uri "$baseUrl/api/auth/me" -Method Get -Headers $headers
|
|
|
|
if ($meResponse.tenantRole -ne "TenantOwner") {
|
|
throw "Role not persisted, got $($meResponse.tenantRole)"
|
|
}
|
|
|
|
Write-Host " Role persisted after login" -ForegroundColor Gray
|
|
}
|
|
|
|
# Test 11: Role in Refreshed Token
|
|
Test-Api "Role Preserved in Refreshed Token" {
|
|
$body = @{ refreshToken = $script:rbacRefreshToken } | ConvertTo-Json
|
|
$response = Invoke-RestMethod -Uri "$baseUrl/api/auth/refresh" `
|
|
-Method Post -ContentType "application/json" -Body $body
|
|
|
|
$headers = @{ "Authorization" = "Bearer $($response.accessToken)" }
|
|
$meResponse = Invoke-RestMethod -Uri "$baseUrl/api/auth/me" -Method Get -Headers $headers
|
|
|
|
if ($meResponse.tenantRole -ne "TenantOwner") {
|
|
throw "Role not preserved in refresh, got $($meResponse.tenantRole)"
|
|
}
|
|
|
|
Write-Host " Role preserved after token refresh" -ForegroundColor Gray
|
|
}
|
|
|
|
# Test 12: JWT Claims
|
|
Test-Api "JWT Claims Inspection" {
|
|
$headers = @{ "Authorization" = "Bearer $($script:rbacAccessToken)" }
|
|
$response = Invoke-RestMethod -Uri "$baseUrl/api/auth/me" -Method Get -Headers $headers
|
|
|
|
$required = @("userId", "email", "tenantRole", "role", "tenantId")
|
|
foreach ($claim in $required) {
|
|
if (-not $response.$claim) {
|
|
throw "Missing claim: $claim"
|
|
}
|
|
}
|
|
|
|
Write-Host " All required claims present" -ForegroundColor Gray
|
|
}
|
|
|
|
# ============================================================================
|
|
# PHASE 3: REGRESSION TESTS
|
|
# ============================================================================
|
|
|
|
Write-Host "`n" -ForegroundColor Yellow
|
|
Write-Host "=====================================" -ForegroundColor Yellow
|
|
Write-Host " PHASE 3: REGRESSION TESTS" -ForegroundColor Yellow
|
|
Write-Host "=====================================" -ForegroundColor Yellow
|
|
|
|
# Test 13: Password Hashing
|
|
Test-Api "Password Hashing (Day 4 Regression)" {
|
|
$slug = "hash-$(Get-Random -Minimum 1000 -Maximum 9999)"
|
|
$body = @{
|
|
tenantName = "Hash Test"
|
|
tenantSlug = $slug
|
|
subscriptionPlan = "Free"
|
|
adminEmail = "hash@test.com"
|
|
adminPassword = "Password@123"
|
|
adminFullName = "Hash Tester"
|
|
} | ConvertTo-Json
|
|
|
|
$regResponse = Invoke-RestMethod -Uri "$baseUrl/api/tenants/register" `
|
|
-Method Post -ContentType "application/json" -Body $body
|
|
|
|
# Try login
|
|
$loginBody = @{
|
|
tenantSlug = $slug
|
|
email = "hash@test.com"
|
|
password = "Password@123"
|
|
} | ConvertTo-Json
|
|
|
|
$loginResponse = Invoke-RestMethod -Uri "$baseUrl/api/auth/login" `
|
|
-Method Post -ContentType "application/json" -Body $loginBody
|
|
|
|
if (-not $loginResponse.accessToken) {
|
|
throw "Login failed after registration"
|
|
}
|
|
|
|
Write-Host " Password hashing working correctly" -ForegroundColor Gray
|
|
}
|
|
|
|
# Test 14: JWT Authentication
|
|
Test-Api "JWT Authentication (Day 4 Regression)" {
|
|
$headers = @{ "Authorization" = "Bearer $($script:rbacAccessToken)" }
|
|
$response = Invoke-RestMethod -Uri "$baseUrl/api/auth/me" -Method Get -Headers $headers
|
|
|
|
if (-not $response.userId) {
|
|
throw "JWT authentication failed"
|
|
}
|
|
|
|
Write-Host " JWT authentication working" -ForegroundColor Gray
|
|
}
|
|
|
|
# ============================================================================
|
|
# TEST SUMMARY
|
|
# ============================================================================
|
|
|
|
Write-Host "`n" -ForegroundColor Magenta
|
|
Write-Host "===================================================" -ForegroundColor Magenta
|
|
Write-Host " TEST EXECUTION SUMMARY" -ForegroundColor Magenta
|
|
Write-Host "===================================================" -ForegroundColor Magenta
|
|
|
|
Write-Host "`nTotal Tests: $totalTests" -ForegroundColor White
|
|
Write-Host "Passed: $passedTests" -ForegroundColor Green
|
|
Write-Host "Failed: $failedTests" -ForegroundColor $(if ($failedTests -eq 0) { "Green" } else { "Red" })
|
|
|
|
$passRate = if ($totalTests -gt 0) { [math]::Round(($passedTests / $totalTests) * 100, 2) } else { 0 }
|
|
Write-Host "Pass Rate: $passRate%" -ForegroundColor $(if ($passRate -ge 95) { "Green" } elseif ($passRate -ge 80) { "Yellow" } else { "Red" })
|
|
|
|
if ($failedTests -gt 0) {
|
|
Write-Host "`nFailed Tests:" -ForegroundColor Red
|
|
foreach ($error in $errors) {
|
|
Write-Host " - $($error.Name)" -ForegroundColor Red
|
|
Write-Host " $($error.Error)" -ForegroundColor DarkRed
|
|
}
|
|
}
|
|
|
|
Write-Host "`n===================================================" -ForegroundColor Magenta
|
|
Write-Host " DEPLOYMENT RECOMMENDATION" -ForegroundColor Magenta
|
|
Write-Host "===================================================" -ForegroundColor Magenta
|
|
|
|
if ($passRate -eq 100) {
|
|
Write-Host "`n[EXCELLENT] All tests passed. Ready for production!" -ForegroundColor Green
|
|
Write-Host "Recommendation: DEPLOY" -ForegroundColor Green
|
|
exit 0
|
|
} elseif ($passRate -ge 95) {
|
|
Write-Host "`n[GOOD] Minor issues found. Review failed tests." -ForegroundColor Yellow
|
|
Write-Host "Recommendation: CONDITIONAL DEPLOY" -ForegroundColor Yellow
|
|
exit 0
|
|
} elseif ($passRate -ge 80) {
|
|
Write-Host "`n[WARNING] Multiple issues found. Fix before deploy." -ForegroundColor Yellow
|
|
Write-Host "Recommendation: DO NOT DEPLOY" -ForegroundColor Yellow
|
|
exit 1
|
|
} else {
|
|
Write-Host "`n[CRITICAL] Major issues found. DO NOT DEPLOY!" -ForegroundColor Red
|
|
Write-Host "Recommendation: DO NOT DEPLOY" -ForegroundColor Red
|
|
exit 1
|
|
}
|