148 lines
4.8 KiB
TypeScript
148 lines
4.8 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import path from 'node:path';
|
|
import {
|
|
buildWindowsHydratedPath,
|
|
hydrateWindowsProcessPath,
|
|
parseWindowsProfilePathEntries,
|
|
parseWindowsRegistryPathOutput,
|
|
} from '@/process/startup/windowsPath';
|
|
|
|
const windowsOnlyIt = process.platform === 'win32' ? it : it.skip;
|
|
|
|
describe('parseWindowsRegistryPathOutput', () => {
|
|
it('extracts and expands the Path value from reg.exe output', () => {
|
|
const output = `
|
|
|
|
HKEY_CURRENT_USER\\Environment
|
|
Path REG_EXPAND_SZ D:\\AgentBin;%USERPROFILE%\\AppData\\Roaming\\npm
|
|
`;
|
|
|
|
expect(
|
|
parseWindowsRegistryPathOutput(output, {
|
|
USERPROFILE: 'C:\\Users\\zhoukai',
|
|
})
|
|
).toEqual(['D:\\AgentBin', 'C:\\Users\\zhoukai\\AppData\\Roaming\\npm']);
|
|
});
|
|
|
|
it('returns an empty list when reg.exe does not return a Path value', () => {
|
|
const output = `
|
|
|
|
HKEY_CURRENT_USER\\Environment
|
|
TEMP REG_SZ C:\\Temp
|
|
`;
|
|
|
|
expect(parseWindowsRegistryPathOutput(output, {})).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('parseWindowsProfilePathEntries', () => {
|
|
it('extracts PATH additions from PowerShell profile snippets', () => {
|
|
const content = `
|
|
$env:Path += ';D:\\Portable\\opencode\\bin'
|
|
$env:PATH = "$env:USERPROFILE\\.local\\bin;$env:PATH"
|
|
`;
|
|
|
|
expect(
|
|
parseWindowsProfilePathEntries(content, {
|
|
USERPROFILE: 'C:\\Users\\zhoukai',
|
|
})
|
|
).toEqual(['D:\\Portable\\opencode\\bin', 'C:\\Users\\zhoukai\\.local\\bin']);
|
|
});
|
|
|
|
it('extracts PATH additions from Git Bash profile snippets', () => {
|
|
const content = `
|
|
export PATH="$HOME/.bun/bin:/d/AgentBin:$PATH"
|
|
`;
|
|
|
|
expect(
|
|
parseWindowsProfilePathEntries(content, {
|
|
HOME: 'C:\\Users\\zhoukai',
|
|
})
|
|
).toEqual(['C:\\Users\\zhoukai\\.bun\\bin', 'D:\\AgentBin']);
|
|
});
|
|
});
|
|
|
|
describe('buildWindowsHydratedPath', () => {
|
|
it('merges missing user and machine registry paths into the current process PATH', () => {
|
|
const hydrated = buildWindowsHydratedPath({
|
|
currentPath: 'C:\\Windows\\System32;C:\\Users\\zhoukai\\AppData\\Roaming\\npm',
|
|
userRegistryOutput: `
|
|
|
|
HKEY_CURRENT_USER\\Environment
|
|
Path REG_EXPAND_SZ D:\\AgentBin;%USERPROFILE%\\AppData\\Roaming\\npm
|
|
`,
|
|
machineRegistryOutput: `
|
|
|
|
HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment
|
|
Path REG_EXPAND_SZ C:\\Program Files\\Git\\cmd;C:\\Windows\\System32
|
|
`,
|
|
profileContents: [],
|
|
fallbackPathEntries: [],
|
|
env: {
|
|
USERPROFILE: 'C:\\Users\\zhoukai',
|
|
},
|
|
});
|
|
|
|
expect(hydrated).toBe(
|
|
'C:\\Program Files\\Git\\cmd;C:\\Windows\\System32;D:\\AgentBin;C:\\Users\\zhoukai\\AppData\\Roaming\\npm'
|
|
);
|
|
});
|
|
|
|
it('merges shell profile and fallback paths when they are not in registry PATH', () => {
|
|
const hydrated = buildWindowsHydratedPath({
|
|
currentPath: 'C:\\Windows\\System32',
|
|
userRegistryOutput: '',
|
|
machineRegistryOutput: '',
|
|
profileContents: [`$env:Path += ';D:\\Portable\\opencode\\bin'`],
|
|
fallbackPathEntries: ['C:\\Users\\zhoukai\\AppData\\Roaming\\npm', 'D:\\Portable\\opencode\\bin'],
|
|
env: {
|
|
USERPROFILE: 'C:\\Users\\zhoukai',
|
|
},
|
|
});
|
|
|
|
expect(hydrated).toBe(
|
|
'D:\\Portable\\opencode\\bin;C:\\Users\\zhoukai\\AppData\\Roaming\\npm;C:\\Windows\\System32'
|
|
);
|
|
});
|
|
|
|
windowsOnlyIt('hydrates PATH from real Windows profile files and existing fallback directories', () => {
|
|
const root = mkdtempSync(path.join(tmpdir(), 'aionui-windows-path-'));
|
|
const userProfile = path.join(root, 'User');
|
|
const appData = path.join(userProfile, 'AppData', 'Roaming');
|
|
const localAppData = path.join(userProfile, 'AppData', 'Local');
|
|
const profileDir = path.join(userProfile, 'Documents', 'PowerShell');
|
|
const profileOnlyBin = path.join(userProfile, 'portable-opencode', 'bin');
|
|
const npmBin = path.join(appData, 'npm');
|
|
const windowsApps = path.join(localAppData, 'Microsoft', 'WindowsApps');
|
|
|
|
try {
|
|
mkdirSync(profileOnlyBin, { recursive: true });
|
|
mkdirSync(npmBin, { recursive: true });
|
|
mkdirSync(windowsApps, { recursive: true });
|
|
mkdirSync(profileDir, { recursive: true });
|
|
writeFileSync(
|
|
path.join(profileDir, 'Microsoft.PowerShell_profile.ps1'),
|
|
`$env:Path += ';${profileOnlyBin}'\n`,
|
|
'utf8'
|
|
);
|
|
|
|
const env: NodeJS.ProcessEnv = {
|
|
Path: 'C:\\Windows\\System32',
|
|
USERPROFILE: userProfile,
|
|
APPDATA: appData,
|
|
LOCALAPPDATA: localAppData,
|
|
};
|
|
|
|
const hydrated = hydrateWindowsProcessPath(env);
|
|
|
|
expect(hydrated.split(';')).toEqual(expect.arrayContaining([profileOnlyBin, npmBin, windowsApps]));
|
|
expect(env.PATH).toBe(hydrated);
|
|
expect(env.Path).toBe(hydrated);
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|