Tests/Preview.Tests.ps1

# El modulo se importa en el ambito del fichero, no en BeforeAll: InModuleScope
# se resuelve durante el descubrimiento de Pester, antes de que BeforeAll corra.
$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
}

# Estas pruebas son la red de seguridad de la promesa principal del modulo:
# "vista previa por defecto". Se ejecutan en Linux simulando Windows con Mock,
# de modo que verifican el CONTRATO del orquestador (que parametros pasa a cada
# modulo) sin ejecutar DISM, sfc ni borrar nada.
Describe 'Vista previa por defecto' {

    BeforeEach {
        InModuleScope -ModuleName TechCare { Initialize-TechCareState }
    }

    InModuleScope -ModuleName TechCare {

        BeforeEach {
            Mock -CommandName Test-TechCarePlatform -MockWith { $true }
            Mock -CommandName Test-TechCarePrerequisite -MockWith {
                [pscustomobject]@{
                    IsWindows          = $true
                    IsAdmin            = $true
                    IsSupportedWindows = $true
                    OSCaption          = 'Microsoft Windows 11 Pro'
                    OSBuild            = 22631
                    IsManaged          = $false
                    ManagedReasons     = [string[]]@()
                    RelaunchCommand    = ''
                    PSVersion          = '7.0.0'
                    PSEdition          = 'Core'
                    ModuleVersion      = '0.1.0'
                }
            }
            Mock -CommandName Get-TechCareDiagnostics -MockWith { Add-TechCareResult -Module 'Diagnose' -Status 'Green' }
            Mock -CommandName Invoke-TechCareRepair   -MockWith { Add-TechCareResult -Module 'Repair' -Status 'Green' }
            Mock -CommandName Invoke-TechCareUpdate   -MockWith { Add-TechCareResult -Module 'Update' -Status 'Green' }
            Mock -CommandName Invoke-TechCareCleanup  -MockWith { Add-TechCareResult -Module 'Cleanup' -Status 'Green' }
            Mock -CommandName Get-TechCarePerformance -MockWith { Add-TechCareResult -Module 'Performance' -Status 'Green' }
            Mock -CommandName Get-TechCareSecurity    -MockWith { Add-TechCareResult -Module 'Security' -Status 'Green' }
            Mock -CommandName New-TechCareReport      -MockWith {
                [pscustomobject]@{ HtmlPath = 'informe.html'; JsonPath = 'informe.json'; LogPath = 'log.txt'; Overall = 'Green'; Written = $true }
            }
            # Se mockea el seam privado, no Checkpoint-Computer: ese cmdlet no
            # existe en Linux y Mock no puede sustituir un comando ausente.
            Mock -CommandName New-TechCareRestorePoint -MockWith {
                [pscustomobject]@{ Created = $true; Method = 'mock'; Message = '' }
            }
        }

        It 'sin -Apply propaga -WhatIf a TODOS los modulos que escriben' {
            # Este es el defecto que tenia el diseno: el orquestador llamaba a los
            # modulos con -Confirm:$false y sin -WhatIf, asi que una pasada "de
            # vista previa" ejecutaba DISM y borraba los temporales de verdad.
            [void](Invoke-TechCare -MaintenanceProfile Full -ReportPath $TestDrive)

            foreach ($nombre in @('Invoke-TechCareRepair', 'Invoke-TechCareUpdate', 'Invoke-TechCareCleanup', 'Get-TechCarePerformance', 'Get-TechCareSecurity')) {
                Should -Invoke -CommandName $nombre -Times 1 -Exactly -ParameterFilter {
                    $WhatIf -eq $true
                } -Because "$nombre debe recibir -WhatIf cuando no se pidio -Apply"
            }
        }

        It 'sin -Apply no crea punto de restauracion' {
            [void](Invoke-TechCare -MaintenanceProfile Standard -ReportPath $TestDrive)
            Should -Invoke -CommandName New-TechCareRestorePoint -Times 0 -Exactly
        }

        It 'con -Apply los modulos reciben -WhatIf en falso' {
            [void](Invoke-TechCare -MaintenanceProfile Standard -Apply -Force -ReportPath $TestDrive)
            Should -Invoke -CommandName Invoke-TechCareCleanup -Times 1 -Exactly -ParameterFilter {
                $WhatIf -eq $false
            }
        }

        It 'con -Apply crea punto de restauracion antes de tocar nada' {
            [void](Invoke-TechCare -MaintenanceProfile Standard -Apply -Force -ReportPath $TestDrive)
            Should -Invoke -CommandName New-TechCareRestorePoint -Times 1 -Exactly
        }

        It 'con -Apply y -SkipRestorePoint no crea punto de restauracion' {
            [void](Invoke-TechCare -MaintenanceProfile Standard -Apply -Force -SkipRestorePoint -ReportPath $TestDrive)
            Should -Invoke -CommandName New-TechCareRestorePoint -Times 0 -Exactly
        }

        It '-WhatIf explicito fuerza vista previa incluso con -Apply' {
            [void](Invoke-TechCare -MaintenanceProfile Standard -Apply -Force -ReportPath $TestDrive -WhatIf)
            Should -Invoke -CommandName New-TechCareRestorePoint -Times 0 -Exactly
            Should -Invoke -CommandName Invoke-TechCareCleanup -Times 1 -Exactly -ParameterFilter {
                $WhatIf -eq $true
            }
        }

        It 'no pasa -PowerPlan cuando el tecnico no lo indico' {
            # Pasar -PowerPlan '' viola el ValidateSet de Get-TechCarePerformance y
            # tumbaba el modulo Performance entero con un error de validacion.
            [void](Invoke-TechCare -MaintenanceProfile Full -ReportPath $TestDrive)
            Should -Invoke -CommandName Get-TechCarePerformance -Times 1 -Exactly -ParameterFilter {
                -not $PSBoundParameters.ContainsKey('PowerPlan')
            }
        }

        It 'pasa -PowerPlan cuando si se indico' {
            [void](Invoke-TechCare -MaintenanceProfile Full -PowerPlan HighPerformance -ReportPath $TestDrive)
            Should -Invoke -CommandName Get-TechCarePerformance -Times 1 -Exactly -ParameterFilter {
                $PowerPlan -eq 'HighPerformance'
            }
        }

        It 'propaga -Apply al modulo de seguridad y no al reves' {
            [void](Invoke-TechCare -MaintenanceProfile Standard -ReportPath $TestDrive)
            Should -Invoke -CommandName Get-TechCareSecurity -Times 1 -Exactly -ParameterFilter { $Apply -eq $false }
        }

        It 'ejecuta los modulos en el orden canonico aunque se pidan desordenados' {
            [void](Invoke-TechCare -Modules Report, Cleanup, Diagnose -ReportPath $TestDrive)
            $modulos = @($script:State.Results | ForEach-Object { $_.Module })
            $modulos | Should -Be @('Diagnose', 'Cleanup')
            # Report no deja resultado propio: su mock devuelve el objeto de rutas.
            Should -Invoke -CommandName New-TechCareReport -Times 1 -Exactly
        }

        It 'un modulo que lanza no detiene la pasada y queda en rojo' {
            Mock -CommandName Invoke-TechCareCleanup -MockWith { throw 'disco desconectado' }
            [void](Invoke-TechCare -MaintenanceProfile Basic -ReportPath $TestDrive)
            $cleanup = @($script:State.Results | Where-Object { $_.Module -eq 'Cleanup' })
            $cleanup.Count | Should -Be 1
            $cleanup[0].Status | Should -Be 'Red'
            $cleanup[0].Findings -join ' ' | Should -BeLike '*disco desconectado*'
            Should -Invoke -CommandName New-TechCareReport -Times 1 -Exactly -Because 'el informe se genera aunque un modulo falle'
        }

        It 'dos pasadas seguidas no acumulan resultados de la primera' {
            # El estado vivia en el modulo y nunca se reiniciaba, asi que la
            # segunda pasada informaba los resultados de las dos.
            [void](Invoke-TechCare -MaintenanceProfile Basic -ReportPath $TestDrive)
            $primera = @($script:State.Results).Count
            [void](Invoke-TechCare -MaintenanceProfile Basic -ReportPath $TestDrive)
            @($script:State.Results).Count | Should -Be $primera
        }

        It 'cada pasada tiene su propio identificador' {
            [void](Invoke-TechCare -MaintenanceProfile Basic -ReportPath $TestDrive)
            $id1 = $script:State.RunId
            [void](Invoke-TechCare -MaintenanceProfile Basic -ReportPath $TestDrive)
            $script:State.RunId | Should -Not -Be $id1
        }

        It '-Apply sin privilegios de administrador aborta antes de tocar nada' {
            Mock -CommandName Test-TechCarePrerequisite -MockWith {
                [pscustomobject]@{
                    IsWindows = $true; IsAdmin = $false; IsSupportedWindows = $true
                    OSCaption = 'Microsoft Windows 11 Pro'; OSBuild = 22631
                    IsManaged = $false; ManagedReasons = [string[]]@()
                    RelaunchCommand = 'Start-Process pwsh -Verb RunAs'
                    PSVersion = '7.0.0'; PSEdition = 'Core'; ModuleVersion = '0.1.0'
                }
            }
            { Invoke-TechCare -MaintenanceProfile Standard -Apply -Force } | Should -Throw -ExpectedMessage '*administrador*'
            Should -Invoke -CommandName Invoke-TechCareCleanup -Times 0 -Exactly
        }

        It 'en un equipo gestionado y sin -Force no aplica sin confirmacion' {
            Mock -CommandName Test-TechCarePrerequisite -MockWith {
                [pscustomobject]@{
                    IsWindows = $true; IsAdmin = $true; IsSupportedWindows = $true
                    OSCaption = 'Microsoft Windows 11 Enterprise'; OSBuild = 22631
                    IsManaged = $true
                    ManagedReasons = [string[]]@('Equipo administrado por MDM / Intune')
                    RelaunchCommand = ''; PSVersion = '7.0.0'; PSEdition = 'Core'; ModuleVersion = '0.1.0'
                }
            }
            # ShouldContinue sin host interactivo responde "no": la pasada se
            # cancela, que es el lado seguro del fallo.
            [void](Invoke-TechCare -MaintenanceProfile Standard -Apply -ReportPath $TestDrive -ErrorAction SilentlyContinue)
            Should -Invoke -CommandName Invoke-TechCareCleanup -Times 0 -Exactly
            Should -Invoke -CommandName New-TechCareRestorePoint -Times 0 -Exactly
        }
    }
}