🧪 Test fetchRepoStats in github.ts#3122
Conversation
Co-authored-by: Hmbown <101357273+Hmbown@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive test suite for the fetchRepoStats function in web/lib/github.test.ts, covering successful data retrieval, error handling, issue calculations, and contributor count extraction. The review feedback highlights that the mock implementations hardcode the repository name (Hmbown/CodeWhale), which could lead to test failures if the repository is dynamically configured via environment variables. It is recommended to use relative path segment matching instead to make the tests more robust.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| fetchMock.mockImplementation(async (url: string) => { | ||
| if (url.includes("/repos/Hmbown/CodeWhale/contributors")) { | ||
| return { | ||
| ok: true, | ||
| headers: new Headers({ | ||
| link: '<https://api.github.com/repositories/123/contributors?per_page=1&anon=true&page=200>; rel="last"', | ||
| }), | ||
| json: async () => [], | ||
| }; | ||
| } | ||
| if (url.includes("/repos/Hmbown/CodeWhale/releases/latest")) { | ||
| return { | ||
| ok: true, | ||
| json: async () => ({ | ||
| tag_name: "v1.0.0", | ||
| published_at: "2026-05-01T00:00:00Z", | ||
| html_url: "https://github.com/Hmbown/CodeWhale/releases/tag/v1.0.0", | ||
| }), | ||
| }; | ||
| } | ||
| if (url.includes("/search/issues")) { | ||
| return { | ||
| ok: true, | ||
| json: async () => ({ | ||
| total_count: 5, | ||
| }), | ||
| }; | ||
| } | ||
| if (url.includes("/repos/Hmbown/CodeWhale")) { | ||
| return { | ||
| ok: true, | ||
| json: async () => ({ | ||
| stargazers_count: 100, | ||
| forks_count: 20, | ||
| open_issues_count: 15, | ||
| }), | ||
| }; | ||
| } | ||
| return { ok: false }; | ||
| }); |
There was a problem hiding this comment.
The mock URL matching currently hardcodes the repository name Hmbown/CodeWhale. Since REPO is initialized at the module level in github.ts using process.env.GITHUB_REPO, any environment configuration that overrides this variable will cause the mock matching to fail and the tests to break.
We can make the mock matching completely robust and independent of the repository name by matching relative path segments (e.g., /contributors, /releases/latest, and /repos/) in order.
fetchMock.mockImplementation(async (url: string) => {
if (url.includes("/contributors")) {
return {
ok: true,
headers: new Headers({
link: '<https://api.github.com/repositories/123/contributors?per_page=1&anon=true&page=200>; rel="last"',
}),
json: async () => [],
};
}
if (url.includes("/releases/latest")) {
return {
ok: true,
json: async () => ({
tag_name: "v1.0.0",
published_at: "2026-05-01T00:00:00Z",
html_url: "https://github.com/Hmbown/CodeWhale/releases/tag/v1.0.0",
}),
};
}
if (url.includes("/search/issues")) {
return {
ok: true,
json: async () => ({
total_count: 5,
}),
};
}
if (url.includes("/repos/")) {
return {
ok: true,
json: async () => ({
stargazers_count: 100,
forks_count: 20,
open_issues_count: 15,
}),
};
}
return { ok: false };
});| fetchMock.mockImplementation(async (url: string) => { | ||
| if (url.includes("/search/issues")) { | ||
| return { | ||
| ok: true, | ||
| json: async () => ({ total_count: 10 }), // 10 PRs | ||
| }; | ||
| } | ||
| if (url.includes("/repos/Hmbown/CodeWhale")) { | ||
| // Excludes contributors and releases strings matching exactly | ||
| if (url.endsWith("/Hmbown/CodeWhale")) { | ||
| return { | ||
| ok: true, | ||
| json: async () => ({ open_issues_count: 5 }), // 5 total issues | ||
| }; | ||
| } | ||
| } | ||
| return { ok: false, headers: new Headers() }; | ||
| }); |
There was a problem hiding this comment.
Similarly, we should avoid hardcoding the repository name in this mock implementation to prevent test failures when process.env.GITHUB_REPO is configured differently.
fetchMock.mockImplementation(async (url: string) => {
if (url.includes("/search/issues")) {
return {
ok: true,
json: async () => ({ total_count: 10 }), // 10 PRs
};
}
if (url.includes("/repos/") && !url.includes("/contributors") && !url.includes("/releases")) {
return {
ok: true,
json: async () => ({ open_issues_count: 5 }), // 5 total issues
};
}
return { ok: false, headers: new Headers() };
});
🎯 What: The testing gap addressed was that
fetchRepoStatsinweb/lib/github.tshad no unit tests, relying on global fetch which wasn't mocked.📊 Coverage: Now tests cover successful API interactions, handling of failed API responses (returning fallbacks), behavior without an authorization token, preventing the calculation of open issues from falling below zero, and extracting contributor count from array length when a link header is missing.
✨ Result: Test coverage significantly improved for the GitHub data aggregation function using
vitestto effectively mock globalfetchand timers, allowing for confident and reliable refactoring of the stats compilation logic.PR created automatically by Jules for task 15633264489521112016 started by @Hmbown