From a3bdfffcf2e6a611a1cbdcc9a312be44527ac043 Mon Sep 17 00:00:00 2001 From: Zhao <24781737@qq.com> Date: Thu, 20 Aug 2026 12:19:31 +0800 Subject: [PATCH] test(smoke): execute and extend Codex UTF-8 regression --- skills/scripts/smoke.ps1 | 431 +++++++++--------- .../scripts/test-bootstrap-codex-encoding.ps1 | 22 + 2 files changed, 231 insertions(+), 222 deletions(-) diff --git a/skills/scripts/smoke.ps1 b/skills/scripts/smoke.ps1 index fc7fc66..9b609f6 100644 --- a/skills/scripts/smoke.ps1 +++ b/skills/scripts/smoke.ps1 @@ -1,231 +1,218 @@ -#Requires -Version 5.1 -# reverse-skill smoke entrypoint: verify + script parse + master-route sample matrix. -# Usage: -# powershell -NoProfile -ExecutionPolicy Bypass -File skills/scripts/smoke.ps1 -# powershell -File skills/scripts/smoke.ps1 -LogDir C:\path\to\logs -param( - [string] $LogDir = '', - [string] $PackageRoot = '' -) -$ErrorActionPreference = 'Continue' - -$scriptDir = $PSScriptRoot -if (-not $scriptDir) { $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path } -$skillsRoot = Split-Path -Parent $scriptDir -if (-not $PackageRoot) { $PackageRoot = Split-Path -Parent $skillsRoot } - -if ([string]::IsNullOrWhiteSpace($LogDir)) { - $stamp = Get-Date -Format 'yyyyMMdd-HHmmss' - $tmpBase = if ($env:TEMP) { $env:TEMP } else { [System.IO.Path]::GetTempPath() } - $LogDir = Join-Path $tmpBase ("rs-smoke-{0}" -f $stamp) -} -New-Item -ItemType Directory -Path $LogDir -Force | Out-Null - -$fail = New-Object System.Collections.Generic.List[string] -function Ok([string] $m) { Write-Host ("[OK] {0}" -f $m) -ForegroundColor Green } -function Bad([string] $m) { - Write-Host ("[FAIL] {0}" -f $m) -ForegroundColor Red - [void]$fail.Add($m) -} - -# Prefer the same host that launched smoke (pwsh on GHA windows-latest). -# Bare "powershell" often resolves to Windows PowerShell 5.1, which mis-parses -# UTF-8 scripts without BOM when nested from pwsh. -$SmokeHostExe = $null -try { - $procPath = (Get-Process -Id $PID -ErrorAction Stop).Path - if ($procPath -and (Test-Path -LiteralPath $procPath)) { $SmokeHostExe = $procPath } -} catch { } -if (-not $SmokeHostExe) { - $cmd = Get-Command pwsh -ErrorAction SilentlyContinue - if ($cmd -and $cmd.Source) { - $SmokeHostExe = $cmd.Source - } else { - $SmokeHostExe = Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe' - } -} -Write-Host ("=== reverse-skill smoke | LogDir={0} | Host={1} ===" -f $LogDir, $SmokeHostExe) - -# --- 1) routing coherence --- -$verify = Join-Path $scriptDir 'verify-routing-coherence.ps1' -if (-not (Test-Path -LiteralPath $verify)) { - Bad 'verify-routing-coherence.ps1 missing' - $verifyExit = 1 -} else { - $vLog = Join-Path $LogDir '01-verify.txt' - & $SmokeHostExe -NoProfile -ExecutionPolicy Bypass -File $verify 2>&1 | Tee-Object -FilePath $vLog | Out-Null - $verifyExit = $LASTEXITCODE - if ($verifyExit -eq 0) { Ok 'verify-routing-coherence exit 0' } else { Bad ("verify-routing-coherence exit {0}" -f $verifyExit) } -} - -# --- 2) parse key scripts --- -$scripts = @( - 'verify-routing-coherence.ps1', - 'master-route.ps1', - 'case-init.ps1', - 'lib/WorkRoot.ps1', - 'lib/RouteScope.ps1', - 'bootstrap-reverse.ps1', - 'refresh-tool-index.ps1', - 'smoke.ps1', - 'append-evidence.ps1', - 'case-guard.ps1', - 'test-routing.ps1', - 'extract-summaries.ps1', +#Requires -Version 5.1 +# reverse-skill smoke entrypoint: verify + script parse + master-route sample matrix. +# Usage: +# powershell -NoProfile -ExecutionPolicy Bypass -File skills/scripts/smoke.ps1 +# powershell -File skills/scripts/smoke.ps1 -LogDir C:\path\to\logs +param( + [string] $LogDir = '', + [string] $PackageRoot = '' +) +$ErrorActionPreference = 'Continue' + +$scriptDir = $PSScriptRoot +if (-not $scriptDir) { $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path } +$skillsRoot = Split-Path -Parent $scriptDir +if (-not $PackageRoot) { $PackageRoot = Split-Path -Parent $skillsRoot } + +if ([string]::IsNullOrWhiteSpace($LogDir)) { + $stamp = Get-Date -Format 'yyyyMMdd-HHmmss' + $tmpBase = if ($env:TEMP) { $env:TEMP } else { [System.IO.Path]::GetTempPath() } + $LogDir = Join-Path $tmpBase ("rs-smoke-{0}" -f $stamp) +} +New-Item -ItemType Directory -Path $LogDir -Force | Out-Null + +$fail = New-Object System.Collections.Generic.List[string] +function Ok([string] $m) { Write-Host ("[OK] {0}" -f $m) -ForegroundColor Green } +function Bad([string] $m) { + Write-Host ("[FAIL] {0}" -f $m) -ForegroundColor Red + [void]$fail.Add($m) +} + +# Prefer the same host that launched smoke (pwsh on GHA windows-latest). +# Bare "powershell" often resolves to Windows PowerShell 5.1, which mis-parses +# UTF-8 scripts without BOM when nested from pwsh. +$SmokeHostExe = $null +try { + $procPath = (Get-Process -Id $PID -ErrorAction Stop).Path + if ($procPath -and (Test-Path -LiteralPath $procPath)) { $SmokeHostExe = $procPath } +} catch { } +if (-not $SmokeHostExe) { + $cmd = Get-Command pwsh -ErrorAction SilentlyContinue + if ($cmd -and $cmd.Source) { + $SmokeHostExe = $cmd.Source + } else { + $SmokeHostExe = Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe' + } +} +Write-Host ("=== reverse-skill smoke | LogDir={0} | Host={1} ===" -f $LogDir, $SmokeHostExe) + +# --- 1) routing coherence --- +$verify = Join-Path $scriptDir 'verify-routing-coherence.ps1' +if (-not (Test-Path -LiteralPath $verify)) { + Bad 'verify-routing-coherence.ps1 missing' + $verifyExit = 1 +} else { + $vLog = Join-Path $LogDir '01-verify.txt' + & $SmokeHostExe -NoProfile -ExecutionPolicy Bypass -File $verify 2>&1 | Tee-Object -FilePath $vLog | Out-Null + $verifyExit = $LASTEXITCODE + if ($verifyExit -eq 0) { Ok 'verify-routing-coherence exit 0' } else { Bad ("verify-routing-coherence exit {0}" -f $verifyExit) } +} + +# --- 2) parse key scripts --- +$scripts = @( + 'verify-routing-coherence.ps1', + 'master-route.ps1', + 'case-init.ps1', + 'lib\WorkRoot.ps1', + 'bootstrap-reverse.ps1', + 'refresh-tool-index.ps1', + 'smoke.ps1', + 'append-evidence.ps1', + 'case-guard.ps1', + 'test-routing.ps1', + 'extract-summaries.ps1' 'test-bootstrap-codex-encoding.ps1' ) -$parseOk = 0 -$parseFail = 0 -$parseLog = New-Object System.Collections.Generic.List[string] -foreach ($name in $scripts) { - $p = Join-Path $scriptDir $name - if (-not (Test-Path -LiteralPath $p)) { - # append-evidence is required for P0; others should exist - if ($name -eq 'append-evidence.ps1' -or $name -eq 'smoke.ps1') { - Bad ("script missing: {0}" -f $name) - $parseFail++ - [void]$parseLog.Add("MISSING $name") - } - continue - } - $errs = $null - $tokens = $null - $null = [System.Management.Automation.Language.Parser]::ParseFile($p, [ref]$tokens, [ref]$errs) - if ($errs -and $errs.Count -gt 0) { - Bad ("parse fail {0}: {1}" -f $name, $errs[0].Message) - $parseFail++ - [void]$parseLog.Add("FAIL $name $($errs[0].Message)") - } else { - Ok ("parse {0}" -f $name) - $parseOk++ - [void]$parseLog.Add("OK $name") - } -} -$idaScripts = @( - 'ida-reverse/scripts/start.ps1', - 'ida-reverse/scripts/open.ps1', - 'ida-reverse/scripts/watchdog.ps1', - 'ida-reverse/scripts/install-autostart.ps1', - 'ida-reverse/scripts/start-gui.ps1', - 'ida-reverse/scripts/IdaOpenHelpers.ps1' -) -foreach ($rel in $idaScripts) { - $p = Join-Path $skillsRoot $rel - $name = $rel - if (-not (Test-Path -LiteralPath $p)) { - Bad ("script missing: {0}" -f $name) - $parseFail++ - [void]$parseLog.Add("MISSING $name") - continue - } - $errs = $null - $tokens = $null - $null = [System.Management.Automation.Language.Parser]::ParseFile($p, [ref]$tokens, [ref]$errs) - if ($errs -and $errs.Count -gt 0) { - Bad ("parse fail {0}: {1}" -f $name, $errs[0].Message) - $parseFail++ - [void]$parseLog.Add("FAIL $name $($errs[0].Message)") - } else { - Ok ("parse {0}" -f $name) - $parseOk++ - [void]$parseLog.Add("OK $name") - } -} +$parseOk = 0 +$parseFail = 0 +$parseLog = New-Object System.Collections.Generic.List[string] +foreach ($name in $scripts) { + $p = Join-Path $scriptDir $name + if (-not (Test-Path -LiteralPath $p)) { + # append-evidence is required for P0; others should exist + if ($name -eq 'append-evidence.ps1' -or $name -eq 'smoke.ps1') { + Bad ("script missing: {0}" -f $name) + $parseFail++ + [void]$parseLog.Add("MISSING $name") + } + continue + } + $errs = $null + $tokens = $null + $null = [System.Management.Automation.Language.Parser]::ParseFile($p, [ref]$tokens, [ref]$errs) + if ($errs -and $errs.Count -gt 0) { + Bad ("parse fail {0}: {1}" -f $name, $errs[0].Message) + $parseFail++ + [void]$parseLog.Add("FAIL $name $($errs[0].Message)") + } else { + Ok ("parse {0}" -f $name) + $parseOk++ + [void]$parseLog.Add("OK $name") + } +} $parseLog -join [Environment]::NewLine | Set-Content (Join-Path $LogDir '02-parse.txt') -Encoding UTF8 -# --- 3) master-route sample matrix --- -$mr = Join-Path $scriptDir 'master-route.ps1' -$cases = @( - @{ Name = 'apk'; Hint = 'decompile APK with jadx apktool smali'; Expect = 'apk-reverse' }, - @{ Name = 'js'; Hint = 'js reverse frontend sign jshook encrypted param'; Expect = 'js-reverse' }, - @{ Name = 'ida'; Hint = 'IDA decompile PE binary disassemble'; Expect = 'ida-reverse' }, - @{ Name = 'pentest'; Hint = 'nmap nuclei sqlmap ffuf pentest bug bounty'; Expect = 'pentest-tools' }, - @{ Name = 'llm'; Hint = 'LLM prompt inject jailbreak agent security garak'; Expect = 'llm-security' }, - @{ Name = 'zh-apk'; Hint = '安卓 APK 加固 反编译'; Expect = 'apk-reverse' }, - @{ Name = 'zh-pentest'; Hint = '渗透测试 端口扫描 SQL注入'; Expect = 'pentest-tools' }, - @{ Name = 'zh-js'; Hint = '前端签名 JS逆向 加密参数'; Expect = 'js-reverse' }, - @{ Name = 'evidence'; Hint = 'case review evidence chain traceability'; Expect = 'case-review' } -) -$routeOk = 0 -$routeFail = 0 -$routeSummary = New-Object System.Collections.Generic.List[string] -if (-not (Test-Path -LiteralPath $mr)) { - Bad 'master-route.ps1 missing' +# --- 3) Codex config UTF-8 round-trip regression --- +$encodingTest = Join-Path $scriptDir 'test-bootstrap-codex-encoding.ps1' +if (-not (Test-Path -LiteralPath $encodingTest)) { + Bad 'test-bootstrap-codex-encoding.ps1 missing' } else { - foreach ($c in $cases) { - $outFile = Join-Path $LogDir ("route-{0}.txt" -f $c.Name) - $raw = & $SmokeHostExe -NoProfile -ExecutionPolicy Bypass -File $mr -Hint $c.Hint 2>&1 | Out-String - $raw | Set-Content -Path $outFile -Encoding UTF8 - if ($raw -match [regex]::Escape($c.Expect)) { - Ok ("route {0} -> {1}" -f $c.Name, $c.Expect) - $routeOk++ - [void]$routeSummary.Add("PASS $($c.Name)") - } else { - Bad ("route {0} expect {1}" -f $c.Name, $c.Expect) - $routeFail++ - [void]$routeSummary.Add("FAIL $($c.Name)") - } - } -} -$routeSummary -join [Environment]::NewLine | Set-Content (Join-Path $LogDir '03-route-summary.txt') -Encoding UTF8 - -# --- 4) Evidence ID immutability --- -$appendEvidence = Join-Path $scriptDir 'append-evidence.ps1' -$evidenceCase = Join-Path $LogDir 'evidence-immutability' -if (-not (Test-Path -LiteralPath $appendEvidence)) { - Bad 'append-evidence.ps1 missing for immutability check' -} else { - New-Item -ItemType Directory -Path $evidenceCase -Force | Out-Null - & $SmokeHostExe -NoProfile -ExecutionPolicy Bypass -File $appendEvidence ` - -CaseRoot $evidenceCase ` - -Id 'E-IMMUTABLE' ` - -Title 'first write' ` - -ReproCommand 'echo first' 2>&1 | Out-Null - $firstEvidenceExit = $LASTEXITCODE - if ($firstEvidenceExit -ne 0) { - Bad ("initial Evidence append exit {0}" -f $firstEvidenceExit) + $encodingLog = Join-Path $LogDir '03-codex-encoding.txt' + & $SmokeHostExe -NoProfile -ExecutionPolicy Bypass -File $encodingTest ` + -ScratchDir (Join-Path $LogDir 'codex-encoding') 2>&1 | + Tee-Object -FilePath $encodingLog | Out-Null + $encodingExit = $LASTEXITCODE + if ($encodingExit -eq 0) { + Ok 'Codex config UTF-8 round-trip regression' } else { - $evidencePath = Join-Path $evidenceCase 'evidence/E-IMMUTABLE.md' - $indexPath = Join-Path $evidenceCase 'evidence/INDEX.md' - $beforeEvidence = (Get-FileHash -LiteralPath $evidencePath -Algorithm SHA256).Hash - $beforeIndex = (Get-FileHash -LiteralPath $indexPath -Algorithm SHA256).Hash - - & $SmokeHostExe -NoProfile -ExecutionPolicy Bypass -File $appendEvidence ` - -CaseRoot $evidenceCase ` - -Id 'E-IMMUTABLE' ` - -Title 'second write' ` - -ReproCommand 'echo second' 2>&1 | Out-Null - $duplicateEvidenceExit = $LASTEXITCODE - - $afterEvidence = (Get-FileHash -LiteralPath $evidencePath -Algorithm SHA256).Hash - $afterIndex = (Get-FileHash -LiteralPath $indexPath -Algorithm SHA256).Hash - if ($duplicateEvidenceExit -eq 0) { - Bad 'duplicate Evidence ID was accepted' - } elseif ($beforeEvidence -ne $afterEvidence) { - Bad 'existing Evidence changed after duplicate append' - } elseif ($beforeIndex -ne $afterIndex) { - Bad 'Evidence index changed after duplicate append' - } else { - Ok 'duplicate Evidence ID rejected without mutation' - } + Bad ("Codex config UTF-8 regression exit {0}" -f $encodingExit) } } -# --- summary --- -$summary = @( - "VERIFY_EXIT=$verifyExit", - "PARSE ok=$parseOk fail=$parseFail", - "ROUTE ok=$routeOk fail=$routeFail / $($cases.Count)", - "FAIL_COUNT=$($fail.Count)", - "LogDir=$LogDir" -) -$summary -join [Environment]::NewLine | Set-Content (Join-Path $LogDir 'SUMMARY.txt') -Encoding UTF8 -Write-Host '=== SMOKE SUMMARY ===' -$summary | ForEach-Object { Write-Host $_ } -if ($fail.Count -gt 0) { - $fail | Set-Content (Join-Path $LogDir 'failures.txt') -Encoding UTF8 - Write-Host 'OVERALL: FAIL' -ForegroundColor Red - exit 1 -} -Write-Host 'OVERALL: ALL PASS' -ForegroundColor Green -exit 0 +# --- 4) master-route sample matrix --- +$mr = Join-Path $scriptDir 'master-route.ps1' +$cases = @( + @{ Name = 'apk'; Hint = 'decompile APK with jadx apktool smali'; Expect = 'apk-reverse' }, + @{ Name = 'js'; Hint = 'js reverse frontend sign jshook encrypted param'; Expect = 'js-reverse' }, + @{ Name = 'ida'; Hint = 'IDA decompile PE binary disassemble'; Expect = 'ida-reverse' }, + @{ Name = 'pentest'; Hint = 'nmap nuclei sqlmap ffuf pentest bug bounty'; Expect = 'pentest-tools' }, + @{ Name = 'llm'; Hint = 'LLM prompt inject jailbreak agent security garak'; Expect = 'llm-security' }, + @{ Name = 'zh-apk'; Hint = '安卓 APK 加固 反编译'; Expect = 'apk-reverse' }, + @{ Name = 'zh-pentest'; Hint = '渗透测试 端口扫描 SQL注入'; Expect = 'pentest-tools' }, + @{ Name = 'zh-js'; Hint = '前端签名 JS逆向 加密参数'; Expect = 'js-reverse' }, + @{ Name = 'evidence'; Hint = 'case review evidence chain traceability'; Expect = 'case-review' } +) +$routeOk = 0 +$routeFail = 0 +$routeSummary = New-Object System.Collections.Generic.List[string] +if (-not (Test-Path -LiteralPath $mr)) { + Bad 'master-route.ps1 missing' +} else { + foreach ($c in $cases) { + $outFile = Join-Path $LogDir ("route-{0}.txt" -f $c.Name) + $raw = & $SmokeHostExe -NoProfile -ExecutionPolicy Bypass -File $mr -Hint $c.Hint 2>&1 | Out-String + $raw | Set-Content -Path $outFile -Encoding UTF8 + if ($raw -match [regex]::Escape($c.Expect)) { + Ok ("route {0} -> {1}" -f $c.Name, $c.Expect) + $routeOk++ + [void]$routeSummary.Add("PASS $($c.Name)") + } else { + Bad ("route {0} expect {1}" -f $c.Name, $c.Expect) + $routeFail++ + [void]$routeSummary.Add("FAIL $($c.Name)") + } + } +} +$routeSummary -join [Environment]::NewLine | Set-Content (Join-Path $LogDir '03-route-summary.txt') -Encoding UTF8 + +# --- 5) Evidence ID immutability --- +$appendEvidence = Join-Path $scriptDir 'append-evidence.ps1' +$evidenceCase = Join-Path $LogDir 'evidence-immutability' +if (-not (Test-Path -LiteralPath $appendEvidence)) { + Bad 'append-evidence.ps1 missing for immutability check' +} else { + New-Item -ItemType Directory -Path $evidenceCase -Force | Out-Null + & $SmokeHostExe -NoProfile -ExecutionPolicy Bypass -File $appendEvidence ` + -CaseRoot $evidenceCase ` + -Id 'E-IMMUTABLE' ` + -Title 'first write' ` + -ReproCommand 'echo first' 2>&1 | Out-Null + $firstEvidenceExit = $LASTEXITCODE + if ($firstEvidenceExit -ne 0) { + Bad ("initial Evidence append exit {0}" -f $firstEvidenceExit) + } else { + $evidencePath = Join-Path $evidenceCase 'evidence\E-IMMUTABLE.md' + $indexPath = Join-Path $evidenceCase 'evidence\INDEX.md' + $beforeEvidence = (Get-FileHash -LiteralPath $evidencePath -Algorithm SHA256).Hash + $beforeIndex = (Get-FileHash -LiteralPath $indexPath -Algorithm SHA256).Hash + + & $SmokeHostExe -NoProfile -ExecutionPolicy Bypass -File $appendEvidence ` + -CaseRoot $evidenceCase ` + -Id 'E-IMMUTABLE' ` + -Title 'second write' ` + -ReproCommand 'echo second' 2>&1 | Out-Null + $duplicateEvidenceExit = $LASTEXITCODE + + $afterEvidence = (Get-FileHash -LiteralPath $evidencePath -Algorithm SHA256).Hash + $afterIndex = (Get-FileHash -LiteralPath $indexPath -Algorithm SHA256).Hash + if ($duplicateEvidenceExit -eq 0) { + Bad 'duplicate Evidence ID was accepted' + } elseif ($beforeEvidence -ne $afterEvidence) { + Bad 'existing Evidence changed after duplicate append' + } elseif ($beforeIndex -ne $afterIndex) { + Bad 'Evidence index changed after duplicate append' + } else { + Ok 'duplicate Evidence ID rejected without mutation' + } + } +} + +# --- summary --- +$summary = @( + "VERIFY_EXIT=$verifyExit", + "PARSE ok=$parseOk fail=$parseFail", + "ROUTE ok=$routeOk fail=$routeFail / $($cases.Count)", + "FAIL_COUNT=$($fail.Count)", + "LogDir=$LogDir" +) +$summary -join [Environment]::NewLine | Set-Content (Join-Path $LogDir 'SUMMARY.txt') -Encoding UTF8 +Write-Host '=== SMOKE SUMMARY ===' +$summary | ForEach-Object { Write-Host $_ } +if ($fail.Count -gt 0) { + $fail | Set-Content (Join-Path $LogDir 'failures.txt') -Encoding UTF8 + Write-Host 'OVERALL: FAIL' -ForegroundColor Red + exit 1 +} +Write-Host 'OVERALL: ALL PASS' -ForegroundColor Green +exit 0 + diff --git a/skills/scripts/test-bootstrap-codex-encoding.ps1 b/skills/scripts/test-bootstrap-codex-encoding.ps1 index 3e06173..1043b82 100644 --- a/skills/scripts/test-bootstrap-codex-encoding.ps1 +++ b/skills/scripts/test-bootstrap-codex-encoding.ps1 @@ -65,6 +65,11 @@ function Test-CodexConfigRoundTrip { command = 'cmd' args = @('/c', 'echo', '中文参数') } + Set-CodexMcpServer -ServerName 'encoding-test' -ServerDefinition @{ + type = 'stdio' + command = 'cmd' + args = @('/c', 'echo', '中文参数') + } } finally { $env:CODEX_CONFIG_PATH = $oldConfigPath @@ -80,6 +85,7 @@ function Test-CodexConfigRoundTrip { } Assert-True ($text.Contains("[projects.'e:\codex目录\blender脚本管理器']")) "$Name preserves Chinese TOML path and closing quote" Assert-True ($text.Contains('[mcp_servers.encoding-test]')) "$Name adds MCP server" + Assert-True (([regex]::Matches($text, '(?m)^\[mcp_servers\.encoding-test\]\r?$')).Count -eq 1) "$Name updates MCP server idempotently" Assert-True ($text.Contains('中文参数')) "$Name preserves new Chinese values" if ($Newline -eq "`r`n") { Assert-True (-not ($text -replace "`r`n", '').Contains("`n")) "$Name preserves CRLF newlines" @@ -89,6 +95,22 @@ function Test-CodexConfigRoundTrip { Test-CodexConfigRoundTrip -Name 'utf8-no-bom-crlf' -WithBom $false -Newline "`r`n" Test-CodexConfigRoundTrip -Name 'utf8-bom-lf' -WithBom $true -Newline "`n" +$newPath = Join-Path $ScratchDir 'new-config.toml' +$oldConfigPath = $env:CODEX_CONFIG_PATH +try { + $env:CODEX_CONFIG_PATH = $newPath + Set-CodexMcpServer -ServerName 'new-config-test' -ServerDefinition @{ command = '中文命令' } +} +finally { + $env:CODEX_CONFIG_PATH = $oldConfigPath +} +$newBytes = [System.IO.File]::ReadAllBytes($newPath) +$newHasBom = $newBytes.Length -ge 3 -and $newBytes[0] -eq 0xEF -and $newBytes[1] -eq 0xBB -and $newBytes[2] -eq 0xBF +Assert-True (-not $newHasBom) 'new config uses UTF-8 without BOM' +$newText = [System.Text.Encoding]::UTF8.GetString($newBytes) +Assert-True ($newText.Contains('[mcp_servers.new-config-test]')) 'new config contains MCP server block' +Assert-True ($newText.Contains('中文命令')) 'new config preserves Chinese values' + $invalidPath = Join-Path $ScratchDir 'invalid-utf8.toml' [System.IO.File]::WriteAllBytes($invalidPath, [byte[]](0x5B, 0x80, 0x5D)) $oldConfigPath = $env:CODEX_CONFIG_PATH