Skip to content

Commit 3c2e394

Browse files
committed
Add E2E coverage for WSL profile agents
Add environment-gated integration coverage for profile-scoped WSL agent routing, settings hot reload, and authenticated chat. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8a64788a-aabb-458d-a6d4-878a5cc3f69b
1 parent 680f605 commit 3c2e394

2 files changed

Lines changed: 170 additions & 3 deletions

File tree

test/e2e/README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,15 @@ environment. Current status (run on the Store package):
2626
| `Feature.AgentProposedCommand.Tests.ps1` | §2 agent-proposed command Insert/Run into the shell pane (non-autofix chat path) | 2 |
2727
| `Feature.AgentMatrix.Tests.ps1` | §2 non-Copilot built-in agents (Claude/Codex/Gemini) connect+chat through the ACP adapter — ONE consolidated case (Copilot is the in-depth suite); skips when none installed+authed | 1 |
2828
| `Feature.PerTabAgent.Tests.ps1` | C225-C228: `/agent` picker/direct selection, invalid-id safety, per-tab isolation/shared-master reuse, and global-default/override behavior | 6 |
29+
| `Feature.WslAgentBackend.Tests.ps1` | PR #481 profile-scoped WSL agent backend: settings hot reload, helper/master source routing, and authenticated chat | 2 (environment-gated) |
2930
| `Feature.AgentChat.Tests.ps1` / `Feature.AgentPopup.Tests.ps1` | agent chat + `/` popup/menu interaction | 1 + 3 |
3031

3132
**Coverage: all 101 automatable `[E2E]` checklist items are implemented.**
32-
**Test status: 98 feature cases pass + 2 documented skips** (`wta sessions list` is
33-
identity-gated — see `Feature.SessionList.Tests.ps1`); the 101 checklist items map to these
34-
cases plus the deterministic settings/persistence assertions. Remaining
33+
**Test status: 98 baseline feature cases pass + 2 documented skips** (`wta sessions list` is
34+
identity-gated — see `Feature.SessionList.Tests.ps1`), plus 2 PR #481 WSL-backend cases that
35+
run only when a dev package, runnable distro, and native supported agent are available; the
36+
chat case also requires authentication. The 101 checklist items map to the baseline cases
37+
plus the deterministic settings/persistence assertions. Remaining
3538
environment-dependent items are tracked and auto-skipped when their prerequisite is absent:
3639
**other agent CLIs** (`Feature.AgentMatrix.Tests.ps1` now covers Claude/Codex/Gemini chat,
3740
auth-gated per CLI — each Context runs only when that CLI is installed *and* authenticated,
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#Requires -Modules @{ ModuleName='Pester'; ModuleVersion='5.0.0' }
2+
# PR #481: a profile can pin its agent pane to a supported ACP agent installed
3+
# inside that profile's WSL distro. The routing assertion is deterministic once
4+
# a native WSL agent is present; the chat assertion additionally requires auth.
5+
6+
BeforeDiscovery {
7+
$devPkg = Get-AppxPackage | Where-Object { $_.PackageFamilyName -like 'IntelligentTerminal_*' }
8+
$script:Ready = [bool](
9+
$devPkg -and
10+
(Get-Command winapp -ErrorAction SilentlyContinue) -and
11+
(Get-Command wsl.exe -ErrorAction SilentlyContinue)
12+
)
13+
}
14+
15+
Describe 'Feature profile-scoped WSL agent backend' -Tag 'Feature' -Skip:(-not $script:Ready) {
16+
BeforeAll {
17+
Import-Module (Join-Path $PSScriptRoot '..\ItE2E\ItE2E.psd1') -Force
18+
19+
$script:app = $null
20+
$script:skipReason = $null
21+
$script:oldAgentPaneId = $null
22+
$script:profileGuid = '{e2e48100-4810-4810-9810-000000000001}'
23+
$script:profileName = 'ITE2E PR481 WSL Agent'
24+
25+
$distroProbe = Invoke-Native -FilePath 'wsl.exe' -Arguments @(
26+
'-e', 'sh', '-lc', 'printf "%s" "${WSL_DISTRO_NAME:-}"'
27+
) -TimeoutSec 45
28+
$script:distro = $distroProbe.StdOut.Trim()
29+
if ($distroProbe.ExitCode -ne 0 -or -not $script:distro) {
30+
$script:skipReason = 'no runnable default WSL distro is available'
31+
return
32+
}
33+
if ($script:distro -match '["\r\n]') {
34+
$script:skipReason = 'the default WSL distro name cannot be represented safely in a test profile'
35+
return
36+
}
37+
38+
# Match the product's source probe: reject Windows executables leaked
39+
# through WSL interop, and require npx for adapter-backed agents.
40+
$agentProbeScript = @'
41+
for id in copilot gemini opencode claude codex; do
42+
path="$(command -v "$id" 2>/dev/null || true)"
43+
[ -n "$path" ] || continue
44+
lower="$(printf '%s' "$path" | tr '[:upper:]' '[:lower:]')"
45+
case "$lower" in
46+
/mnt/*|*.exe|*.cmd|*.bat) continue ;;
47+
esac
48+
case "$id" in
49+
claude|codex)
50+
npx_path="$(command -v npx 2>/dev/null || true)"
51+
npx_lower="$(printf '%s' "$npx_path" | tr '[:upper:]' '[:lower:]')"
52+
case "$npx_lower" in
53+
''|/mnt/*|*.exe|*.cmd|*.bat) continue ;;
54+
esac
55+
;;
56+
esac
57+
printf '%s\n' "$id"
58+
break
59+
done
60+
'@
61+
$agentProbe = Invoke-Native -FilePath 'wsl.exe' -Arguments @(
62+
'-d', $script:distro, '--', 'bash', '-lc', $agentProbeScript
63+
) -TimeoutSec 45
64+
$knownAgents = @('copilot', 'gemini', 'opencode', 'claude', 'codex')
65+
$script:agent = @(
66+
$agentProbe.StdOut -split '\r?\n' |
67+
ForEach-Object { $_.Trim() } |
68+
Where-Object { $_ -in $knownAgents }
69+
) | Select-Object -First 1
70+
if ($agentProbe.ExitCode -ne 0 -or -not $script:agent) {
71+
$script:skipReason = "$($script:distro) has no supported native Linux ACP agent"
72+
return
73+
}
74+
75+
$unconfiguredProfile = [pscustomobject][ordered]@{
76+
guid = $script:profileGuid
77+
name = $script:profileName
78+
commandline = "wsl.exe -d `"$($script:distro)`""
79+
}
80+
$unconfiguredProfiles = [pscustomobject][ordered]@{
81+
defaults = [pscustomobject]@{}
82+
list = @($unconfiguredProfile)
83+
}
84+
$script:backend = "wsl:$($script:distro):$($script:agent)"
85+
$script:app = Start-Terminal -Package Dev -PassFre $true -Settings @{
86+
acpAgent = $script:agent
87+
defaultProfile = $script:profileGuid
88+
profiles = $unconfiguredProfiles
89+
}
90+
$oldAgentPane = Get-AgentPaneSession -App $script:app
91+
if ($oldAgentPane) {
92+
$script:oldAgentPaneId = $oldAgentPane.PaneSessionId
93+
}
94+
95+
# Apply the profile backend only after Start-Terminal has captured log
96+
# offsets. This exercises settings hot reload and keeps source-routing
97+
# assertions scoped to this run.
98+
$configuredProfile = [pscustomobject][ordered]@{
99+
guid = $script:profileGuid
100+
name = $script:profileName
101+
commandline = "wsl.exe -d `"$($script:distro)`""
102+
agentPaneBackend = $script:backend
103+
}
104+
$configuredProfiles = [pscustomobject][ordered]@{
105+
defaults = [pscustomobject]@{}
106+
list = @($configuredProfile)
107+
}
108+
Set-WtSetting -App $script:app -Key 'profiles' -Value $configuredProfiles | Out-Null
109+
Open-AgentPane -App $script:app | Out-Null
110+
}
111+
AfterAll {
112+
if ($script:app) {
113+
Stop-Terminal -App $script:app
114+
}
115+
}
116+
117+
It 'Hot reload routes the profile agent through its WSL distro without host fallback' {
118+
if ($script:skipReason) {
119+
Set-ItResult -Skipped -Because $script:skipReason
120+
return
121+
}
122+
123+
$settings = Get-WtSettingsObject -App $script:app
124+
$profile = @($settings.profiles.list) |
125+
Where-Object { $_.guid -eq $script:profileGuid } |
126+
Select-Object -First 1
127+
$profile.agentPaneBackend | Should -Be $script:backend
128+
129+
if ($script:oldAgentPaneId) {
130+
(Test-Until -TimeoutSec 30 -IntervalSec 0.5 -Condition {
131+
-not (Get-AgentPaneSession -App $script:app -PaneSessionId $script:oldAgentPaneId)
132+
}) | Should -BeTrue -Because 'changing this profile backend must replace its old host helper'
133+
}
134+
135+
$agentPattern = [regex]::Escape($script:agent)
136+
$sourcePattern = [regex]::Escape("wsl:$($script:distro)")
137+
Assert-Log -App $script:app -Name 'wta-main_master.log' -Pattern (
138+
'resolving agent CLI for helper.*requested_agent_id=Some\("' + $agentPattern +
139+
'"\).*resolved_agent_source=' + $sourcePattern
140+
) -TimeoutSec 60
141+
Assert-Log -App $script:app -Name 'wta-main_master.log' -Pattern (
142+
'agent CLI spawned.*agent_source=' + $sourcePattern
143+
) -TimeoutSec 60
144+
}
145+
146+
It 'The profile-selected WSL agent connects and answers a chat round trip' {
147+
if ($script:skipReason) {
148+
Set-ItResult -Skipped -Because $script:skipReason
149+
return
150+
}
151+
if (-not (Wait-AgentReady -App $script:app -TimeoutSec 150)) {
152+
Set-ItResult -Skipped -Because "$($script:agent) is installed in $($script:distro) but is not authenticated or its ACP server did not connect"
153+
return
154+
}
155+
156+
$oldAgentPaneIds = @($script:oldAgentPaneId) | Where-Object { $_ }
157+
$agentPane = Wait-NewAgentPaneSession -App $script:app `
158+
-ExcludePaneSessionId $oldAgentPaneIds -TimeoutSec 30
159+
Send-AgentPrompt -App $script:app -PaneSessionId $agentPane.PaneSessionId `
160+
-Text 'What is 480 plus 1? Reply with only the number.' | Out-Null
161+
Assert-AgentPaneText -App $script:app -PaneSessionId $agentPane.PaneSessionId `
162+
-Pattern '\b481\b' -TimeoutSec 150
163+
}
164+
}

0 commit comments

Comments
 (0)