Tests/Report.Tests.ps1

BeforeAll {
    $script:ModulePath = Join-Path -Path (Split-Path -Parent $PSCommandPath) -ChildPath '../TechCare.psd1'
    Import-Module -Name $script:ModulePath -Force
}

AfterAll {
    Remove-Module -Name TechCare -Force -ErrorAction SilentlyContinue
}

Describe 'Generacion del informe' {

    BeforeEach {
        # Cada prueba parte de un estado limpio: el informe agrega lo que haya en
        # memoria, asi que un resultado colado de la prueba anterior falsearia el
        # estado global que se esta comprobando. Pester exige que este BeforeEach
        # viva dentro del Describe, no en la raiz del fichero.
        InModuleScope -ModuleName TechCare { Initialize-TechCareState }
    }

    It 'escribe HTML, JSON y log, y los tres son autocontenidos' {
        $destino = Join-Path -Path $TestDrive -ChildPath 'r1'
        InModuleScope -ModuleName TechCare {
            [void](Add-TechCareResult -Module 'Diagnose' -Status 'Green' -Findings @('Equipo correcto') -Recommendations @('Nada pendiente'))
        }
        $out = New-TechCareReport -ReportPath $destino -Confirm:$false

        $out.Written | Should -BeTrue
        Test-Path -LiteralPath $out.HtmlPath | Should -BeTrue
        Test-Path -LiteralPath $out.JsonPath | Should -BeTrue
        Test-Path -LiteralPath $out.LogPath  | Should -BeTrue

        $html = Get-Content -LiteralPath $out.HtmlPath -Raw
        $html | Should -BeLike '*TechCare*'
        # Autocontenido quiere decir sin recursos remotos: un informe que pida un
        # CSS a internet no se puede leer en un equipo sin red ni adjuntar a un
        # ticket, y filtraria que el informe se abrio.
        $html | Should -Not -Match 'src\s*=\s*"https?://'
        $html | Should -Not -Match '<link[^>]+href\s*=\s*"https?://'
    }

    It 'el JSON declara su esquema y es parseable' {
        $destino = Join-Path -Path $TestDrive -ChildPath 'r2'
        InModuleScope -ModuleName TechCare {
            [void](Add-TechCareResult -Module 'Cleanup' -Status 'Green' -Actions @('Limpiado C:\Temp') -SpaceFreedBytes 1048576)
        }
        $out = New-TechCareReport -ReportPath $destino -Confirm:$false
        $json = Get-Content -LiteralPath $out.JsonPath -Raw | ConvertFrom-Json

        $json.schemaVersion   | Should -Be 1
        $json.overall         | Should -Be 'Green'
        $json.spaceFreedBytes | Should -Be 1048576
        $json.runId           | Should -Not -BeNullOrEmpty
        $json.moduleVersion   | Should -Be ((Get-Module -Name TechCare).Version.ToString())
        # Los bytes en crudo son el campo que debe parsear una herramienta: el
        # texto formateado depende de la cultura del equipo.
        $json.spaceFreedBytes | Should -BeOfType [long]
    }

    It 'los tres ficheros se escriben en UTF-8 SIN BOM' {
        # Set-Content -Encoding UTF8 antepone BOM en Windows PowerShell 5.1 y no
        # en PowerShell 7: el mismo informe saldria distinto segun la consola, y
        # jq rechaza un JSON que empiece por BOM.
        InModuleScope -ModuleName TechCare {
            [void](Add-TechCareResult -Module 'Diagnose' -Status 'Green' -Findings @('Equipo correcto'))
        }
        $out = New-TechCareReport -ReportPath (Join-Path -Path $TestDrive -ChildPath 'bom') -Confirm:$false
        foreach ($ruta in @($out.HtmlPath, $out.JsonPath, $out.LogPath)) {
            $bytes = [System.IO.File]::ReadAllBytes($ruta)
            # El log puede estar vacio de forma legitima, asi que se compara solo
            # lo que haya: indexar tres bytes a ciegas reventaba con un fichero
            # de cero bytes y el fallo parecia aleatorio segun el orden de las
            # pruebas, porque otro fichero de pruebas dejaba lineas en el log.
            $cabecera = @()
            for ($i = 0; $i -lt [math]::Min(3, $bytes.Length); $i++) { $cabecera += $bytes[$i] }
            ($cabecera -join ',') | Should -Not -Be '239,187,191' -Because "$ruta no debe empezar por BOM"
        }
    }

    It 'un modulo en rojo tine de rojo el estado global' {
        InModuleScope -ModuleName TechCare {
            [void](Add-TechCareResult -Module 'Diagnose' -Status 'Green')
            [void](Add-TechCareResult -Module 'Security' -Status 'Red' -Findings @('Firewall apagado'))
            [void](Add-TechCareResult -Module 'Cleanup' -Status 'Yellow')
        }
        (New-TechCareReport -ReportPath (Join-Path -Path $TestDrive -ChildPath 'r3') -Confirm:$false).Overall | Should -Be 'Red'
    }

    It 'un amarillo sin rojos deja el estado global en amarillo' {
        InModuleScope -ModuleName TechCare {
            [void](Add-TechCareResult -Module 'Diagnose' -Status 'Green')
            [void](Add-TechCareResult -Module 'Cleanup' -Status 'Yellow')
        }
        (New-TechCareReport -ReportPath (Join-Path -Path $TestDrive -ChildPath 'r4') -Confirm:$false).Overall | Should -Be 'Yellow'
    }

    It 'sin resultados el estado global es Gris, no Verde' {
        # Un informe vacio no es un equipo sano. Decir Verde seria el peor fallo
        # posible en una herramienta de revision.
        (New-TechCareReport -ReportPath (Join-Path -Path $TestDrive -ChildPath 'r5') -Confirm:$false).Overall | Should -Be 'Gray'
    }

    It 'escapa el marcado que llegue en un hallazgo' {
        InModuleScope -ModuleName TechCare {
            [void](Add-TechCareResult -Module 'Diagnose' -Status 'Green' -Findings @('Fichero: <img src=x onerror=alert(1)>'))
        }
        $out = New-TechCareReport -ReportPath (Join-Path -Path $TestDrive -ChildPath 'r6') -Confirm:$false
        $html = Get-Content -LiteralPath $out.HtmlPath -Raw
        $html | Should -Not -BeLike '*<img src=x*'
        $html | Should -BeLike '*&lt;img src=x*'
    }

    It 'con -WhatIf no crea ni la carpeta ni los ficheros' {
        # El defecto original creaba la carpeta antes de consultar ShouldProcess,
        # asi que -WhatIf dejaba rastro en disco.
        $destino = Join-Path -Path $TestDrive -ChildPath 'nunca-debe-existir'
        $out = New-TechCareReport -ReportPath $destino -WhatIf
        $out.Written | Should -BeFalse
        Test-Path -LiteralPath $destino | Should -BeFalse
    }

    It 'acepta marca de tecnico y empresa y las escapa' {
        InModuleScope -ModuleName TechCare {
            [void](Add-TechCareResult -Module 'Diagnose' -Status 'Green')
        }
        $out = New-TechCareReport -ReportPath (Join-Path -Path $TestDrive -ChildPath 'r7') `
            -TechnicianName 'Eduard Arbona' -CompanyName 'Soporte & Mantenimiento' -ContactInfo 'soporte@ejemplo.test' -Confirm:$false
        $html = Get-Content -LiteralPath $out.HtmlPath -Raw
        $html | Should -BeLike '*Eduard Arbona*'
        $html | Should -BeLike '*Soporte &amp; Mantenimiento*'
    }

    It 'funciona sin -ReportPath, cayendo a la carpeta temporal del usuario' {
        # $env:TEMP no existe en Linux ni en macOS; la ruta por defecto usa
        # GetTempPath precisamente para que la suite corra en los tres.
        InModuleScope -ModuleName TechCare {
            [void](Add-TechCareResult -Module 'Diagnose' -Status 'Green')
        }
        $out = New-TechCareReport -Confirm:$false
        try {
            $out.Written | Should -BeTrue
            Test-Path -LiteralPath $out.HtmlPath | Should -BeTrue
        } finally {
            $carpeta = Split-Path -Parent $out.HtmlPath
            if (Test-Path -LiteralPath $carpeta) { Remove-Item -LiteralPath $carpeta -Recurse -Force -ErrorAction SilentlyContinue }
        }
    }
}