Esa ruta es el peor caso posible y existe en produccion: la ruta por omision C:\Aduanasoft\CloudRestoreAS es PREFIJO DE CADENA de ella, asi que cualquier comparacion hecha con startsWith daria por iguales dos instalaciones distintas — y el resultado seria el fallo silencioso otra vez, actualizar una carpeta y arrancar la otra. El flujo ya la manejaba bien (Test-SamePath compara por igualdad exacta tras normalizar), pero nada lo probaba: la emulacion usaba declarada/otra-carpeta, nombres sin relacion entre si, que un startsWith mal puesto pasaria sin problema. - Escenario `sufijo` en emular-actualizacion-windows.ps1: instalacion en ...\CloudRestoreAS-win y tarea apuntando a ...\CloudRestoreAS. Verificado en Windows: reapunta la tarea, el proceso queda corriendo desde -win y el sello en la version nueva. - Nuevo scripts/probar-funciones-install.ps1: extrae las funciones del instalador por AST y las ejercita contra una tarea simulada, sin elevacion. Cubre los casos limite de la comparacion de rutas (el par de prefijo en ambos sentidos, comillas, barra final, mayusculas, `..`, ruta vacia) y que Sync-AgentTaskPath falle cuando no puede corregir. BUILD.md documenta por que se compara por igualdad exacta y no por prefijo. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
112 lines
5.8 KiB
PowerShell
112 lines
5.8 KiB
PowerShell
<#
|
|
Pruebas de las funciones de install.ps1, sin instalar nada.
|
|
|
|
Las funciones se extraen del script por AST y se ejercitan contra una tarea programada simulada.
|
|
Así se prueba el COMPORTAMIENTO —no solo que el archivo parsee— sin necesitar elevación ni
|
|
registrar una tarea 'CloudRestoreAS' de verdad en la máquina.
|
|
|
|
Complementa a emular-actualizacion-windows.ps1: ese cubre el flujo completo, este cubre los casos
|
|
límite de la comparación de rutas, que es donde se esconden los fallos silenciosos.
|
|
|
|
Uso: .\probar-funciones-install.ps1
|
|
#>
|
|
param([string]$Installer = (Join-Path (Split-Path -Parent $PSScriptRoot) 'install.ps1'))
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
$tokens = $null; $errores = $null
|
|
$ast = [System.Management.Automation.Language.Parser]::ParseFile(
|
|
(Resolve-Path $Installer).Path, [ref]$tokens, [ref]$errores)
|
|
if ($errores -and $errores.Count -gt 0) {
|
|
Write-Host "install.ps1 no parsea: $($errores.Count) error(es)" -ForegroundColor Red
|
|
$errores | ForEach-Object { Write-Host (" L" + $_.Extent.StartLineNumber + ": " + $_.Message) }
|
|
exit 1
|
|
}
|
|
|
|
# Variables de ámbito de script que usan las funciones.
|
|
$Prefix = 'C:\Aduanasoft\CloudRestoreAS-win'
|
|
$dest = Join-Path $Prefix 'CloudRestoreAS.exe'
|
|
$TaskName = 'CloudRestoreAS'
|
|
$ProcName = 'CloudRestoreAS'
|
|
|
|
$queremos = @('Write-Step', 'Write-Ok', 'Write-Warn', 'Get-NormalizedPath', 'Test-SamePath',
|
|
'Get-AgentTaskExecute', 'Repair-AgentTaskPath', 'Sync-AgentTaskPath')
|
|
foreach ($f in $ast.FindAll({ param($n)
|
|
$n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true)) {
|
|
if ($queremos -contains $f.Name) { . ([scriptblock]::Create($f.Extent.Text)) }
|
|
}
|
|
|
|
$fallos = 0
|
|
function Assert($cond, $msg) {
|
|
if ($cond) { Write-Host " OK $msg" } else { Write-Host " FALLA $msg" -ForegroundColor Red; $script:fallos++ }
|
|
}
|
|
|
|
Write-Host '=== Test-SamePath: rutas donde una es prefijo de cadena de la otra ==='
|
|
# El caso que existe en produccion. Un `-like "$a*"` o un StartsWith aqui daria por iguales dos
|
|
# instalaciones distintas, no se corregiria la tarea, y la actualizacion no surtiria efecto.
|
|
$porOmision = 'C:\Aduanasoft\CloudRestoreAS'
|
|
$personalizada = 'C:\Aduanasoft\CloudRestoreAS-win'
|
|
Assert (-not (Test-SamePath $porOmision $personalizada)) 'no confunde la ruta por omision con la -win'
|
|
Assert (-not (Test-SamePath $personalizada $porOmision)) 'ni al reves'
|
|
Assert (-not (Test-SamePath "$porOmision\CloudRestoreAS.exe" "$personalizada\CloudRestoreAS.exe")) `
|
|
'tampoco con el ejecutable completo'
|
|
Assert (Test-SamePath $personalizada $personalizada) 'y la personalizada sigue siendo igual a si misma'
|
|
|
|
Write-Host '=== Test-SamePath: normalizacion ==='
|
|
Assert (Test-SamePath "`"$personalizada`"" $personalizada) 'tolera comillas (la tarea las guarda asi)'
|
|
Assert (Test-SamePath $personalizada.ToUpper() $personalizada) 'NTFS no distingue mayusculas'
|
|
Assert (Test-SamePath "$personalizada\" $personalizada) 'ignora la barra final'
|
|
Assert (Test-SamePath "$Prefix\..\CloudRestoreAS-win\x.exe" "$Prefix\x.exe") 'normaliza .. en la ruta'
|
|
Assert (-not (Test-SamePath '' $personalizada)) 'una ruta vacia no es igual a nada'
|
|
|
|
Write-Host '=== Get-AgentTaskExecute ==='
|
|
function Get-AgentTask { [pscustomobject]@{ Actions = @(
|
|
[pscustomobject]@{ Execute = "`"$porOmision\CloudRestoreAS.exe`""; Arguments = '--start-engine --headless' }) } }
|
|
Assert ((Get-AgentTaskExecute) -eq "$porOmision\CloudRestoreAS.exe") 'devuelve la ruta sin comillas'
|
|
|
|
# Una accion ComHandler no tiene .Execute: con StrictMode, tocarla a ciegas seria un error.
|
|
function Get-AgentTask { [pscustomobject]@{ Actions = @(
|
|
[pscustomobject]@{ ClassId = '{guid}' },
|
|
[pscustomobject]@{ Execute = "$porOmision\CloudRestoreAS.exe"; Arguments = '' }) } }
|
|
Assert ((Get-AgentTaskExecute) -eq "$porOmision\CloudRestoreAS.exe") 'se salta acciones sin Execute'
|
|
|
|
function Get-AgentTask { $null }
|
|
Assert ((Get-AgentTaskExecute) -eq '') 'sin tarea devuelve cadena vacia'
|
|
|
|
Write-Host '=== Sync-AgentTaskPath: el fallo silencioso ==='
|
|
$script:reapuntadoA = $null
|
|
$script:argsPreservados = $null
|
|
function Set-ScheduledTask { param($TaskName, $Action)
|
|
$ejec = @($Action | Where-Object { $_.PSObject.Properties['Execute'] -and $_.Execute })
|
|
$script:reapuntadoA = $ejec[0].Execute
|
|
$script:argsPreservados = $ejec[0].Arguments
|
|
return $true }
|
|
function New-ScheduledTaskAction { param($Execute, $Argument, $WorkingDirectory)
|
|
[pscustomobject]@{ Execute = $Execute; Arguments = $Argument } }
|
|
|
|
function Get-AgentTask { [pscustomobject]@{ Actions = @(
|
|
[pscustomobject]@{ Execute = "$porOmision\CloudRestoreAS.exe"; Arguments = '--start-engine --headless' }) } }
|
|
Sync-AgentTaskPath
|
|
Assert ($script:reapuntadoA -eq $dest) "reapunta de la carpeta por omision a $dest"
|
|
Assert ($script:argsPreservados -eq '--start-engine --headless') 'conserva los argumentos originales'
|
|
|
|
$script:reapuntadoA = $null
|
|
function Get-AgentTask { [pscustomobject]@{ Actions = @(
|
|
[pscustomobject]@{ Execute = $dest; Arguments = '--start-engine --headless' }) } }
|
|
Sync-AgentTaskPath
|
|
Assert ($null -eq $script:reapuntadoA) 'una tarea ya alineada no se toca'
|
|
|
|
# Si no se puede corregir tiene que FALLAR, no arrancar el binario viejo en silencio.
|
|
function Set-ScheduledTask { param($TaskName, $Action) throw 'Acceso denegado' }
|
|
function Get-AgentTask { [pscustomobject]@{ Actions = @(
|
|
[pscustomobject]@{ Execute = "$porOmision\CloudRestoreAS.exe"; Arguments = '' }) } }
|
|
$lanzo = $false; $msg = ''
|
|
try { Sync-AgentTaskPath } catch { $lanzo = $true; $msg = $_.Exception.Message }
|
|
Assert $lanzo 'sin permiso para corregir -> lanza en vez de seguir'
|
|
Assert ($lanzo -and $msg -match 'sin efecto') 'el mensaje explica que la actualizacion no surtiria efecto'
|
|
|
|
Write-Host ''
|
|
if ($fallos -gt 0) { Write-Host "FALLOS: $fallos" -ForegroundColor Red; exit 1 }
|
|
Write-Host 'TODO OK' -ForegroundColor Green
|