mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-29 15:44:13 +00:00
* feat: add certs and proxy config to bru.sendRequest API Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: handle URL string argument in bru.sendRequest When bru.sendRequest is called with a plain URL string instead of a config object, the function now normalizes it to { url: string } before processing. This fixes the case where spreading a string created an invalid config object. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add variable interpolation to bru.sendRequest certs and proxy config Interpolate environment variables in clientCertificates and proxy configuration for bru.sendRequest API, enabling use of variables like {{CERT_PATH}} or {{PROXY_HOST}} in certificate paths and proxy settings. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: use interpolateObject for certs and proxy config interpolation - Add interpolateObject to electron's interpolate-string.js using buildCombinedVars pattern (matches CLI implementation) - Simplify cert-utils.js by using interpolateObject instead of manual field-by-field interpolation - Add interpolation for clientCertificates and proxy config in CLI's run-single-request.js for bru.sendRequest Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: add all variable types to sendRequest interpolation options - Add globalEnvVars, collectionVariables, folderVariables, requestVariables to sendRequestInterpolationOptions for complete variable support - Use cached system proxy instead of redundant getSystemProxy() call - Remove duplicate getOptions() call Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: skip CA cert loading when TLS verification is disabled Only load CA certificates when shouldVerifyTls is true, since they are not used for validation when TLS verification is disabled. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
199 lines
6.3 KiB
TypeScript
199 lines
6.3 KiB
TypeScript
import sendRequest, { createSendRequest } from './send-request';
|
|
|
|
jest.mock('../network', () => ({
|
|
makeAxiosInstance: jest.fn()
|
|
}));
|
|
|
|
jest.mock('../utils/http-https-agents', () => ({
|
|
getHttpHttpsAgents: jest.fn()
|
|
}));
|
|
|
|
import { makeAxiosInstance } from '../network';
|
|
import { getHttpHttpsAgents } from '../utils/http-https-agents';
|
|
|
|
const mockMakeAxiosInstance = makeAxiosInstance as jest.Mock;
|
|
const mockGetHttpHttpsAgents = getHttpHttpsAgents as jest.Mock;
|
|
|
|
describe('sendRequest', () => {
|
|
let mockAxios: jest.Mock;
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
mockAxios = jest.fn();
|
|
mockMakeAxiosInstance.mockReturnValue(mockAxios);
|
|
mockGetHttpHttpsAgents.mockResolvedValue({ httpAgent: null, httpsAgent: null });
|
|
});
|
|
|
|
describe('without callback', () => {
|
|
test('should return response directly', async () => {
|
|
const mockResponse = { data: 'test', status: 200 };
|
|
mockAxios.mockResolvedValue(mockResponse);
|
|
|
|
const result = await sendRequest({ url: 'http://example.com' });
|
|
|
|
expect(result).toBe(mockResponse);
|
|
});
|
|
|
|
test('should reject on request error', async () => {
|
|
const error = new Error('Network error');
|
|
mockAxios.mockRejectedValue(error);
|
|
|
|
await expect(sendRequest({ url: 'http://example.com' })).rejects.toThrow('Network error');
|
|
});
|
|
|
|
test('should handle URL string instead of config object', async () => {
|
|
const mockResponse = { data: 'pong', status: 200 };
|
|
mockAxios.mockResolvedValue(mockResponse);
|
|
|
|
const result = await sendRequest('http://example.com/ping');
|
|
|
|
expect(result).toBe(mockResponse);
|
|
expect(mockAxios).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
url: 'http://example.com/ping'
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('with callback', () => {
|
|
test('should call callback with response and return response', async () => {
|
|
const mockResponse = { data: 'test', status: 200 };
|
|
mockAxios.mockResolvedValue(mockResponse);
|
|
const callback = jest.fn();
|
|
|
|
const result = await sendRequest({ url: 'http://example.com' }, callback);
|
|
|
|
expect(callback).toHaveBeenCalledWith(null, mockResponse);
|
|
expect(result).toBe(mockResponse);
|
|
});
|
|
|
|
test('should call callback with error on request failure', async () => {
|
|
const error = new Error('Network error');
|
|
mockAxios.mockRejectedValue(error);
|
|
const callback = jest.fn();
|
|
|
|
await sendRequest({ url: 'http://example.com' }, callback);
|
|
|
|
expect(callback).toHaveBeenCalledWith(error, null);
|
|
});
|
|
|
|
test('should reject if callback throws on success', async () => {
|
|
const mockResponse = { data: 'test', status: 200 };
|
|
mockAxios.mockResolvedValue(mockResponse);
|
|
const callbackError = new Error('Callback error');
|
|
const callback = jest.fn().mockRejectedValue(callbackError);
|
|
|
|
await expect(sendRequest({ url: 'http://example.com' }, callback)).rejects.toThrow(
|
|
'Callback error'
|
|
);
|
|
});
|
|
|
|
test('should reject if callback throws on error', async () => {
|
|
const requestError = new Error('Network error');
|
|
mockAxios.mockRejectedValue(requestError);
|
|
const callbackError = new Error('Callback error');
|
|
const callback = jest.fn().mockRejectedValue(callbackError);
|
|
|
|
await expect(sendRequest({ url: 'http://example.com' }, callback)).rejects.toThrow(
|
|
'Callback error'
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('createSendRequest', () => {
|
|
let mockAxios: jest.Mock;
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
mockAxios = jest.fn();
|
|
mockMakeAxiosInstance.mockReturnValue(mockAxios);
|
|
});
|
|
|
|
test('should apply agents from config', async () => {
|
|
const mockHttpAgent = { name: 'httpAgent' };
|
|
const mockHttpsAgent = { name: 'httpsAgent' };
|
|
mockGetHttpHttpsAgents.mockResolvedValue({
|
|
httpAgent: mockHttpAgent,
|
|
httpsAgent: mockHttpsAgent
|
|
});
|
|
const mockResponse = { data: 'test' };
|
|
mockAxios.mockResolvedValue(mockResponse);
|
|
|
|
const customSendRequest = createSendRequest({ proxyConfig: {} });
|
|
await customSendRequest({ url: 'https://example.com' });
|
|
|
|
expect(mockGetHttpHttpsAgents).toHaveBeenCalledWith({
|
|
proxyConfig: {},
|
|
requestUrl: 'https://example.com'
|
|
});
|
|
expect(mockAxios).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
httpAgent: mockHttpAgent,
|
|
httpsAgent: mockHttpsAgent
|
|
})
|
|
);
|
|
});
|
|
|
|
test('should not override agents if already set in requestConfig', async () => {
|
|
const configHttpAgent = { name: 'configAgent' };
|
|
const configHttpsAgent = { name: 'configHttpsAgent' };
|
|
mockGetHttpHttpsAgents.mockResolvedValue({
|
|
httpAgent: { name: 'ignored' },
|
|
httpsAgent: { name: 'ignored' }
|
|
});
|
|
mockAxios.mockResolvedValue({ data: 'test' });
|
|
|
|
const customSendRequest = createSendRequest({ proxyConfig: {} });
|
|
await customSendRequest({
|
|
url: 'https://example.com',
|
|
httpAgent: configHttpAgent,
|
|
httpsAgent: configHttpsAgent
|
|
});
|
|
|
|
expect(mockAxios).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
httpAgent: configHttpAgent,
|
|
httpsAgent: configHttpsAgent
|
|
})
|
|
);
|
|
});
|
|
|
|
test('should not call getHttpHttpsAgents when no config provided', async () => {
|
|
mockAxios.mockResolvedValue({ data: 'test' });
|
|
|
|
const customSendRequest = createSendRequest();
|
|
await customSendRequest({ url: 'https://example.com' });
|
|
|
|
expect(mockGetHttpHttpsAgents).not.toHaveBeenCalled();
|
|
});
|
|
|
|
test('should handle URL string and apply agents from config', async () => {
|
|
const mockHttpAgent = { name: 'httpAgent' };
|
|
const mockHttpsAgent = { name: 'httpsAgent' };
|
|
mockGetHttpHttpsAgents.mockResolvedValue({
|
|
httpAgent: mockHttpAgent,
|
|
httpsAgent: mockHttpsAgent
|
|
});
|
|
const mockResponse = { data: 'pong' };
|
|
mockAxios.mockResolvedValue(mockResponse);
|
|
|
|
const customSendRequest = createSendRequest({ collectionPath: '/test' });
|
|
const result = await customSendRequest('https://example.com/ping');
|
|
|
|
expect(result).toBe(mockResponse);
|
|
expect(mockGetHttpHttpsAgents).toHaveBeenCalledWith({
|
|
collectionPath: '/test',
|
|
requestUrl: 'https://example.com/ping'
|
|
});
|
|
expect(mockAxios).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
url: 'https://example.com/ping',
|
|
httpAgent: mockHttpAgent,
|
|
httpsAgent: mockHttpsAgent
|
|
})
|
|
);
|
|
});
|
|
});
|