Tests/Exports.Tests.ps1
|
BeforeAll { $script:Root = Split-Path -Parent (Split-Path -Parent $PSCommandPath) $script:ModulePath = Join-Path -Path $script:Root -ChildPath 'TechCare.psd1' Import-Module -Name $script:ModulePath -Force $script:Manifest = Import-PowerShellDataFile -Path $script:ModulePath $script:Publicos = @( 'Get-TechCareDiagnostics', 'Get-TechCarePerformance', 'Get-TechCareProfile', 'Get-TechCareSecurity', 'Invoke-TechCare', 'Invoke-TechCareCleanup', 'Invoke-TechCareRepair', 'Invoke-TechCareUpdate', 'New-TechCareReport', 'Test-TechCarePrerequisite' ) $script:Privados = @( 'Add-TechCareResult', 'ConvertTo-TechCareHtml', 'Format-TechCareSize', 'Get-TechCareFreeSpace', 'Get-TechCareManagedState', 'Get-TechCarePathSize', 'Initialize-TechCareState', 'Invoke-TechCareProcess', 'New-TechCareRestorePoint', 'Test-TechCareAdmin', 'Test-TechCareConsent', 'Test-TechCarePlatform', 'Write-TechCareLog' ) } AfterAll { Remove-Module -Name TechCare -Force -ErrorAction SilentlyContinue } Describe 'Contrato de exportaciones' { It 'exporta exactamente los diez cmdlets publicos' { $exportadas = @((Get-Module -Name TechCare).ExportedFunctions.Keys | Sort-Object) $exportadas | Should -Be @($script:Publicos | Sort-Object) } It 'no exporta ninguna funcion privada' { $exportadas = @((Get-Module -Name TechCare).ExportedFunctions.Keys) foreach ($nombre in $script:Privados) { $exportadas | Should -Not -Contain $nombre -Because "$nombre es interno y no forma parte de la API" } } It 'las funciones privadas no quedan accesibles en la sesion' { foreach ($nombre in $script:Privados) { @(Get-Command -Name $nombre -ErrorAction SilentlyContinue).Count | Should -Be 0 -Because "$nombre no debe poder llamarse desde fuera del modulo" } } It 'no exporta variables ni alias' { (Get-Module -Name TechCare).ExportedVariables.Count | Should -Be 0 (Get-Module -Name TechCare).ExportedAliases.Count | Should -Be 0 (Get-Module -Name TechCare).ExportedCmdlets.Count | Should -Be 0 } It 'FunctionsToExport del manifiesto coincide con lo que hay en Public' { # Si no coinciden, PowerShell Gallery publica un modulo que exporta algo # distinto de lo documentado, y eso solo se descubre tras publicar. $enDisco = @(Get-ChildItem -LiteralPath (Join-Path -Path $script:Root -ChildPath 'Public') -Filter '*.ps1' -File | ForEach-Object { $_.BaseName } | Sort-Object) @($script:Manifest.FunctionsToExport | Sort-Object) | Should -Be $enDisco } It 'no usa comodines en FunctionsToExport' { # Un comodin obliga a PowerShell a cargar el modulo para saber que exporta # y rompe el autocompletado y el autoloading. foreach ($f in $script:Manifest.FunctionsToExport) { $f | Should -Not -BeLike '*`**' } } It 'cada fichero de Public y Private define una sola funcion, con el nombre del fichero' { foreach ($carpeta in @('Public', 'Private')) { foreach ($file in (Get-ChildItem -LiteralPath (Join-Path -Path $script:Root -ChildPath $carpeta) -Filter '*.ps1' -File)) { $tokens = $null $errores = $null $ast = [System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref]$tokens, [ref]$errores) @($errores).Count | Should -Be 0 -Because "$($file.Name) debe parsear sin errores" $funciones = @($ast.FindAll({ $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $false)) $funciones.Count | Should -Be 1 -Because "$($file.Name) debe definir una sola funcion" $funciones[0].Name | Should -Be $file.BaseName } } } } Describe 'Manifiesto del modulo' { It 'es un manifiesto valido para PowerShell' { { Test-ModuleManifest -Path $script:ModulePath -ErrorAction Stop } | Should -Not -Throw } It 'declara version 0.1.0 y las dos ediciones' { $script:Manifest.ModuleVersion | Should -Be '0.1.0' $script:Manifest.PowerShellVersion | Should -Be '5.1' @($script:Manifest.CompatiblePSEditions) | Should -Contain 'Desktop' @($script:Manifest.CompatiblePSEditions) | Should -Contain 'Core' } It 'acredita a Eduard Arbona como autor' { $script:Manifest.Author | Should -Be 'Eduard Arbona' $script:Manifest.Copyright | Should -BeLike '*Eduard Arbona*' } It 'no declara dependencias obligatorias' { # La promesa del README es que funciona con Windows recien instalado. # Se comprueba con ContainsKey y no con .Count: una clave ausente # devuelve $null, y @($null).Count vale 1, no 0. foreach ($clave in @('RequiredModules', 'RequiredAssemblies', 'NestedModules', 'ModuleList')) { if ($script:Manifest.ContainsKey($clave)) { @($script:Manifest[$clave]).Count | Should -Be 0 -Because "$clave debe estar vacia" } } } It 'publica Description, Tags, ProjectUri, LicenseUri e IconUri' { $psdata = $script:Manifest.PrivateData.PSData $script:Manifest.Description | Should -Not -BeNullOrEmpty $script:Manifest.Description.Length | Should -BeGreaterThan 60 @($psdata.Tags).Count | Should -BeGreaterThan 5 $psdata.ProjectUri | Should -Be 'https://github.com/earbona23/TechCare' $psdata.LicenseUri | Should -BeLike 'https://github.com/earbona23/TechCare/*' $psdata.IconUri | Should -Be 'https://raw.githubusercontent.com/earbona23/TechCare/main/assets/icon.png' } It 'el IconUri apunta a raw y no a la pagina blob de GitHub' { # raw.githubusercontent.com devuelve el PNG; github.com/.../blob/... sirve # una pagina HTML y la Gallery no pinta ningun icono. $script:Manifest.PrivateData.PSData.IconUri | Should -BeLike 'https://raw.githubusercontent.com/*' $script:Manifest.PrivateData.PSData.IconUri | Should -Not -BeLike '*/blob/*' } It 'las ReleaseNotes son texto, no una URL suelta' { # La Gallery renderiza este campo COMO las notas de version. Una URL ahi # le ensena al lector un enlace donde esperaba leer que cambio. $notas = $script:Manifest.PrivateData.PSData.ReleaseNotes $notas | Should -Not -BeNullOrEmpty $notas.Trim() | Should -Not -Match '^https?://\S+$' $notas | Should -BeLike '*0.1.0*' $notas.Length | Should -BeGreaterThan 120 } It 'declara las etiquetas de edicion que usa la Gallery para filtrar' { @($script:Manifest.PrivateData.PSData.Tags) | Should -Contain 'PSEdition_Desktop' @($script:Manifest.PrivateData.PSData.Tags) | Should -Contain 'PSEdition_Core' } It 'la version del manifiesto coincide con la del modulo en memoria' { InModuleScope -ModuleName TechCare { $script:ModuleVersion | Should -Be '0.1.0' } } It 'el icono referenciado existe en el repositorio' { Test-Path -LiteralPath (Join-Path -Path $script:Root -ChildPath 'assets/icon.png') | Should -BeTrue } } |