Skip to content

Commit

Permalink
chore: changes after review
Browse files Browse the repository at this point in the history
  • Loading branch information
DmitryAnansky committed Jan 20, 2025
1 parent 7caf654 commit 6fb553c
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 70 deletions.
90 changes: 39 additions & 51 deletions packages/cli/src/__tests__/commands/push-region.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,67 +19,54 @@ jest.mock('fs', () => ({
readdirSync: () => [],
}));

// Mock OpenAPI core
jest.mock('@redocly/openapi-core', () => ({
...jest.requireActual('@redocly/openapi-core'),
getMergedConfig: jest.fn().mockImplementation((config) => config),
bundle: jest.fn().mockResolvedValue({
bundle: { parsed: {} },
problems: {
totals: { errors: 0, warnings: 0 },
items: [],
[Symbol.iterator]: function* () {
yield* this.items;
},
},
}),
RedoclyClient: jest.fn().mockImplementation((region?: string) => ({
domain: region === 'eu' ? 'eu.redocly.com' : 'redoc.ly',
isAuthorizedWithRedoclyByRegion: jest.fn().mockResolvedValue(false),
login: jest.fn().mockResolvedValue({}),
registryApi: {
prepareFileUpload: jest.fn().mockResolvedValue({
signedUploadUrl: 'https://example.com',
filePath: 'test.yaml',
}),
pushApi: jest.fn().mockResolvedValue({}),
},
})),
}));
(getMergedConfig as jest.Mock).mockImplementation((config) => config);

// Mock OpenAPI core
jest.mock('@redocly/openapi-core');
jest.mock('../../commands/login');
jest.mock('../../utils/miscellaneous');

// Mock global fetch
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve({}),
headers: new Headers(),
statusText: 'OK',
redirected: false,
type: 'default',
url: '',
clone: () => ({} as Response),
body: new ReadableStream(),
bodyUsed: false,
arrayBuffer: async () => new ArrayBuffer(0),
blob: async () => new Blob(),
formData: async () => new FormData(),
text: async () => '',
} as Response)
);

const mockPromptClientToken = promptClientToken as jest.MockedFunction<typeof promptClientToken>;

describe('push-with-region', () => {
const redoclyClient = require('@redocly/openapi-core').__redoclyClient;
redoclyClient.isAuthorizedWithRedoclyByRegion = jest.fn().mockResolvedValue(false);

const originalFetch = fetch;

beforeAll(() => {
// Mock global fetch
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve({}),
headers: new Headers(),
statusText: 'OK',
redirected: false,
type: 'default',
url: '',
clone: () => ({} as Response),
body: new ReadableStream(),
bodyUsed: false,
arrayBuffer: async () => new ArrayBuffer(0),
blob: async () => new Blob(),
formData: async () => new FormData(),
text: async () => '',
} as Response)
);
});

afterAll(() => {
global.fetch = originalFetch;
});

beforeEach(() => {
jest.clearAllMocks();
jest.spyOn(process.stdout, 'write').mockImplementation(() => true);
});

it('should call login with default domain when region is US', async () => {
redoclyClient.domain = 'redocly.com';
await handlePush({
argv: {
upsert: true,
Expand All @@ -92,10 +79,11 @@ describe('push-with-region', () => {
});

expect(mockPromptClientToken).toBeCalledTimes(1);
expect(mockPromptClientToken).toHaveBeenCalledWith('redoc.ly');
expect(mockPromptClientToken).toHaveBeenCalledWith(redoclyClient.domain);
});

it('should call login with EU domain when region is EU', async () => {
redoclyClient.domain = 'eu.redocly.com';
// Update config for EU region
const euConfig = { ...ConfigFixture, region: 'eu' };

Expand All @@ -111,6 +99,6 @@ describe('push-with-region', () => {
});

expect(mockPromptClientToken).toBeCalledTimes(1);
expect(mockPromptClientToken).toHaveBeenCalledWith('eu.redocly.com');
expect(mockPromptClientToken).toHaveBeenCalledWith(redoclyClient.domain);
});
});
1 change: 0 additions & 1 deletion packages/cli/src/__tests__/commands/push.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ describe('push', () => {
const redoclyClient = require('@redocly/openapi-core').__redoclyClient;

beforeEach(() => {
jest.clearAllMocks();
jest.spyOn(process.stdout, 'write').mockImplementation(() => true);
});

Expand Down
34 changes: 30 additions & 4 deletions packages/cli/src/__tests__/fetch-with-timeout.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,46 @@ import { HttpsProxyAgent } from 'https-proxy-agent';

jest.mock('@redocly/openapi-core');

const signalInstance = new AbortController().signal;

const mockFetch = jest.fn(() =>
Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve({}),
headers: new Headers(),
statusText: 'OK',
redirected: false,
type: 'default',
url: '',
clone: () => ({} as Response),
body: null,
bodyUsed: false,
arrayBuffer: async () => new ArrayBuffer(0),
blob: async () => new Blob(),
formData: async () => new FormData(),
text: async () => '',
signal: signalInstance,
dispatcher: undefined,
} as Response)
);

const originalFetch = global.fetch;
global.fetch = mockFetch;

describe('fetchWithTimeout', () => {
beforeAll(() => {
// @ts-ignore
global.setTimeout = jest.fn();
global.clearTimeout = jest.fn();
// Add global fetch mock
global.fetch = jest.fn();
});

beforeEach(() => {
(getProxyAgent as jest.Mock).mockReturnValueOnce(undefined);
});

afterEach(() => {
jest.clearAllMocks();
afterAll(() => {
global.fetch = originalFetch;
});

it('should call fetch with signal', async () => {
Expand All @@ -30,6 +55,7 @@ describe('fetchWithTimeout', () => {
'url',
expect.objectContaining({
signal: expect.any(AbortSignal),
dispatcher: undefined,
})
);
expect(global.clearTimeout).toHaveBeenCalledTimes(1);
Expand Down
24 changes: 10 additions & 14 deletions packages/cli/src/utils/fetch-with-timeout.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
import { getProxyAgent } from '@redocly/openapi-core';

import type { Agent } from 'node:http';

export const DEFAULT_FETCH_TIMEOUT = 3000;

export type FetchWithTimeoutOptions = RequestInit & {
timeout?: number;
dispatcher?: Agent;
};

export default async (url: string, { timeout, ...options }: FetchWithTimeoutOptions = {}) => {
const requestOptions = {
...options,
} as RequestInit;

// Only set agent if proxy is configured
const proxyAgent = getProxyAgent();
if (proxyAgent) {
// @ts-expect-error Node.js fetch has different type for agent
requestOptions.dispatcher = proxyAgent;
}

if (!timeout) {
return fetch(url, requestOptions);
return fetch(url, {
...options,
dispatcher: getProxyAgent(),
} as FetchWithTimeoutOptions);
}

const controller = new globalThis.AbortController();
Expand All @@ -28,9 +23,10 @@ export default async (url: string, { timeout, ...options }: FetchWithTimeoutOpti
}, timeout);

const res = await fetch(url, {
...requestOptions,
signal: controller.signal,
});
...options,
dispatcher: getProxyAgent(),
} as FetchWithTimeoutOptions);

clearTimeout(timeoutId);
return res;
Expand Down

0 comments on commit 6fb553c

Please sign in to comment.