mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-05-23 04:17:45 +00:00
Co-authored-by: a7m-1st <Ahmed.jimi.awelkeir500@gmail.com> Co-authored-by: eigent-ai <camel@eigent.ai> Co-authored-by: Wendong-Fan <133094783+Wendong-Fan@users.noreply.github.com> Co-authored-by: Wendong-Fan <w3ndong.fan@gmail.com>
793 lines
24 KiB
TypeScript
793 lines
24 KiB
TypeScript
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
|
|
|
import { vi } from 'vitest';
|
|
|
|
/**
|
|
* Environment state management for testing installation flows
|
|
* This module provides utilities to simulate different system states
|
|
*/
|
|
|
|
export interface MockEnvironmentState {
|
|
filesystem: {
|
|
venvExists: boolean;
|
|
versionFileExists: boolean;
|
|
versionFileContent: string;
|
|
installingLockExists: boolean;
|
|
installedLockExists: boolean;
|
|
backendPathExists: boolean;
|
|
pyprojectExists: boolean;
|
|
// New fields for process.ts functions
|
|
eigentDirExists: boolean;
|
|
eigentBinDirExists: boolean;
|
|
eigentCacheDirExists: boolean;
|
|
eigentVenvsDirExists: boolean;
|
|
eigentRuntimeDirExists: boolean;
|
|
resourcesDirExists: boolean;
|
|
binariesExist: { [name: string]: boolean };
|
|
oldVenvsExist: string[]; // List of old venv directories that exist
|
|
};
|
|
processes: {
|
|
uvAvailable: boolean;
|
|
bunAvailable: boolean;
|
|
uvicornRunning: boolean;
|
|
uvSyncInProgress: boolean;
|
|
installationInProgress: boolean;
|
|
};
|
|
app: {
|
|
currentVersion: string;
|
|
userData: string;
|
|
appPath: string;
|
|
isPackaged: boolean;
|
|
resourcesPath: string;
|
|
};
|
|
system: {
|
|
platform: 'win32' | 'darwin' | 'linux';
|
|
homedir: string;
|
|
};
|
|
network: {
|
|
canConnectToMirror: boolean;
|
|
canConnectToDefault: boolean;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Mock implementations for Node.js fs module
|
|
*/
|
|
export function createFileSystemMock() {
|
|
const mockState: MockEnvironmentState = {
|
|
filesystem: {
|
|
venvExists: true,
|
|
versionFileExists: true,
|
|
versionFileContent: '1.0.0',
|
|
installingLockExists: false,
|
|
installedLockExists: true,
|
|
backendPathExists: true,
|
|
pyprojectExists: true,
|
|
eigentDirExists: true,
|
|
eigentBinDirExists: true,
|
|
eigentCacheDirExists: true,
|
|
eigentVenvsDirExists: true,
|
|
eigentRuntimeDirExists: true,
|
|
resourcesDirExists: true,
|
|
binariesExist: { uv: true, bun: true },
|
|
oldVenvsExist: [],
|
|
},
|
|
processes: {
|
|
uvAvailable: true,
|
|
bunAvailable: true,
|
|
uvicornRunning: false,
|
|
uvSyncInProgress: false,
|
|
installationInProgress: false,
|
|
},
|
|
app: {
|
|
currentVersion: '1.0.0',
|
|
userData: '/mock/user/data',
|
|
appPath: '/mock/app/path',
|
|
isPackaged: false,
|
|
resourcesPath: '/mock/resources/path',
|
|
},
|
|
system: {
|
|
platform: 'win32',
|
|
homedir: '/mock/home',
|
|
},
|
|
network: {
|
|
canConnectToMirror: true,
|
|
canConnectToDefault: true,
|
|
},
|
|
};
|
|
|
|
const fsMock = {
|
|
existsSync: vi.fn().mockImplementation((path: string) => {
|
|
if (!path || typeof path !== 'string') return false;
|
|
if (path.includes('version.txt'))
|
|
return mockState.filesystem.versionFileExists;
|
|
if (path.includes('uv_installing.lock'))
|
|
return mockState.filesystem.installingLockExists;
|
|
if (path.includes('uv_installed.lock'))
|
|
return mockState.filesystem.installedLockExists;
|
|
if (path.includes('.venv')) return mockState.filesystem.venvExists;
|
|
if (path.includes('backend'))
|
|
return mockState.filesystem.backendPathExists;
|
|
if (path.includes('pyproject.toml'))
|
|
return mockState.filesystem.pyprojectExists;
|
|
if (path.includes('.eigent/bin') || path.includes('.eigent\\bin'))
|
|
return mockState.filesystem.eigentBinDirExists;
|
|
if (path.includes('.eigent/cache') || path.includes('.eigent\\cache'))
|
|
return mockState.filesystem.eigentCacheDirExists;
|
|
if (path.includes('.eigent/venvs') || path.includes('.eigent\\venvs'))
|
|
return mockState.filesystem.eigentVenvsDirExists;
|
|
if (path.includes('.eigent/runtime') || path.includes('.eigent\\runtime'))
|
|
return mockState.filesystem.eigentRuntimeDirExists;
|
|
if (
|
|
path.includes('.eigent') &&
|
|
!path.includes('bin') &&
|
|
!path.includes('cache') &&
|
|
!path.includes('venvs') &&
|
|
!path.includes('runtime')
|
|
) {
|
|
return mockState.filesystem.eigentDirExists;
|
|
}
|
|
if (path.includes('resources'))
|
|
return mockState.filesystem.resourcesDirExists;
|
|
// Check for specific binaries
|
|
for (const [name, exists] of Object.entries(
|
|
mockState.filesystem.binariesExist
|
|
)) {
|
|
if (path.includes(name + '.exe') || path.endsWith(name)) {
|
|
return exists;
|
|
}
|
|
}
|
|
// Check for old venv directories
|
|
for (const oldVenv of mockState.filesystem.oldVenvsExist) {
|
|
if (path.includes(oldVenv)) return true;
|
|
}
|
|
return true;
|
|
}),
|
|
|
|
readFileSync: vi
|
|
.fn()
|
|
.mockImplementation((path: string, _encoding?: string) => {
|
|
if (!path || typeof path !== 'string') return '';
|
|
if (path.includes('version.txt')) {
|
|
return mockState.filesystem.versionFileContent;
|
|
}
|
|
if (path.includes('pyproject.toml')) {
|
|
return `
|
|
[project]
|
|
name = "backend"
|
|
version = "1.0.0"
|
|
dependencies = ["fastapi", "uvicorn"]
|
|
`;
|
|
}
|
|
return '';
|
|
}),
|
|
|
|
writeFileSync: vi
|
|
.fn()
|
|
.mockImplementation((path: string, content: string) => {
|
|
if (!path || typeof path !== 'string') return;
|
|
if (path.includes('version.txt')) {
|
|
mockState.filesystem.versionFileContent = content;
|
|
mockState.filesystem.versionFileExists = true;
|
|
} else if (path.includes('uv_installing.lock')) {
|
|
mockState.filesystem.installingLockExists = true;
|
|
} else if (path.includes('uv_installed.lock')) {
|
|
mockState.filesystem.installedLockExists = true;
|
|
}
|
|
}),
|
|
|
|
unlinkSync: vi.fn().mockImplementation((path: string) => {
|
|
if (!path || typeof path !== 'string') return;
|
|
if (path.includes('uv_installing.lock')) {
|
|
mockState.filesystem.installingLockExists = false;
|
|
} else if (path.includes('uv_installed.lock')) {
|
|
mockState.filesystem.installedLockExists = false;
|
|
} else if (path.includes('version.txt')) {
|
|
mockState.filesystem.versionFileExists = false;
|
|
}
|
|
}),
|
|
|
|
mkdirSync: vi.fn().mockImplementation((path: string, _options?: any) => {
|
|
if (!path || typeof path !== 'string') return;
|
|
if (path.includes('backend')) {
|
|
mockState.filesystem.backendPathExists = true;
|
|
} else if (
|
|
path.includes('.eigent/bin') ||
|
|
path.includes('.eigent\\bin')
|
|
) {
|
|
mockState.filesystem.eigentBinDirExists = true;
|
|
} else if (
|
|
path.includes('.eigent/cache') ||
|
|
path.includes('.eigent\\cache')
|
|
) {
|
|
mockState.filesystem.eigentCacheDirExists = true;
|
|
} else if (
|
|
path.includes('.eigent/venvs') ||
|
|
path.includes('.eigent\\venvs')
|
|
) {
|
|
mockState.filesystem.eigentVenvsDirExists = true;
|
|
} else if (
|
|
path.includes('.eigent/runtime') ||
|
|
path.includes('.eigent\\runtime')
|
|
) {
|
|
mockState.filesystem.eigentRuntimeDirExists = true;
|
|
} else if (path.includes('.eigent')) {
|
|
mockState.filesystem.eigentDirExists = true;
|
|
}
|
|
}),
|
|
|
|
rmSync: vi.fn().mockImplementation((path: string, _options?: any) => {
|
|
if (!path || typeof path !== 'string') return;
|
|
// Handle cleanup of old venvs
|
|
for (let i = 0; i < mockState.filesystem.oldVenvsExist.length; i++) {
|
|
if (path.includes(mockState.filesystem.oldVenvsExist[i])) {
|
|
mockState.filesystem.oldVenvsExist.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
}),
|
|
|
|
readdirSync: vi.fn().mockImplementation((path: string, _options?: any) => {
|
|
if (!path || typeof path !== 'string') return [];
|
|
if (path.includes('.eigent/venvs')) {
|
|
// Return old venv directories for cleanup testing
|
|
return mockState.filesystem.oldVenvsExist.map((venv) => ({
|
|
name: venv,
|
|
isDirectory: () => true,
|
|
}));
|
|
}
|
|
return [];
|
|
}),
|
|
|
|
// State control methods
|
|
mockState,
|
|
|
|
reset: () => {
|
|
Object.assign(mockState, {
|
|
filesystem: {
|
|
venvExists: true,
|
|
versionFileExists: true,
|
|
versionFileContent: '1.0.0',
|
|
installingLockExists: false,
|
|
installedLockExists: true,
|
|
backendPathExists: true,
|
|
pyprojectExists: true,
|
|
eigentDirExists: true,
|
|
eigentBinDirExists: true,
|
|
eigentCacheDirExists: true,
|
|
eigentVenvsDirExists: true,
|
|
eigentRuntimeDirExists: true,
|
|
resourcesDirExists: true,
|
|
binariesExist: { uv: true, bun: true },
|
|
oldVenvsExist: [],
|
|
},
|
|
processes: {
|
|
uvAvailable: true,
|
|
bunAvailable: true,
|
|
uvicornRunning: false,
|
|
uvSyncInProgress: false,
|
|
installationInProgress: false,
|
|
},
|
|
app: {
|
|
currentVersion: '1.0.0',
|
|
userData: '/mock/user/data',
|
|
appPath: '/mock/app/path',
|
|
isPackaged: false,
|
|
resourcesPath: '/mock/resources/path',
|
|
},
|
|
system: {
|
|
platform: 'win32',
|
|
homedir: '/mock/home',
|
|
},
|
|
network: {
|
|
canConnectToMirror: true,
|
|
canConnectToDefault: true,
|
|
},
|
|
});
|
|
},
|
|
};
|
|
|
|
return fsMock;
|
|
}
|
|
|
|
/**
|
|
* Mock implementations for child_process spawn
|
|
*/
|
|
export function createProcessMock() {
|
|
const processMock = {
|
|
spawn: vi.fn(),
|
|
mockState: {} as MockEnvironmentState,
|
|
|
|
setupSpawnMock: (mockState: MockEnvironmentState) => {
|
|
processMock.mockState = mockState;
|
|
|
|
processMock.spawn.mockImplementation(
|
|
(command: string, args: string[], _options: any) => {
|
|
// Mock process events
|
|
const mockProcess = {
|
|
stdout: {
|
|
on: vi
|
|
.fn()
|
|
.mockImplementation(
|
|
(event: string, callback: (data: Buffer) => void) => {
|
|
if (event === 'data') {
|
|
// Simulate different process outputs based on command
|
|
setTimeout(() => {
|
|
if (command.includes('uv') && args.includes('sync')) {
|
|
mockState.processes.uvSyncInProgress = true;
|
|
callback(
|
|
Buffer.from('Resolved 10 packages in 1.2s\n')
|
|
);
|
|
setTimeout(() => {
|
|
callback(Buffer.from('Installing packages...\n'));
|
|
setTimeout(() => {
|
|
callback(Buffer.from('Installation complete\n'));
|
|
mockState.processes.uvSyncInProgress = false;
|
|
}, 100);
|
|
}, 50);
|
|
} else if (command.includes('uvicorn')) {
|
|
mockState.processes.uvicornRunning = true;
|
|
callback(
|
|
Buffer.from(
|
|
'Uvicorn running on http://127.0.0.1:8000\n'
|
|
)
|
|
);
|
|
}
|
|
}, 10);
|
|
}
|
|
}
|
|
),
|
|
},
|
|
stderr: {
|
|
on: vi
|
|
.fn()
|
|
.mockImplementation(
|
|
(event: string, callback: (data: Buffer) => void) => {
|
|
if (event === 'data') {
|
|
// Simulate error scenarios
|
|
if (
|
|
!mockState.processes.uvAvailable &&
|
|
command.includes('uv')
|
|
) {
|
|
setTimeout(() => {
|
|
callback(Buffer.from('uv: command not found\n'));
|
|
}, 10);
|
|
}
|
|
}
|
|
}
|
|
),
|
|
},
|
|
on: vi
|
|
.fn()
|
|
.mockImplementation(
|
|
(event: string, callback: (code: number) => void) => {
|
|
if (event === 'close') {
|
|
setTimeout(() => {
|
|
if (command.includes('uv') && args.includes('sync')) {
|
|
const exitCode =
|
|
mockState.processes.uvAvailable &&
|
|
mockState.network.canConnectToDefault
|
|
? 0
|
|
: 1;
|
|
callback(exitCode);
|
|
} else {
|
|
callback(0);
|
|
}
|
|
}, 150);
|
|
}
|
|
}
|
|
),
|
|
kill: vi.fn(),
|
|
};
|
|
|
|
return mockProcess;
|
|
}
|
|
);
|
|
},
|
|
|
|
reset: () => {
|
|
processMock.spawn.mockReset();
|
|
},
|
|
};
|
|
|
|
return processMock;
|
|
}
|
|
|
|
/**
|
|
* Mock for Electron app module
|
|
*/
|
|
export function createElectronAppMock() {
|
|
const appMock = {
|
|
getVersion: vi.fn(),
|
|
getPath: vi.fn(),
|
|
getAppPath: vi.fn(),
|
|
isPackaged: false,
|
|
mockState: {} as MockEnvironmentState,
|
|
|
|
setup: (mockState: MockEnvironmentState) => {
|
|
appMock.mockState = mockState;
|
|
appMock.getVersion.mockReturnValue(mockState.app.currentVersion);
|
|
appMock.getAppPath.mockReturnValue(mockState.app.appPath);
|
|
appMock.isPackaged = mockState.app.isPackaged;
|
|
appMock.getPath.mockImplementation((name: string) => {
|
|
if (name === 'userData') return mockState.app.userData;
|
|
return '/mock/path';
|
|
});
|
|
|
|
// Mock process.resourcesPath for packaged apps
|
|
if (mockState.app.isPackaged) {
|
|
Object.defineProperty(process, 'resourcesPath', {
|
|
value: mockState.app.resourcesPath,
|
|
configurable: true,
|
|
});
|
|
}
|
|
},
|
|
|
|
reset: () => {
|
|
appMock.getVersion.mockReset();
|
|
appMock.getPath.mockReset();
|
|
appMock.getAppPath.mockReset();
|
|
},
|
|
};
|
|
|
|
return appMock;
|
|
}
|
|
|
|
/**
|
|
* Mock for OS module
|
|
*/
|
|
export function createOsMock() {
|
|
const osMock = {
|
|
homedir: vi.fn().mockReturnValue('/mock/home'),
|
|
mockState: {} as MockEnvironmentState,
|
|
|
|
setup: (mockState: MockEnvironmentState) => {
|
|
osMock.mockState = mockState;
|
|
osMock.homedir.mockReturnValue(mockState.system.homedir || '/mock/home');
|
|
},
|
|
|
|
reset: () => {
|
|
osMock.homedir.mockReset();
|
|
osMock.homedir.mockReturnValue('/mock/home');
|
|
},
|
|
};
|
|
|
|
return osMock;
|
|
}
|
|
|
|
/**
|
|
* Mock for path module
|
|
*/
|
|
export function createPathMock() {
|
|
return {
|
|
join: vi.fn((...args) => {
|
|
const validArgs = args.filter(
|
|
(arg) => arg != null && arg !== undefined && arg !== ''
|
|
);
|
|
return validArgs.length > 0
|
|
? validArgs.join(process.platform === 'win32' ? '\\' : '/')
|
|
: '';
|
|
}),
|
|
resolve: vi.fn((...args) => {
|
|
const validArgs = args.filter(
|
|
(arg) => arg != null && arg !== undefined && arg !== ''
|
|
);
|
|
return validArgs.length > 0
|
|
? validArgs.join(process.platform === 'win32' ? '\\' : '/')
|
|
: '';
|
|
}),
|
|
dirname: vi.fn((path: string) => {
|
|
if (!path || typeof path !== 'string') return '';
|
|
const parts = path.split(process.platform === 'win32' ? '\\' : '/');
|
|
return parts.slice(0, -1).join(process.platform === 'win32' ? '\\' : '/');
|
|
}),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Mock for process utilities from electron/main/utils/process.ts
|
|
*/
|
|
export function createProcessUtilsMock() {
|
|
const utilsMock = {
|
|
getResourcePath: vi.fn(),
|
|
getBackendPath: vi.fn(),
|
|
runInstallScript: vi.fn(),
|
|
getBinaryName: vi.fn(),
|
|
getBinaryPath: vi.fn(),
|
|
getCachePath: vi.fn(),
|
|
getVenvPath: vi.fn(),
|
|
getVenvsBaseDir: vi.fn(),
|
|
cleanupOldVenvs: vi.fn(),
|
|
isBinaryExists: vi.fn(),
|
|
getUvEnv: vi.fn(),
|
|
mockState: {} as MockEnvironmentState,
|
|
|
|
setup: (mockState: MockEnvironmentState) => {
|
|
utilsMock.mockState = mockState;
|
|
|
|
utilsMock.getUvEnv.mockReturnValue({
|
|
UV_PYTHON_INSTALL_DIR: `${mockState.system.homedir}/.eigent/cache/uv_python`,
|
|
UV_TOOL_DIR: `${mockState.system.homedir}/.eigent/cache/uv_tool`,
|
|
UV_PROJECT_ENVIRONMENT: `${mockState.system.homedir}/.eigent/venvs/backend-mock`,
|
|
UV_HTTP_TIMEOUT: '300',
|
|
});
|
|
|
|
utilsMock.getResourcePath.mockReturnValue(
|
|
`${mockState.app.appPath}/resources`
|
|
);
|
|
|
|
utilsMock.getBackendPath.mockReturnValue(
|
|
mockState.app.isPackaged
|
|
? `${mockState.app.resourcesPath}/backend`
|
|
: `${mockState.app.appPath}/backend`
|
|
);
|
|
|
|
utilsMock.runInstallScript.mockImplementation(
|
|
async (scriptPath: string) => {
|
|
// Simulate successful script execution and update binary state
|
|
if (scriptPath.includes('install-uv')) {
|
|
mockState.filesystem.binariesExist['uv'] = true;
|
|
mockState.processes.uvAvailable = true;
|
|
} else if (scriptPath.includes('install-bun')) {
|
|
mockState.filesystem.binariesExist['bun'] = true;
|
|
mockState.processes.bunAvailable = true;
|
|
}
|
|
return true;
|
|
}
|
|
);
|
|
|
|
utilsMock.getBinaryName.mockImplementation(async (name: string) => {
|
|
return mockState.system.platform === 'win32' ? `${name}.exe` : name;
|
|
});
|
|
|
|
utilsMock.getBinaryPath.mockImplementation(async (name?: string) => {
|
|
const binDir = `${mockState.system.homedir}/.eigent/bin`;
|
|
if (!name) return binDir;
|
|
const binaryName =
|
|
mockState.system.platform === 'win32' ? `${name}.exe` : name;
|
|
return `${binDir}/${binaryName}`;
|
|
});
|
|
|
|
utilsMock.getCachePath.mockImplementation((folder: string) => {
|
|
return `${mockState.system.homedir}/.eigent/cache/${folder}`;
|
|
});
|
|
|
|
utilsMock.getVenvPath.mockImplementation((version: string) => {
|
|
return `${mockState.system.homedir}/.eigent/venvs/backend-${version}`;
|
|
});
|
|
|
|
utilsMock.getVenvsBaseDir.mockReturnValue(
|
|
`${mockState.system.homedir}/.eigent/venvs`
|
|
);
|
|
|
|
utilsMock.cleanupOldVenvs.mockImplementation(
|
|
async (currentVersion: string) => {
|
|
// Simulate cleanup by removing old venvs from mock state
|
|
mockState.filesystem.oldVenvsExist =
|
|
mockState.filesystem.oldVenvsExist.filter((venv) =>
|
|
venv.includes(`backend-${currentVersion}`)
|
|
);
|
|
}
|
|
);
|
|
|
|
utilsMock.isBinaryExists.mockImplementation(async (name: string) => {
|
|
return mockState.filesystem.binariesExist[name] || false;
|
|
});
|
|
},
|
|
|
|
reset: () => {
|
|
Object.values(utilsMock).forEach((fn) => {
|
|
if (typeof fn === 'function' && 'mockReset' in fn) {
|
|
fn.mockReset();
|
|
}
|
|
});
|
|
},
|
|
};
|
|
|
|
return utilsMock;
|
|
}
|
|
|
|
/**
|
|
* Mock for electron-log
|
|
*/
|
|
export function createLogMock() {
|
|
return {
|
|
info: vi.fn(),
|
|
error: vi.fn(),
|
|
warn: vi.fn(),
|
|
debug: vi.fn(),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Complete environment setup for testing
|
|
* Note: vi.mock calls should be done at the top level of test files, not here
|
|
*/
|
|
export function setupMockEnvironment() {
|
|
const fsMock = createFileSystemMock();
|
|
const processMock = createProcessMock();
|
|
const appMock = createElectronAppMock();
|
|
const osMock = createOsMock();
|
|
const pathMock = createPathMock();
|
|
const processUtilsMock = createProcessUtilsMock();
|
|
const logMock = createLogMock();
|
|
|
|
// Set up the shared state
|
|
processMock.setupSpawnMock(fsMock.mockState);
|
|
appMock.setup(fsMock.mockState);
|
|
osMock.setup(fsMock.mockState);
|
|
processUtilsMock.setup(fsMock.mockState);
|
|
|
|
return {
|
|
fsMock,
|
|
processMock,
|
|
appMock,
|
|
osMock,
|
|
pathMock,
|
|
processUtilsMock,
|
|
logMock,
|
|
mockState: fsMock.mockState,
|
|
|
|
// Utility functions for test scenarios
|
|
scenarios: {
|
|
freshInstall: () => {
|
|
fsMock.mockState.filesystem.venvExists = false;
|
|
fsMock.mockState.filesystem.versionFileExists = false;
|
|
fsMock.mockState.filesystem.installedLockExists = false;
|
|
fsMock.mockState.filesystem.binariesExist = { uv: false, bun: false };
|
|
fsMock.mockState.processes.uvAvailable = false;
|
|
fsMock.mockState.processes.bunAvailable = false;
|
|
},
|
|
|
|
versionUpdate: (oldVersion: string, newVersion: string) => {
|
|
fsMock.mockState.filesystem.versionFileContent = oldVersion;
|
|
fsMock.mockState.app.currentVersion = newVersion;
|
|
appMock.getVersion.mockReturnValue(newVersion);
|
|
},
|
|
|
|
venvRemoved: () => {
|
|
fsMock.mockState.filesystem.venvExists = false;
|
|
fsMock.mockState.filesystem.installedLockExists = false;
|
|
},
|
|
|
|
networkIssues: () => {
|
|
fsMock.mockState.network.canConnectToDefault = false;
|
|
fsMock.mockState.network.canConnectToMirror = true;
|
|
},
|
|
|
|
completeFailure: () => {
|
|
fsMock.mockState.network.canConnectToDefault = false;
|
|
fsMock.mockState.network.canConnectToMirror = false;
|
|
fsMock.mockState.processes.uvAvailable = false;
|
|
fsMock.mockState.filesystem.binariesExist = { uv: false, bun: false };
|
|
|
|
// Note: installCommandTool is defined in the install-deps module,
|
|
// not in process utils, so it should be mocked in the test itself
|
|
},
|
|
|
|
uvicornStartupInstall: () => {
|
|
fsMock.mockState.processes.uvicornRunning = false;
|
|
fsMock.mockState.filesystem.installedLockExists = false;
|
|
// Uvicorn will detect missing deps and start installation
|
|
},
|
|
|
|
installationInProgress: () => {
|
|
fsMock.mockState.filesystem.installingLockExists = true;
|
|
fsMock.mockState.processes.installationInProgress = true;
|
|
},
|
|
|
|
// New scenarios for process.ts testing
|
|
packagedApp: () => {
|
|
fsMock.mockState.app.isPackaged = true;
|
|
appMock.isPackaged = true;
|
|
},
|
|
|
|
multipleOldVenvs: (currentVersion: string) => {
|
|
fsMock.mockState.filesystem.oldVenvsExist = [
|
|
'backend-0.9.0',
|
|
'backend-0.9.5',
|
|
'backend-1.0.1-beta',
|
|
`backend-${currentVersion}`, // This should not be cleaned up
|
|
];
|
|
},
|
|
|
|
macOSEnvironment: () => {
|
|
fsMock.mockState.system.platform = 'darwin';
|
|
Object.defineProperty(process, 'platform', {
|
|
value: 'darwin',
|
|
configurable: true,
|
|
});
|
|
},
|
|
|
|
linuxEnvironment: () => {
|
|
fsMock.mockState.system.platform = 'linux';
|
|
Object.defineProperty(process, 'platform', {
|
|
value: 'linux',
|
|
configurable: true,
|
|
});
|
|
},
|
|
|
|
missingEigentDirectories: () => {
|
|
fsMock.mockState.filesystem.eigentDirExists = false;
|
|
fsMock.mockState.filesystem.eigentBinDirExists = false;
|
|
fsMock.mockState.filesystem.eigentCacheDirExists = false;
|
|
fsMock.mockState.filesystem.eigentVenvsDirExists = false;
|
|
fsMock.mockState.filesystem.eigentRuntimeDirExists = false;
|
|
},
|
|
},
|
|
|
|
reset: () => {
|
|
fsMock.reset();
|
|
processMock.reset();
|
|
appMock.reset();
|
|
osMock.reset();
|
|
processUtilsMock.reset();
|
|
|
|
// Reset process.platform to original
|
|
Object.defineProperty(process, 'platform', {
|
|
value: 'win32',
|
|
configurable: true,
|
|
});
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Factory functions for creating mocks that can be used in vi.mock calls
|
|
* These should be called at the top level of test files
|
|
*/
|
|
export function createMockFactories() {
|
|
return {
|
|
fs: () => createFileSystemMock(),
|
|
childProcess: () => createProcessMock(),
|
|
os: () => ({ default: createOsMock() }),
|
|
path: () => ({ default: createPathMock() }),
|
|
electron: () => ({
|
|
app: createElectronAppMock(),
|
|
BrowserWindow: vi.fn(),
|
|
}),
|
|
electronLog: () => ({ default: createLogMock() }),
|
|
processUtils: () => createProcessUtilsMock(),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Test utility to wait for async state changes
|
|
*/
|
|
export function waitForStateChange<T>(
|
|
stateGetter: () => T,
|
|
expectedValue: T,
|
|
timeout: number = 1000
|
|
): Promise<void> {
|
|
return new Promise((resolve, reject) => {
|
|
const startTime = Date.now();
|
|
|
|
const check = () => {
|
|
if (stateGetter() === expectedValue) {
|
|
resolve();
|
|
} else if (Date.now() - startTime > timeout) {
|
|
reject(
|
|
new Error(
|
|
`Timeout waiting for state change. Expected: ${expectedValue}, got: ${stateGetter()}`
|
|
)
|
|
);
|
|
} else {
|
|
setTimeout(check, 10);
|
|
}
|
|
};
|
|
|
|
check();
|
|
});
|
|
}
|