Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {
describe, test, expect, vi, beforeEach
} from 'vitest';
import {DateTime} from "luxon";
import {mockTimeseries} from '../../mocks/tinybird-contributors-leaderboard-response.mock';
import type {ContributorsLeaderboardResponse} from "~~/server/data/tinybird/contributors-leaderboard-source";

const mockFetchFromTinybird = vi.fn();

describe('Contributors Leaderboard Data Source', () => {
beforeEach(() => {
mockFetchFromTinybird.mockClear();

// Here be dragons! vi.doMock is not hoisted, and thus it is executed after the original import statement.
// This means that the import for tinybird.ts inside active-contributors-data-source.ts would still be used,
// and thus not mocked. This means we need to import the module again after the mock is set, whenever we want to
// use it.
vi.doMock(import("./tinybird"), () => ({
fetchFromTinybird: mockFetchFromTinybird,
}));
})

test('should fetch contributors leaderboard data with correct parameters', async () => {
// We have to import this here again because vi.doMock is not hoisted. See the explanation in beforeEach().
const {fetchContributorsLeaderboard} = await import("~~/server/data/tinybird/contributors-leaderboard-source");

mockFetchFromTinybird.mockResolvedValue(mockTimeseries);

const startDate = DateTime.utc(2024, 3, 20);
const endDate = DateTime.utc(2025, 3, 20);

const filter = {
project: 'the-linux-kernel-organization',
startDate,
endDate
};

const result = await fetchContributorsLeaderboard(filter);

expect(mockFetchFromTinybird).toHaveBeenCalledWith(
'/v0/pipes/contributors_leaderboard.json',
filter
);

const expectedResult: ContributorsLeaderboardResponse = {
meta: {
offset: 0,
limit: 10,
total: 10
},
data: mockTimeseries.data.map((item) => ({
avatar: item.avatar,
name: item.displayName,
contributions: item.contributionCount,
contributionValue: 0,
contributionPercentage: item.contributionPercentage
}))
};

expect(result).toEqual(expectedResult);
});

// TODO: Add checks for invalid dates, invalid data, sql injections, and other edge cases.
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
https://api.us-west-2.aws.tinybird.co/v0/pipes/contributors_leaderboard.json?startDate=2024-03-20 00:00:00&endDate=2025-03-20 00:00:00&project=the-linux-kernel-organization */
export const mockTimeseries = {
meta: [
{
name: "id",
type: "String"
},
{
name: "avatar",
type: "String"
},
{
name: "displayName",
type: "String"
},
{
name: "contributionCount",
type: "UInt64"
},
{
name: "contributionPercentage",
type: "Float64"
}
],
data: [
{
id: "b17e3beb-09e2-447f-a12d-00c716dd00db",
avatar: "https://avatars.githubusercontent.com/u/6732289?v=4",
displayName: "Jakub Kicinski",
contributionCount: 9234,
contributionPercentage: 3
},
{
id: "a3bbff07-7cec-4885-b688-d8b377a580c6",
avatar: "https://avatars.githubusercontent.com/u/118310711?v=4",
displayName: "Alex Deucher",
contributionCount: 6625,
contributionPercentage: 2
},
{
id: "7f65feb0-589b-11ee-bf26-d732180a3416",
avatar: "https://avatars.githubusercontent.com/u/2903?u=bfd4ef3385bcf27ae093f2bacf99b78dc917018c&v=4",
displayName: "Mark Brown",
contributionCount: 5584,
contributionPercentage: 2
},
{
id: "eb7fc260-77d6-11ef-b115-a7c57b0467ff",
avatar: "https://secure.gravatar.com/avatar/cbd18395260b6be2575187286a262f9a.jpg?s=72&d=https%3A%2F%2Fa.slack-edge.com%2Fdf10d%2Fimg%2Favatars%2Fava_0004-72.png",
displayName: "Greg Kroah-Hartman",
contributionCount: 5511,
contributionPercentage: 2
},
{
id: "d47386a6-3890-42c3-8c26-6872a8e38c2c",
avatar: "https://avatars.githubusercontent.com/u/1024025?v=4",
displayName: "Linus Torvalds",
contributionCount: 5446,
contributionPercentage: 2
},
{
id: "6ac1a927-efc8-4b42-9cd4-683f7ec2e4c8",
avatar: "https://s.gravatar.com/avatar/4f4a1afa06be96dc0a4b75b4ae8f3492?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Flb.png",
displayName: "Len Brown",
contributionCount: 5364,
contributionPercentage: 2
},
{
id: "7b2a3b66-d42a-442c-99c0-5cdc4f0c5a56",
avatar: "https://media.licdn.com/dms/image/v2/C4D03AQFofsfYWL-Gtg/profile-displayphoto-shrink_400_400/profile-displayphoto-shrink_400_400/0/1627292194991?e=1736380800&v=beta&t=gujAz-1ypJh9OSu3ZtzS23n4APf4IJzzwBVwLLgcty4",
displayName: "Krzysztof Kozlowski",
contributionCount: 3766,
contributionPercentage: 1
},
{
id: "03a88390-f8f7-11ee-9733-23f194e8fceb",
avatar: "https://avatars.githubusercontent.com/u/1993710?v=4",
displayName: "Kent Overstreet",
contributionCount: 3344,
contributionPercentage: 1
},
{
id: "f215f342-dd22-4f08-b70a-f2cb589b9f41",
avatar: "https://avatars.githubusercontent.com/u/912574?v=4",
displayName: "Bjorn Andersson",
contributionCount: 3125,
contributionPercentage: 1
},
{
id: "f2451180-9920-11ee-8724-bf3a96c0a5bd",
avatar: "https://media.licdn.com/dms/image/v2/C5603AQFymNPkSZIMFg/profile-displayphoto-shrink_400_400/profile-displayphoto-shrink_400_400/0/1516505180451?e=1736380800&v=beta&t=pLElFMol59TjHGiBzoUCOjiVM5l8kqWV4RGZ07BJ2ys",
displayName: "Jonathan Cameron",
contributionCount: 2874,
contributionPercentage: 1
}
],
rows: 10,
rows_before_limit_at_least: 6748,
statistics: {
elapsed: 1.813963029,
rows_read: 1901972,
bytes_read: 153093428
}
};
Comment on lines 3 to 105
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Use anonymized mock data for testing

The mock data includes what appears to be real GitHub usernames, avatar URLs, and LinkedIn profile URLs with expiration dates (lines 71, 92). Consider:

  1. Using fictional or anonymized data for testing
  2. Replacing LinkedIn URLs that have expiration dates with static placeholder URLs
  3. Using generic avatar URLs that won't expire

This ensures tests remain reliable over time and prevents any potential privacy concerns.


🌐 Web query:

Do GitHub avatar URLs like "https://avatars.githubusercontent.com/u/6732289?v=4" expire or remain stable over time?

💡 Result:

GitHub avatar URLs that include the user’s numeric ID (e.g., https://avatars.githubusercontent.com/u/6732289?v=4) remain stable over time and do not expire, even if the username is changed. This stability is due to the use of GitHub’s internal user ID, which is permanent and unaffected by username updates[1][5].

Key Details:

  • User ID-Based URLs: GitHub assigns a unique numeric ID to each user account, which persists regardless of username changes. For example, the URL structure https://avatars.githubusercontent.com/u/{user-id} uses this ID, ensuring the avatar link remains valid[1][5].
  • Manual Updates Required: While the URL itself is stable, updating the avatar image requires manually uploading a new picture. The URL will then reflect the latest version when accessed[10].
  • Caching Considerations: GitHub avatar URLs may experience temporary caching issues (e.g., outdated images displayed), but these are resolved automatically as caches refresh[7].

For long-term reliability, use the ID-based URL format. To find your GitHub user ID:

  1. Visit your profile settings on GitHub.
  2. Check the "GitHub ID" field under the "Profile" section[1].

This ensures links to your avatar remain consistent even if your username changes.

Citations:


Action Required: Anonymize Mock Data in frontend/server/mocks/tinybird-contributors-leaderboard-response.mock.ts

  • Replace real personal data (e.g., actual GitHub usernames and display names) with fictional or anonymized values to avoid using live user information in tests.
  • Although GitHub avatar URLs (e.g., "https://avatars.githubusercontent.com/u/6732289?v=4") are stable over time, consider using generic placeholder URLs to prevent any direct association with real users.
  • Replace URLs that include expiration parameters (such as the LinkedIn/profile image URLs seen on lines 71 and 92) with static placeholders to ensure long-term reliability of tests.