-
Notifications
You must be signed in to change notification settings - Fork 7
Open
Description
Description
The src/utils/github.ts file (317 lines) handles all GitHub API interactions but lacks integration tests. This is critical functionality that needs testing to ensure API calls work correctly, error handling is robust, and rate limiting is respected.
Current State
- ❌ No integration tests for
GitHubServiceclass - ❌ No tests for API error scenarios
- ❌ No tests for rate limiting behavior
- ❌ No tests for organization-level community health file detection
- ✅ GitHub API is properly abstracted in
GitHubService - ✅ Error handling exists but needs testing
Acceptance Criteria
Test Coverage Goals
- Integration tests for all public methods in
GitHubService - Error handling tests for common API failure scenarios
- Rate limiting tests to ensure proper handling
- Mock API responses for consistent testing
- Real API tests (optional, with test repository)
Methods to Test
Core Methods
-
getRepositoryInfo(owner, repo)- Main repository data fetching -
createRepository(name, description, isPrivate)- Repository creation - Private helper methods (if exposed for testing)
Organization Community Health Files
- Detection of org-level CONTRIBUTING.md
- Detection of org-level CODE_OF_CONDUCT.md
- Detection of org-level issue templates
- Detection of org-level PR templates
- Fallback behavior when org files don't exist
Error Scenarios
- Repository not found (404)
- Rate limit exceeded (403)
- Network timeouts
- Invalid tokens
- Private repository access denied
Implementation Suggestions
Test File Structure
// src/__tests__/utils/github.test.ts
import { GitHubService } from '../../utils/github';
import { RepositoryInfo } from '../../types';
// Mock the @octokit/rest module
jest.mock('@octokit/rest');
describe('GitHubService', () => {
let githubService: GitHubService;
let mockOctokit: jest.Mocked<any>;
beforeEach(() => {
mockOctokit = {
rest: {
repos: {
get: jest.fn(),
create: jest.fn(),
getContent: jest.fn()
},
issues: {
list: jest.fn()
},
search: {
issuesAndPullRequests: jest.fn()
}
}
};
githubService = new GitHubService('fake-token');
(githubService as any).octokit = mockOctokit;
});
describe('getRepositoryInfo', () => {
it('successfully fetches repository information', async () => {
// Mock successful API responses
mockOctokit.rest.repos.get.mockResolvedValue({
data: {
name: 'test-repo',
description: 'Test repository',
topics: ['test', 'typescript'],
archived: false,
default_branch: 'main'
}
});
mockOctokit.rest.repos.getContent.mockImplementation((params) => {
if (params.path === 'README.md') {
return Promise.resolve({ data: { type: 'file' } });
}
return Promise.reject({ status: 404 });
});
const result = await githubService.getRepositoryInfo('test-owner', 'test-repo');
expect(result).toMatchObject({
owner: 'test-owner',
repo: 'test-repo',
name: 'test-repo',
description: 'Test repository',
topics: ['test', 'typescript'],
hasReadme: true,
isArchived: false
});
});
it('handles repository not found error', async () => {
mockOctokit.rest.repos.get.mockRejectedValue({
status: 404,
message: 'Not Found'
});
await expect(
githubService.getRepositoryInfo('nonexistent', 'repo')
).rejects.toThrow('Repository not found');
});
it('handles rate limiting', async () => {
mockOctokit.rest.repos.get.mockRejectedValue({
status: 403,
message: 'API rate limit exceeded'
});
await expect(
githubService.getRepositoryInfo('test', 'repo')
).rejects.toThrow('rate limit');
});
});
describe('Organization Community Health Files', () => {
it('detects CONTRIBUTING.md in organization .github repo', async () => {
// Mock repo without CONTRIBUTING.md
mockOctokit.rest.repos.getContent
.mockRejectedValueOnce({ status: 404 }) // repo level
.mockResolvedValueOnce({ data: { type: 'file' } }); // org level
const result = await githubService.getRepositoryInfo('test-org', 'test-repo');
expect(result.hasContributing).toBe(true);
});
});
});Mock Data Helpers
// src/__tests__/helpers/githubMocks.ts
export const mockRepoResponse = {
data: {
name: 'test-repo',
description: 'A test repository',
topics: ['typescript', 'cli'],
archived: false,
default_branch: 'main',
license: { key: 'mit' }
}
};
export const mockIssuesResponse = {
data: [
{
labels: [{ name: 'good first issue' }],
state: 'open'
},
{
labels: [{ name: 'help wanted' }],
state: 'open'
}
]
};
export const mockFileNotFound = {
status: 404,
message: 'Not Found'
};
export const mockRateLimit = {
status: 403,
message: 'API rate limit exceeded'
};Real API Tests (Optional)
// src/__tests__/integration/github-real.test.ts
// These tests run against real GitHub API (skip in CI)
describe('GitHub API Integration (Real)', () => {
const githubService = new GitHubService(process.env.GITHUB_TEST_TOKEN);
// Only run if token is available
const skipIfNoToken = process.env.GITHUB_TEST_TOKEN ? describe : describe.skip;
skipIfNoToken('Real API calls', () => {
it('evaluates a known good repository', async () => {
const result = await githubService.getRepositoryInfo('facebook', 'react');
expect(result.hasReadme).toBe(true);
expect(result.hasLicense).toBe(true);
});
});
});Files to Create
src/__tests__/utils/github.test.ts(main test file)src/__tests__/helpers/githubMocks.ts(mock data)src/__tests__/integration/github-real.test.ts(optional real API tests)
Test Scenarios to Cover
Happy Path
- Repository with all community health files
- Repository using organization-level files
- Public repository with good first issues
- Repository creation with valid parameters
Error Handling
- 404 Not Found (repository doesn't exist)
- 403 Forbidden (private repo, rate limit)
- Network timeouts and connection errors
- Invalid token authentication
- Malformed API responses
Edge Cases
- Repository with no description
- Repository with excessive topics
- Archived repository
- Repository with mixed file presence
Benefits
- 🔍 Ensures API interactions work correctly
- 🛡️ Validates error handling and edge cases
- ⚡ Catches breaking changes in GitHub API
- 🎯 Tests critical organization-level file detection
- 🚀 Provides confidence for API-related changes
Testing Strategy
- Unit tests with mocks - Fast, reliable, run in CI
- Integration tests - Test actual API behavior (optional)
- Error simulation - Test failure scenarios
- Performance tests - Ensure rate limiting works
Resources
Dependencies
- Should be implemented after basic test infrastructure is set up
- Requires understanding of GitHub API responses
- May need test GitHub token for real API tests
Estimated Effort
Medium to Hard - Complex API interactions and error scenarios to test.
Great for contributors who want to ensure robust GitHub API integration! 🔗
Metadata
Metadata
Assignees
Labels
No labels