Commit all scripts
This commit is contained in:
379
colaflow-api/qa-day5-test.ps1
Normal file
379
colaflow-api/qa-day5-test.ps1
Normal file
@@ -0,0 +1,379 @@
|
||||
# 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
|
||||
}
|
||||
Reference in New Issue
Block a user