Skip to content

Commit fe54126

Browse files
authored
merge: test: Enhance OG route with error resilience coverage (#8159)
## Description Fixes #6773 ## Pillar - [ ] 🎨 Pillar 1 — New Theme Design - [ ] 📐 Pillar 2 — Geometric SVG Improvement - [ ] 🕐 Pillar 3 — Timezone Logic Optimization - [x] 🛠️ Other (Bug fix, refactoring, docs) ## Visual Preview N/A ## Checklist before requesting a review: - [x] I have read the `CONTRIBUTING.md` file. - [ ] I have tested these changes locally (`localhost:3000/api/streak?user=YOUR_USERNAME`). - [x] I have run `npm run format` and `npm run lint` locally and resolved all errors (CI will fail otherwise). - [x] My commits follow the Conventional Commits format (e.g., `feat(themes): ...`, `fix(calculate): ...`). - [ ] I have updated `README.md` if I added a new theme or URL parameter. - [ ] I have started the repo. - [x] I have made sure that i have only one commit to merge in this PR. - [ ] The SVG output matches the CommitPulse "premium quality" aesthetic standard (no raw elements, smooth animations, correct fonts). - [x] (Recommended) I joined the CommitPulse Discord community for contributor discussions, mentorship, and faster PR support.
2 parents 755d9e9 + ef49d98 commit fe54126

1 file changed

Lines changed: 113 additions & 0 deletions

File tree

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest';
2+
import { NextRequest } from 'next/server';
3+
import { GET } from './route';
4+
5+
vi.mock('@/lib/github', () => ({
6+
fetchGitHubContributions: vi.fn(),
7+
}));
8+
9+
vi.mock('@/lib/calculate', () => ({
10+
calculateStreak: vi.fn(),
11+
}));
12+
13+
vi.mock('@/lib/logger', () => ({
14+
logger: {
15+
error: vi.fn(),
16+
},
17+
}));
18+
19+
import { fetchGitHubContributions } from '@/lib/github';
20+
import { calculateStreak } from '@/lib/calculate';
21+
import { logger } from '@/lib/logger';
22+
23+
describe('OG Route Error Resilience', () => {
24+
beforeEach(() => {
25+
vi.clearAllMocks();
26+
27+
vi.mocked(fetchGitHubContributions).mockResolvedValue({} as never);
28+
29+
vi.mocked(calculateStreak).mockReturnValue({
30+
totalContributions: 100,
31+
longestStreak: 10,
32+
currentStreak: 5,
33+
todayDate: '2026-07-18',
34+
});
35+
});
36+
37+
it('returns a valid image response when GitHub contribution fetching throws an exception', async () => {
38+
vi.mocked(fetchGitHubContributions).mockRejectedValueOnce(
39+
new Error('Unexpected GitHub service failure')
40+
);
41+
42+
const request = new NextRequest('http://localhost:3000/api/og?user=testuser');
43+
44+
const response = await GET(request);
45+
46+
expect(response).toBeDefined();
47+
expect(response.status).toBe(200);
48+
});
49+
50+
it('logs the exception when contribution fetching fails', async () => {
51+
const error = new Error('Database connectivity failure');
52+
53+
vi.mocked(fetchGitHubContributions).mockRejectedValueOnce(error);
54+
55+
const request = new NextRequest('http://localhost:3000/api/og?user=testuser');
56+
57+
await GET(request);
58+
59+
expect(logger.error).toHaveBeenCalledWith(
60+
'Stats fetch failed',
61+
expect.objectContaining({
62+
source: 'OG',
63+
error,
64+
})
65+
);
66+
});
67+
68+
it('uses fallback values when contribution fetching fails instead of crashing', async () => {
69+
vi.mocked(fetchGitHubContributions).mockRejectedValueOnce(new Error('Service unavailable'));
70+
71+
const request = new NextRequest('http://localhost:3000/api/og?user=testuser');
72+
73+
const response = await GET(request);
74+
75+
expect(response.status).toBe(200);
76+
expect(calculateStreak).not.toHaveBeenCalled();
77+
});
78+
79+
it('recovers from malformed contribution data when streak calculation throws', async () => {
80+
vi.mocked(fetchGitHubContributions).mockResolvedValueOnce({
81+
calendar: {},
82+
} as never);
83+
84+
vi.mocked(calculateStreak).mockImplementationOnce(() => {
85+
throw new Error('Invalid contribution data');
86+
});
87+
88+
const request = new NextRequest('http://localhost:3000/api/og?user=testuser');
89+
90+
const response = await GET(request);
91+
92+
expect(response).toBeDefined();
93+
expect(response.status).toBe(200);
94+
expect(logger.error).toHaveBeenCalled();
95+
});
96+
97+
it('continues serving subsequent requests after an earlier request fails', async () => {
98+
vi.mocked(fetchGitHubContributions)
99+
.mockRejectedValueOnce(new Error('Temporary GitHub failure'))
100+
.mockResolvedValueOnce({} as never);
101+
102+
const failedRequest = new NextRequest('http://localhost:3000/api/og?user=failinguser');
103+
104+
const recoveredRequest = new NextRequest('http://localhost:3000/api/og?user=recovereduser');
105+
106+
const firstResponse = await GET(failedRequest);
107+
const secondResponse = await GET(recoveredRequest);
108+
109+
expect(firstResponse.status).toBe(200);
110+
expect(secondResponse.status).toBe(200);
111+
expect(fetchGitHubContributions).toHaveBeenCalledTimes(2);
112+
});
113+
});

0 commit comments

Comments
 (0)