Skip to content

Commit 4e27e7f

Browse files
test: add cacheKey unit tests (JhaSourav07#566)
## Description - exported cacheKey function - added unit tests for cacheKey - tested with and without year - tested lowercase conversion - tested all three kind values - Fixes JhaSourav07#364 ## 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. - [x] 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). - [ ] (Recommended) I joined the CommitPulse Discord community for contributor discussions, mentorship, and faster PR support.
1 parent 652af5c commit 4e27e7f

2 files changed

Lines changed: 15 additions & 59 deletions

File tree

lib/github.test.ts

Lines changed: 14 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
clearGitHubApiCacheForTests,
1010
GITHUB_CACHE_TTL_MS,
1111
validateGitHubUsername,
12-
fetchWithRetry,
12+
cacheKey,
1313
} from './github';
1414
import type { ContributionCalendar } from '../types';
1515

@@ -536,73 +536,29 @@ describe('validateGitHubUsername', () => {
536536
expect(validateGitHubUsername('in--valid')).toBe(false);
537537
});
538538
});
539-
describe('fetchWithRetry', () => {
540-
beforeEach(() => {
541-
vi.spyOn(global, 'fetch');
542-
});
543539

544-
afterEach(() => {
545-
vi.restoreAllMocks();
540+
describe('cacheKey', () => {
541+
it('creates key without year', () => {
542+
expect(cacheKey('profile', 'DeepSikha')).toBe('profile:deepsikha');
546543
});
547544

548-
it('resolves immediately on first attempt when fetch returns 200', async () => {
549-
vi.mocked(fetch).mockResolvedValueOnce(mockResponse({ ok: true }, 200));
550-
551-
const res = await fetchWithRetry('https://api.github.com/rest', {});
552-
553-
expect(res.status).toBe(200);
554-
expect(fetch).toHaveBeenCalledTimes(1);
545+
it('creates key with year', () => {
546+
expect(cacheKey('contributions', 'DeepSikha', '2025')).toBe('contributions:deepsikha:2025');
555547
});
556548

557-
it('retries once on 429 (rate limit) and resolves on second attempt', async () => {
558-
vi.useFakeTimers();
559-
vi.mocked(fetch)
560-
.mockResolvedValueOnce(mockResponse({}, 429))
561-
.mockResolvedValueOnce(mockResponse({}, 200));
562-
563-
const promise = fetchWithRetry('https://api.github.com/rest', {});
564-
await vi.runAllTimersAsync();
565-
const res = await promise;
566-
567-
expect(res.status).toBe(200);
568-
expect(fetch).toHaveBeenCalledTimes(2);
569-
vi.useRealTimers();
549+
it('converts username to lowercase', () => {
550+
expect(cacheKey('repos', 'DeEpSiKhA')).toBe('repos:deepsikha');
570551
});
571552

572-
it('retries once on 500 (server error) and resolves on second attempt', async () => {
573-
vi.useFakeTimers();
574-
vi.mocked(fetch)
575-
.mockResolvedValueOnce(mockResponse({}, 500))
576-
.mockResolvedValueOnce(mockResponse({}, 200));
577-
578-
const promise = fetchWithRetry('https://api.github.com/rest', {});
579-
await vi.runAllTimersAsync();
580-
const res = await promise;
581-
582-
expect(res.status).toBe(200);
583-
expect(fetch).toHaveBeenCalledTimes(2);
584-
vi.useRealTimers();
553+
it('supports profile kind', () => {
554+
expect(cacheKey('profile', 'testuser')).toContain('profile');
585555
});
586556

587-
it('returns the bad response after exhausting all retries on 429', async () => {
588-
vi.useFakeTimers();
589-
vi.mocked(fetch).mockResolvedValue(mockResponse({}, 429));
590-
591-
const promise = fetchWithRetry('https://api.github.com/rest', {});
592-
await vi.runAllTimersAsync();
593-
const res = await promise;
594-
595-
expect(res.status).toBe(429);
596-
vi.useRealTimers();
557+
it('supports repos kind', () => {
558+
expect(cacheKey('repos', 'testuser')).toContain('repos');
597559
});
598560

599-
it('propagates network error after exhausting all retries', async () => {
600-
// Use real timers so retry delays actually fire and resolve cleanly
601-
vi.useRealTimers();
602-
vi.mocked(fetch).mockRejectedValue(new Error('network failure'));
603-
604-
await expect(fetchWithRetry('https://api.github.com/rest', {})).rejects.toThrow(
605-
'network failure'
606-
);
561+
it('supports contributions kind', () => {
562+
expect(cacheKey('contributions', 'testuser')).toContain('contributions');
607563
});
608564
});

lib/github.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ const contributionsCache = new TTLCache<ContributionCalendar>(MAX_CONTRIBUTIONS_
129129
const profileCache = new TTLCache<GitHubUserProfile>(MAX_PROFILE_CACHE_SIZE);
130130
const reposCache = new TTLCache<GitHubRepo[]>(MAX_REPOS_CACHE_SIZE);
131131

132-
function cacheKey(
132+
export function cacheKey(
133133
kind: 'contributions' | 'profile' | 'repos',
134134
username: string,
135135
year?: string

0 commit comments

Comments
 (0)