mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
chore: add a promise based wait group for the shell variables (#7647)
This commit is contained in:
@@ -3,7 +3,7 @@ const path = require('path');
|
||||
const { execSync } = require('node:child_process');
|
||||
const isDev = require('electron-is-dev');
|
||||
const os = require('os');
|
||||
const { initializeShellEnv } = require('@usebruno/requests');
|
||||
const { initializeShellEnv, waitForShellEnv } = require('./store/shell-env-state');
|
||||
const { percentageToZoomLevel } = require('@usebruno/common');
|
||||
|
||||
if (isDev) {
|
||||
@@ -181,8 +181,7 @@ if (useSingleInstance && !gotTheLock) {
|
||||
|
||||
// Prepare the renderer once the app is ready
|
||||
app.on('ready', async () => {
|
||||
// Ensure shell environment is loaded before any operations that need it
|
||||
await initializeShellEnv();
|
||||
initializeShellEnv();
|
||||
|
||||
if (isDev) {
|
||||
const { installExtension, REDUX_DEVTOOLS, REACT_DEVELOPER_TOOLS } = require('electron-devtools-installer');
|
||||
@@ -203,8 +202,10 @@ app.on('ready', async () => {
|
||||
|
||||
// Initialize system proxy cache early (non-blocking)
|
||||
const { fetchSystemProxy } = require('./store/system-proxy');
|
||||
fetchSystemProxy().catch((err) => {
|
||||
console.warn('Failed to initialize system proxy cache:', err);
|
||||
waitForShellEnv().then(() => {
|
||||
fetchSystemProxy().catch((err) => {
|
||||
console.warn('Failed to initialize system proxy cache:', err);
|
||||
});
|
||||
});
|
||||
|
||||
Menu.setApplicationMenu(menu);
|
||||
|
||||
27
packages/bruno-electron/src/store/shell-env-state.js
Normal file
27
packages/bruno-electron/src/store/shell-env-state.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const { initializeShellEnv: _initializeShellEnv } = require('@usebruno/requests');
|
||||
|
||||
const TIMEOUT_MS = 60_000;
|
||||
|
||||
let _promise = null;
|
||||
|
||||
const _initWithTimeout = () => {
|
||||
let timer;
|
||||
const timeout = new Promise((_, reject) => {
|
||||
timer = setTimeout(() => {
|
||||
_promise = null;
|
||||
reject(new Error('Shell environment initialization timed out'));
|
||||
}, TIMEOUT_MS);
|
||||
});
|
||||
return Promise.race([_initializeShellEnv(), timeout]).finally(() => clearTimeout(timer));
|
||||
};
|
||||
|
||||
const initializeShellEnv = () => {
|
||||
if (!_promise) _promise = _initWithTimeout();
|
||||
};
|
||||
|
||||
const waitForShellEnv = () => {
|
||||
if (!_promise) _promise = _initWithTimeout();
|
||||
return _promise;
|
||||
};
|
||||
|
||||
module.exports = { initializeShellEnv, waitForShellEnv };
|
||||
105
packages/bruno-electron/src/store/tests/shell-env-state.spec.js
Normal file
105
packages/bruno-electron/src/store/tests/shell-env-state.spec.js
Normal file
@@ -0,0 +1,105 @@
|
||||
let mockInitialize;
|
||||
|
||||
jest.mock('@usebruno/requests', () => ({
|
||||
initializeShellEnv: (...args) => mockInitialize(...args)
|
||||
}));
|
||||
|
||||
describe('shell-env-state', () => {
|
||||
let initializeShellEnv, waitForShellEnv;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
mockInitialize = jest.fn(() => Promise.resolve());
|
||||
({ initializeShellEnv, waitForShellEnv } = require('../shell-env-state'));
|
||||
});
|
||||
|
||||
describe('initializeShellEnv', () => {
|
||||
it('calls the underlying initializer exactly once on first call', () => {
|
||||
initializeShellEnv();
|
||||
initializeShellEnv();
|
||||
initializeShellEnv();
|
||||
initializeShellEnv();
|
||||
initializeShellEnv();
|
||||
initializeShellEnv();
|
||||
initializeShellEnv();
|
||||
expect(mockInitialize).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('returns undefined (fire-and-forget)', () => {
|
||||
const result = initializeShellEnv();
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('waitForShellEnv', () => {
|
||||
it('returns a promise', () => {
|
||||
const result = waitForShellEnv();
|
||||
expect(result).toBeInstanceOf(Promise);
|
||||
});
|
||||
|
||||
it('resolves when the underlying promise resolves', async () => {
|
||||
mockInitialize = jest.fn(() => Promise.resolve('shell-ready'));
|
||||
|
||||
await expect(waitForShellEnv()).resolves.toBe('shell-ready');
|
||||
});
|
||||
|
||||
it('returns the same promise on repeated calls', () => {
|
||||
const p1 = waitForShellEnv();
|
||||
const p2 = waitForShellEnv();
|
||||
expect(p1).toBe(p2);
|
||||
});
|
||||
|
||||
it('does not reinitialize if initializeShellEnv was already called', () => {
|
||||
initializeShellEnv();
|
||||
waitForShellEnv();
|
||||
expect(mockInitialize).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('propagates rejection from the underlying initializer', async () => {
|
||||
const err = new Error('shell init failed');
|
||||
mockInitialize = jest.fn(() => Promise.reject(err));
|
||||
|
||||
await expect(waitForShellEnv()).rejects.toThrow('shell init failed');
|
||||
});
|
||||
|
||||
describe('timeout', () => {
|
||||
beforeEach(() => jest.useFakeTimers());
|
||||
afterEach(() => jest.useRealTimers());
|
||||
|
||||
it('rejects after 60 seconds', async () => {
|
||||
mockInitialize = jest.fn(() => new Promise(() => {})); // never resolves
|
||||
({ waitForShellEnv } = require('../shell-env-state'));
|
||||
|
||||
const p = waitForShellEnv();
|
||||
jest.advanceTimersByTime(60_000);
|
||||
|
||||
await expect(p).rejects.toThrow('Shell environment initialization timed out');
|
||||
});
|
||||
|
||||
it('resets the promise after timeout so next call retries', async () => {
|
||||
mockInitialize = jest.fn(() => new Promise(() => {}));
|
||||
({ initializeShellEnv, waitForShellEnv } = require('../shell-env-state'));
|
||||
|
||||
const p = waitForShellEnv();
|
||||
jest.advanceTimersByTime(60_000);
|
||||
await expect(p).rejects.toThrow('timed out');
|
||||
|
||||
// After timeout _promise is null — next call should reinitialize
|
||||
mockInitialize = jest.fn(() => Promise.resolve('retry-ok'));
|
||||
const p2 = waitForShellEnv();
|
||||
await expect(p2).resolves.toBe('retry-ok');
|
||||
expect(mockInitialize).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not time out if the initializer resolves in time', async () => {
|
||||
mockInitialize = jest.fn(() => Promise.resolve('fast'));
|
||||
({ waitForShellEnv } = require('../shell-env-state'));
|
||||
|
||||
const p = waitForShellEnv();
|
||||
jest.advanceTimersByTime(59_999);
|
||||
|
||||
await expect(p).resolves.toBe('fast');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user