Files
aiturk-hermes-ide/apps/desktop/electron/wsl-path-bridge-profile.test.ts
T

243 lines
10 KiB
TypeScript

/**
* Profile-scoped eligibility for the WSL path bridge (#66447).
*
* The single-profile tests in wsl-path-bridge.test.ts and the Windows-platform
* gate tests in wsl-path-bridge-gate.test.ts cover the *what* (paths pass
* through unchanged when bridging is disabled) but not the *which profile*. The
* desktop is multi-profile: the renderer can swap the live gateway onto any
* profile (primary or pool) without reloading the window — so the bridge
* eligibility MUST be keyed off the **currently active profile's** backend
* configuration, not a process-global boolean. This file proves the
* per-profile contract:
*
* 1. local primary profile → bridge ON (preserved)
* 2. remote primary profile → bridge OFF (preserved)
* 3. local primary + remote non-primary → bridge OFF when the non-primary
* is active; bridge ON when the primary is active again — **no bleed**.
* 4. remote primary + local non-primary → bridge ON when the non-primary
* is active; bridge OFF when the primary is active again — **no bleed**.
* 5. profile-scoped calls accept a profile argument; absent it falls back
* to the live gateway profile that the renderer announced.
* 6. afterEach resets state so tests don't bleed into each other.
*
* These tests are pure behavior: they assert the eligibility function's output
* and the public selectors' output against observable calls. They do NOT read
* the implementation source — only the public surface exported from
* `./wsl-path-bridge`.
*/
import assert from 'node:assert/strict'
import { afterEach, describe, test } from 'vitest'
import {
isWslBridgeActive,
resolveLocalReadPath,
resolvePickerDefaultPath,
setActiveGatewayProfile,
setWslBridgeActive,
setWslBridgeProfileState,
wslPosixToWindowsAccessible
} from './wsl-path-bridge'
// ── helpers ──────────────────────────────────────────────────────────
const PROFILE_PRIMARY = 'default'
const PROFILE_LOCAL = 'team-local'
const PROFILE_REMOTE = 'team-remote'
/** Reset every profile key the bridge knows about to a clean state. */
afterEach(() => {
setWslBridgeProfileState(PROFILE_PRIMARY, true)
setWslBridgeProfileState(PROFILE_LOCAL, true)
setWslBridgeProfileState(PROFILE_REMOTE, false)
setActiveGatewayProfile(PROFILE_PRIMARY)
setWslBridgeActive(true)
})
// ── single-profile contract (preserved behaviour) ────────────────────
describe('WSL bridge profile eligibility — single-profile contract preserved', () => {
test('primary local → bridge ON (preserved)', () => {
setWslBridgeProfileState(PROFILE_PRIMARY, true)
setActiveGatewayProfile(PROFILE_PRIMARY)
assert.equal(isWslBridgeActive(), true)
assert.equal(
resolvePickerDefaultPath('/home/alex', 'Ubuntu', PROFILE_PRIMARY),
'\\\\wsl.localhost\\Ubuntu\\home\\alex'
)
})
test('primary remote → bridge OFF (preserved)', () => {
setWslBridgeProfileState(PROFILE_PRIMARY, false)
setActiveGatewayProfile(PROFILE_PRIMARY)
assert.equal(isWslBridgeActive(), false)
assert.equal(resolvePickerDefaultPath('/home/alex', 'Ubuntu', PROFILE_PRIMARY), '/home/alex')
assert.equal(resolveLocalReadPath('/home/alex/proj', undefined, PROFILE_PRIMARY), '/home/alex/proj')
})
})
// ── multi-profile regression (the gap) ────────────────────────────────
describe('WSL bridge profile eligibility — multi-profile (no cross-profile bleed)', () => {
test('local primary + remote non-primary → non-primary OFF, primary ON', () => {
// Primary is a local backend (WSL on this Windows host). A second profile
// points at a remote host whose POSIX paths the Windows host CANNOT open
// via wsl.exe — bridging must be OFF for it.
setWslBridgeProfileState(PROFILE_PRIMARY, true)
setWslBridgeProfileState(PROFILE_REMOTE, false)
// Non-primary remote is foregrounded — bridge must be OFF for it.
setActiveGatewayProfile(PROFILE_REMOTE)
assert.equal(isWslBridgeActive(), false)
assert.equal(resolvePickerDefaultPath('/home/alex', 'Ubuntu', PROFILE_REMOTE), '/home/alex')
assert.equal(resolveLocalReadPath('/home/alex/proj', undefined, PROFILE_REMOTE), '/home/alex/proj')
// Swap back to local primary — bridge must be ON again, no bleed.
setActiveGatewayProfile(PROFILE_PRIMARY)
assert.equal(isWslBridgeActive(), true)
assert.equal(
resolvePickerDefaultPath('/home/alex', 'Ubuntu', PROFILE_PRIMARY),
'\\\\wsl.localhost\\Ubuntu\\home\\alex'
)
})
test('remote primary + local non-primary → non-primary ON, primary OFF', () => {
// Primary is remote (no local WSL paths). A second profile is local —
// bridging should be ON for it because its paths CAN be opened locally.
setWslBridgeProfileState(PROFILE_PRIMARY, false)
setWslBridgeProfileState(PROFILE_LOCAL, true)
// Local non-primary foregrounded — bridge ON for it.
setActiveGatewayProfile(PROFILE_LOCAL)
assert.equal(isWslBridgeActive(), true)
assert.equal(
resolvePickerDefaultPath('/home/alex', 'Ubuntu', PROFILE_LOCAL),
'\\\\wsl.localhost\\Ubuntu\\home\\alex'
)
// Swap back to remote primary — bridge OFF for it, no bleed.
setActiveGatewayProfile(PROFILE_PRIMARY)
assert.equal(isWslBridgeActive(), false)
assert.equal(resolvePickerDefaultPath('/home/alex', 'Ubuntu', PROFILE_PRIMARY), '/home/alex')
})
test('three profiles: each profile behaves independently', () => {
setWslBridgeProfileState(PROFILE_PRIMARY, true)
setWslBridgeProfileState(PROFILE_LOCAL, true)
setWslBridgeProfileState(PROFILE_REMOTE, false)
// Same path, different profiles, different outcomes.
assert.equal(
resolvePickerDefaultPath('/home/alex', 'Ubuntu', PROFILE_PRIMARY),
'\\\\wsl.localhost\\Ubuntu\\home\\alex'
)
assert.equal(
resolvePickerDefaultPath('/home/alex', 'Ubuntu', PROFILE_LOCAL),
'\\\\wsl.localhost\\Ubuntu\\home\\alex'
)
assert.equal(resolvePickerDefaultPath('/home/alex', 'Ubuntu', PROFILE_REMOTE), '/home/alex')
})
})
// ── profile-argument contract ────────────────────────────────────────
describe('WSL bridge profile eligibility — selector argument contract', () => {
test("selector with explicit profile → that profile's bridge state", () => {
setWslBridgeProfileState(PROFILE_PRIMARY, true)
setWslBridgeProfileState(PROFILE_REMOTE, false)
setActiveGatewayProfile(PROFILE_PRIMARY)
// Explicit profile wins over the live gateway.
assert.equal(resolvePickerDefaultPath('/home/alex', 'Ubuntu', PROFILE_REMOTE), '/home/alex')
assert.equal(
resolvePickerDefaultPath('/home/alex', 'Ubuntu', PROFILE_PRIMARY),
'\\\\wsl.localhost\\Ubuntu\\home\\alex'
)
})
test("selector with no profile → live gateway profile's bridge state", () => {
setWslBridgeProfileState(PROFILE_PRIMARY, true)
setWslBridgeProfileState(PROFILE_REMOTE, false)
setActiveGatewayProfile(PROFILE_REMOTE)
// No profile argument → fallback to active gateway profile (remote → OFF).
assert.equal(resolvePickerDefaultPath('/home/alex', 'Ubuntu'), '/home/alex')
assert.equal(resolveLocalReadPath('/home/alex/proj'), '/home/alex/proj')
setActiveGatewayProfile(PROFILE_PRIMARY)
assert.equal(resolvePickerDefaultPath('/home/alex', 'Ubuntu'), '\\\\wsl.localhost\\Ubuntu\\home\\alex')
})
test('selector with unknown profile → bridge ON (defaults to active for new profiles)', () => {
// A profile that has never been seeded should default to the safe
// "bridge ON" behaviour so a brand-new local profile isn't accidentally
// disabled. The renderer seeds the state at boot; an unknown key here is
// either a renderer race or a profile created mid-session.
setWslBridgeProfileState(PROFILE_PRIMARY, false)
setActiveGatewayProfile(PROFILE_PRIMARY)
assert.equal(
resolvePickerDefaultPath('/home/alex', 'Ubuntu', 'unknown-profile'),
'\\\\wsl.localhost\\Ubuntu\\home\\alex'
)
})
})
// ── legacy back-compat: setWslBridgeActive targets the active profile ─
describe('WSL bridge profile eligibility — legacy toggle targets active profile', () => {
test('setWslBridgeActive(false) flips the active profile, not a process-global', () => {
setWslBridgeProfileState(PROFILE_PRIMARY, true)
setWslBridgeProfileState(PROFILE_REMOTE, true)
setActiveGatewayProfile(PROFILE_REMOTE)
setWslBridgeActive(false)
// Active profile (REMOTE) flipped to OFF; primary untouched.
assert.equal(isWslBridgeActive(), false)
assert.equal(
resolvePickerDefaultPath('/home/alex', 'Ubuntu', PROFILE_PRIMARY),
'\\\\wsl.localhost\\Ubuntu\\home\\alex'
)
assert.equal(resolvePickerDefaultPath('/home/alex', 'Ubuntu', PROFILE_REMOTE), '/home/alex')
})
test('setWslBridgeActive(true) restores the active profile only', () => {
setWslBridgeProfileState(PROFILE_PRIMARY, true)
setWslBridgeProfileState(PROFILE_REMOTE, false)
setActiveGatewayProfile(PROFILE_REMOTE)
setWslBridgeActive(true)
// Active (REMOTE) restored; primary unchanged.
assert.equal(isWslBridgeActive(), true)
assert.equal(
resolvePickerDefaultPath('/home/alex', 'Ubuntu', PROFILE_REMOTE),
'\\\\wsl.localhost\\Ubuntu\\home\\alex'
)
assert.equal(
resolvePickerDefaultPath('/home/alex', 'Ubuntu', PROFILE_PRIMARY),
'\\\\wsl.localhost\\Ubuntu\\home\\alex'
)
})
})
// ── wslPosixToWindowsAccessible stays pure / unchanged ───────────────
describe('WSL bridge profile eligibility — POSIX translation stays pure', () => {
test('wslPosixToWindowsAccessible ignores bridge state (pure translation)', () => {
setWslBridgeProfileState(PROFILE_PRIMARY, false)
setActiveGatewayProfile(PROFILE_PRIMARY)
// The translator does NOT consult the bridge state — it's pure POSIX → UNC.
// Tests elsewhere assert that the bridge gate short-circuits BEFORE this
// function is reached. A regression here would mean coupling leaked into
// a helper that should stay side-effect-free.
assert.equal(
wslPosixToWindowsAccessible('/home/alex/proj', 'Ubuntu'),
'\\\\wsl.localhost\\Ubuntu\\home\\alex\\proj'
)
assert.equal(wslPosixToWindowsAccessible('/mnt/c/Users/alex', 'Ubuntu'), 'C:\\Users\\alex')
})
})