Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/windows-installer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,59 @@ jobs:
-AgentDockChecksumFile $env:AMD64_CHECKSUM `
-FakeCloudflaredBinary $fakeCloudflared

- name: Test Named Tunnel install update and rollback lifecycle
shell: powershell
run: |
$ErrorActionPreference = 'Stop'
$env:CGO_ENABLED = '0'
$env:GOOS = 'windows'
$env:GOARCH = 'amd64'

$fakeCloudflared = Join-Path $env:RUNNER_TEMP 'fake-cloudflared-named.exe'
go build -trimpath -o $fakeCloudflared .\scripts\test\testdata\fake-cloudflared

function Build-VersionedAgentDock {
param([string] $Version, [string] $OutputPath)

$sourcePath = (Resolve-Path '.\internal\buildinfo\buildinfo.go').Path
$source = Get-Content -LiteralPath $sourcePath -Raw
$replacement = [regex]::Replace(
$source,
'const Version = "[^"]+"',
"const Version = `"$Version`"",
1
)
if ($replacement -eq $source) {
throw 'Could not replace buildinfo.Version for Named Tunnel E2E.'
}

$replacementPath = Join-Path $env:RUNNER_TEMP "buildinfo-$Version.go"
$overlayPath = Join-Path $env:RUNNER_TEMP "buildinfo-$Version-overlay.json"
[IO.File]::WriteAllText($replacementPath, $replacement, [Text.UTF8Encoding]::new($false))
$replacements = @{}
$replacements[$sourcePath] = $replacementPath
[IO.File]::WriteAllText(
$overlayPath,
(@{ Replace = $replacements } | ConvertTo-Json -Depth 4),
[Text.UTF8Encoding]::new($false)
)
go build -trimpath -overlay $overlayPath -o $OutputPath .\cmd\agentdock
}

$sourceCore = Join-Path $env:RUNNER_TEMP 'agentdock-named-source.exe'
$trialCore = Join-Path $env:RUNNER_TEMP 'agentdock-named-trial.exe'
Build-VersionedAgentDock -Version '0.0.0-named-source-e2e' -OutputPath $sourceCore
Build-VersionedAgentDock -Version '999.0.0-named-trial-e2e' -OutputPath $trialCore

& .\scripts\test\test-windows-named-tunnel-lifecycle.ps1 `
-InstallerPath .\scripts\install\install.ps1 `
-UninstallerPath .\scripts\install\uninstall-windows.ps1 `
-TargetAgentDockArchive $env:AMD64_ARCHIVE `
-TargetAgentDockChecksumFile $env:AMD64_CHECKSUM `
-SourceCoreBinary $sourceCore `
-TrialCoreBinary $trialCore `
-FakeCloudflaredBinary $fakeCloudflared

- name: Test Setup activation deferral
shell: powershell
run: |
Expand Down
7 changes: 7 additions & 0 deletions scripts/governance/inventory.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@ scripts:
legacy_oversize: true
oversize_justification: Windows Quick Tunnel 生命周期,依赖真实 Task Scheduler/进程。

- path: scripts/test/test-windows-named-tunnel-lifecycle.ps1
class: test
owner: installer
public_contract: false
deletable: false
oversize_justification: Windows Named Tunnel 三代生命周期 E2E,覆盖安装、同版 repair、升级、DPAPI/固定地址保持与认证失败回滚,需要真实 Windows 进程和 Installer Engine 编排。


- path: scripts/test/test-windows-runtime-launch-diagnostics.ps1
class: test
Expand Down
57 changes: 57 additions & 0 deletions scripts/test/install_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,63 @@ func TestWindowsRuntimeDiagnosticsPassesNativeTaskLauncher(t *testing.T) {
}
}

func TestWindowsNamedTunnelLifecycleCoversPreservationAndRollback(t *testing.T) {
lifecycleData, err := os.ReadFile(filepath.Join("..", "..", "scripts", "test", "test-windows-named-tunnel-lifecycle.ps1"))
if err != nil {
t.Fatalf("read Windows Named Tunnel lifecycle test: %v", err)
}
fakeData, err := os.ReadFile(filepath.Join("..", "..", "scripts", "test", "testdata", "fake-cloudflared", "main.go"))
if err != nil {
t.Fatalf("read fake cloudflared: %v", err)
}
workflowData, err := os.ReadFile(filepath.Join("..", "..", ".github", "workflows", "windows-installer.yml"))
if err != nil {
t.Fatalf("read Windows Installer workflow: %v", err)
}

lifecycle := strings.ReplaceAll(string(lifecycleData), "\r\n", "\n")
for _, want := range []string{
"-TunnelTokenFile $stableTokenFile",
"Invoke-Installer -Archive $sourcePayload.Archive -Checksum $sourcePayload.Checksum",
"Invoke-Installer -Archive $TargetAgentDockArchive -Checksum $TargetAgentDockChecksumFile",
"'-OfflineArchive', $trialPayload.Archive",
"'-TunnelTokenFile', $invalidTokenFile",
"Assert-NoTunnelTokenInProcessArguments",
"(Get-FileHash -LiteralPath $tunnelTokenPath -Algorithm SHA256).Hash -ne $ExpectedTokenHash",
"-ExpectedVersion $targetVersion",
"versions\\$trialVersion",
} {
if !strings.Contains(lifecycle, want) {
t.Fatalf("Named Tunnel lifecycle test must cover install/repair/update/rollback preservation; missing %q", want)
}
}

fake := strings.ReplaceAll(string(fakeData), "\r\n", "\n")
for _, want := range []string{
`os.Getenv("TUNNEL_TOKEN")`,
`strings.Contains(argument, token)`,
`agentdock-test-invalid-named-token`,
`Provided Tunnel token is not valid.`,
`Registered tunnel connection`,
} {
if !strings.Contains(fake, want) {
t.Fatalf("fake cloudflared must model Named Tunnel environment/readiness without exposing the Token; missing %q", want)
}
}

workflow := strings.ReplaceAll(string(workflowData), "\r\n", "\n")
for _, want := range []string{
"Test Named Tunnel install update and rollback lifecycle",
"Build-VersionedAgentDock -Version '0.0.0-named-source-e2e'",
"Build-VersionedAgentDock -Version '999.0.0-named-trial-e2e'",
".\\scripts\\test\\test-windows-named-tunnel-lifecycle.ps1",
} {
if !strings.Contains(workflow, want) {
t.Fatalf("Windows Installer workflow must run the full Named Tunnel lifecycle; missing %q", want)
}
}
}

func TestWindowsStandardUserE2EWaitsForDirectProcessWithTimeout(t *testing.T) {
launcherData, err := os.ReadFile(filepath.Join("..", "..", "scripts", "test", "run-windows-installer-e2e-as-standard-user.ps1"))
if err != nil {
Expand Down
Loading