# MilliSim System - Full API Integration Test Script $baseUrl = "http://localhost:5173" $apiBase = "$baseUrl/api" $token = $null $passCount = 0 $failCount = 0 $results = @() function Write-TestResult { param($name, $success, $detail = "") $status = if ($success) { "PASS" } else { "FAIL" } $color = if ($success) { "Green" } else { "Red" } Write-Host "[$status] $name - $detail" -ForegroundColor $color $script:results += [PSCustomObject]@{ Name = $name Status = $status Detail = $detail } if ($success) { $script:passCount++ } else { $script:failCount++ } } function Get-CaptchaCode { param($svg) $m = [regex]::Matches($svg, ']*>([A-Za-z0-9])') $code = "" foreach ($x in $m) { $code += $x.Groups[1].Value } return $code } function Safe-Invoke { param($uri, $method, $headers = $null, $body = $null) $params = @{ Uri = $uri Method = $method ContentType = "application/json" } if ($headers) { $params.Headers = $headers } if ($body) { $params.Body = $body } try { $resp = Invoke-RestMethod @params return @{ Success = $true; Data = $resp; Error = $null } } catch { $errDetail = $_.Exception.Message if ($_.Exception.Response) { try { $sr = New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream()) $errBody = $sr.ReadToEnd() | ConvertFrom-Json $errDetail = $errBody.message } catch {} } return @{ Success = $false; Data = $null; Error = $errDetail } } } Write-Host "========================================" -ForegroundColor Cyan Write-Host "MilliSim System - Full API Integration Test" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "" # Part 1: Public APIs Write-Host "--- Part 1: Public API Tests ---" -ForegroundColor Yellow # 1. Captcha $r = Safe-Invoke "$apiBase/auth/captcha" "Get" if ($r.Success) { $captchaCode = Get-CaptchaCode -svg $r.Data.data.svg $captchaId = $r.Data.data.captchaId Write-TestResult "Captcha" ($r.Data.code -eq 200 -and $captchaCode.Length -eq 4) "code=$($r.Data.code), captcha=$captchaCode" } else { Write-TestResult "Captcha" $false $r.Error } # 2. Public Projects $r = Safe-Invoke "$apiBase/projects/public?page=1&pageSize=10" "Get" if ($r.Success) { Write-TestResult "Public Projects" ($r.Data.code -eq 200) "count=$($r.Data.data.items.Count)" } else { Write-TestResult "Public Projects" $false $r.Error } # 3. Categories $r = Safe-Invoke "$apiBase/categories" "Get" if ($r.Success) { Write-TestResult "Categories" ($r.Data.code -eq 200) "count=$($r.Data.data.Count)" } else { Write-TestResult "Categories" $false $r.Error } # 4. Platforms $r = Safe-Invoke "$apiBase/platforms" "Get" if ($r.Success) { Write-TestResult "Platforms" ($r.Data.code -eq 200) "count=$($r.Data.data.Count)" } else { Write-TestResult "Platforms" $false $r.Error } # 5. Tags $r = Safe-Invoke "$apiBase/tags" "Get" if ($r.Success) { Write-TestResult "Tags" ($r.Data.code -eq 200) "count=$($r.Data.data.Count)" } else { Write-TestResult "Tags" $false $r.Error } # 6. Banners $r = Safe-Invoke "$apiBase/banners" "Get" if ($r.Success) { Write-TestResult "Banners" ($r.Data.code -eq 200) "count=$($r.Data.data.Count)" } else { Write-TestResult "Banners" $false $r.Error } # 7. Partners $r = Safe-Invoke "$apiBase/partners" "Get" if ($r.Success) { Write-TestResult "Partners" ($r.Data.code -eq 200) "count=$($r.Data.data.Count)" } else { Write-TestResult "Partners" $false $r.Error } # 8. Project Detail $r = Safe-Invoke "$apiBase/projects/public/1" "Get" if ($r.Success) { Write-TestResult "Project Detail" ($r.Data.code -eq 200) "title=$($r.Data.data.name)" } else { Write-TestResult "Project Detail" $false $r.Error } # 9. Wake Protocols (public) $r = Safe-Invoke "$apiBase/system/wake-protocols" "Get" if ($r.Success) { Write-TestResult "Wake Protocols" ($r.Data.code -eq 200) "count=$($r.Data.data.Count)" } else { Write-TestResult "Wake Protocols" $false $r.Error } Write-Host "" # Part 2: Login Auth Tests Write-Host "--- Part 2: Login Auth Tests ---" -ForegroundColor Yellow # 10. Wrong captcha login $body = @{ username = "admin"; password = "admin123"; captchaId = $captchaId; captchaCode = "XXXX" } | ConvertTo-Json $r = Safe-Invoke "$apiBase/auth/login" "Post" $null $body if (-not $r.Success -or $r.Data.code -eq 400) { $msg = if ($r.Data) { $r.Data.message } else { $r.Error } Write-TestResult "Wrong Captcha Login" $true "rejected: $msg" } else { Write-TestResult "Wrong Captcha Login" $false "should be rejected" } # 11. Correct login $r = Safe-Invoke "$apiBase/auth/captcha" "Get" if ($r.Success) { $captchaCode = Get-CaptchaCode -svg $r.Data.data.svg $captchaId = $r.Data.data.captchaId $body = @{ username = "admin"; password = "admin123"; captchaId = $captchaId; captchaCode = $captchaCode } | ConvertTo-Json $r = Safe-Invoke "$apiBase/auth/login" "Post" $null $body if ($r.Success -and $r.Data.code -eq 200) { $token = $r.Data.data.token Write-TestResult "Admin Login" $true "user=$($r.Data.data.username), role=$($r.Data.data.roleName), tokenLen=$($token.Length)" } else { $msg = if ($r.Data) { $r.Data.message } else { $r.Error } Write-TestResult "Admin Login" $false $msg } } else { Write-TestResult "Admin Login" $false "captcha failed: $($r.Error)" } Write-Host "" # Part 3: Authenticated API Tests Write-Host "--- Part 3: Authenticated API Tests (Admin Token) ---" -ForegroundColor Yellow $headers = @{ Authorization = "Bearer $token" } # 12. Profile $r = Safe-Invoke "$apiBase/auth/profile" "Get" $headers if ($r.Success) { Write-TestResult "Get Profile" ($r.Data.code -eq 200) "user=$($r.Data.data.username), role=$($r.Data.data.roleName)" } else { Write-TestResult "Get Profile" $false $r.Error } # 13. Users List $r = Safe-Invoke "$apiBase/users?page=1&pageSize=10" "Get" $headers if ($r.Success) { Write-TestResult "Users List" ($r.Data.code -eq 200) "count=$($r.Data.data.items.Count)" } else { Write-TestResult "Users List" $false $r.Error } # 14. User Detail $r = Safe-Invoke "$apiBase/users/1" "Get" $headers if ($r.Success) { Write-TestResult "User Detail" ($r.Data.code -eq 200) "user=$($r.Data.data.username)" } else { Write-TestResult "User Detail" $false $r.Error } # 15. System Settings $r = Safe-Invoke "$apiBase/system/settings" "Get" $headers if ($r.Success) { Write-TestResult "System Settings" ($r.Data.code -eq 200) "count=$($r.Data.data.Count)" } else { Write-TestResult "System Settings" $false $r.Error } # 16. Operation Logs $r = Safe-Invoke "$apiBase/system/logs?page=1&pageSize=10" "Get" $headers if ($r.Success) { Write-TestResult "Operation Logs" ($r.Data.code -eq 200) "count=$($r.Data.data.items.Count)" } else { Write-TestResult "Operation Logs" $false $r.Error } # 17. Backups $r = Safe-Invoke "$apiBase/system/backups" "Get" $headers if ($r.Success) { Write-TestResult "Backups List" ($r.Data.code -eq 200) "count=$($r.Data.data.Count)" } else { Write-TestResult "Backups List" $false $r.Error } # 18. Notifications $r = Safe-Invoke "$apiBase/notifications?page=1&pageSize=10" "Get" $headers if ($r.Success) { Write-TestResult "Notifications List" ($r.Data.code -eq 200) "count=$($r.Data.data.items.Count)" } else { Write-TestResult "Notifications List" $false $r.Error } # 19. Unread Count $r = Safe-Invoke "$apiBase/notifications/unread-count" "Get" $headers if ($r.Success) { Write-TestResult "Unread Count" ($r.Data.code -eq 200) "unread=$($r.Data.data)" } else { Write-TestResult "Unread Count" $false $r.Error } # 20. Authorizations $r = Safe-Invoke "$apiBase/authorizations?page=1&pageSize=10" "Get" $headers if ($r.Success) { Write-TestResult "Authorizations List" ($r.Data.code -eq 200) "count=$($r.Data.data.items.Count)" } else { Write-TestResult "Authorizations List" $false $r.Error } # 21. Consultations $r = Safe-Invoke "$apiBase/consultations?page=1&pageSize=10" "Get" $headers if ($r.Success) { Write-TestResult "Consultations List" ($r.Data.code -eq 200) "count=$($r.Data.data.items.Count)" } else { Write-TestResult "Consultations List" $false $r.Error } # 22. Dashboard Stats $r = Safe-Invoke "$apiBase/dashboard" "Get" $headers if ($r.Success) { Write-TestResult "Dashboard Stats" ($r.Data.code -eq 200) "projects=$($r.Data.data.stats.projectCount), customers=$($r.Data.data.stats.customerCount)" } else { Write-TestResult "Dashboard Stats" $false $r.Error } # 23. Dashboard Trends if ($r.Success) { Write-TestResult "Dashboard Trends" ($r.Data.data.trends.Count -ge 0) "points=$($r.Data.data.trends.Count)" } else { Write-TestResult "Dashboard Trends" $false "see Dashboard Stats" } # 24. Admin Projects List $r = Safe-Invoke "$apiBase/projects?page=1&pageSize=10" "Get" $headers if ($r.Success) { Write-TestResult "Admin Projects List" ($r.Data.code -eq 200) "count=$($r.Data.data.items.Count)" } else { Write-TestResult "Admin Projects List" $false $r.Error } Write-Host "" # Part 4: CRUD Tests Write-Host "--- Part 4: CRUD Operation Tests ---" -ForegroundColor Yellow # 25. Create Category $newCategoryId = 0 $body = @{ name = "TestCategory_$(Get-Random)"; sort = 99; icon = "folder" } | ConvertTo-Json $r = Safe-Invoke "$apiBase/categories" "Post" $headers $body if ($r.Success -and $r.Data.code -eq 200) { $newCategoryId = $r.Data.data.id Write-TestResult "Create Category" $true "id=$newCategoryId" } else { $msg = if ($r.Data) { $r.Data.message } else { $r.Error } Write-TestResult "Create Category" $false $msg } # 26. Update Category if ($newCategoryId -gt 0) { $body = @{ name = "TestCategoryUpdated_$(Get-Random)"; sort = 99; icon = "folder" } | ConvertTo-Json $r = Safe-Invoke "$apiBase/categories/$newCategoryId" "Put" $headers $body if ($r.Success) { Write-TestResult "Update Category" ($r.Data.code -eq 200) "id=$newCategoryId" } else { Write-TestResult "Update Category" $false $r.Error } } # 27. Delete Category if ($newCategoryId -gt 0) { $r = Safe-Invoke "$apiBase/categories/$newCategoryId" "Delete" $headers if ($r.Success) { Write-TestResult "Delete Category" ($r.Data.code -eq 200) "id=$newCategoryId" } else { Write-TestResult "Delete Category" $false $r.Error } } # 28. Create Tag $newTagId = 0 $body = @{ name = "TestTag_$(Get-Random)"; color = "#1677FF" } | ConvertTo-Json $r = Safe-Invoke "$apiBase/tags" "Post" $headers $body if ($r.Success -and $r.Data.code -eq 200) { $newTagId = $r.Data.data.id Write-TestResult "Create Tag" $true "id=$newTagId" } else { $msg = if ($r.Data) { $r.Data.message } else { $r.Error } Write-TestResult "Create Tag" $false $msg } # 29. Delete Tag if ($newTagId -gt 0) { $r = Safe-Invoke "$apiBase/tags/$newTagId" "Delete" $headers if ($r.Success) { Write-TestResult "Delete Tag" ($r.Data.code -eq 200) "id=$newTagId" } else { Write-TestResult "Delete Tag" $false $r.Error } } # 30. Create Platform $newPlatformId = 0 $body = @{ name = "TestPlatform_$(Get-Random)"; code = "TST$(Get-Random)"; icon = "desktop" } | ConvertTo-Json $r = Safe-Invoke "$apiBase/platforms" "Post" $headers $body if ($r.Success -and $r.Data.code -eq 200) { $newPlatformId = $r.Data.data.id Write-TestResult "Create Platform" $true "id=$newPlatformId" } else { $msg = if ($r.Data) { $r.Data.message } else { $r.Error } Write-TestResult "Create Platform" $false $msg } # 31. Delete Platform if ($newPlatformId -gt 0) { $r = Safe-Invoke "$apiBase/platforms/$newPlatformId" "Delete" $headers if ($r.Success) { Write-TestResult "Delete Platform" ($r.Data.code -eq 200) "id=$newPlatformId" } else { Write-TestResult "Delete Platform" $false $r.Error } } # 32. Create Banner $newBannerId = 0 $body = @{ title = "TestBanner_$(Get-Random)"; image = "/uploads/test.jpg"; link = "/projects"; sortOrder = 99; status = "Active" } | ConvertTo-Json $r = Safe-Invoke "$apiBase/banners" "Post" $headers $body if ($r.Success -and $r.Data.code -eq 200) { $newBannerId = $r.Data.data.id Write-TestResult "Create Banner" $true "id=$newBannerId" } else { $msg = if ($r.Data) { $r.Data.message } else { $r.Error } Write-TestResult "Create Banner" $false $msg } # 33. Delete Banner if ($newBannerId -gt 0) { $r = Safe-Invoke "$apiBase/banners/$newBannerId" "Delete" $headers if ($r.Success) { Write-TestResult "Delete Banner" ($r.Data.code -eq 200) "id=$newBannerId" } else { Write-TestResult "Delete Banner" $false $r.Error } } # 34. Create Partner $newPartnerId = 0 $body = @{ name = "TestPartner_$(Get-Random)"; logo = "/uploads/logo.jpg"; link = "https://example.com"; sort = 99 } | ConvertTo-Json $r = Safe-Invoke "$apiBase/partners" "Post" $headers $body if ($r.Success -and $r.Data.code -eq 200) { $newPartnerId = $r.Data.data.id Write-TestResult "Create Partner" $true "id=$newPartnerId" } else { $msg = if ($r.Data) { $r.Data.message } else { $r.Error } Write-TestResult "Create Partner" $false $msg } # 35. Delete Partner if ($newPartnerId -gt 0) { $r = Safe-Invoke "$apiBase/partners/$newPartnerId" "Delete" $headers if ($r.Success) { Write-TestResult "Delete Partner" ($r.Data.code -eq 200) "id=$newPartnerId" } else { Write-TestResult "Delete Partner" $false $r.Error } } # 36. Create User (Sales) $newUserId = 0 $body = @{ username = "testsales_$(Get-Random)"; password = "Test1234"; nickname = "TestSales"; role = "Sales"; phone = "13800000000"; email = "test@test.com" } | ConvertTo-Json $r = Safe-Invoke "$apiBase/users" "Post" $headers $body if ($r.Success -and $r.Data.code -eq 200) { $newUserId = $r.Data.data.id Write-TestResult "Create User" $true "id=$newUserId" } else { $msg = if ($r.Data) { $r.Data.message } else { $r.Error } Write-TestResult "Create User" $false $msg } # 37. Delete User if ($newUserId -gt 0) { $r = Safe-Invoke "$apiBase/users/$newUserId" "Delete" $headers if ($r.Success) { Write-TestResult "Delete User" ($r.Data.code -eq 200) "id=$newUserId" } else { Write-TestResult "Delete User" $false $r.Error } } # 38. Submit Consultation $newConsultationId = 0 $body = @{ name = "TestUser"; contact = "13800000000"; content = "Test consultation content" } | ConvertTo-Json $r = Safe-Invoke "$apiBase/consultations" "Post" $null $body if ($r.Success -and $r.Data.code -eq 200) { $newConsultationId = $r.Data.data.id Write-TestResult "Submit Consultation" $true "id=$newConsultationId" } else { $msg = if ($r.Data) { $r.Data.message } else { $r.Error } Write-TestResult "Submit Consultation" $false $msg } # 39. Mark Consultation as Read if ($newConsultationId -gt 0) { $r = Safe-Invoke "$apiBase/consultations/$newConsultationId/read" "Post" $headers if ($r.Success) { Write-TestResult "Mark Consultation Read" ($r.Data.code -eq 200) "id=$newConsultationId" } else { Write-TestResult "Mark Consultation Read" $false $r.Error } } # 40. Delete Consultation if ($newConsultationId -gt 0) { $r = Safe-Invoke "$apiBase/consultations/$newConsultationId" "Delete" $headers if ($r.Success) { Write-TestResult "Delete Consultation" ($r.Data.code -eq 200) "id=$newConsultationId" } else { Write-TestResult "Delete Consultation" $false $r.Error } } Write-Host "" # Part 5: Permission Isolation Tests Write-Host "--- Part 5: Permission Isolation Tests ---" -ForegroundColor Yellow # 41. No Token Access $r = Safe-Invoke "$apiBase/auth/profile" "Get" if (-not $r.Success) { Write-TestResult "No Token Access" $true "rejected (401)" } else { Write-TestResult "No Token Access" $false "should be 401" } # 42. No Token Access Settings $r = Safe-Invoke "$apiBase/system/settings" "Get" if (-not $r.Success) { Write-TestResult "No Token Settings" $true "rejected (401)" } else { Write-TestResult "No Token Settings" $false "should be 401" } Write-Host "" # Part 6: Frontend Page Tests Write-Host "--- Part 6: Frontend Page Tests ---" -ForegroundColor Yellow # 43. Home Page try { $resp = Invoke-WebRequest -Uri "$baseUrl/" -Method Get -UseBasicParsing Write-TestResult "Home Page" ($resp.StatusCode -eq 200) "HTTP $($resp.StatusCode), len=$($resp.Content.Length)" } catch { Write-TestResult "Home Page" $false $_.Exception.Message } # 44. Projects Page try { $resp = Invoke-WebRequest -Uri "$baseUrl/projects" -Method Get -UseBasicParsing Write-TestResult "Projects Page" ($resp.StatusCode -eq 200) "HTTP $($resp.StatusCode)" } catch { Write-TestResult "Projects Page" $false $_.Exception.Message } # 45. Login Page try { $resp = Invoke-WebRequest -Uri "$baseUrl/login" -Method Get -UseBasicParsing Write-TestResult "Login Page" ($resp.StatusCode -eq 200) "HTTP $($resp.StatusCode)" } catch { Write-TestResult "Login Page" $false $_.Exception.Message } # 46. Admin Login Page try { $resp = Invoke-WebRequest -Uri "$baseUrl/admin/login" -Method Get -UseBasicParsing Write-TestResult "Admin Login Page" ($resp.StatusCode -eq 200) "HTTP $($resp.StatusCode)" } catch { Write-TestResult "Admin Login Page" $false $_.Exception.Message } # 47. Frontend Assets try { $resp = Invoke-WebRequest -Uri "$baseUrl/" -Method Get -UseBasicParsing $content = $resp.Content $hasJs = $content -match "script.+src" $hasCss = $content -match "link.+href" Write-TestResult "Frontend Assets" ($hasJs -or $hasCss) "JS=$hasJs, CSS=$hasCss" } catch { Write-TestResult "Frontend Assets" $false $_.Exception.Message } Write-Host "" Write-Host "========================================" -ForegroundColor Cyan Write-Host "Test Summary" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan $totalColor = if ($failCount -eq 0) { "Green" } else { "Yellow" } Write-Host "Pass: $passCount Fail: $failCount Total: $($passCount + $failCount)" -ForegroundColor $totalColor Write-Host "" if ($failCount -gt 0) { Write-Host "Failed Items:" -ForegroundColor Red $results | Where-Object { $_.Status -eq "FAIL" } | ForEach-Object { Write-Host " - $($_.Name): $($_.Detail)" -ForegroundColor Red } } Write-Host ""