| from datetime import datetime |
| import json |
| import os |
| import sys |
| from unittest.mock import Mock, patch |
| import pytest |
|
|
| |
| sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
|
|
| from github_routes import github_bp |
| from github_service import GitHubService |
|
|
|
|
| class TestGitHubRoutes: |
| """Test suite for GitHub integration routes""" |
|
|
| def setup_method(self): |
| """Set up test fixtures before each test method""" |
| self.app = Mock() |
| self.app.testing = True |
| self.client = self.app.test_client() |
|
|
| |
| self.app.register_blueprint(github_bp) |
|
|
| |
| self.mock_service = Mock(spec=GitHubService) |
| self.mock_service.access_token = "test_token" |
|
|
| |
| import github_routes |
| github_routes.github_service = self.mock_service |
|
|
| def test_github_status_success(self): |
| """Test GitHub status endpoint with successful authentication""" |
| |
| self.mock_service.health_check.return_value = { |
| "status": "healthy", |
| "service": "github", |
| "user": "testuser", |
| "rate_limit_remaining": 4999, |
| "rate_limit_reset": 1700000000, |
| "timestamp": datetime.now().isoformat() |
| } |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/status', headers={'X-User-ID': 'test_user'}) |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['service'] == 'github' |
| assert data['user_id'] == 'test_user' |
| assert data['status'] == 'healthy' |
| assert data['authenticated'] is True |
| assert 'timestamp' in data |
|
|
| def test_github_status_unauthorized(self): |
| """Test GitHub status endpoint without authentication""" |
| |
| self.mock_service.access_token = None |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/status', headers={'X-User-ID': 'test_user'}) |
|
|
| assert response.status_code == 401 |
| data = json.loads(response.data) |
| assert data['ok'] is False |
| assert 'GitHub access token required' in data['error'] |
|
|
| def test_set_github_token_success(self): |
| """Test setting GitHub access token successfully""" |
| with self.app.test_client() as client: |
| response = client.post('/api/github/auth/set-token', |
| json={'access_token': 'new_test_token'}) |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['message'] == 'GitHub access token set successfully' |
|
|
| |
| self.mock_service.set_access_token.assert_called_once_with('new_test_token') |
|
|
| def test_set_github_token_missing_token(self): |
| """Test setting GitHub access token with missing token""" |
| with self.app.test_client() as client: |
| response = client.post('/api/github/auth/set-token', json={}) |
|
|
| assert response.status_code == 400 |
| data = json.loads(response.data) |
| assert data['ok'] is False |
| assert 'access_token is required' in data['error'] |
|
|
| def test_get_user_profile_success(self): |
| """Test getting user profile successfully""" |
| mock_profile = { |
| "id": 1, |
| "login": "testuser", |
| "name": "Test User", |
| "email": "test@example.com", |
| "avatar_url": "https://example.com/avatar.jpg" |
| } |
| self.mock_service.get_user_profile.return_value = mock_profile |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/user/profile') |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['profile'] == mock_profile |
| self.mock_service.get_user_profile.assert_called_once() |
|
|
| def test_get_user_profile_failure(self): |
| """Test getting user profile when API fails""" |
| self.mock_service.get_user_profile.return_value = None |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/user/profile') |
|
|
| assert response.status_code == 500 |
| data = json.loads(response.data) |
| assert data['ok'] is False |
| assert 'Failed to fetch user profile' in data['error'] |
|
|
| def test_get_organizations_success(self): |
| """Test getting organizations successfully""" |
| mock_orgs = [ |
| {"id": 1, "login": "org1", "name": "Organization 1"}, |
| {"id": 2, "login": "org2", "name": "Organization 2"} |
| ] |
| self.mock_service.get_organizations.return_value = mock_orgs |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/user/organizations') |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['organizations'] == mock_orgs |
| assert data['count'] == 2 |
|
|
| def test_get_repositories_user(self): |
| """Test getting user repositories""" |
| mock_repos = [ |
| {"id": 1, "name": "repo1", "private": False}, |
| {"id": 2, "name": "repo2", "private": True} |
| ] |
| self.mock_service.get_repositories.return_value = mock_repos |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/repositories') |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['repositories'] == mock_repos |
| assert data['count'] == 2 |
| self.mock_service.get_repositories.assert_called_once_with(org=None, visibility='all') |
|
|
| def test_get_repositories_org(self): |
| """Test getting organization repositories""" |
| mock_repos = [{"id": 1, "name": "org-repo", "private": False}] |
| self.mock_service.get_repositories.return_value = mock_repos |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/repositories?org=testorg&visibility=public') |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['org'] == 'testorg' |
| assert data['visibility'] == 'public' |
| self.mock_service.get_repositories.assert_called_once_with(org='testorg', visibility='public') |
|
|
| def test_get_repository_success(self): |
| """Test getting specific repository successfully""" |
| mock_repo = { |
| "id": 1, |
| "name": "test-repo", |
| "full_name": "owner/test-repo", |
| "private": False |
| } |
| self.mock_service.get_repository.return_value = mock_repo |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/repositories/owner/test-repo') |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['repository'] == mock_repo |
|
|
| def test_get_repository_not_found(self): |
| """Test getting non-existent repository""" |
| self.mock_service.get_repository.return_value = None |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/repositories/owner/nonexistent') |
|
|
| assert response.status_code == 404 |
| data = json.loads(response.data) |
| assert data['ok'] is False |
| assert 'Repository owner/nonexistent not found' in data['error'] |
|
|
| def test_create_repository_success(self): |
| """Test creating repository successfully""" |
| mock_repo = { |
| "id": 1, |
| "name": "new-repo", |
| "full_name": "user/new-repo", |
| "private": True |
| } |
| self.mock_service.create_repository.return_value = mock_repo |
|
|
| with self.app.test_client() as client: |
| response = client.post('/api/github/repositories', |
| json={ |
| 'name': 'new-repo', |
| 'description': 'Test repository', |
| 'private': True, |
| 'auto_init': True |
| }) |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['repository'] == mock_repo |
| self.mock_service.create_repository.assert_called_once_with( |
| name='new-repo', |
| description='Test repository', |
| private=True, |
| auto_init=True |
| ) |
|
|
| def test_create_repository_missing_name(self): |
| """Test creating repository without required name""" |
| with self.app.test_client() as client: |
| response = client.post('/api/github/repositories', |
| json={'description': 'Test repository'}) |
|
|
| assert response.status_code == 400 |
| data = json.loads(response.data) |
| assert data['ok'] is False |
| assert 'Repository name is required' in data['error'] |
|
|
| def test_get_issues_success(self): |
| """Test getting issues successfully""" |
| mock_issues = [ |
| {"id": 1, "title": "Issue 1", "state": "open"}, |
| {"id": 2, "title": "Issue 2", "state": "open"} |
| ] |
| self.mock_service.get_issues.return_value = mock_issues |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/repositories/owner/repo/issues?state=open&labels=bug,enhancement') |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['issues'] == mock_issues |
| assert data['state'] == 'open' |
| self.mock_service.get_issues.assert_called_once_with( |
| 'owner', 'repo', state='open', labels=['bug', 'enhancement'] |
| ) |
|
|
| def test_create_issue_success(self): |
| """Test creating issue successfully""" |
| mock_issue = { |
| "id": 1, |
| "title": "New Issue", |
| "body": "Issue description", |
| "state": "open" |
| } |
| self.mock_service.create_issue.return_value = mock_issue |
|
|
| with self.app.test_client() as client: |
| response = client.post('/api/github/repositories/owner/repo/issues', |
| json={ |
| 'title': 'New Issue', |
| 'body': 'Issue description', |
| 'labels': ['bug', 'enhancement'], |
| 'assignees': ['user1'] |
| }) |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['issue'] == mock_issue |
| self.mock_service.create_issue.assert_called_once_with( |
| owner='owner', |
| repo='repo', |
| title='New Issue', |
| body='Issue description', |
| labels=['bug', 'enhancement'], |
| assignees=['user1'] |
| ) |
|
|
| def test_create_issue_missing_title(self): |
| """Test creating issue without required title""" |
| with self.app.test_client() as client: |
| response = client.post('/api/github/repositories/owner/repo/issues', |
| json={'body': 'Issue description'}) |
|
|
| assert response.status_code == 400 |
| data = json.loads(response.data) |
| assert data['ok'] is False |
| assert 'Issue title is required' in data['error'] |
|
|
| def test_get_pull_requests_success(self): |
| """Test getting pull requests successfully""" |
| mock_prs = [ |
| {"id": 1, "title": "PR 1", "state": "open"}, |
| {"id": 2, "title": "PR 2", "state": "closed"} |
| ] |
| self.mock_service.get_pull_requests.return_value = mock_prs |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/repositories/owner/repo/pulls?state=open') |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['pull_requests'] == mock_prs |
| assert data['state'] == 'open' |
| self.mock_service.get_pull_requests.assert_called_once_with( |
| 'owner', 'repo', state='open' |
| ) |
|
|
| def test_create_pull_request_success(self): |
| """Test creating pull request successfully""" |
| mock_pr = { |
| "id": 1, |
| "title": "New PR", |
| "head": "feature-branch", |
| "base": "main", |
| "state": "open" |
| } |
| self.mock_service.create_pull_request.return_value = mock_pr |
|
|
| with self.app.test_client() as client: |
| response = client.post('/api/github/repositories/owner/repo/pulls', |
| json={ |
| 'title': 'New PR', |
| 'head': 'feature-branch', |
| 'base': 'main', |
| 'body': 'PR description' |
| }) |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['pull_request'] == mock_pr |
| self.mock_service.create_pull_request.assert_called_once_with( |
| owner='owner', |
| repo='repo', |
| title='New PR', |
| head='feature-branch', |
| base='main', |
| body='PR description' |
| ) |
|
|
| def test_create_pull_request_missing_fields(self): |
| """Test creating pull request with missing required fields""" |
| with self.app.test_client() as client: |
| response = client.post('/api/github/repositories/owner/repo/pulls', |
| json={'title': 'New PR'}) |
|
|
| assert response.status_code == 400 |
| data = json.loads(response.data) |
| assert data['ok'] is False |
| assert "Field 'head' is required" in data['error'] |
|
|
| def test_get_workflow_runs_success(self): |
| """Test getting workflow runs successfully""" |
| mock_workflows = [ |
| {"id": 1, "name": "CI", "status": "completed"}, |
| {"id": 2, "name": "Deploy", "status": "in_progress"} |
| ] |
| self.mock_service.get_workflow_runs.return_value = mock_workflows |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/repositories/owner/repo/workflows?branch=main') |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['workflow_runs'] == mock_workflows |
| assert data['branch'] == 'main' |
| self.mock_service.get_workflow_runs.assert_called_once_with( |
| 'owner', 'repo', branch='main' |
| ) |
|
|
| def test_search_code_success(self): |
| """Test searching code successfully""" |
| mock_results = [ |
| {"id": 1, "name": "file1.py", "path": "src/file1.py"}, |
| {"id": 2, "name": "file2.py", "path": "src/file2.py"} |
| ] |
| self.mock_service.search_code.return_value = mock_results |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/search/code?q=test+query&org=testorg') |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['query'] == 'test query' |
| assert data['org'] == 'testorg' |
| assert data['results'] == mock_results |
| self.mock_service.search_code.assert_called_once_with( |
| query='test query', org='testorg' |
| ) |
|
|
| def test_search_code_missing_query(self): |
| """Test searching code without query""" |
| with self.app.test_client() as client: |
| response = client.get('/api/github/search/code') |
|
|
| assert response.status_code == 400 |
| data = json.loads(response.data) |
| assert data['ok'] is False |
| assert "Search query parameter 'q' is required" in data['error'] |
|
|
| def test_search_issues_success(self): |
| """Test searching issues successfully""" |
| mock_results = [ |
| {"id": 1, "title": "Issue 1", "state": "open"}, |
| {"id": 2, "title": "Issue 2", "state": "closed"} |
| ] |
| self.mock_service.search_issues.return_value = mock_results |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/search/issues?q=bug&org=testorg') |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['query'] == 'bug' |
| assert data['org'] == 'testorg' |
| assert data['results'] == mock_results |
| self.mock_service.search_issues.assert_called_once_with( |
| query='bug', org='testorg' |
| ) |
|
|
| def test_get_rate_limit_success(self): |
| """Test getting rate limit successfully""" |
| mock_rate_limit = { |
| "resources": { |
| "core": {"limit": 5000, "remaining": 4999, "reset": 1700000000}, |
| "search": {"limit": 30, "remaining": 29, "reset": 1700000000} |
| } |
| } |
| self.mock_service.get_rate_limit.return_value = mock_rate_limit |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/rate-limit') |
|
|
| assert response.status_code == 200 |
| data = json.loads(response.data) |
| assert data['ok'] is True |
| assert data['rate_limit'] == mock_rate_limit |
|
|
| def test_get_rate_limit_failure(self): |
| """Test getting rate limit when API fails""" |
| self.mock_service.get_rate_limit.return_value = None |
|
|
| with self.app.test_client() as client: |
| response = client.get('/api/github/rate-limit') |
|
|
| assert response.status_code == 500 |
| data = json.loads(response.data) |
| assert data['ok'] is False |
| assert 'Failed to fetch rate limit' in data['error'] |
|
|
| def test_health_check_success(self): |
| """Test health check endpoint""" |
| mock_health = { |
| "status": "healthy", |
|
|