113 lines
5.2 KiB
PowerShell
113 lines
5.2 KiB
PowerShell
$ErrorActionPreference = 'Stop'
|
|
|
|
$modulePath = Join-Path $PSScriptRoot '..\scripts\LocalDev.psm1'
|
|
Import-Module $modulePath -Force
|
|
|
|
function Assert-Equal {
|
|
param($Expected, $Actual, [string]$Message)
|
|
|
|
if ($Expected -ne $Actual) {
|
|
throw "$Message (expected '$Expected', got '$Actual')"
|
|
}
|
|
}
|
|
|
|
function Assert-Throws {
|
|
param([scriptblock]$Action, [string]$Pattern, [string]$Message)
|
|
|
|
try {
|
|
& $Action
|
|
}
|
|
catch {
|
|
if ($_.Exception.Message -notmatch $Pattern) {
|
|
throw "$Message (unexpected error: $($_.Exception.Message))"
|
|
}
|
|
return
|
|
}
|
|
throw "$Message (no error was thrown)"
|
|
}
|
|
|
|
$fixture = Join-Path ([System.IO.Path]::GetTempPath()) ("zarinpal-local-dev-" + [guid]::NewGuid())
|
|
New-Item -ItemType Directory -Path $fixture | Out-Null
|
|
|
|
try {
|
|
$validEnv = Join-Path $fixture '.env'
|
|
@(
|
|
'# ignored comment'
|
|
'PLAIN=value'
|
|
'QUOTED="value with spaces"'
|
|
'EMPTY='
|
|
) | Set-Content -LiteralPath $validEnv -Encoding utf8
|
|
|
|
$values = Read-DotEnvFile -Path $validEnv
|
|
Assert-Equal 'value' $values.PLAIN 'Plain dotenv values should parse'
|
|
Assert-Equal 'value with spaces' $values.QUOTED 'Quoted dotenv values should parse'
|
|
Assert-Equal '' $values.EMPTY 'Blank dotenv values should parse'
|
|
|
|
$duplicateEnv = Join-Path $fixture 'duplicate.env'
|
|
@('KEY=one', 'KEY=two') | Set-Content -LiteralPath $duplicateEnv -Encoding utf8
|
|
Assert-Throws { Read-DotEnvFile -Path $duplicateEnv } 'duplicate variable' 'Duplicate variables must fail closed'
|
|
|
|
$invalidEnv = Join-Path $fixture 'invalid.env'
|
|
'NOT A KEY=value' | Set-Content -LiteralPath $invalidEnv -Encoding utf8
|
|
Assert-Throws { Read-DotEnvFile -Path $invalidEnv } 'invalid dotenv entry' 'Malformed variables must fail closed'
|
|
|
|
$backend = Join-Path $fixture 'backend'
|
|
$frontend = Join-Path $fixture 'frontend'
|
|
New-Item -ItemType Directory -Path (Join-Path $backend 'cmd\api') -Force | Out-Null
|
|
New-Item -ItemType Directory -Path (Join-Path $backend 'cmd\worker') -Force | Out-Null
|
|
New-Item -ItemType Directory -Path (Join-Path $backend 'cmd\scheduler') -Force | Out-Null
|
|
New-Item -ItemType Directory -Path $frontend -Force | Out-Null
|
|
'{"scripts":{"dev":"vite"}}' | Set-Content -LiteralPath (Join-Path $frontend 'package.json') -Encoding utf8
|
|
|
|
$commands = Resolve-LocalDevCommands -BackendPath $backend -FrontendPath $frontend
|
|
Assert-Equal './cmd/api' $commands.Api.Arguments[1] 'API command should follow the backend contract'
|
|
Assert-Equal './cmd/worker' $commands.Worker.Arguments[1] 'Worker command should follow the backend contract'
|
|
Assert-Equal './cmd/scheduler' $commands.Scheduler.Arguments[1] 'Scheduler command should follow the backend contract'
|
|
Assert-Equal 'run' $commands.Frontend.Arguments[0] 'Frontend should use its checked-in dev script'
|
|
|
|
$stateDirectory = Join-Path $fixture '.local'
|
|
New-Item -ItemType Directory -Path $stateDirectory -Force | Out-Null
|
|
$currentProcess = Get-Process -Id $PID
|
|
@([pscustomobject]@{
|
|
Name = 'test-host'
|
|
Pid = $currentProcess.Id
|
|
StartTimeUtc = $currentProcess.StartTime.ToUniversalTime().ToString('o')
|
|
}) | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $stateDirectory 'processes.json') -Encoding utf8
|
|
$status = @(Get-LocalDevStatus -DeploymentPath $fixture)
|
|
Assert-Equal $true $status[0].Running 'Status should recognize a recorded live process after JSON date conversion'
|
|
|
|
$childTreeRoot = Start-Process -FilePath 'cmd.exe' -ArgumentList @('/c', 'powershell.exe -NoProfile -Command "Start-Sleep -Seconds 60"') -WindowStyle Hidden -PassThru
|
|
try {
|
|
$childTreeRoot.Refresh()
|
|
@([pscustomobject]@{
|
|
Name = 'test-tree'
|
|
Pid = $childTreeRoot.Id
|
|
StartTimeUtc = $childTreeRoot.StartTime.ToUniversalTime().ToString('o')
|
|
}) | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $stateDirectory 'processes.json') -Encoding utf8
|
|
|
|
$deadline = [DateTime]::UtcNow.AddSeconds(5)
|
|
do {
|
|
$child = Get-CimInstance Win32_Process -Filter "ParentProcessId=$($childTreeRoot.Id)" -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
if (-not $child) { Start-Sleep -Milliseconds 50 }
|
|
} while (-not $child -and [DateTime]::UtcNow -lt $deadline)
|
|
if (-not $child) { throw 'Test child process did not start' }
|
|
|
|
Stop-LocalDev -DeploymentPath $fixture
|
|
Assert-Equal $null (Get-Process -Id $childTreeRoot.Id -ErrorAction SilentlyContinue) 'Stop should terminate the recorded wrapper process'
|
|
Assert-Equal $null (Get-Process -Id $child.ProcessId -ErrorAction SilentlyContinue) 'Stop should terminate descendants of the recorded wrapper process'
|
|
}
|
|
finally {
|
|
if (Get-Process -Id $childTreeRoot.Id -ErrorAction SilentlyContinue) {
|
|
& taskkill.exe /PID $childTreeRoot.Id /T /F | Out-Null
|
|
}
|
|
}
|
|
|
|
Remove-Item -LiteralPath (Join-Path $backend 'cmd\worker') -Recurse
|
|
Assert-Throws { Resolve-LocalDevCommands -BackendPath $backend -FrontendPath $frontend } 'cmd/worker' 'A missing worker entrypoint must be actionable'
|
|
|
|
Write-Output 'local-dev tests passed'
|
|
}
|
|
finally {
|
|
Remove-Item -LiteralPath $fixture -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|