/** * 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') }) })