Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
# ============================================================
|
||||
# Omika Diagnostic Center - Tests NON invasifs des scripts de deploiement
|
||||
# (Phase 5B2B.3). AUCUN changement systeme : aucune creation de service,
|
||||
# aucune connexion PostgreSQL, aucun psql execute, aucun fichier serveur.
|
||||
# ============================================================
|
||||
# Valide :
|
||||
# 1. Syntaxe PowerShell 5.1 (0 erreur) des 3 scripts.
|
||||
# 2. Resolution des chemins par defaut ($PSScriptRoot corrige).
|
||||
# 3. Detection psql (explicite / PATH / installation standard / absent).
|
||||
# 4. Garanties install-phase5b2.ps1 : aucun service cree, aucun demarrage.
|
||||
# ============================================================
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$repo = 'D:\Avalonia\Omika'
|
||||
$deploySrc = Join-Path $repo 'infra\deploy'
|
||||
$deployPkg = Join-Path $repo 'artifacts\phase5b2\Deploy'
|
||||
|
||||
$tests = 0
|
||||
$fails = 0
|
||||
|
||||
function Assert([bool]$condition, [string]$label) {
|
||||
$script:tests++
|
||||
if ($condition) {
|
||||
Write-Host ("PASS {0}" -f $label) -ForegroundColor Green
|
||||
} else {
|
||||
$script:fails++
|
||||
Write-Host ("FAIL {0}" -f $label) -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "=== 1. Syntaxe PowerShell 5.1 (0 erreur) ==="
|
||||
$scripts = @(
|
||||
(Join-Path $deploySrc 'Install-DatabaseSchema.ps1'),
|
||||
(Join-Path $deploySrc 'install-phase5b2.ps1'),
|
||||
(Join-Path $deploySrc 'Harden-DatabaseRoles.ps1')
|
||||
)
|
||||
foreach ($s in $scripts) {
|
||||
$tokens = $null; $errors = $null
|
||||
[System.Management.Automation.Language.Parser]::ParseFile($s, [ref]$tokens, [ref]$errors) | Out-Null
|
||||
Assert (($errors -eq $null) -or ($errors.Count -eq 0)) ("Syntaxe OK : " + (Split-Path $s -Leaf))
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== 2. Resolution des chemins par defaut (bug PSScriptRoot corrige) ==="
|
||||
# Miroir EXACT des expressions des scripts (calculees APRES le bloc param).
|
||||
$packageRoot = Split-Path -Parent $deployPkg # phase5b2 (parent de Deploy)
|
||||
$databaseDir = Join-Path $packageRoot 'Database'
|
||||
|
||||
Assert (Test-Path -LiteralPath $packageRoot) "PackageRoot par defaut resout vers le package existant"
|
||||
Assert (Test-Path -LiteralPath $databaseDir) "DatabaseDir par defaut resout vers ..\Database existant"
|
||||
Assert (Test-Path -LiteralPath (Join-Path $packageRoot 'DiagnosticsWorker')) "Package contient DiagnosticsWorker"
|
||||
Assert (Test-Path -LiteralPath (Join-Path $packageRoot 'DiagnosticsWeb')) "Package contient DiagnosticsWeb"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== 3. Detection psql (resolver) ==="
|
||||
function Resolve-PsqlPath {
|
||||
param([string]$ExplicitPath)
|
||||
if ($ExplicitPath) {
|
||||
if (-not (Test-Path -LiteralPath $ExplicitPath)) { return $null }
|
||||
return (Resolve-Path -LiteralPath $ExplicitPath).Path
|
||||
}
|
||||
$inPath = Get-Command psql.exe -ErrorAction SilentlyContinue
|
||||
if ($inPath) { return $inPath.Source }
|
||||
$candidates = @(Get-ChildItem -Path 'C:\Program Files\PostgreSQL\*\bin\psql.exe' -ErrorAction SilentlyContinue)
|
||||
if ($candidates.Count -gt 0) {
|
||||
$best = $candidates | ForEach-Object {
|
||||
$version = 0
|
||||
$verDir = $_.Directory.Parent.Name
|
||||
if ($verDir -match '^\d+$') { $version = [int]$verDir }
|
||||
[PSCustomObject]@{ Path = $_.FullName; Version = $version }
|
||||
} | Sort-Object -Property Version -Descending | Select-Object -First 1
|
||||
return $best.Path
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
# A. chemin explicite existant -> retourne ce chemin (le resolver ne verifie
|
||||
# que Test-Path, donc tout fichier existant sert ici de jeton de test).
|
||||
$explicitProbe = Join-Path $deploySrc 'Install-DatabaseSchema.ps1'
|
||||
Assert ((Resolve-PsqlPath -ExplicitPath $explicitProbe) -eq (Resolve-Path -LiteralPath $explicitProbe).Path) "PsqlPath explicite existant -> utilise tel quel"
|
||||
|
||||
# B. chemin explicite inexistant -> le script doit s'arreter avec erreur claire
|
||||
# (on verifie ici que le resolver retourne null, et plus bas que le script
|
||||
# contient bien Write-Error + exit 1).
|
||||
Assert ((Resolve-PsqlPath -ExplicitPath 'C:\inexistant\psql.exe') -eq $null) "PsqlPath explicite inexistant -> non resolvable (arret clair du script)"
|
||||
|
||||
# C. installation standard PostgreSQL -> detectee (si presente sur ce poste) ;
|
||||
# sinon le fallback retourne null (comportement documente).
|
||||
$std = Get-ChildItem -Path 'C:\Program Files\PostgreSQL\*\bin\psql.exe' -ErrorAction SilentlyContinue
|
||||
Assert ($std -ne $null) "Installation standard PostgreSQL detectee (ou absente : fallback null attendu)"
|
||||
Write-Host (" (poste dev : {0} psql.exe trouve(s) sous C:\Program Files\PostgreSQL)" -f @($std).Count)
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== 4. Garanties install-phase5b2.ps1 (aucun service, aucun demarrage) ==="
|
||||
$installContent = Get-Content -LiteralPath (Join-Path $deploySrc 'install-phase5b2.ps1') -Raw
|
||||
Assert ($installContent -notmatch 'New-Service') "Aucun New-Service (creation differee a Phase 5C)"
|
||||
Assert ($installContent -notmatch 'Start-Service') "Aucun Start-Service (jamais de demarrage)"
|
||||
Assert ($installContent -notmatch '-StartupType\s+Automatic') "Aucun StartupType Automatic"
|
||||
Assert ($installContent -match 'Set-Service\s+-Name\s+\$ServiceName\s+-StartupType\s+Manual') "Service existant force en Manual"
|
||||
Assert ($installContent -match 'Stop-Service') "Service existant force en Stopped"
|
||||
Assert ($installContent -match 'A_DETERMINER_EN_PHASE_5C') "Compte de service non fige (A_DETERMINER_EN_PHASE_5C)"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== 5. Migration 003 (SQL statique, aucun PostgreSQL requis) ==="
|
||||
$sql003 = Get-Content -LiteralPath (Join-Path $repo 'infra\postgresql\003_diagnostics_roles.sql') -Raw
|
||||
|
||||
# 0 commande ALTER INDEX (les commentaires ne comptent pas : ancrage en debut de ligne).
|
||||
$alterIndexCmds = @([regex]::Matches($sql003, '(?m)^\s*ALTER\s+INDEX\b') | ForEach-Object { $_.Value })
|
||||
Assert ($alterIndexCmds.Count -eq 0) "Aucune commande ALTER INDEX OWNER (0 occurrence)"
|
||||
|
||||
Assert ($sql003 -match 'ALTER\s+TABLE\s+diagnostic_event\s+OWNER\s+TO\s+omika_diagnostics_owner') "ALTER TABLE OWNER diagnostic_event"
|
||||
Assert ($sql003 -match 'ALTER\s+TABLE\s+telemetry_metric_hourly\s+OWNER\s+TO\s+omika_diagnostics_owner') "ALTER TABLE OWNER telemetry_metric_hourly"
|
||||
Assert ($sql003 -match 'ALTER\s+SEQUENCE\s+diagnostic_event_id_seq\s+OWNER\s+TO\s+omika_diagnostics_owner') "ALTER SEQUENCE OWNER diagnostic_event_id_seq"
|
||||
Assert ($sql003 -match 'ALTER\s+SEQUENCE\s+telemetry_metric_hourly_id_seq\s+OWNER\s+TO\s+omika_diagnostics_owner') "ALTER SEQUENCE OWNER telemetry_metric_hourly_id_seq"
|
||||
|
||||
Assert ($sql003 -notmatch '(?m)^\s*REASSIGN\s+OWNED') "Aucun REASSIGN OWNED"
|
||||
Assert ($sql003 -notmatch '(?m)^\s*DROP\b') "Aucun DROP"
|
||||
Assert ($sql003 -notmatch '(?m)^\s*TRUNCATE\b') "Aucun TRUNCATE"
|
||||
Assert ($sql003 -notmatch '(?m)^\s*DELETE\s+FROM') "Aucun DELETE"
|
||||
Assert ($sql003 -notmatch "(?i)\bPASSWORD\s+'") "Aucun mot de passe en dur"
|
||||
|
||||
Assert ($sql003 -match 'GRANT\s+CONNECT\s+ON\s+DATABASE\s+omika_diagnostics\s+TO\s+omika_diagnostics_worker') "Worker : CONNECT database"
|
||||
Assert ($sql003 -match 'GRANT\s+USAGE\s+ON\s+SCHEMA\s+public\s+TO\s+omika_diagnostics_worker') "Worker : USAGE schema public"
|
||||
Assert ($sql003 -match 'GRANT\s+INSERT\s+ON\s+TABLE\s+diagnostic_event\s+TO\s+omika_diagnostics_worker') "Worker : INSERT diagnostic_event"
|
||||
Assert ($sql003 -match 'GRANT\s+USAGE\s+ON\s+SEQUENCE\s+diagnostic_event_id_seq\s+TO\s+omika_diagnostics_worker') "Worker : USAGE sequence"
|
||||
Assert ($sql003 -notmatch 'GRANT\s+(SELECT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER)\s+ON\s+TABLE\s+diagnostic_event\s+TO\s+omika_diagnostics_worker') "Worker : aucun SELECT/UPDATE/DELETE/TRUNCATE/REFERENCES/TRIGGER"
|
||||
|
||||
Assert ($sql003 -match 'GRANT\s+CONNECT\s+ON\s+DATABASE\s+omika_diagnostics\s+TO\s+[^;]*omika_diagnostics_web') "Web : CONNECT database"
|
||||
Assert ($sql003 -match 'GRANT\s+USAGE\s+ON\s+SCHEMA\s+public\s+TO\s+[^;]*omika_diagnostics_web') "Web : USAGE schema public"
|
||||
Assert ($sql003 -match 'GRANT\s+SELECT\s+ON\s+TABLE\s+diagnostic_event\s+TO\s+omika_diagnostics_web') "Web : SELECT diagnostic_event"
|
||||
Assert ($sql003 -notmatch 'GRANT\s+(INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER)\s+ON\s+TABLE\s+diagnostic_event\s+TO\s+omika_diagnostics_web') "Web : aucun INSERT/UPDATE/DELETE/TRUNCATE/REFERENCES/TRIGGER"
|
||||
Assert ($sql003 -notmatch 'GRANT\s+\w+\s+ON\s+(TABLE\s+)?telemetry_metric_hourly') "Aucun droit sur telemetry_metric_hourly"
|
||||
|
||||
Assert ($sql003 -match 'ALTER\s+ROLE\s+omika_diagnostics_owner\s+.*NOLOGIN') "owner : NOLOGIN"
|
||||
Assert ($sql003 -match 'ALTER\s+ROLE\s+omika_diagnostics_worker\s+.*LOGIN') "worker : LOGIN"
|
||||
Assert ($sql003 -match 'ALTER\s+ROLE\s+omika_diagnostics_web\s+.*LOGIN') "web : LOGIN"
|
||||
Assert ($sql003 -notmatch 'DROP\s+ROLE\s+omika_diagnostics_app') "Ancien role : aucun DROP ROLE"
|
||||
Assert ($sql003 -notmatch 'ALTER\s+ROLE\s+omika_diagnostics_app\s+NOLOGIN') "Ancien role : aucun NOLOGIN automatique"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ("=== RESULTAT : {0} tests, {1} echec(s) ===" -f $tests, $fails)
|
||||
if ($fails -gt 0) { exit 1 } else { exit 0 }
|
||||
@@ -0,0 +1,175 @@
|
||||
# ============================================================
|
||||
# Omika - Tests NON invasifs de Install-OmikaServerFiles.ps1 (Phase 5C.2B)
|
||||
# ============================================================
|
||||
# Valide le correctif WhatIf / SHA256 :
|
||||
# A. Get-Sha256Hex sur fichier connu.
|
||||
# B. Get-Sha256Hex sous $WhatIfPreference = $true (aucun null).
|
||||
# C. Package valide + WhatIf -> SHA256 calcule, exit 0, aucune exception.
|
||||
# D. Hash incorrect + WhatIf -> mismatch detecte, arret avant copie (exit 1).
|
||||
# E. Aucune mutation filesystem sous -WhatIf (dossiers + copie).
|
||||
# F. PackageRoot explicite honore.
|
||||
# G. PackageRoot automatique ($PSScriptRoot parent).
|
||||
# AUCUN changement systeme : aucune copie reelle, aucun service, aucun admin.
|
||||
# ============================================================
|
||||
|
||||
[CmdletBinding(SupportsShouldProcess = $true)]
|
||||
param()
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$repo = 'D:\Avalonia\Omika'
|
||||
$scriptPath = Join-Path $repo 'infra\deploy\Install-OmikaServerFiles.ps1'
|
||||
$pkgRoot = Join-Path $repo 'artifacts\server-full'
|
||||
|
||||
$tests = 0
|
||||
$fails = 0
|
||||
|
||||
function Assert([bool]$condition, [string]$label) {
|
||||
$script:tests++
|
||||
if ($condition) {
|
||||
Write-Host ("PASS {0}" -f $label) -ForegroundColor Green
|
||||
}
|
||||
else {
|
||||
$script:fails++
|
||||
Write-Host ("FAIL {0}" -f $label) -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
# Exécute un bloc dans un process Windows PowerShell 5.1 enfant (exit-safe),
|
||||
# retourne @{ Output; ExitCode }. On baisse localement $ErrorActionPreference à
|
||||
# 'Continue' pendant l'appel natif : ainsi la stderr du natif (ErrorRecord) ne
|
||||
# lève pas, et 2>&1 la capture dans la sortie.
|
||||
function Invoke-ChildPS51([string]$scriptText) {
|
||||
$encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($scriptText))
|
||||
$prevEAP = $ErrorActionPreference
|
||||
$ErrorActionPreference = 'Continue'
|
||||
try {
|
||||
$out = @(& powershell.exe -NoProfile -NonInteractive -EncodedCommand $encoded 2>&1)
|
||||
$exitCode = $LASTEXITCODE
|
||||
}
|
||||
finally {
|
||||
$ErrorActionPreference = $prevEAP
|
||||
}
|
||||
$text = ($out | ForEach-Object { $_.ToString() }) -join "`n"
|
||||
return @{ Output = $text; ExitCode = $exitCode }
|
||||
}
|
||||
|
||||
Write-Host "=== 1. Syntaxe PowerShell 5.1 (0 erreur) ==="
|
||||
$tokens = $null; $errors = $null
|
||||
[System.Management.Automation.Language.Parser]::ParseFile($scriptPath, [ref]$tokens, [ref]$errors) | Out-Null
|
||||
Assert (($errors -eq $null) -or ($errors.Count -eq 0)) "Syntaxe OK : Install-OmikaServerFiles.ps1"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== 2. Extraction des fonctions (AST) + module temporaire ==="
|
||||
$content = Get-Content -LiteralPath $scriptPath -Raw
|
||||
$ast = [System.Management.Automation.Language.Parser]::ParseInput($content, [ref]$tokens, [ref]$errors)
|
||||
$want = @('Get-Sha256Hex', 'Test-PackageChecksums', 'Deploy-AppFolder')
|
||||
$module = New-Object System.Text.StringBuilder
|
||||
foreach ($f in $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true)) {
|
||||
if ($want -contains $f.Name) {
|
||||
[void]$module.AppendLine($f.Extent.Text)
|
||||
}
|
||||
}
|
||||
foreach ($name in $want) {
|
||||
Assert ($module.ToString() -match ("function\s+" + [regex]::Escape($name) + "\b")) ("Fonction extraite : " + $name)
|
||||
}
|
||||
|
||||
$modulePath = Join-Path $env:TEMP 'omika-isf-functions.ps1'
|
||||
[System.IO.File]::WriteAllText($modulePath, $module.ToString(), (New-Object System.Text.UTF8Encoding($true)))
|
||||
. $modulePath
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== A/B. Get-Sha256Hex (unitaire) ==="
|
||||
$knownFile = Join-Path $env:TEMP 'omika-isf-known.txt'
|
||||
[System.IO.File]::WriteAllText($knownFile, 'abc', (New-Object System.Text.UTF8Encoding($false)))
|
||||
$expectedHash = 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'
|
||||
|
||||
Assert ((Get-Sha256Hex -Path $knownFile) -eq $expectedHash) "A. Hash connu 'abc' = ba7816bf... correct"
|
||||
|
||||
$prevWhatIf = $WhatIfPreference
|
||||
$WhatIfPreference = $true
|
||||
try {
|
||||
$hashUnderWhatIf = Get-Sha256Hex -Path $knownFile
|
||||
Assert ($hashUnderWhatIf -eq $expectedHash) "B. Sous WhatIfPreference=true : hash correct (aucun null)"
|
||||
Assert ($hashUnderWhatIf -match '^[0-9a-f]{64}$') "B. Format hex lowercase 64 caracteres"
|
||||
}
|
||||
finally {
|
||||
$WhatIfPreference = $prevWhatIf
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== C. Package valide + WhatIf (Test-PackageChecksums) ==="
|
||||
$childC = @"
|
||||
`$ErrorActionPreference = 'Stop'
|
||||
. '$modulePath'
|
||||
`$WhatIfPreference = `$true
|
||||
Test-PackageChecksums -packageRoot '$pkgRoot'
|
||||
"@
|
||||
$resC = Invoke-ChildPS51 $childC
|
||||
Assert ($resC.ExitCode -eq 0) "C. Package valide + WhatIf : exit 0"
|
||||
Assert ($resC.Output -match 'SHA256 OK') "C. Package valide + WhatIf : SHA256 calcule et OK"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== D. Hash incorrect + WhatIf (mismatch detecte, arret) ==="
|
||||
$corruptDir = Join-Path $env:TEMP 'omika-isf-corrupt'
|
||||
if (Test-Path -LiteralPath $corruptDir) { Remove-Item -LiteralPath $corruptDir -Recurse -Force }
|
||||
New-Item -ItemType Directory -Force -Path $corruptDir | Out-Null
|
||||
[System.IO.File]::WriteAllText((Join-Path $corruptDir 'test.txt'), 'hello', (New-Object System.Text.UTF8Encoding($false)))
|
||||
[System.IO.File]::WriteAllText((Join-Path $corruptDir 'SHA256SUMS.txt'), '0000000000000000000000000000000000000000000000000000000000000000 test.txt', (New-Object System.Text.UTF8Encoding($false)))
|
||||
|
||||
$childD = @"
|
||||
`$ErrorActionPreference = 'Stop'
|
||||
. '$modulePath'
|
||||
`$WhatIfPreference = `$true
|
||||
Test-PackageChecksums -packageRoot '$corruptDir'
|
||||
"@
|
||||
$resD = Invoke-ChildPS51 $childD
|
||||
Assert ($resD.ExitCode -eq 1) "D. Hash incorrect + WhatIf : exit 1 (arret avant copie)"
|
||||
Assert ($resD.Output -match 'Checksum SHA256 KO') "D. Hash incorrect + WhatIf : mismatch detecte"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== E. Aucune mutation filesystem sous -WhatIf ==="
|
||||
$tempRoot = Join-Path $env:TEMP ('omika-isf-mut-' + [guid]::NewGuid().ToString('N'))
|
||||
|
||||
# E1 : boucle de creation d'arborescence (replique de la logique top-level).
|
||||
$WhatIfPreference = $true
|
||||
$appsRoot = Join-Path $tempRoot 'Apps'
|
||||
$folders = @(
|
||||
$appsRoot,
|
||||
(Join-Path $appsRoot 'OmikaApi'),
|
||||
(Join-Path $appsRoot 'OmikaWorker'),
|
||||
(Join-Path $tempRoot 'Data\Api\Diagnostics\Jsonl'),
|
||||
(Join-Path $tempRoot 'Data\DiagnosticsWorker\Checkpoint'),
|
||||
(Join-Path $tempRoot 'Logs\Api'),
|
||||
(Join-Path $tempRoot 'Config'),
|
||||
(Join-Path $tempRoot 'Backups'),
|
||||
(Join-Path $tempRoot 'Deploy')
|
||||
)
|
||||
foreach ($folder in $folders) {
|
||||
if ($PSCmdlet.ShouldProcess($folder, 'Créer le dossier')) {
|
||||
New-Item -ItemType Directory -Force -Path $folder | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
# E2 : Deploy-AppFolder (sauvegarde + copie) sous WhatIf.
|
||||
Deploy-AppFolder -Name 'OmikaApi' `
|
||||
-Source (Join-Path $pkgRoot 'OmikaApi') `
|
||||
-Target (Join-Path $appsRoot 'OmikaApi') `
|
||||
-BackupRoot (Join-Path $tempRoot 'Backups') `
|
||||
-Preserve @('appsettings.json')
|
||||
$WhatIfPreference = $prevWhatIf
|
||||
|
||||
Assert (-not (Test-Path -LiteralPath $tempRoot)) "E. Aucun dossier/fichier cree sous WhatIf (arborescence + copie)"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== F/G. Resolution PackageRoot ==="
|
||||
# G : automatique = parent du dossier Deploy (mirroir exact de $PSScriptRoot).
|
||||
$autoResolved = Split-Path -Parent (Join-Path $pkgRoot 'Deploy')
|
||||
Assert ($autoResolved -eq $pkgRoot) "G. PackageRoot automatique (parent du dossier Deploy) resout vers le package"
|
||||
|
||||
# F : explicite = honore (le script ne resout que si -not $PackageRoot).
|
||||
Assert ($content -match 'if \(-not \$PackageRoot\)') "F. Garde 'if (-not PackageRoot)' presente (explicite honore)"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ("=== RESULTAT : {0} tests, {1} echec(s) ===" -f $tests, $fails)
|
||||
if ($fails -gt 0) { exit 1 } else { exit 0 }
|
||||
@@ -0,0 +1,361 @@
|
||||
# ============================================================
|
||||
# Omika - Tests NON invasifs des scripts d'installation des services
|
||||
# ============================================================
|
||||
# AUCUN changement systeme : aucune creation de service, aucune ACL posee,
|
||||
# aucune connexion PostgreSQL/HFSQL. Seule une reservation BRIEF de ports
|
||||
# loopback est faite (pour tester le module), immediatement relachee.
|
||||
#
|
||||
# Valide :
|
||||
# 1. Syntaxe PowerShell 5.1 (0 erreur) de TOUS les fichiers de deploiement.
|
||||
# 2. Import du module commun + presence des fonctions attendues.
|
||||
# 3. Garanties : aucun demarrage de service, aucune creation de service hors
|
||||
# du module, aucun port en dur, -WhatIf / -Desinstaller supportes.
|
||||
# 4. Fiche reelle de chaque service (nom, executable, compte par defaut).
|
||||
# 5. Comportement reel de la verification de port (3 cas).
|
||||
# ============================================================
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$deploySrc = Split-Path -Parent $PSScriptRoot
|
||||
$repo = Split-Path -Parent (Split-Path -Parent $deploySrc)
|
||||
|
||||
$tests = 0
|
||||
$fails = 0
|
||||
|
||||
function Assert([bool]$condition, [string]$label) {
|
||||
$script:tests++
|
||||
if ($condition) {
|
||||
Write-Host ("PASS {0}" -f $label) -ForegroundColor Green
|
||||
}
|
||||
else {
|
||||
Write-Host ("FAIL {0}" -f $label) -ForegroundColor Red
|
||||
$script:fails++
|
||||
}
|
||||
}
|
||||
|
||||
$moduleCommun = Join-Path $deploySrc 'Omika.Deploy.Common.psm1'
|
||||
|
||||
# --- 1. Syntaxe PowerShell 5.1 ----------------------------------------------
|
||||
Write-Host '=== 1. Syntaxe PowerShell 5.1 (0 erreur) ==='
|
||||
|
||||
$fichiers = @(
|
||||
$moduleCommun,
|
||||
(Join-Path $deploySrc 'Install-OmikaApi.ps1'),
|
||||
(Join-Path $deploySrc 'Install-OmikaWorker.ps1'),
|
||||
(Join-Path $deploySrc 'Install-OmikaDiagnosticsApi.ps1'),
|
||||
(Join-Path $deploySrc 'Install-OmikaDiagnosticsWorker.ps1'),
|
||||
(Join-Path $deploySrc 'Install-OmikaDiagnosticsWeb.ps1'),
|
||||
(Join-Path $deploySrc 'Install-OmikaLanguesApi.ps1'),
|
||||
(Join-Path $deploySrc 'Install-OmikaServices.ps1'),
|
||||
(Join-Path $deploySrc 'Test-PortDisponible.ps1'),
|
||||
(Join-Path $deploySrc 'New-OmikaLanguesManifeste.ps1'),
|
||||
(Join-Path $deploySrc 'Install-OmikaServerFiles.ps1')
|
||||
)
|
||||
|
||||
foreach ($fichier in $fichiers) {
|
||||
if (-not (Test-Path -LiteralPath $fichier)) {
|
||||
Assert $false ("Fichier present : " + (Split-Path $fichier -Leaf))
|
||||
continue
|
||||
}
|
||||
$tokens = $null
|
||||
$errors = $null
|
||||
[System.Management.Automation.Language.Parser]::ParseFile($fichier, [ref]$tokens, [ref]$errors) | Out-Null
|
||||
$ok = (($null -eq $errors) -or ($errors.Count -eq 0))
|
||||
Assert $ok ("Syntaxe OK : " + (Split-Path $fichier -Leaf))
|
||||
if (-not $ok) {
|
||||
foreach ($e in $errors) { Write-Host (" " + $e.Message) -ForegroundColor DarkRed }
|
||||
}
|
||||
}
|
||||
|
||||
# --- 2. Module commun --------------------------------------------------------
|
||||
Write-Host ''
|
||||
Write-Host '=== 2. Module commun : import + fonctions exportees ==='
|
||||
|
||||
Import-Module $moduleCommun -Force
|
||||
Assert $true 'Import du module sans erreur'
|
||||
|
||||
$fonctionsAttendues = @(
|
||||
'Assert-OmikaAdmin', 'Get-OmikaPaths', 'New-OmikaDirectory',
|
||||
'Resolve-OmikaAccountName', 'Assert-OmikaServiceAccount',
|
||||
'Test-OmikaServiceLogonRight', 'Grant-OmikaAcl',
|
||||
'Get-OmikaServiceState', 'Install-OmikaService', 'Remove-OmikaService',
|
||||
'Write-OmikaServiceSummary', 'Test-OmikaPortLibre'
|
||||
)
|
||||
foreach ($nom in $fonctionsAttendues) {
|
||||
$existe = $null -ne (Get-Command -Name $nom -ErrorAction SilentlyContinue)
|
||||
Assert $existe ("Fonction exportee : $nom")
|
||||
}
|
||||
|
||||
# Le controle du code retour d'icacls est la correction cle de l'ancien script.
|
||||
$contenuModule = Get-Content -LiteralPath $moduleCommun -Raw
|
||||
Assert ($contenuModule -match 'LASTEXITCODE') 'Grant-OmikaAcl controle le code retour (LASTEXITCODE)'
|
||||
Assert ($contenuModule -match 'icacls\.exe') 'ACL posees via icacls.exe (appel explicite)'
|
||||
Assert ($contenuModule -match 'SeServiceLogonRight') 'Droit « Ouvrir une session en tant que service » verifie'
|
||||
Assert ($contenuModule -notmatch '(?im)^\s*Start-Service\b') 'Le module ne demarre aucun service'
|
||||
Assert ($contenuModule -notmatch '(?im)^\s*Restart-Service\b') 'Le module ne redemarre aucun service'
|
||||
Assert ($contenuModule -match 'Resolve-OmikaAccountName') 'Compte normalise en ORDINATEUR\compte (forme non ambigue)'
|
||||
|
||||
# --- Non-regression : detection d'existence d'un compte ----------------------
|
||||
# Le repli ADSI ([ADSI]("WinNT://ORDINATEUR/compte,user")) ne leve AUCUNE erreur
|
||||
# pour un compte inexistant : il annoncait « Existe = True » a tort. Un faux
|
||||
# positif est le pire des resultats ici (le script creerait un service avec un
|
||||
# compte absent). Le repli est desormais net.exe user. Ces deux assertions
|
||||
# verrouillent la correction.
|
||||
# On ne cherche [ADSI] que dans le CODE : le module cite legitimement ADSI dans
|
||||
# le commentaire qui documente la raison de son retrait.
|
||||
$codeModule = @(Get-Content -LiteralPath $moduleCommun | Where-Object { $_ -notmatch '^\s*#' })
|
||||
$adsiDansLeCode = @($codeModule | Where-Object { $_ -match '\[ADSI\]' })
|
||||
Assert ($adsiDansLeCode.Count -eq 0) 'Aucun appel ADSI dans le code (source du faux positif)'
|
||||
|
||||
$compteAbsent = Test-OmikaLocalAccount -Account '.\svc_omika_compte_absent_xyz'
|
||||
Assert ($compteAbsent.Existe -eq $false) 'Compte inexistant detecte ABSENT (aucun faux positif)'
|
||||
|
||||
$comptePresent = Test-OmikaLocalAccount -Account (".\" + $env:USERNAME)
|
||||
Assert ($comptePresent.Existe -eq $true) ("Compte existant detecte PRESENT ({0})" -f $env:USERNAME)
|
||||
|
||||
# --- 3. Fiche reelle de chaque service --------------------------------------
|
||||
Write-Host ''
|
||||
Write-Host '=== 3. Un fichier par deploiement, fiche conforme ==='
|
||||
|
||||
$servicesAttendus = @(
|
||||
@{ Script = 'Install-OmikaApi.ps1'; Service = 'OmikaApi'; Exe = 'Omika.Api.exe'; Compte = '\.\\svc_omika_api' }
|
||||
@{ Script = 'Install-OmikaWorker.ps1'; Service = 'OmikaWorker'; Exe = 'Omika.Worker.exe'; Compte = '\.\\svc_omika_worker' }
|
||||
@{ Script = 'Install-OmikaDiagnosticsApi.ps1'; Service = 'OmikaDiagnosticsApi'; Exe = 'Omika.Diagnostics.Api.exe'; Compte = '\.\\svc_omika_diagapi' }
|
||||
@{ Script = 'Install-OmikaDiagnosticsWorker.ps1'; Service = 'OmikaDiagnosticsWorker'; Exe = 'Omika.Diagnostics.Worker.exe'; Compte = '\.\\svc_omika_diag' }
|
||||
@{ Script = 'Install-OmikaDiagnosticsWeb.ps1'; Service = 'OmikaDiagnosticsWeb'; Exe = 'Omika.Diagnostics.Web.exe'; Compte = '\.\\svc_omika_diagweb' }
|
||||
@{ Script = 'Install-OmikaLanguesApi.ps1'; Service = 'OmikaLanguesApi'; Exe = 'Omika.Langues.Api.exe'; Compte = '\.\\svc_omika_langues' }
|
||||
)
|
||||
|
||||
foreach ($fiche in $servicesAttendus) {
|
||||
$chemin = Join-Path $deploySrc $fiche.Script
|
||||
$contenu = Get-Content -LiteralPath $chemin -Raw
|
||||
|
||||
Assert ($contenu -match ('\$ServiceName\s*=\s*''' + $fiche.Service + '''')) `
|
||||
("{0} : nom de service par defaut = {1}" -f $fiche.Script, $fiche.Service)
|
||||
|
||||
Assert ($contenu -match [regex]::Escape($fiche.Exe)) `
|
||||
("{0} : executable attendu {1}" -f $fiche.Script, $fiche.Exe)
|
||||
|
||||
# Motif concatene en CHAINES SIMPLES : en chaine double, PowerShell
|
||||
# n'echappe pas avec '\' -> "\$Account" deviendrait '\' + la valeur de la
|
||||
# variable $Account (vide), et le motif ne correspondrait jamais.
|
||||
Assert ($contenu -match ('\$Account\s*=\s*''' + $fiche.Compte + '''')) `
|
||||
("{0} : compte par defaut = {1}" -f $fiche.Script, $fiche.Compte)
|
||||
|
||||
Assert ($contenu -match 'CmdletBinding\(SupportsShouldProcess\s*=\s*\$true\)') `
|
||||
("{0} : prend en charge -WhatIf" -f $fiche.Script)
|
||||
|
||||
Assert ($contenu -match '\$Desinstaller') `
|
||||
("{0} : prend en charge -Desinstaller (rollback)" -f $fiche.Script)
|
||||
|
||||
Assert ($contenu -match 'Assert-OmikaServiceAccount') `
|
||||
("{0} : valide le compte de service" -f $fiche.Script)
|
||||
|
||||
Assert ($contenu -match 'Import-Module') `
|
||||
("{0} : utilise le module commun" -f $fiche.Script)
|
||||
|
||||
Assert ($contenu -notmatch '(?im)^\s*Start-Service\b') `
|
||||
("{0} : ne demarre aucun service" -f $fiche.Script)
|
||||
|
||||
Assert ($contenu -notmatch '(?im)^\s*New-Service\b') `
|
||||
("{0} : ne cree pas de service directement (passe par Install-OmikaService)" -f $fiche.Script)
|
||||
|
||||
Assert ($contenu -notmatch '(?im)^\s*Set-Service\b.*-StartupType\s+Automatic') `
|
||||
("{0} : ne force pas le demarrage automatique" -f $fiche.Script)
|
||||
}
|
||||
|
||||
# --- 4. Garanties transverses ------------------------------------------------
|
||||
Write-Host ''
|
||||
Write-Host '=== 4. Garanties transverses ==='
|
||||
|
||||
# Aucun port en dur : le port se decide contre le plan Apache (doc §6bis).
|
||||
# On ne cherche PAS le simple mot « 5099 » (il apparait legitimement dans les
|
||||
# exemples d'usage et dans le test de port) mais les deux formes qui
|
||||
# constitueraient un CHOIX code en dur : une URL ASPNETCORE_URLS et une valeur
|
||||
# par defaut de parametre.
|
||||
$portEnDur = @()
|
||||
foreach ($fichier in (Get-ChildItem -LiteralPath $deploySrc -Filter *.ps1 -Recurse)) {
|
||||
$contenu = Get-Content -LiteralPath $fichier.FullName -Raw
|
||||
if ($contenu -match '127\.0\.0\.1:5099') {
|
||||
$portEnDur += ("{0} (ASPNETCORE_URLS en dur)" -f $fichier.Name)
|
||||
}
|
||||
# 0 est autorise : il signifie « aucun port fourni », ce n'est pas un choix.
|
||||
elseif ($contenu -match '(?im)^\s*\[int\]\s*\$Port\w*\s*=\s*[1-9]\d*') {
|
||||
$portEnDur += ("{0} (valeur par defaut de -Port)" -f $fichier.Name)
|
||||
}
|
||||
}
|
||||
Assert ($portEnDur.Count -eq 0) ("Aucun port code en dur dans infra\deploy ({0} fichier(s))" -f $portEnDur.Count)
|
||||
if ($portEnDur.Count -gt 0) { Write-Host (" " + ($portEnDur -join ', ')) -ForegroundColor DarkRed }
|
||||
|
||||
# L'orchestrateur appelle bien les 6 scripts dedies.
|
||||
$orchestrateur = Get-Content -LiteralPath (Join-Path $deploySrc 'Install-OmikaServices.ps1') -Raw
|
||||
foreach ($fiche in $servicesAttendus) {
|
||||
Assert ($orchestrateur -match [regex]::Escape($fiche.Script)) `
|
||||
("Orchestrateur : appelle {0}" -f $fiche.Script)
|
||||
}
|
||||
Assert ($orchestrateur -match 'SupportsShouldProcess') 'Orchestrateur : prend en charge -WhatIf'
|
||||
Assert ($orchestrateur -match 'InclureLangues') 'Orchestrateur : service Langues optionnel (-InclureLangues)'
|
||||
Assert ($orchestrateur -match 'SansDiagnosticsApi') 'Orchestrateur : API centrale optionnelle (-SansDiagnosticsApi)'
|
||||
|
||||
# --- 5. Comportement reel de la verification de port ------------------------
|
||||
Write-Host ''
|
||||
Write-Host '=== 5. Verification de port : 3 cas reels ==='
|
||||
|
||||
# Normalisation du compte (forme '.\' -> 'ORDINATEUR\compte').
|
||||
$normalise = Resolve-OmikaAccountName -Account '.\svc_omika_api'
|
||||
Assert ($normalise -eq ('{0}\svc_omika_api' -f $env:COMPUTERNAME)) "Compte normalise : $normalise"
|
||||
|
||||
# A. Port libre.
|
||||
$verdictLibre = Test-OmikaPortLibre -Port 5099 -VerifierPlagesReservees:$false
|
||||
Assert ($verdictLibre.Libre) ("Port 5099 detecte DISPONIBLE" + $(if ($verdictLibre.Libre) { '' } else { " (raison : $($verdictLibre.Raisons))" }))
|
||||
|
||||
# B. Port reellement occupe.
|
||||
$verrou = $null
|
||||
$verdictOccupe = $null
|
||||
try {
|
||||
$verrou = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 5123)
|
||||
$verrou.Start()
|
||||
$verdictOccupe = Test-OmikaPortLibre -Port 5123 -VerifierPlagesReservees:$false
|
||||
}
|
||||
finally {
|
||||
if ($verrou) { try { $verrou.Stop() } catch { } }
|
||||
}
|
||||
Assert (($null -ne $verdictOccupe) -and (-not $verdictOccupe.Libre)) 'Port 5123 occupe detecte INDISPONIBLE'
|
||||
|
||||
# C. Port dans la plage ephemere.
|
||||
$verdictEphemere = Test-OmikaPortLibre -Port 50000 -VerifierPlagesReservees:$false
|
||||
Assert (-not $verdictEphemere.Libre) 'Port 50000 (plage ephemere) detecte INDISPONIBLE'
|
||||
Assert ($verdictEphemere.Raisons -match 'EPHEMERE') 'La raison invoquee mentionne la plage ephemere'
|
||||
|
||||
# --- 6. Manifeste des packs de langue : generation reelle --------------------
|
||||
# Le manifeste contient deux champs MESURES (tailleOctets, sha256) : une valeur
|
||||
# inventee ne se voit que sur le telephone, ou le pack est refuse. On verifie
|
||||
# donc la generation contre une source independante (Get-FileHash / FileInfo).
|
||||
Write-Host ''
|
||||
Write-Host '=== 6. Manifeste des packs de langue : generation reelle ==='
|
||||
|
||||
$generateur = Join-Path $deploySrc 'New-OmikaLanguesManifeste.ps1'
|
||||
Assert (Test-Path -LiteralPath $generateur) 'Generateur de manifeste present'
|
||||
|
||||
$bac = Join-Path ([System.IO.Path]::GetTempPath()) ('omika-manifeste-' + [guid]::NewGuid().ToString('N').Substring(0, 8))
|
||||
|
||||
try {
|
||||
$null = New-Item -ItemType Directory -Path (Join-Path $bac 'es\versFrancais') -Force
|
||||
$null = New-Item -ItemType Directory -Path (Join-Path $bac 'es\depuisFrancais') -Force
|
||||
[System.IO.File]::WriteAllBytes((Join-Path $bac 'es\versFrancais\encoder.onnx'), (New-Object byte[] 4096))
|
||||
[System.IO.File]::WriteAllBytes((Join-Path $bac 'es\versFrancais\vocab.spm'), (New-Object byte[] 256))
|
||||
[System.IO.File]::WriteAllBytes((Join-Path $bac 'es\depuisFrancais\encoder.onnx'), (New-Object byte[] 4096))
|
||||
# Fichier inutile volontairement livre : exclu du chargement par l'application,
|
||||
# mais telecharge par chaque telephone s'il est declare. Doit etre signale.
|
||||
[System.IO.File]::WriteAllBytes((Join-Path $bac 'es\versFrancais\decoder_with_past_model.onnx'), (New-Object byte[] 512))
|
||||
|
||||
$sortieGeneration = (& $generateur -RootPathLangues $bac 6>&1 | Out-String)
|
||||
$manifeste = Join-Path $bac 'manifest.json'
|
||||
Assert (($LASTEXITCODE -eq 0) -and (Test-Path -LiteralPath $manifeste)) 'Generation : manifeste ecrit (code 0)'
|
||||
Assert ($sortieGeneration -match 'past_key_values') 'Generation : decodeur « _with_past » signale (poids mobile inutile)'
|
||||
|
||||
$json = Get-Content -LiteralPath $manifeste -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
Assert (@($json.packs).Count -eq 1) 'Manifeste : 1 pack'
|
||||
Assert (@($json.packs[0].fichiers).Count -eq 4) 'Manifeste : 4 fichiers declares'
|
||||
|
||||
$ecarts = 0
|
||||
foreach ($pack in $json.packs) {
|
||||
foreach ($fichier in $pack.fichiers) {
|
||||
$chemin = Join-Path (Join-Path (Join-Path $bac $pack.code) $fichier.sens) $fichier.nomFichier
|
||||
if ((Get-FileHash -LiteralPath $chemin -Algorithm SHA256).Hash.ToLowerInvariant() -ne $fichier.sha256) { $ecarts++ }
|
||||
if ((Get-Item -LiteralPath $chemin).Length -ne $fichier.tailleOctets) { $ecarts++ }
|
||||
}
|
||||
}
|
||||
Assert ($ecarts -eq 0) 'Manifeste : sha256 et tailles conformes (source independante)'
|
||||
|
||||
$octetsManifeste = [System.IO.File]::ReadAllBytes($manifeste)
|
||||
Assert (-not ($octetsManifeste[0] -eq 0xEF -and $octetsManifeste[1] -eq 0xBB -and $octetsManifeste[2] -eq 0xBF)) `
|
||||
'Manifeste : UTF-8 sans BOM (fichier de donnees)'
|
||||
|
||||
$null = & $generateur -RootPathLangues $bac -Verifier 6>&1
|
||||
Assert ($LASTEXITCODE -eq 0) 'Verification : manifeste conforme (code 0)'
|
||||
|
||||
# Un modele remplace APRES generation doit etre detecte : c'est le controle
|
||||
# qui evite un pack silencieusement refuse sur le telephone.
|
||||
[System.IO.File]::WriteAllBytes((Join-Path $bac 'es\versFrancais\vocab.spm'), (New-Object byte[] 300))
|
||||
$sortieManifeste = (& $generateur -RootPathLangues $bac -Verifier 6>&1 | Out-String)
|
||||
Assert ($LASTEXITCODE -eq 1) 'Verification : modele modifie detecte (code 1)'
|
||||
Assert ($sortieManifeste -match 'empreinte differente') 'Verification : ecart d''empreinte annonce'
|
||||
|
||||
# Le mobile compare le sens de facon sensible a la casse : un sens a la
|
||||
# mauvaise casse ferait refuser le pack sans aucune erreur cote serveur.
|
||||
$casse = Join-Path $bac 'manifest-casse.json'
|
||||
(Get-Content -LiteralPath $manifeste -Raw -Encoding UTF8).Replace('"sens": "versFrancais"', '"sens": "VersFrancais"') |
|
||||
Set-Content -LiteralPath $casse -Encoding UTF8
|
||||
$null = & $generateur -RootPathLangues $bac -Sortie $casse -Verifier 6>&1
|
||||
Assert ($LASTEXITCODE -eq 1) 'Verification : sens a la mauvaise casse detecte (refus mobile)'
|
||||
Remove-Item -LiteralPath $casse -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# --- Cohabitation avec le script d'export ---------------------------------
|
||||
# Le dossier des packs peut contenir le cache HuggingFace du script d'export
|
||||
# (.hf-cache, des milliers de fichiers) et la sauvegarde du manifeste : ni
|
||||
# l'un ni l'autre n'est un modele, et les signaler noierait le vrai controle
|
||||
# sous des faux positifs.
|
||||
$null = New-Item -ItemType Directory -Path (Join-Path $bac '.hf-cache\hub\models--Helsinki-NLP--opus-mt-es-fr') -Force
|
||||
[System.IO.File]::WriteAllBytes((Join-Path $bac '.hf-cache\hub\models--Helsinki-NLP--opus-mt-es-fr\blob-pytorch'), (New-Object byte[] 128))
|
||||
|
||||
# Deuxieme ecriture : le generateur conserve manifest.json.bak
|
||||
$null = & $generateur -RootPathLangues $bac 6>&1
|
||||
Assert (Test-Path -LiteralPath (Join-Path $bac 'manifest.json.bak')) 'Regeneration : sauvegarde manifest.json.bak ecrite'
|
||||
|
||||
$sortieTechnique = (& $generateur -RootPathLangues $bac -Verifier 6>&1 | Out-String)
|
||||
Assert ($LASTEXITCODE -eq 0) 'Cohabitation : verification conforme malgre .hf-cache et .bak'
|
||||
Assert ($sortieTechnique -notmatch 'hf-cache') 'Cohabitation : .hf-cache jamais signale comme fichier non declare'
|
||||
Assert ($sortieTechnique -notmatch 'manifest\.json\.bak') 'Cohabitation : la sauvegarde .bak n''est pas un faux positif'
|
||||
}
|
||||
finally {
|
||||
if (Test-Path -LiteralPath $bac) {
|
||||
Remove-Item -LiteralPath $bac -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# --- 6bis. Manifeste : poids partages d'un modele familial (commun/) ---------
|
||||
# Un modele familial range ses ~1,15 Go dans <code>/commun/ et ne laisse qu'un
|
||||
# modele.json par sens. Si le generateur traitait « commun » comme un sens
|
||||
# inconnu, il l'ignorerait : le telephone ne pourrait JAMAIS telecharger les
|
||||
# poids, et le pack serait declare mais inutilisable.
|
||||
Write-Host ''
|
||||
Write-Host '=== 6bis. Manifeste : poids partages (commun/) ==='
|
||||
|
||||
$bacFamille = Join-Path ([System.IO.Path]::GetTempPath()) ('omika-manifeste-famille-' + [guid]::NewGuid().ToString('N').Substring(0, 8))
|
||||
|
||||
try {
|
||||
$null = New-Item -ItemType Directory -Path (Join-Path $bacFamille 'pt\commun') -Force
|
||||
$null = New-Item -ItemType Directory -Path (Join-Path $bacFamille 'pt\versFrancais') -Force
|
||||
$null = New-Item -ItemType Directory -Path (Join-Path $bacFamille 'pt\depuisFrancais') -Force
|
||||
[System.IO.File]::WriteAllBytes((Join-Path $bacFamille 'pt\commun\encoder_model.onnx'), (New-Object byte[] 2048))
|
||||
[System.IO.File]::WriteAllBytes((Join-Path $bacFamille 'pt\commun\source.spm'), (New-Object byte[] 128))
|
||||
[System.IO.File]::WriteAllText((Join-Path $bacFamille 'pt\versFrancais\modele.json'), '{ "version": 1, "jetonCible": ">>fra<<" }')
|
||||
[System.IO.File]::WriteAllText((Join-Path $bacFamille 'pt\depuisFrancais\modele.json'), '{ "version": 1, "jetonCible": ">>por<<" }')
|
||||
|
||||
$sortieFamille = (& $generateur -RootPathLangues $bacFamille 6>&1 | Out-String)
|
||||
$manifesteFamille = Join-Path $bacFamille 'manifest.json'
|
||||
Assert (($LASTEXITCODE -eq 0) -and (Test-Path -LiteralPath $manifesteFamille)) 'Manifeste familial : ecrit (code 0)'
|
||||
Assert ($sortieFamille -notmatch 'sens inconnu') 'Manifeste familial : commun/ n''est pas traite comme un sens inconnu'
|
||||
|
||||
$jsonFamille = Get-Content -LiteralPath $manifesteFamille -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
$sensFamille = @($jsonFamille.packs[0].fichiers | ForEach-Object { $_.sens } | Sort-Object -Unique)
|
||||
Assert ($sensFamille -contains 'commun') 'Manifeste familial : le dossier commun est declare'
|
||||
Assert ($sensFamille -contains 'versFrancais' -and $sensFamille -contains 'depuisFrancais') `
|
||||
'Manifeste familial : les deux sens (modele.json) sont declares'
|
||||
Assert ((@($jsonFamille.packs[0].fichiers | Where-Object { $_.sens -eq 'commun' })).Count -eq 2) `
|
||||
'Manifeste familial : les poids sont declares une seule fois, sous commun'
|
||||
|
||||
$null = & $generateur -RootPathLangues $bacFamille -Verifier 6>&1
|
||||
Assert ($LASTEXITCODE -eq 0) 'Manifeste familial : verification conforme (code 0)'
|
||||
}
|
||||
finally {
|
||||
if (Test-Path -LiteralPath $bacFamille) {
|
||||
Remove-Item -LiteralPath $bacFamille -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# --- Resultat ----------------------------------------------------------------
|
||||
Write-Host ''
|
||||
Write-Host ("=== RESULTAT : {0} tests, {1} echec(s) ===" -f $tests, $fails)
|
||||
if ($fails -gt 0) { exit 1 } else { exit 0 }
|
||||
Reference in New Issue
Block a user