mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-25 05:35:41 +00:00
Assigning `defaults.headers.common = { 'User-Agent': ... }` replaced the
entire common headers object, nuking axios's built-in default:
Accept: application/json, text/plain, */*
This caused servers relying on content-negotiation to receive requests
with no Accept header. Fix by extending the existing object with a
property assignment instead.
Add regression tests for both electron and CLI axios instances verifying
that Accept is preserved and User-Agent is set correctly.
Co-authored-by: Pragadesh-45 <temporaryg7904@gmail.com>
210 lines
6.1 KiB
JavaScript
210 lines
6.1 KiB
JavaScript
// Mock electron before requiring axios-instance
|
|
jest.mock('electron', () => ({
|
|
app: {
|
|
getVersion: () => '1.0.0'
|
|
}
|
|
}));
|
|
|
|
// Mock preferences
|
|
jest.mock('../../src/store/preferences', () => ({
|
|
preferencesUtil: {
|
|
shouldStoreCookies: () => false,
|
|
shouldSendCookies: () => false,
|
|
isSslSessionCachingEnabled: () => true
|
|
}
|
|
}));
|
|
|
|
// Mock cookies
|
|
jest.mock('../../src/utils/cookies', () => ({
|
|
addCookieToJar: jest.fn(),
|
|
getCookieStringForUrl: jest.fn()
|
|
}));
|
|
|
|
// Mock proxy-util
|
|
jest.mock('../../src/utils/proxy-util', () => ({
|
|
setupProxyAgents: jest.fn()
|
|
}));
|
|
|
|
// Mock form-data
|
|
jest.mock('../../src/utils/form-data', () => ({
|
|
createFormData: jest.fn()
|
|
}));
|
|
|
|
const { makeAxiosInstance } = require('../../src/ipc/network/axios-instance');
|
|
|
|
function createStubAdapter() {
|
|
let capturedConfig = null;
|
|
|
|
const adapter = (config) => {
|
|
capturedConfig = config;
|
|
return Promise.resolve({
|
|
data: {},
|
|
status: 200,
|
|
statusText: 'OK',
|
|
headers: {},
|
|
config
|
|
});
|
|
};
|
|
|
|
adapter.getConfig = () => capturedConfig;
|
|
|
|
return adapter;
|
|
}
|
|
|
|
describe('axios-instance: default headers', () => {
|
|
test('setting User-Agent does not clobber the axios default Accept header', async () => {
|
|
const stubAdapter = createStubAdapter();
|
|
const instance = makeAxiosInstance();
|
|
|
|
await instance({ url: 'https://api.example.com/test', method: 'get', adapter: stubAdapter });
|
|
|
|
// axios.create() sets Accept by default; assigning a new object to defaults.headers.common
|
|
// would nuke it. Guard against that regression.
|
|
expect(stubAdapter.getConfig().headers['Accept']).toMatch(/application\/json/);
|
|
});
|
|
|
|
test('sets User-Agent header to bruno-runtime version', async () => {
|
|
const stubAdapter = createStubAdapter();
|
|
const instance = makeAxiosInstance();
|
|
|
|
await instance({ url: 'https://api.example.com/test', method: 'get', adapter: stubAdapter });
|
|
|
|
expect(stubAdapter.getConfig().headers['User-Agent']).toMatch(/^bruno-runtime\//);
|
|
});
|
|
});
|
|
|
|
describe('axios-instance: DNS lookup behavior (GitHub #7343)', () => {
|
|
let axiosInstance;
|
|
|
|
beforeEach(() => {
|
|
axiosInstance = makeAxiosInstance();
|
|
});
|
|
|
|
test('should set custom lookup function for localhost URLs', async () => {
|
|
const stubAdapter = createStubAdapter();
|
|
|
|
await axiosInstance({
|
|
url: 'http://localhost:3000/api/test',
|
|
method: 'get',
|
|
adapter: stubAdapter
|
|
});
|
|
|
|
const config = stubAdapter.getConfig();
|
|
expect(config.lookup).toBeDefined();
|
|
expect(typeof config.lookup).toBe('function');
|
|
});
|
|
|
|
test('should set custom lookup function for 127.0.0.1 URLs', async () => {
|
|
const stubAdapter = createStubAdapter();
|
|
|
|
await axiosInstance({
|
|
url: 'http://127.0.0.1:8080/api/test',
|
|
method: 'get',
|
|
adapter: stubAdapter
|
|
});
|
|
|
|
const config = stubAdapter.getConfig();
|
|
expect(config.lookup).toBeDefined();
|
|
expect(typeof config.lookup).toBe('function');
|
|
});
|
|
|
|
test('should set custom lookup function for ::1 (IPv6 localhost) URLs', async () => {
|
|
const stubAdapter = createStubAdapter();
|
|
|
|
await axiosInstance({
|
|
url: 'http://[::1]:8080/api/test',
|
|
method: 'get',
|
|
adapter: stubAdapter
|
|
});
|
|
|
|
const config = stubAdapter.getConfig();
|
|
expect(config.lookup).toBeDefined();
|
|
expect(typeof config.lookup).toBe('function');
|
|
});
|
|
|
|
test('should set custom lookup function for *.localhost domains (RFC 6761)', async () => {
|
|
const stubAdapter = createStubAdapter();
|
|
|
|
await axiosInstance({
|
|
url: 'http://api.localhost:3000/test',
|
|
method: 'get',
|
|
adapter: stubAdapter
|
|
});
|
|
|
|
const config = stubAdapter.getConfig();
|
|
expect(config.lookup).toBeDefined();
|
|
expect(typeof config.lookup).toBe('function');
|
|
});
|
|
|
|
test('should NOT set custom lookup for external domains', async () => {
|
|
const stubAdapter = createStubAdapter();
|
|
|
|
await axiosInstance({
|
|
url: 'https://api.example.com/test',
|
|
method: 'get',
|
|
adapter: stubAdapter
|
|
});
|
|
|
|
const config = stubAdapter.getConfig();
|
|
expect(config.lookup).toBeUndefined();
|
|
});
|
|
|
|
test('should NOT set custom lookup for httpbin.org', async () => {
|
|
const stubAdapter = createStubAdapter();
|
|
|
|
await axiosInstance({
|
|
url: 'https://httpbin.org/get',
|
|
method: 'get',
|
|
adapter: stubAdapter
|
|
});
|
|
|
|
const config = stubAdapter.getConfig();
|
|
expect(config.lookup).toBeUndefined();
|
|
});
|
|
|
|
test('should clear inherited lookup when URL changes from localhost to external domain', async () => {
|
|
// This simulates what happens during a redirect:
|
|
// 1. Original request to localhost sets lookup
|
|
// 2. Redirect spreads config including lookup
|
|
// 3. New request to external domain should clear the lookup
|
|
const stubAdapter = createStubAdapter();
|
|
const inheritedLookup = (_hostname, _options, callback) => {
|
|
callback(null, '127.0.0.1', 4);
|
|
};
|
|
|
|
await axiosInstance({
|
|
url: 'https://external-auth-provider.com/oauth/authorize',
|
|
method: 'get',
|
|
adapter: stubAdapter,
|
|
lookup: inheritedLookup // Simulates inherited lookup from redirect
|
|
});
|
|
|
|
const config = stubAdapter.getConfig();
|
|
// The lookup should be cleared for external domains
|
|
expect(config.lookup).toBeUndefined();
|
|
});
|
|
|
|
test('should replace inherited lookup with a fresh one when redirecting localhost to localhost', async () => {
|
|
// Simulates a redirect from one localhost endpoint to another:
|
|
// the inherited lookup from the original request should be replaced
|
|
// (not just kept) by a fresh localhost lookup function.
|
|
const stubAdapter = createStubAdapter();
|
|
const inheritedLookup = (_hostname, _options, callback) => {
|
|
callback(null, '127.0.0.1', 4);
|
|
};
|
|
|
|
await axiosInstance({
|
|
url: 'http://localhost:3182/redirected',
|
|
method: 'get',
|
|
adapter: stubAdapter,
|
|
lookup: inheritedLookup // Simulates inherited lookup from redirect
|
|
});
|
|
|
|
const config = stubAdapter.getConfig();
|
|
// Should have a lookup set for localhost, but it should be a fresh one
|
|
expect(config.lookup).toBeDefined();
|
|
expect(typeof config.lookup).toBe('function');
|
|
expect(config.lookup).not.toBe(inheritedLookup);
|
|
});
|
|
});
|