diff --git a/electron/main/init.ts b/electron/main/init.ts index 11ed6ef3..58a26c0f 100644 --- a/electron/main/init.ts +++ b/electron/main/init.ts @@ -6,7 +6,7 @@ import path from 'path' import * as net from "net"; import { ipcMain, BrowserWindow, app } from 'electron' import { promisify } from 'util' -import { detectInstallationLogs, PromiseReturnType } from "./install-deps"; +import { PromiseReturnType } from "./install-deps"; const execAsync = promisify(exec); @@ -170,8 +170,6 @@ export async function startBackend(setPort?: (port: number) => void): Promise { if (!data) return; const msg = data.toString().trimEnd(); - //Detect if uv sync is run - detectInstallationLogs(msg); if (msg.toLowerCase().includes("error") || msg.toLowerCase().includes("traceback")) { log.error(`BACKEND: ${msg}`); diff --git a/electron/main/install-deps.ts b/electron/main/install-deps.ts index 257fca38..7156b1c1 100644 --- a/electron/main/install-deps.ts +++ b/electron/main/install-deps.ts @@ -677,104 +677,4 @@ export async function installDependencies(version: string): Promise - msg.includes(pattern) && !msg.includes("Uvicorn running on") - )) { - dependencyInstallationDetected = true; - log.info('[BACKEND STARTUP] UV dependency installation detected during uvicorn startup'); - - // Create installing lock file to maintain consistency with install-deps.ts - InstallLogs.setLockPath(); - log.info('[BACKEND STARTUP] Created uv_installing.lock file'); - - // Notify frontend that installation has started (only once) - if (!installationNotificationSent) { - installationNotificationSent = true; - const notificationSent = safeMainWindowSend('install-dependencies-start'); - if (notificationSent) { - log.info('[BACKEND STARTUP] Notified frontend of dependency installation start'); - } else { - log.warn('[BACKEND STARTUP] Failed to notify frontend of dependency installation start'); - } - } - } - - // Send installation logs to frontend if installation was detected - if (dependencyInstallationDetected && !msg.includes("Uvicorn running on")) { - safeMainWindowSend('install-dependencies-log', { - type: msg.toLowerCase().includes("error") || msg.toLowerCase().includes("traceback") ? 'stderr' : 'stdout', - data: msg - }); - } - - // Check if installation is complete (uvicorn starts successfully) - if (dependencyInstallationDetected && msg.includes("Uvicorn running on")) { - log.info('[BACKEND STARTUP] UV dependency installation completed, uvicorn started successfully'); - - // Clean up installing lock and create installed lock - InstallLogs.cleanLockPath(); - fs.writeFileSync(installedLockPath, ''); - log.info('[BACKEND STARTUP] Created uv_installed.lock file'); - - safeMainWindowSend('install-dependencies-complete', { - success: true, - message: 'Dependencies installed successfully during backend startup' - }); - } - - // Handle installation failures - if (dependencyInstallationDetected && ( - msg.toLowerCase().includes("failed to resolve dependencies") || - msg.toLowerCase().includes("installation failed") || - msg.includes("× No solution found when resolving dependencies") - )) { - log.error('[BACKEND STARTUP] UV dependency installation failed'); - - // Clean up installing lock file - InstallLogs.cleanLockPath(); - log.info('[BACKEND STARTUP] Cleaned up uv_installing.lock file after failure'); - - safeMainWindowSend('install-dependencies-complete', { - success: false, - error: 'Dependency installation failed during backend startup' - }); - } } \ No newline at end of file diff --git a/test/unit/electron/install-deps.test.ts b/test/unit/electron/install-deps.test.ts index b1ee8adc..73b784cd 100644 --- a/test/unit/electron/install-deps.test.ts +++ b/test/unit/electron/install-deps.test.ts @@ -429,181 +429,4 @@ describe('Install Deps Module', () => { // expect(mockEnv.processUtilsMock.cleanupOldVenvs).toHaveBeenCalledWith('1.0.0') // }) -// }) - -// describe('detectInstallationLogs', () => { -// beforeEach(() => { -// // Reset the module-level state variables -// vi.resetModules() -// }) - -// it('should detect UV dependency installation patterns', () => { -// const installationPatterns = [ -// 'Resolved 10 packages in 1.2s', -// 'Downloaded package xyz', -// 'Installing numpy==1.21.0', -// 'Built wheel for package', -// 'Prepared virtual environment', -// 'Syncing dependencies', -// 'Creating virtualenv at .venv', -// 'Updating package index', -// 'Audited 15 packages' -// ] - -// installationPatterns.forEach(pattern => { -// // The function has side effects, so we can't easily test return values -// // Instead, we test that it doesn't throw and processes the input -// expect(() => installDeps.detectInstallationLogs(pattern)).not.toThrow() -// }) -// }) - -// it('should handle uvicorn startup messages', () => { -// const uvicornMessages = [ -// 'Uvicorn running on http://127.0.0.1:8000', -// 'Application startup complete', -// 'Server started successfully' -// ] - -// uvicornMessages.forEach(message => { -// expect(() => installDeps.detectInstallationLogs(message)).not.toThrow() -// }) -// }) - -// it('should handle installation failure messages', () => { -// const failureMessages = [ -// '× No solution found when resolving dependencies', -// 'failed to resolve dependencies', -// 'installation failed' -// ] - -// failureMessages.forEach(message => { -// expect(() => installDeps.detectInstallationLogs(message)).not.toThrow() -// }) -// }) -// }) - -// describe('Error Handling and Edge Cases', () => { -// it('should handle file system permission errors gracefully', async () => { -// // Mock filesystem error -// mockEnv.fsMock.writeFileSync.mockImplementation((path: string) => { -// if (path.includes('version.txt')) { -// throw new Error('Permission denied') -// } -// }) - -// const mockWin = { -// webContents: { send: vi.fn() }, -// isDestroyed: vi.fn().mockReturnValue(false) -// } - -// // The function should handle errors gracefully -// const result = await installDeps.checkAndInstallDepsOnUpdate({ -// win: mockWin, -// forceInstall: true -// }) - -// // Should still return a result, even if there are file system errors -// expect(result).toBeDefined() -// expect(typeof result.success).toBe('boolean') -// expect(typeof result.message).toBe('string') -// }) - -// it('should handle timezone-based mirror selection for China', async () => { -// // Mock Intl.DateTimeFormat for China timezone -// const originalDateTimeFormat = global.Intl.DateTimeFormat -// const mockDateTimeFormat = vi.fn().mockImplementation(() => ({ -// resolvedOptions: () => ({ timeZone: 'Asia/Shanghai' }) -// })) as any -// global.Intl.DateTimeFormat = mockDateTimeFormat - -// try { -// // Set up scenario where default fails but mirror succeeds -// mockEnv.scenarios.networkIssues() - -// const result = await installDeps.installDependencies('1.0.0') - -// expect(result.success).toBe(true) -// expect(result.message).toContain('Dependencies installed successfully with mirror') -// } finally { -// // Restore original -// global.Intl.DateTimeFormat = originalDateTimeFormat -// } -// }) - -// it('should handle invalid version strings', async () => { -// const result = await installDeps.installDependencies('') - -// // Should handle empty version string gracefully -// expect(result).toBeDefined() -// expect(typeof result.success).toBe('boolean') -// }) - -// it('should handle missing backend directory', async () => { -// mockEnv.mockState.filesystem.backendPathExists = false - -// const result = await installDeps.installDependencies('1.0.0') - -// // Should create the directory and continue -// expect(mockEnv.fsMock.mkdirSync).toHaveBeenCalledWith( -// expect.stringContaining('backend'), -// { recursive: true } -// ) -// expect(result).toBeDefined() -// }) -// }) - -// describe('Integration Tests', () => { -// it('should handle complete fresh installation workflow', async () => { -// // Set up completely fresh system -// mockEnv.scenarios.freshInstall() - -// const mockWin = { -// webContents: { send: vi.fn() }, -// isDestroyed: vi.fn().mockReturnValue(false) -// } - -// const result = await installDeps.checkAndInstallDepsOnUpdate({ -// win: mockWin, -// forceInstall: false -// }) - -// expect(result.success).toBe(true) -// expect(mockWin.webContents.send).toHaveBeenCalledWith( -// 'update-notification', -// expect.objectContaining({ -// type: 'version-update', -// reason: 'version file not exist' -// }) -// ) -// }) - -// it('should handle version update with missing tools', async () => { -// // Version file exists but tools are missing -// mockEnv.mockState.filesystem.versionFileExists = true -// mockEnv.mockState.filesystem.versionFileContent = '0.9.0' -// mockEnv.mockState.app.currentVersion = '1.0.0' -// mockEnv.mockState.filesystem.binariesExist = { 'uv': false, 'bun': false } - -// const mockWin = { -// webContents: { send: vi.fn() }, -// isDestroyed: vi.fn().mockReturnValue(false) -// } - -// const result = await installDeps.checkAndInstallDepsOnUpdate({ -// win: mockWin, -// forceInstall: false -// }) - -// expect(result.success).toBe(true) -// expect(mockWin.webContents.send).toHaveBeenCalledWith( -// 'update-notification', -// expect.objectContaining({ -// type: 'version-update', -// currentVersion: '1.0.0', -// previousVersion: '0.9.0', -// reason: 'version not match' -// }) -// ) -// }) -// }) -// }) \ No newline at end of file +// }) \ No newline at end of file