[CmdletBinding()] param( [string]$PublishDirectory = (Join-Path $PSScriptRoot '..\artifacts\native-smoke-publish') ) $ErrorActionPreference = 'Stop' $repositoryRoot = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path $publishPath = [IO.Path]::GetFullPath( $(if ([IO.Path]::IsPathRooted($PublishDirectory)) { $PublishDirectory } else { Join-Path $repositoryRoot $PublishDirectory })) dotnet publish (Join-Path $repositoryRoot 'src\Eis.Web\Eis.Web.csproj') ` --configuration Release ` --output $publishPath ` --no-restore if ($LASTEXITCODE -ne 0) { throw 'ASP.NET Core 发布失败。' } $tempBase = [IO.Path]::GetFullPath([IO.Path]::GetTempPath()) $testRoot = Join-Path $tempBase ('eis-native-smoke-' + [Guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Path $testRoot | Out-Null $listener = [Net.Sockets.TcpListener]::new([Net.IPAddress]::Loopback, 0) $listener.Start() $port = ([Net.IPEndPoint]$listener.LocalEndpoint).Port $listener.Stop() $baseUrl = "http://127.0.0.1:$port" $stdoutPath = Join-Path $testRoot 'stdout.log' $stderrPath = Join-Path $testRoot 'stderr.log' $databasePath = Join-Path $testRoot 'eis.sqlite' $process = $null try { $process = Start-Process ` -FilePath 'dotnet' ` -ArgumentList (Join-Path $publishPath 'Eis.Web.dll') ` -WorkingDirectory $publishPath ` -Environment @{ ASPNETCORE_ENVIRONMENT = 'Development' ASPNETCORE_URLS = $baseUrl DATABASE_CLIENT = 'sqlite' SQLITE_PATH = $databasePath INITIAL_ADMIN_USERNAME = 'admin' INITIAL_ADMIN_PASSWORD = 'NativeSmoke123456' INITIAL_ADMIN_DISPLAY_NAME = '原生冒烟管理员' REDIS_URL = '' REDIS_SESSION_URL = '' } ` -RedirectStandardOutput $stdoutPath ` -RedirectStandardError $stderrPath ` -WindowStyle Hidden ` -PassThru $ready = $false for ($attempt = 0; $attempt -lt 60; $attempt++) { if ($process.HasExited) { break } try { $live = Invoke-RestMethod -Uri "$baseUrl/health/live" -TimeoutSec 2 $ready = $true break } catch { Start-Sleep -Milliseconds 250 } } if (-not $ready) { $stdout = Get-Content -Raw $stdoutPath -ErrorAction SilentlyContinue $stderr = Get-Content -Raw $stderrPath -ErrorAction SilentlyContinue throw "ASP.NET Core 发布产物未就绪。`nstdout: $stdout`nstderr: $stderr" } $migration = Invoke-RestMethod -Uri "$baseUrl/health/migration" -TimeoutSec 5 $homeResponse = Invoke-WebRequest -Uri "$baseUrl/" -TimeoutSec 5 $applicationScript = Invoke-WebRequest -Uri "$baseUrl/js/app.js" -TimeoutSec 10 $clientApiModule = Invoke-WebRequest -Uri "$baseUrl/js/client/api.js" -TimeoutSec 10 $regionDataModule = Invoke-WebRequest -Uri "$baseUrl/js/data/china-regions.js" -TimeoutSec 10 $ckeditor = Invoke-WebRequest -Uri "$baseUrl/vendor/ckeditor5/ckeditor5.js" -TimeoutSec 10 $session = [Microsoft.PowerShell.Commands.WebRequestSession]::new() $loginBody = @{ username = 'admin' password = 'NativeSmoke123456' } | ConvertTo-Json -Compress $login = Invoke-RestMethod ` -Uri "$baseUrl/api/auth/login" ` -Method Post ` -ContentType 'application/json' ` -Body $loginBody ` -WebSession $session ` -TimeoutSec 5 $currentUser = Invoke-RestMethod ` -Uri "$baseUrl/api/auth/me" ` -WebSession $session ` -TimeoutSec 5 $missing = Invoke-WebRequest ` -Uri "$baseUrl/api/removed-legacy-route" ` -SkipHttpErrorCheck ` -TimeoutSec 5 if ($live.status -ne 'healthy' -or $migration.legacyApiRemoved -ne $true -or $homeResponse.StatusCode -ne 200 -or $homeResponse.Content -notmatch '/js/app\.js' -or $applicationScript.RawContentLength -lt 100000 -or $applicationScript.Content -notmatch "from './client/api\.js'" -or $clientApiModule.RawContentLength -lt 1000 -or $regionDataModule.RawContentLength -lt 100000 -or $ckeditor.RawContentLength -lt 100000 -or $login.user.role -ne 'admin' -or $login.user.adminLevel -ne 'super' -or $currentUser.user.username -ne 'admin' -or $missing.StatusCode -ne 404) { throw '原生宿主冒烟断言失败。' } [pscustomobject]@{ Process = $process.ProcessName Live = $live.status LegacyRemoved = $migration.legacyApiRemoved HomeStatus = $homeResponse.StatusCode FrontendBytes = $applicationScript.RawContentLength CkeditorBytes = $ckeditor.RawContentLength LoginRole = $login.user.role CurrentUser = $currentUser.user.username UnknownApiStatus = $missing.StatusCode DatabaseTarget = $databasePath } | Format-List } finally { if ($null -ne $process -and -not $process.HasExited) { Stop-Process -Id $process.Id -Force $process.WaitForExit() } $resolvedTestRoot = [IO.Path]::GetFullPath($testRoot) if ($resolvedTestRoot.StartsWith($tempBase, [StringComparison]::OrdinalIgnoreCase) -and (Test-Path -LiteralPath $resolvedTestRoot)) { Remove-Item -LiteralPath $resolvedTestRoot -Recurse -Force } }