tests/completion_probe.ps1

<#
fount 命令补全的自检:用命令行字符串造 AST,直接调 Get-FountArgumentCompletion。
只断言与已装 part 无关的补全结果,依赖 part 内容的位置只打印出来供人肉核对。
 
用法:pwsh -File tests/completion_probe.ps1
#>

param([string]$RepoRoot = (Split-Path -Parent $PSScriptRoot))

if (-not (Get-Command fount.ps1 -ErrorAction Ignore)) {
    Write-Host '找不到 fount.ps1,先装上 fount 再跑这个自检。' -ForegroundColor Red
    exit 1
}

. "$RepoRoot/data.ps1"
. "$RepoRoot/argument_completer.ps1"

$script:Failures = 0

# 取 $Line 在 $Cursor 处的补全结果文本($Cursor 为负表示行尾)。
# Get-FountArgumentCompletion 的 catch 会吞掉内部异常只打个 Write-Host,所以这里把信息流也捞出来当失败。
function Get-Completion([string]$Line, [int]$Cursor = -1) {
    if ($Cursor -lt 0) { $Cursor = $Line.Length }
    $ast = [System.Management.Automation.Language.Parser]::ParseInput($Line, [ref]$null, [ref]$null)
    $commandAst = $ast.Find({ param($node) $node -is [System.Management.Automation.Language.CommandAst] }, $true)
    $wordToComplete = ($Line.Substring(0, $Cursor) -split '\s+')[-1]
    $merged = @(Get-FountArgumentCompletion -WordToComplete $wordToComplete -CommandAst $commandAst -CursorPosition $Cursor 6>&1)
    $errors = @($merged | Where-Object { $_ -is [System.Management.Automation.InformationRecord] })
    if ($errors) { throw "补全内部报错: $(($errors | ForEach-Object { $_.MessageData.Message }) -join ' / ')" }
    $merged | ForEach-Object { if ($_ -is [System.Management.Automation.CompletionResult]) { $_.CompletionText } else { $_ } }
}

function Get-CompletionText([string]$Line, [int]$Cursor = -1) {
    @(Get-Completion $Line $Cursor) -join ' '
}

function Assert-Completion([string]$Line, [string[]]$Expected, [int]$Cursor = -1) {
    if ($Cursor -lt 0) { $Cursor = $Line.Length }
    try { $actualText = Get-CompletionText $Line $Cursor }
    catch { $script:Failures++; Write-Host "FAIL <$Line>@$Cursor`n $($_.Exception.Message)" -ForegroundColor Red; return }
    $expectedText = $Expected -join ' '
    if ($actualText -eq $expectedText) {
        Write-Host "ok <$Line>@$Cursor -> $actualText" -ForegroundColor Green
    }
    else {
        $script:Failures++
        Write-Host "FAIL <$Line>@$Cursor`n 期望 $expectedText`n 实际 $actualText" -ForegroundColor Red
    }
}

function Show-Completion([string]$Line, [int]$Cursor = -1) {
    if ($Cursor -lt 0) { $Cursor = $Line.Length }
    try { $text = Get-CompletionText $Line $Cursor }
    catch { $text = "报错:$($_.Exception.Message)" }
    Write-Host " <$Line>@$Cursor -> $text"
}

$rootCommands = 'background', 'clean', 'debug', 'eval', 'geneexe', 'init', 'keepalive', 'log', 'logo', 'nop', 'open', 'reboot', 'remove', 'run', 'runas', 'server', 'shutdown', 'test', 'update', 'version'
# 非 Windows 上 argument_completer.ps1 会摘掉 geneexe。
if (-not $IsWindows) { $rootCommands = $rootCommands -ne 'geneexe' }

# 各命令节点接受的参数。
Assert-Completion 'fount ' $rootCommands
Assert-Completion 'fount ru' 'run', 'runas'
Assert-Completion 'fount background ' ($rootCommands -ne 'background')
Assert-Completion 'fount background keepalive ' 'debug', 'run', 'runas'
Assert-Completion 'fount keepalive debug ' 'run', 'runas'
# 没有子参数的叶子命令不该补出东西,也不该报错。
Assert-Completion 'fount init ' @()
Assert-Completion 'fount background init ' @()
# 光标停在参数中间/后面时,后面的参数不该改变当前层级。
Assert-Completion 'fount run ' $rootCommands -Cursor 6
# 拿变量当参数时不该炸。
Assert-Completion 'fount run $part ' @()

# 依赖已装 part 与用户名,只打印。
$username = Get-FountUserList | Select-Object -First 1
Show-Completion 'fount run '
Show-Completion 'fount run ch'
Show-Completion 'fount runas '
Show-Completion "fount runas $username "
Show-Completion "fount runas $username shells/"

if ($script:Failures) {
    Write-Host "$script:Failures 项失败" -ForegroundColor Red
    exit 1
}
Write-Host '全部通过' -ForegroundColor Green
exit 0