GET /api/public/home
GET /api/public/notices/{id}
SQLite / MySQL 双数据库只读连接
.env 与现有数据库配置兼容
公告 HTML 安全清洗
学校、班级、考试、科目、报名及系统通知聚合
This commit is contained in:
@@ -5,8 +5,8 @@ $ErrorActionPreference = 'Stop'
|
||||
Set-StrictMode -Version Latest
|
||||
|
||||
$repositoryRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
||||
$temporaryRoot = [System.IO.Path]::GetFullPath([System.IO.Path]::GetTempPath())
|
||||
$testDirectory = Join-Path $temporaryRoot ("eis-migration-smoke-{0}" -f [guid]::NewGuid().ToString('N'))
|
||||
$smokeArtifactRoot = [System.IO.Path]::GetFullPath((Join-Path $repositoryRoot 'artifacts\smoke'))
|
||||
$testDirectory = Join-Path $smokeArtifactRoot ("eis-migration-smoke-{0}" -f [guid]::NewGuid().ToString('N'))
|
||||
$nodeProcess = $null
|
||||
$dotnetProcess = $null
|
||||
|
||||
@@ -93,12 +93,13 @@ try {
|
||||
|
||||
$nodeExecutable = (Get-Command node.exe -ErrorAction Stop).Source
|
||||
$dotnetExecutable = (Get-Command dotnet.exe -ErrorAction Stop).Source
|
||||
$nodeProcess = Start-TestProcess -FileName $nodeExecutable -ArgumentList @('server.mjs') -Environment @{
|
||||
$smokeDatabasePath = Join-Path $testDirectory 'smoke.sqlite'
|
||||
$nodeEnvironment = @{
|
||||
NODE_ENV = 'test'
|
||||
HOST = '127.0.0.1'
|
||||
PORT = [string] $legacyPort
|
||||
DATABASE_CLIENT = 'sqlite'
|
||||
SQLITE_PATH = (Join-Path $testDirectory 'smoke.sqlite')
|
||||
SQLITE_PATH = $smokeDatabasePath
|
||||
INITIAL_ADMIN_USERNAME = 'migration_admin'
|
||||
INITIAL_ADMIN_PASSWORD = 'Migration123!'
|
||||
INITIAL_ADMIN_DISPLAY_NAME = '迁移测试管理员'
|
||||
@@ -107,6 +108,19 @@ try {
|
||||
TOTP_ENCRYPTION_KEY = 'migration-smoke-totp-key-32-characters-minimum'
|
||||
DOCUMENT_VERIFICATION_SECRET = 'migration-smoke-document-key-32-characters-minimum'
|
||||
}
|
||||
$seedProcess = Start-TestProcess -FileName $nodeExecutable -ArgumentList @('scripts/import-test-data.mjs', '--sqlite') -Environment $nodeEnvironment
|
||||
if (-not $seedProcess.WaitForExit(30000)) {
|
||||
$seedProcess.Kill($true)
|
||||
throw 'Timed out while preparing the migration smoke-test database'
|
||||
}
|
||||
$seedOutput = $seedProcess.StandardOutput.ReadToEnd()
|
||||
$seedError = $seedProcess.StandardError.ReadToEnd()
|
||||
if ($seedProcess.ExitCode -ne 0) {
|
||||
throw "Failed to prepare the migration smoke-test database`n$seedOutput`n$seedError"
|
||||
}
|
||||
$seedProcess.Dispose()
|
||||
|
||||
$nodeProcess = Start-TestProcess -FileName $nodeExecutable -ArgumentList @('server.mjs') -Environment $nodeEnvironment
|
||||
|
||||
$projectPath = Join-Path $repositoryRoot 'src\Eis.Web\Eis.Web.csproj'
|
||||
$dotnetProcess = Start-TestProcess -FileName $dotnetExecutable -ArgumentList @(
|
||||
@@ -121,6 +135,8 @@ try {
|
||||
ASPNETCORE_ENVIRONMENT = 'Development'
|
||||
LegacyNode__Enabled = 'true'
|
||||
LegacyNode__BaseUrl = $legacyBaseUrl
|
||||
DATABASE_CLIENT = 'sqlite'
|
||||
SQLITE_PATH = $smokeDatabasePath
|
||||
}
|
||||
|
||||
Wait-ForUrl -Uri "$webBaseUrl/health/live" -Processes @($nodeProcess, $dotnetProcess)
|
||||
@@ -136,28 +152,66 @@ try {
|
||||
throw 'ASP.NET Core did not serve the existing client module'
|
||||
}
|
||||
|
||||
$homePayload = Invoke-RestMethod -Uri "$webBaseUrl/api/public/home"
|
||||
$homeResponse = Invoke-WebRequest -Uri "$webBaseUrl/api/public/home"
|
||||
if ($homeResponse.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
|
||||
throw 'Public home request did not use the native ASP.NET Core endpoint'
|
||||
}
|
||||
$homePayload = $homeResponse.Content | ConvertFrom-Json
|
||||
if ($homePayload.ok -ne $true) {
|
||||
throw 'Legacy public API proxy did not preserve the JSON response'
|
||||
throw 'Native public API did not return a valid JSON response'
|
||||
}
|
||||
$legacyHomePayload = Invoke-RestMethod -Uri "$legacyBaseUrl/api/public/home"
|
||||
if ($homePayload.stats.candidates -ne $legacyHomePayload.stats.candidates -or
|
||||
$homePayload.stats.exams -ne $legacyHomePayload.stats.exams -or
|
||||
$homePayload.stats.registrations -ne $legacyHomePayload.stats.registrations -or
|
||||
$homePayload.schools.Count -ne $legacyHomePayload.schools.Count -or
|
||||
$homePayload.classes.Count -ne $legacyHomePayload.classes.Count -or
|
||||
$homePayload.notices.Count -ne $legacyHomePayload.notices.Count) {
|
||||
throw 'Native public home aggregates do not match the legacy API'
|
||||
}
|
||||
foreach ($legacyExam in $legacyHomePayload.exams) {
|
||||
$nativeExam = $homePayload.exams | Where-Object id -eq $legacyExam.id | Select-Object -First 1
|
||||
if ($null -eq $nativeExam -or $nativeExam.subjects.Count -ne $legacyExam.subjects.Count -or
|
||||
$nativeExam.totalScore -ne $legacyExam.totalScore -or $nativeExam.registrationCount -ne $legacyExam.registrationCount) {
|
||||
throw "Native public exam projection does not match the legacy API for $($legacyExam.id)"
|
||||
}
|
||||
}
|
||||
|
||||
$session = [Microsoft.PowerShell.Commands.WebRequestSession]::new()
|
||||
$loginBody = @{ username = 'migration_admin'; password = 'Migration123!' } | ConvertTo-Json -Compress
|
||||
$loginBody = @{ username = 'admin'; password = '12345678' } | ConvertTo-Json -Compress
|
||||
$login = Invoke-RestMethod -Uri "$webBaseUrl/api/auth/login" -Method Post -ContentType 'application/json' -Body $loginBody -WebSession $session
|
||||
if ($login.ok -ne $true -or $login.user.username -ne 'migration_admin') {
|
||||
if ($login.ok -ne $true -or $login.user.username -ne 'admin') {
|
||||
throw 'Legacy login API proxy did not preserve the response'
|
||||
}
|
||||
|
||||
$currentUser = Invoke-RestMethod -Uri "$webBaseUrl/api/auth/me" -WebSession $session
|
||||
if ($currentUser.user.username -ne 'migration_admin') {
|
||||
if ($currentUser.user.username -ne 'admin') {
|
||||
throw 'Legacy API proxy did not preserve the session cookie'
|
||||
}
|
||||
|
||||
$noticeBody = @{
|
||||
title = 'ASP.NET Core 迁移冒烟通知'
|
||||
content = '<p>原生公开读取验证</p><script>throw new Error("unsafe")</script>'
|
||||
category = '系统测试'
|
||||
pinned = $true
|
||||
status = 'published'
|
||||
} | ConvertTo-Json -Compress
|
||||
$createdNotice = Invoke-RestMethod -Uri "$webBaseUrl/api/admin/notices" -Method Post -ContentType 'application/json' -Body $noticeBody -WebSession $session
|
||||
$noticeResponse = Invoke-WebRequest -Uri "$webBaseUrl/api/public/notices/$($createdNotice.notice.id)"
|
||||
if ($noticeResponse.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
|
||||
throw 'Public notice request did not use the native ASP.NET Core endpoint'
|
||||
}
|
||||
$noticePayload = $noticeResponse.Content | ConvertFrom-Json
|
||||
if ($noticePayload.notice.title -ne 'ASP.NET Core 迁移冒烟通知' -or $noticePayload.notice.content -match '<script') {
|
||||
throw 'Native public notice endpoint did not preserve data or sanitize unsafe content'
|
||||
}
|
||||
|
||||
[pscustomobject]@{
|
||||
AspNetCoreHost = 'passed'
|
||||
StaticAssets = 'passed'
|
||||
JsonProxy = 'passed'
|
||||
SessionCookie = 'passed'
|
||||
NativePublicApi = 'passed'
|
||||
} | Format-List
|
||||
}
|
||||
finally {
|
||||
@@ -173,10 +227,21 @@ finally {
|
||||
|
||||
if (Test-Path -LiteralPath $testDirectory) {
|
||||
$resolvedTestDirectory = [System.IO.Path]::GetFullPath($testDirectory)
|
||||
if (-not $resolvedTestDirectory.StartsWith($temporaryRoot, [StringComparison]::OrdinalIgnoreCase) -or
|
||||
if (-not $resolvedTestDirectory.StartsWith($smokeArtifactRoot, [StringComparison]::OrdinalIgnoreCase) -or
|
||||
-not ([System.IO.Path]::GetFileName($resolvedTestDirectory)).StartsWith('eis-migration-smoke-', [StringComparison]::Ordinal)) {
|
||||
throw "Refusing to remove unexpected smoke-test directory: $resolvedTestDirectory"
|
||||
}
|
||||
Remove-Item -LiteralPath $resolvedTestDirectory -Recurse -Force
|
||||
for ($attempt = 1; $attempt -le 20; $attempt++) {
|
||||
try {
|
||||
Remove-Item -LiteralPath $resolvedTestDirectory -Recurse -Force -ErrorAction Stop
|
||||
break
|
||||
}
|
||||
catch {
|
||||
if ($attempt -eq 20) {
|
||||
throw
|
||||
}
|
||||
Start-Sleep -Milliseconds 200
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user