Test-BookmarkBackupTool.ps1

# =====================================================================================
# BookmarkBackupTool Module - Test Suite
# =====================================================================================
# Validates that the module in this folder loads and works. Safe to run: it only exports
# into a temporary folder and never imports into (or closes) your browsers.
#
# .\Test-BookmarkBackupTool.ps1 # quick checks
# .\Test-BookmarkBackupTool.ps1 -Detailed # also lists every result at the end

#Requires -Version 5.1

[CmdletBinding()]
param(
    [Parameter(Mandatory = $false)]
    [switch]$Detailed
)

$ErrorActionPreference = 'Continue'
$testResults = @()
$testCount = 0
$passCount = 0
$failCount = 0

function Write-TestResult {
    param(
        [string]$TestName,
        [bool]$Passed,
        [string]$Message = ""
    )

    $script:testCount++

    if ($Passed) {
        $script:passCount++
        Write-Host " [PASS] $TestName" -ForegroundColor Green
    } else {
        $script:failCount++
        Write-Host " [FAIL] $TestName" -ForegroundColor Red
        if ($Message) {
            Write-Host " $Message" -ForegroundColor Yellow
        }
    }

    $script:testResults += @{
        Name = $TestName
        Passed = $Passed
        Message = $Message
    }
}

Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "BookmarkBackupTool Module Test Suite" -ForegroundColor Cyan
Write-Host "========================================`n" -ForegroundColor Cyan

$manifestPath = Join-Path $PSScriptRoot 'BookmarkBackupTool.psd1'

# =====================================================================================
# Test 1: Manifest and Module Loading
# =====================================================================================
Write-Host "[Test 1] Manifest and Module Loading" -ForegroundColor Yellow

try {
    $manifest = Test-ModuleManifest -Path $manifestPath -ErrorAction Stop
    Write-TestResult "Manifest Valid" $true
} catch {
    Write-TestResult "Manifest Valid" $false $_.Exception.Message
    Write-Host "`nCRITICAL: Manifest is invalid. Stopping tests." -ForegroundColor Red
    exit 1
}

try {
    Import-Module $manifestPath -Force -ErrorAction Stop
    Write-TestResult "Module Import" $true
} catch {
    Write-TestResult "Module Import" $false $_.Exception.Message
    Write-Host "`nCRITICAL: Module cannot be loaded. Stopping tests." -ForegroundColor Red
    exit 1
}

$module = Get-Module BookmarkBackupTool
Write-TestResult "Module Loaded in Session" ($null -ne $module)
if ($module) {
    Write-TestResult "Loaded Version Matches Manifest ($($manifest.Version))" ($module.Version -eq $manifest.Version)
    $toolVersion = & $module { $script:ToolVersion }
    Write-TestResult "Module Built From Matching Script (v$toolVersion)" ($manifest.Version.ToString().StartsWith("$toolVersion"))
    if ($Detailed) {
        Write-Host " Module Path: $($module.Path)" -ForegroundColor Gray
        Write-Host " Module Version: $($module.Version)" -ForegroundColor Gray
    }
}

# =====================================================================================
# Test 2: Function Exports
# =====================================================================================
Write-Host "`n[Test 2] Function Export Tests" -ForegroundColor Yellow

$requiredFunctions = @(
    'Invoke-BookmarkBackupTool',
    'Export-Bookmarks',
    'Import-Bookmarks',
    'Import-FromZip',
    'Show-GUI',
    'New-BookmarkScheduledTask',
    'Remove-BookmarkScheduledTask',
    'Get-Configuration',
    'Save-Configuration',
    'Install-SQLiteIfMissing',
    'Get-BookmarkConfiguration',
    'Set-BookmarkConfiguration',
    'Get-HomeSharePath',
    'Test-BrowserInstalled',
    'Test-BrowserRunning',
    'Get-BrowserProfiles',
    'Test-BookmarkPrerequisites',
    'Test-BookmarkFileIntegrity'
)

foreach ($func in $requiredFunctions) {
    $exists = Get-Command $func -Module BookmarkBackupTool -ErrorAction SilentlyContinue
    Write-TestResult "Function: $func" ($null -ne $exists)
}

# =====================================================================================
# Test 3: Alias Exports
# =====================================================================================
Write-Host "`n[Test 3] Alias Export Tests" -ForegroundColor Yellow

$requiredAliases = @(
    'Export-BrowserBookmarks',
    'Import-BrowserBookmarks',
    'Backup-Bookmarks',
    'Restore-Bookmarks',
    'Show-BookmarkGUI'
)

foreach ($alias in $requiredAliases) {
    $exists = Get-Alias $alias -ErrorAction SilentlyContinue
    Write-TestResult "Alias: $alias" ($null -ne $exists)
}

# =====================================================================================
# Test 4: Parameters
# =====================================================================================
Write-Host "`n[Test 4] Parameter Tests" -ForegroundColor Yellow

$exportCmd = Get-Command Export-Bookmarks
foreach ($p in 'Path', 'Chrome', 'Edge', 'Firefox', 'ExportHtmlOnly', 'AllProfiles', 'CreateZip', 'WhatIf') {
    Write-TestResult "Export-Bookmarks -$p" ($exportCmd.Parameters.ContainsKey($p))
}
$importCmd = Get-Command Import-Bookmarks
foreach ($p in 'Path', 'CloseBrowserIfRunning', 'AllProfiles', 'Confirm') {
    Write-TestResult "Import-Bookmarks -$p" ($importCmd.Parameters.ContainsKey($p))
}
$invokeCmd = Get-Command Invoke-BookmarkBackupTool
foreach ($p in 'Silent', 'Action', 'TargetPath', 'HtmlOnly', 'CreateScheduledTask', 'ConfigPath') {
    Write-TestResult "Invoke-BookmarkBackupTool -$p" ($invokeCmd.Parameters.ContainsKey($p))
}

# =====================================================================================
# Test 5: Configuration System
# =====================================================================================
Write-Host "`n[Test 5] Configuration System Tests" -ForegroundColor Yellow

try {
    $config = Get-Configuration -ErrorAction Stop
    Write-TestResult "Get-Configuration" ($null -ne $config)
    if ($config) {
        foreach ($key in 'DefaultPath', 'PreferNetworkPath', 'DefaultBrowsers', 'AutoBackupBeforeImport', 'VerifyFileIntegrity') {
            Write-TestResult "Config Has $key" ($config.ContainsKey($key))
        }
    }
} catch {
    Write-TestResult "Get-Configuration" $false $_.Exception.Message
}

try {
    $cfgFile = Join-Path $env:TEMP "BookmarkToolTestConfig_$(Get-Random).json"
    $saved = Set-BookmarkConfiguration -LogRetentionDays 45 -DefaultBrowsers Chrome, Firefox -ConfigFilePath $cfgFile -ErrorAction Stop
    $reloaded = Get-BookmarkConfiguration -ConfigFilePath $cfgFile
    Write-TestResult "Set-/Get-BookmarkConfiguration Round Trip" ($reloaded.LogRetentionDays -eq 45 -and (@($reloaded.DefaultBrowsers) -join ',') -eq 'Chrome,Firefox')
    Remove-Item $cfgFile -Force
    # restore the session's configuration from the user's real file
    & $module { $script:Config = Get-Configuration }
} catch {
    Write-TestResult "Set-/Get-BookmarkConfiguration Round Trip" $false $_.Exception.Message
}

# =====================================================================================
# Test 5b: Utility Commands
# =====================================================================================
Write-Host "`n[Test 5b] Utility Command Tests" -ForegroundColor Yellow

try {
    $p = Get-HomeSharePath
    Write-TestResult "Get-HomeSharePath" ([bool]$p) $p
    Write-TestResult "Test-BookmarkPrerequisites" ((Test-BookmarkPrerequisites) -is [bool])
    foreach ($b in 'Chrome', 'Edge', 'Firefox') {
        $inst = Test-BrowserInstalled -BrowserName $b
        Write-TestResult "Test-BrowserInstalled $b ($inst)" ($inst -is [bool])
        Write-TestResult "Test-BrowserRunning $b" ((Test-BrowserRunning -BrowserName $b) -is [bool])
        if ($inst) {
            $profiles = @(Get-BrowserProfiles -BrowserName $b -AllProfiles)
            Write-TestResult "Get-BrowserProfiles $b -AllProfiles ($($profiles.Count))" ($profiles.Count -ge 1 -and $profiles[0].FullName)
        }
    }
    $bad = Join-Path $env:TEMP "BookmarkToolBad_$(Get-Random).json"; Set-Content $bad 'not json'
    Write-TestResult "Test-BookmarkFileIntegrity Rejects Bad File" (-not (Test-BookmarkFileIntegrity -FilePath $bad -BrowserType Chrome))
    Remove-Item $bad -Force
} catch {
    Write-TestResult "Utility Commands" $false $_.Exception.Message
}

# =====================================================================================
# Test 6: Safe Export (temporary folder only)
# =====================================================================================
Write-Host "`n[Test 6] Export Test (temporary folder)" -ForegroundColor Yellow

$testPath = Join-Path $env:TEMP "BookmarkToolTest_$(Get-Random)"
try {
    $installed = @()
    if (Test-Path "$env:LOCALAPPDATA\Google\Chrome\User Data") { $installed += 'Chrome' }
    if (Test-Path "$env:LOCALAPPDATA\Microsoft\Edge\User Data") { $installed += 'Edge' }
    if (Test-Path "$env:APPDATA\Mozilla\Firefox\Profiles") { $installed += 'Firefox' }
    if ($installed.Count -eq 0) {
        Write-Host " [SKIP] No supported browser profile found on this computer" -ForegroundColor Gray
    } else {
        $splat = @{ Silent = $true; Action = 'Export'; TargetPath = $testPath; CreateZip = $true }
        foreach ($b in $installed) { $splat[$b] = $true }
        Invoke-BookmarkBackupTool @splat *>&1 | Out-Null
        $zip = Get-ChildItem $testPath -Filter 'BookmarkBackup_*.zip' -ErrorAction SilentlyContinue
        Write-TestResult "Silent Export ($($installed -join ', '))" ($null -ne $zip)
        foreach ($b in $installed) {
            $data = Get-ChildItem $testPath -Filter "$($b)_BookmarkData_*" -ErrorAction SilentlyContinue
            Write-TestResult " $b Export File" ($null -ne $data)
        }
        $threw = $false
        try { Invoke-BookmarkBackupTool -Silent -TargetPath $testPath *>&1 | Out-Null } catch { $threw = $true }
        Write-TestResult "Missing -Action Reported as Error" $threw
    }
} catch {
    Write-TestResult "Silent Export" $false $_.Exception.Message
} finally {
    if (Test-Path $testPath) { Remove-Item $testPath -Force -Recurse }
}

# =====================================================================================
# Test 7: Advanced Features
# =====================================================================================
Write-Host "`n[Test 7] Advanced Feature Tests" -ForegroundColor Yellow

try {
    Add-Type -AssemblyName System.Windows.Forms -ErrorAction Stop
    Write-TestResult "Windows Forms Available (GUI)" $true
} catch {
    Write-TestResult "Windows Forms Available (GUI)" $false "GUI may not work"
}

$sqlite = & $module { $script:SQLiteAvailable }
if ($sqlite) { Write-TestResult "System.Data.SQLite Loaded (Firefox HTML/merge/validation)" $true }
else { Write-Host " [INFO] System.Data.SQLite not loaded - Firefox HTML, WAL merge and deep validation are disabled" -ForegroundColor Gray }

# =====================================================================================
# Test Summary
# =====================================================================================
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "Test Summary" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan

$passRate = if ($testCount -gt 0) { [math]::Round(($passCount / $testCount) * 100, 1) } else { 0 }

Write-Host "Total Tests: $testCount" -ForegroundColor White
Write-Host "Passed: $passCount" -ForegroundColor Green
Write-Host "Failed: $failCount" -ForegroundColor $(if($failCount -gt 0){'Red'}else{'Gray'})
Write-Host "Pass Rate: $passRate%" -ForegroundColor $(if($passRate -ge 90){'Green'}elseif($passRate -ge 70){'Yellow'}else{'Red'})

if ($failCount -eq 0) {
    Write-Host "`n[PASS] All tests passed! Module is working correctly." -ForegroundColor Green
} else {
    Write-Host "`n[FAIL] $failCount test(s) failed - review the failures above." -ForegroundColor Red
}

# =====================================================================================
# Detailed Results (Optional)
# =====================================================================================
if ($Detailed) {
    Write-Host "`n========================================" -ForegroundColor Cyan
    Write-Host "Detailed Test Results" -ForegroundColor Cyan
    Write-Host "========================================`n" -ForegroundColor Cyan

    foreach ($result in $testResults) {
        $status = if ($result.Passed) { "[PASS]" } else { "[FAIL]" }
        $color = if ($result.Passed) { "Green" } else { "Red" }

        Write-Host "$status $($result.Name)" -ForegroundColor $color
        if ($result.Message) {
            Write-Host " -> $($result.Message)" -ForegroundColor Yellow
        }
    }
}

Write-Host "`n========================================`n" -ForegroundColor Cyan

if ($failCount -eq 0) { exit 0 } else { exit 1 }