Skip to content

Commit 4bb878e

Browse files
committed
Added conftest.py and moved tests to test/ directory
1 parent 8b7eb48 commit 4bb878e

File tree

2 files changed

+408
-140
lines changed

2 files changed

+408
-140
lines changed

tests/conftest.py

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
"""
2+
Pytest fixtures for GitHub ETL tests.
3+
4+
This module provides reusable test fixtures for mocking external dependencies
5+
and providing sample data for unit and integration tests.
6+
"""
7+
8+
from datetime import datetime, timezone
9+
from typing import Any
10+
from unittest.mock import MagicMock, Mock
11+
12+
import pytest
13+
import requests
14+
from google.cloud import bigquery
15+
16+
17+
@pytest.fixture
18+
def mock_env_vars(monkeypatch) -> dict[str, str]:
19+
"""
20+
Set up common environment variables for tests.
21+
22+
Returns:
23+
Dictionary of environment variables that were set
24+
"""
25+
env_vars = {
26+
"GITHUB_TOKEN": "test_token_123",
27+
"GITHUB_REPOS": "mozilla/firefox",
28+
"BIGQUERY_PROJECT": "test-project",
29+
"BIGQUERY_DATASET": "test_dataset",
30+
}
31+
for key, value in env_vars.items():
32+
monkeypatch.setenv(key, value)
33+
return env_vars
34+
35+
36+
@pytest.fixture
37+
def sample_github_pr() -> dict[str, Any]:
38+
"""
39+
Sample GitHub pull request data from API response.
40+
41+
Returns:
42+
Dictionary representing a single PR from GitHub API
43+
"""
44+
return {
45+
"number": 12345,
46+
"state": "closed",
47+
"title": "Bug 1234567 - Fix memory leak in parser",
48+
"created_at": "2025-01-01T10:00:00Z",
49+
"updated_at": "2025-01-02T15:30:00Z",
50+
"merged_at": "2025-01-02T15:30:00Z",
51+
"labels": [
52+
{"name": "bug"},
53+
{"name": "priority-high"},
54+
],
55+
"user": {
56+
"login": "test_user",
57+
"id": 123,
58+
},
59+
"head": {
60+
"ref": "feature-branch",
61+
"sha": "abc123",
62+
},
63+
"base": {
64+
"ref": "main",
65+
"sha": "def456",
66+
},
67+
"commit_data": [],
68+
"reviewer_data": [],
69+
"comment_data": [],
70+
}
71+
72+
73+
@pytest.fixture
74+
def sample_github_commit() -> dict[str, Any]:
75+
"""
76+
Sample GitHub commit data from API response.
77+
78+
Returns:
79+
Dictionary representing a single commit from GitHub API
80+
"""
81+
return {
82+
"sha": "abc123def456",
83+
"commit": {
84+
"author": {
85+
"name": "Test Author",
86+
"email": "author@example.com",
87+
"date": "2025-01-01T10:00:00Z",
88+
},
89+
"message": "Fix bug in parser",
90+
},
91+
"files": [
92+
{
93+
"filename": "src/parser.py",
94+
"additions": 10,
95+
"deletions": 5,
96+
"changes": 15,
97+
}
98+
],
99+
}
100+
101+
102+
@pytest.fixture
103+
def sample_github_reviewer() -> dict[str, Any]:
104+
"""
105+
Sample GitHub review data from API response.
106+
107+
Returns:
108+
Dictionary representing a single review from GitHub API
109+
"""
110+
return {
111+
"id": 98765,
112+
"user": {
113+
"login": "reviewer_user",
114+
"id": 456,
115+
},
116+
"state": "APPROVED",
117+
"submitted_at": "2025-01-02T12:00:00Z",
118+
"body": "LGTM",
119+
}
120+
121+
122+
@pytest.fixture
123+
def sample_github_comment() -> dict[str, Any]:
124+
"""
125+
Sample GitHub comment data from API response.
126+
127+
Returns:
128+
Dictionary representing a single comment from GitHub API
129+
"""
130+
return {
131+
"id": 111222,
132+
"user": {
133+
"login": "commenter_user",
134+
"id": 789,
135+
},
136+
"created_at": "2025-01-01T14:00:00Z",
137+
"body": "Please check the edge case for null values",
138+
"pull_request_review_id": None,
139+
}
140+
141+
142+
@pytest.fixture
143+
def sample_transformed_data() -> dict[str, list[dict]]:
144+
"""
145+
Sample transformed data ready for BigQuery insertion.
146+
147+
Returns:
148+
Dictionary with keys for each table and transformed row data
149+
"""
150+
return {
151+
"pull_requests": [
152+
{
153+
"pull_request_id": 12345,
154+
"current_status": "closed",
155+
"date_created": "2025-01-01T10:00:00Z",
156+
"date_modified": "2025-01-02T15:30:00Z",
157+
"target_repository": "mozilla/firefox",
158+
"bug_id": 1234567,
159+
"date_landed": "2025-01-02T15:30:00Z",
160+
"date_approved": "2025-01-02T12:00:00Z",
161+
"labels": ["bug", "priority-high"],
162+
}
163+
],
164+
"commits": [
165+
{
166+
"pull_request_id": 12345,
167+
"target_repository": "mozilla/firefox",
168+
"commit_sha": "abc123def456",
169+
"date_created": "2025-01-01T10:00:00Z",
170+
"author_username": "Test Author",
171+
"author_email": None,
172+
"filename": "src/parser.py",
173+
"lines_removed": 5,
174+
"lines_added": 10,
175+
}
176+
],
177+
"reviewers": [
178+
{
179+
"pull_request_id": 12345,
180+
"target_repository": "mozilla/firefox",
181+
"date_reviewed": "2025-01-02T12:00:00Z",
182+
"reviewer_email": None,
183+
"reviewer_username": "reviewer_user",
184+
"status": "APPROVED",
185+
}
186+
],
187+
"comments": [
188+
{
189+
"pull_request_id": 12345,
190+
"target_repository": "mozilla/firefox",
191+
"comment_id": 111222,
192+
"date_created": "2025-01-01T14:00:00Z",
193+
"author_email": None,
194+
"author_username": "commenter_user",
195+
"character_count": 43,
196+
"status": None,
197+
}
198+
],
199+
}
200+
201+
202+
@pytest.fixture
203+
def mock_session() -> Mock:
204+
"""
205+
Mock requests.Session with configurable responses.
206+
207+
Returns:
208+
Mock session object with get() method
209+
"""
210+
session = Mock(spec=requests.Session)
211+
session.headers = {}
212+
return session
213+
214+
215+
@pytest.fixture
216+
def mock_github_response() -> Mock:
217+
"""
218+
Mock requests.Response for GitHub API calls.
219+
220+
Returns:
221+
Mock response with status_code, json(), headers, and links
222+
"""
223+
response = Mock(spec=requests.Response)
224+
response.status_code = 200
225+
response.headers = {
226+
"X-RateLimit-Remaining": "5000",
227+
"X-RateLimit-Reset": "1609459200",
228+
}
229+
response.links = {}
230+
response.text = ""
231+
return response
232+
233+
234+
@pytest.fixture
235+
def mock_rate_limited_response() -> Mock:
236+
"""
237+
Mock requests.Response simulating rate limit exceeded.
238+
239+
Returns:
240+
Mock response with 403 status and rate limit headers
241+
"""
242+
response = Mock(spec=requests.Response)
243+
response.status_code = 403
244+
response.headers = {
245+
"X-RateLimit-Remaining": "0",
246+
"X-RateLimit-Reset": str(int(datetime.now(timezone.utc).timestamp()) + 3600),
247+
}
248+
response.text = "API rate limit exceeded"
249+
return response
250+
251+
252+
@pytest.fixture
253+
def mock_bigquery_client() -> Mock:
254+
"""
255+
Mock BigQuery client for testing load operations.
256+
257+
Returns:
258+
Mock BigQuery client with insert_rows_json() method
259+
"""
260+
client = Mock(spec=bigquery.Client)
261+
client.project = "test-project"
262+
client.insert_rows_json = MagicMock(return_value=[]) # Empty list = no errors
263+
return client
264+
265+
266+
@pytest.fixture
267+
def mock_bigquery_client_with_errors() -> Mock:
268+
"""
269+
Mock BigQuery client that returns insertion errors.
270+
271+
Returns:
272+
Mock BigQuery client that simulates insert failures
273+
"""
274+
client = Mock(spec=bigquery.Client)
275+
client.project = "test-project"
276+
client.insert_rows_json = MagicMock(
277+
return_value=[
278+
{
279+
"index": 0,
280+
"errors": [{"reason": "invalid", "message": "Invalid schema"}],
281+
}
282+
]
283+
)
284+
return client

0 commit comments

Comments
 (0)