-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtest_github_integration.py
More file actions
343 lines (286 loc) · 13.8 KB
/
test_github_integration.py
File metadata and controls
343 lines (286 loc) · 13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
#!/usr/bin/env python3
"""
Test script for GitHub integration in MeistroCraft.
Tests GitHub client functionality without requiring actual API calls.
"""
from github_client import GitHubClient, GitHubClientError, GitHubAuthenticationError, create_github_client
import json
import os
import sys
import unittest
from unittest.mock import Mock, patch, MagicMock
# Add the parent directory to the path to import modules
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
class TestGitHubClient(unittest.TestCase):
"""Test cases for GitHub client functionality."""
def setUp(self):
"""Set up test configuration."""
self.test_config = {
'github_api_key': 'test_token_123',
'github': {
'enabled': True,
'default_branch': 'main',
'auto_create_repos': True,
'enable_webhooks': False,
'rate_limit_delay': 1.0,
'max_retries': 3,
'organization': '',
'default_visibility': 'private',
'auto_initialize': True,
'default_gitignore': 'Python',
'default_license': 'MIT'
}
}
def test_config_loading(self):
"""Test configuration loading and validation."""
# Test with valid config
self.assertIsNotNone(self.test_config.get('github_api_key'))
self.assertIsNotNone(self.test_config.get('github'))
# Test GitHub config defaults
github_config = self.test_config.get('github', {})
self.assertTrue(github_config.get('enabled', True))
self.assertEqual(github_config.get('default_branch'), 'main')
self.assertEqual(github_config.get('default_visibility'), 'private')
@patch('github_client.PYGITHUB_AVAILABLE', False)
def test_fallback_mode_initialization(self):
"""Test initialization in fallback mode (no PyGitHub)."""
with patch.dict(os.environ, {'GITHUB_API_TOKEN': 'test_token'}):
with patch('github_client.requests') as mock_requests:
# Mock successful authentication response
mock_response = Mock()
mock_response.json.return_value = {'login': 'testuser', 'name': 'Test User'}
mock_response.status_code = 200
mock_response.content = True
mock_requests.get.return_value = mock_response
client = GitHubClient(self.test_config)
self.assertTrue(client.is_authenticated())
self.assertEqual(client.get_authenticated_user(), 'testuser')
@patch('github_client.PYGITHUB_AVAILABLE', True)
@patch('github_client.Github')
@patch('github_client.Auth')
def test_pygithub_mode_initialization(self, mock_auth, mock_github):
"""Test initialization with PyGitHub library."""
# Mock PyGitHub components
mock_github_instance = Mock()
mock_user = Mock()
mock_user.login = 'testuser'
mock_user.name = 'Test User'
mock_github_instance.get_user.return_value = mock_user
mock_github.return_value = mock_github_instance
mock_auth_instance = Mock()
mock_auth.Token.return_value = mock_auth_instance
client = GitHubClient(self.test_config)
self.assertTrue(client.is_authenticated())
self.assertEqual(client.get_authenticated_user(), 'testuser')
def test_token_resolution(self):
"""Test GitHub token resolution from different sources."""
# Test config file token
client_config = self.test_config.copy()
client_config['github_api_key'] = 'config_token'
with patch('github_client.PYGITHUB_AVAILABLE', False):
with patch('github_client.requests') as mock_requests:
mock_response = Mock()
mock_response.json.return_value = {'login': 'testuser'}
mock_response.status_code = 200
mock_response.content = True
mock_requests.get.return_value = mock_response
client = GitHubClient(client_config)
# Token should come from config
self.assertIsNotNone(client._api_key)
# Test environment variable token
with patch.dict(os.environ, {'GITHUB_API_TOKEN': 'env_token'}):
config_no_token = {'github': self.test_config['github']}
with patch('github_client.PYGITHUB_AVAILABLE', False):
with patch('github_client.requests') as mock_requests:
mock_response = Mock()
mock_response.json.return_value = {'login': 'testuser'}
mock_response.status_code = 200
mock_response.content = True
mock_requests.get.return_value = mock_response
client = GitHubClient(config_no_token)
self.assertIsNotNone(client._api_key)
def test_error_handling(self):
"""Test error handling for various failure scenarios."""
# Test authentication failure
with patch('github_client.PYGITHUB_AVAILABLE', False):
with patch('github_client.requests') as mock_requests:
mock_response = Mock()
mock_response.status_code = 401
mock_response.json.return_value = {'message': 'Bad credentials'}
mock_requests.get.return_value = mock_response
with self.assertRaises(GitHubAuthenticationError):
GitHubClient(self.test_config)
# Test missing token
config_no_token = {'github': self.test_config['github']}
with patch.dict(os.environ, {}, clear=True):
client = GitHubClient(config_no_token)
self.assertFalse(client.is_authenticated())
def test_rate_limiting(self):
"""Test rate limiting and retry logic."""
with patch('github_client.PYGITHUB_AVAILABLE', False):
with patch('github_client.requests') as mock_requests:
# Setup successful auth first
auth_response = Mock()
auth_response.json.return_value = {'login': 'testuser'}
auth_response.status_code = 200
auth_response.content = True
# Setup rate limit response
rate_limit_response = Mock()
rate_limit_response.status_code = 403
rate_limit_response.json.return_value = {'message': 'rate limit exceeded'}
# Success response after retry
success_response = Mock()
success_response.json.return_value = {'test': 'success'}
success_response.status_code = 200
success_response.content = True
# Configure mock to return different responses
mock_requests.get.side_effect = [
auth_response, # For initial auth
rate_limit_response, # First API call - rate limited
success_response # Retry - success
]
client = GitHubClient(self.test_config)
# Mock time.sleep to speed up test
with patch('github_client.time.sleep'):
# This should trigger rate limit handling
result = client._make_fallback_request('GET', '/test')
self.assertEqual(result['test'], 'success')
def test_repository_operations(self):
"""Test repository creation, forking, and listing operations."""
with patch('github_client.PYGITHUB_AVAILABLE', False):
with patch('github_client.requests') as mock_requests:
# Setup authentication
auth_response = Mock()
auth_response.json.return_value = {'login': 'testuser'}
auth_response.status_code = 200
auth_response.content = True
# Mock repository creation response
create_response = Mock()
create_response.json.return_value = {
'full_name': 'testuser/testrepo',
'html_url': 'https://github.com/testuser/testrepo',
'clone_url': 'https://github.com/testuser/testrepo.git'
}
create_response.status_code = 201
create_response.content = True
mock_requests.get.return_value = auth_response
mock_requests.post.return_value = create_response
client = GitHubClient(self.test_config)
# Test repository creation
repo = client.create_repository('testrepo', 'Test repository')
self.assertEqual(repo['full_name'], 'testuser/testrepo')
def test_client_factory(self):
"""Test the create_github_client factory function."""
# Test successful creation
with patch('github_client.GitHubClient') as mock_client_class:
mock_client = Mock()
mock_client_class.return_value = mock_client
config = {'github': {'enabled': True}}
result = create_github_client(config)
self.assertIsNotNone(result)
# Test disabled GitHub integration
config_disabled = {'github': {'enabled': False}}
result = create_github_client(config_disabled)
self.assertIsNone(result)
# Test authentication error handling
with patch('github_client.GitHubClient') as mock_client_class:
mock_client_class.side_effect = GitHubAuthenticationError("Auth failed")
config = {'github': {'enabled': True}}
result = create_github_client(config)
self.assertIsNone(result)
class TestGitHubIntegration(unittest.TestCase):
"""Integration tests for GitHub functionality with MeistroCraft."""
def test_config_template_validation(self):
"""Test that the configuration template is valid."""
config_template_path = os.path.join(os.path.dirname(__file__), 'config', 'config.template.json')
if os.path.exists(config_template_path):
with open(config_template_path, 'r') as f:
config = json.load(f)
# Check GitHub configuration section exists
self.assertIn('github', config)
github_config = config['github']
# Check required fields
self.assertIn('enabled', github_config)
self.assertIn('default_branch', github_config)
self.assertIn('auto_create_repos', github_config)
self.assertIn('rate_limit_delay', github_config)
self.assertIn('max_retries', github_config)
def test_environment_template_validation(self):
"""Test that the environment template includes GitHub variables."""
env_template_path = os.path.join(os.path.dirname(__file__), 'env.template')
if os.path.exists(env_template_path):
with open(env_template_path, 'r') as f:
content = f.read()
# Check GitHub environment variables are documented
self.assertIn('GITHUB_API_TOKEN', content)
self.assertIn('GITHUB_USERNAME', content)
self.assertIn('GITHUB_ORGANIZATION', content)
def run_github_integration_test():
"""
Run a basic integration test to verify GitHub client functionality.
This can be called from the command line to test the setup.
"""
print("🧪 Running GitHub Integration Tests...")
print("=" * 50)
# Test 1: Configuration loading
print("1. Testing configuration loading...")
try:
from main import load_config
config = load_config('config/config.template.json')
github_config = config.get('github', {})
if github_config:
print(" ✅ GitHub configuration found in template")
else:
print(" ❌ GitHub configuration missing from template")
except Exception as e:
print(f" ❌ Configuration loading failed: {e}")
# Test 2: Client creation (without actual API calls)
print("\n2. Testing GitHub client creation...")
try:
mock_config = {
'github_api_key': 'test_token',
'github': {
'enabled': True,
'default_branch': 'main',
'rate_limit_delay': 1.0,
'max_retries': 3
}
}
# Test with no token (should handle gracefully)
no_token_config = {'github': {'enabled': True}}
client = create_github_client(no_token_config)
if client is None:
print(" ✅ Handles missing token gracefully")
else:
print(" ❌ Should return None when no token provided")
except Exception as e:
print(f" ❌ Client creation test failed: {e}")
# Test 3: Requirements check
print("\n3. Testing requirements...")
try:
import github
print(" ✅ PyGitHub library available")
except ImportError:
print(" ⚠️ PyGitHub library not installed (fallback mode will be used)")
print(" Install with: pip install PyGitHub>=2.1.0")
try:
import requests
print(" ✅ Requests library available")
except ImportError:
print(" ❌ Requests library not available (required for fallback mode)")
print("\n" + "=" * 50)
print("🎯 GitHub Integration Test Summary:")
print(" Phase 1 implementation includes:")
print(" • ✅ GitHub Authentication & Configuration")
print(" • ✅ Repository Management (create, fork, list)")
print(" • ✅ File Operations via GitHub API")
print(" • ✅ Branch Management")
print(" • ✅ CLI Commands and Interactive Mode")
print(" • ✅ Rate Limiting and Error Handling")
print("\n Next: Add GitHub API token to config and test with real API!")
if __name__ == '__main__':
if len(sys.argv) > 1 and sys.argv[1] == '--integration':
run_github_integration_test()
else:
# Run unit tests
unittest.main()