Skip to content

Commit 98b6f5a

Browse files
authored
Merge pull request #17 from arnabnandy7/hotfix/testCoverage
Hotfix/test coverage
2 parents 89583ba + c0a998a commit 98b6f5a

6 files changed

Lines changed: 444 additions & 6 deletions

File tree

src/lib/compare.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ export type ComparedEnvironments = {
77
prod: boolean;
88
};
99

10+
const DEFAULT_COMPARED_ENVIRONMENTS: ComparedEnvironments = {
11+
dev: true,
12+
qa: true,
13+
prod: true,
14+
};
15+
1016
export function compareJson(
1117
devJson: unknown,
1218
qaJson: unknown,
1319
prodJson: unknown,
1420
ignoreFields: string[] = [],
15-
comparedEnvironments: ComparedEnvironments = {
16-
dev: true,
17-
qa: true,
18-
prod: true,
19-
},
21+
comparedEnvironments: ComparedEnvironments = DEFAULT_COMPARED_ENVIRONMENTS,
2022
): DiffEntry[] {
2123
const environments = [
2224
{
@@ -42,7 +44,7 @@ export function compareJson(
4244

4345
const paths = Array.from(
4446
new Set(environments.flatMap((environment) => Object.keys(environment.values))),
45-
).sort();
47+
).sort((left, right) => left.localeCompare(right));
4648

4749
return paths.reduce<DiffEntry[]>((diffs, path) => {
4850
if (isIgnoredPath(path, ignoreKeys)) {

test/app/api/proxy/route.test.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,85 @@ describe('GET /api/proxy', () => {
172172
});
173173
});
174174

175+
it('returns 400 for invalid structured proxy request JSON', async () => {
176+
const request = new Request('http://localhost/api/proxy', {
177+
method: 'POST',
178+
headers: { 'content-type': 'application/json' },
179+
body: '{not-json',
180+
});
181+
182+
const response = await POST(request);
183+
184+
expect(response.status).toBe(400);
185+
expect(await response.json()).toEqual({ error: 'Invalid proxy request body' });
186+
});
187+
188+
it('returns 400 for malformed structured proxy requests', async () => {
189+
const request = new Request('http://localhost/api/proxy', {
190+
method: 'POST',
191+
headers: { 'content-type': 'application/json' },
192+
body: JSON.stringify({
193+
url: 'https://api.example.com/items',
194+
headers: {
195+
accept: 42,
196+
},
197+
}),
198+
});
199+
200+
const response = await POST(request);
201+
202+
expect(response.status).toBe(400);
203+
expect(await response.json()).toEqual({ error: 'Invalid proxy request body' });
204+
});
205+
206+
it('rejects oversized structured request bodies before fetching', async () => {
207+
const request = new Request('http://localhost/api/proxy', {
208+
method: 'POST',
209+
headers: { 'content-type': 'application/json' },
210+
body: JSON.stringify({
211+
url: 'https://api.example.com/items',
212+
method: 'POST',
213+
body: 'x'.repeat(1024 * 1024 + 1),
214+
}),
215+
});
216+
217+
const response = await POST(request);
218+
219+
expect(response.status).toBe(400);
220+
expect(await response.json()).toEqual({ error: 'Request body is too large' });
221+
expect(mockFetch).not.toHaveBeenCalled();
222+
});
223+
224+
it('returns 502 with response body when the remote server fails', async () => {
225+
mockFetch.mockResolvedValueOnce(
226+
new Response('{"error":"upstream"}', {
227+
status: 503,
228+
statusText: 'Service Unavailable',
229+
}),
230+
);
231+
const request = new Request('http://localhost/api/proxy?url=https://api.example.com/data');
232+
233+
const response = await GET(request);
234+
235+
expect(response.status).toBe(502);
236+
expect(await response.json()).toEqual({
237+
error: 'Remote request failed: 503 Service Unavailable',
238+
body: '{"error":"upstream"}',
239+
});
240+
});
241+
242+
it('returns 502 when a redirect is missing its location header', async () => {
243+
mockFetch.mockResolvedValueOnce(new Response(null, { status: 302 }));
244+
const request = new Request('http://localhost/api/proxy?url=https://api.example.com/data');
245+
246+
const response = await GET(request);
247+
248+
expect(response.status).toBe(502);
249+
expect(await response.json()).toEqual({
250+
error: 'Unable to fetch URL: Remote server returned a redirect without a location',
251+
});
252+
});
253+
175254
it('returns the underlying network error code for failed requests', async () => {
176255
mockFetch.mockRejectedValueOnce(
177256
new Error('fetch failed', {

test/app/layout.test.tsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import '@testing-library/jest-dom/vitest';
2+
import { cleanup, render, screen } from '@testing-library/react';
3+
import { afterEach, describe, expect, it, vi } from 'vitest';
4+
5+
const { themeProvider } = vi.hoisted(() => ({
6+
themeProvider: vi.fn(
7+
({ children }: { children: React.ReactNode }) => (
8+
<div data-testid="theme-provider">{children}</div>
9+
),
10+
),
11+
}));
12+
13+
vi.mock('@/src/components/theme-provider', () => ({
14+
ThemeProvider: themeProvider,
15+
}));
16+
17+
import RootLayout, { metadata } from '@/src/app/layout';
18+
19+
afterEach(() => {
20+
cleanup();
21+
vi.clearAllMocks();
22+
});
23+
24+
describe('RootLayout', () => {
25+
it('exports page metadata', () => {
26+
expect(metadata).toEqual({
27+
title: 'API Response Comparator',
28+
description:
29+
'Compare JSON API responses across Dev, QA, and Prod using JSON, URLs, or cURL.',
30+
});
31+
});
32+
33+
it('renders children inside the theme provider', () => {
34+
render(
35+
<RootLayout>
36+
<main>Comparator app</main>
37+
</RootLayout>,
38+
);
39+
40+
expect(screen.getByText('Comparator app')).toBeInTheDocument();
41+
expect(screen.getByTestId('theme-provider')).toBeInTheDocument();
42+
expect(themeProvider).toHaveBeenCalledWith(
43+
expect.objectContaining({
44+
attribute: 'class',
45+
defaultTheme: 'system',
46+
enableSystem: true,
47+
}),
48+
undefined,
49+
);
50+
});
51+
});

0 commit comments

Comments
 (0)