Skip to content

Commit fd368a5

Browse files
fix: psycopg instrumentation fixes (#29)
1 parent 2845a4a commit fd368a5

File tree

20 files changed

+3334
-739
lines changed

20 files changed

+3334
-739
lines changed
Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,6 @@
11
"""Execute test requests against the Django app."""
22

3-
import os
4-
import time
5-
6-
import requests
7-
8-
PORT = os.getenv("PORT", "8000")
9-
BASE_URL = f"http://localhost:{PORT}"
10-
11-
12-
def make_request(method: str, endpoint: str, **kwargs):
13-
"""Make HTTP request and log result."""
14-
url = f"{BASE_URL}{endpoint}"
15-
print(f"→ {method} {endpoint}")
16-
17-
# Set default timeout if not provided
18-
kwargs.setdefault("timeout", 30)
19-
response = requests.request(method, url, **kwargs)
20-
print(f" Status: {response.status_code}")
21-
time.sleep(0.5) # Small delay between requests
22-
return response
23-
3+
from drift.instrumentation.e2e_common.test_utils import make_request, print_request_summary
244

255
if __name__ == "__main__":
266
print("Starting Django test request sequence...\n")
@@ -42,4 +22,4 @@ def make_request(method: str, endpoint: str, **kwargs):
4222
)
4323
make_request("DELETE", "/api/post/1/delete")
4424

45-
print("\nAll requests completed successfully")
25+
print_request_summary()
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
"""Common utilities for Python SDK e2e tests."""
22

33
from .base_runner import Colors, E2ETestRunnerBase
4+
from .test_utils import get_request_count, make_request, print_request_summary
45

5-
__all__ = ["Colors", "E2ETestRunnerBase"]
6+
__all__ = [
7+
"Colors",
8+
"E2ETestRunnerBase",
9+
"get_request_count",
10+
"make_request",
11+
"print_request_summary",
12+
]

drift/instrumentation/e2e_common/base_runner.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def __init__(self, app_port: int = 8000):
4545
self.app_port = app_port
4646
self.app_process: subprocess.Popen | None = None
4747
self.exit_code = 0
48+
self.expected_request_count: int | None = None
4849

4950
# Register signal handlers for cleanup
5051
signal.signal(signal.SIGTERM, self._signal_handler)
@@ -76,6 +77,17 @@ def run_command(self, cmd: list[str], env: dict | None = None, check: bool = Tru
7677

7778
return result
7879

80+
def _parse_request_count(self, output: str):
81+
"""Parse the request count from test_requests.py output."""
82+
for line in output.split("\n"):
83+
if line.startswith("TOTAL_REQUESTS_SENT:"):
84+
try:
85+
count = int(line.split(":")[1])
86+
self.expected_request_count = count
87+
self.log(f"Captured request count: {count}", Colors.GREEN)
88+
except (ValueError, IndexError):
89+
self.log(f"Failed to parse request count from: {line}", Colors.YELLOW)
90+
7991
def wait_for_service(self, check_cmd: list[str], timeout: int = 30, interval: int = 1) -> bool:
8092
"""Wait for a service to become ready."""
8193
elapsed = 0
@@ -138,8 +150,8 @@ def record_traces(self) -> bool:
138150
self.app_process = subprocess.Popen(
139151
["python", "src/app.py"],
140152
env={**os.environ, **env},
141-
stdout=subprocess.PIPE,
142-
stderr=subprocess.STDOUT,
153+
stdout=subprocess.DEVNULL,
154+
stderr=subprocess.DEVNULL,
143155
text=True,
144156
)
145157

@@ -164,7 +176,13 @@ def record_traces(self) -> bool:
164176
# Execute test requests
165177
self.log("Executing test requests...", Colors.GREEN)
166178
try:
167-
self.run_command(["python", "src/test_requests.py"])
179+
# Pass PYTHONPATH so test_requests.py can import from e2e_common
180+
result = self.run_command(
181+
["python", "src/test_requests.py"],
182+
env={"PYTHONPATH": "/sdk"},
183+
)
184+
# Parse request count from output
185+
self._parse_request_count(result.stdout)
168186
except subprocess.CalledProcessError:
169187
self.log("Test requests failed", Colors.RED)
170188
self.exit_code = 1
@@ -257,13 +275,15 @@ def parse_test_results(self, output: str):
257275
idx += 1
258276

259277
all_passed = True
278+
passed_count = 0
260279
for result in results:
261280
test_id = result.get("test_id", "unknown")
262281
passed = result.get("passed", False)
263282
duration = result.get("duration", 0)
264283

265284
if passed:
266285
self.log(f"✓ Test ID: {test_id} (Duration: {duration}ms)", Colors.GREEN)
286+
passed_count += 1
267287
else:
268288
self.log(f"✗ Test ID: {test_id} (Duration: {duration}ms)", Colors.RED)
269289
all_passed = False
@@ -276,6 +296,20 @@ def parse_test_results(self, output: str):
276296
self.log("Some tests failed!", Colors.RED)
277297
self.exit_code = 1
278298

299+
# Validate request count matches passed tests
300+
if self.expected_request_count is not None:
301+
if passed_count < self.expected_request_count:
302+
self.log(
303+
f"✗ Request count mismatch: {passed_count} passed tests != {self.expected_request_count} requests sent",
304+
Colors.RED,
305+
)
306+
self.exit_code = 1
307+
else:
308+
self.log(
309+
f"✓ Request count validation: {passed_count} passed tests >= {self.expected_request_count} requests sent",
310+
Colors.GREEN,
311+
)
312+
279313
except Exception as e:
280314
self.log(f"Failed to parse test results: {e}", Colors.RED)
281315
self.log(f"Raw output:\n{output}", Colors.YELLOW)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""
2+
Shared test utilities for e2e tests.
3+
4+
This module provides common functions used across all instrumentation e2e tests,
5+
including request counting for validation.
6+
"""
7+
8+
import time
9+
10+
import requests
11+
12+
BASE_URL = "http://localhost:8000"
13+
_request_count = 0
14+
15+
16+
def make_request(method, endpoint, **kwargs):
17+
"""Make HTTP request, log result, and track count."""
18+
global _request_count
19+
_request_count += 1
20+
21+
url = f"{BASE_URL}{endpoint}"
22+
print(f"→ {method} {endpoint}")
23+
kwargs.setdefault("timeout", 30)
24+
response = requests.request(method, url, **kwargs)
25+
print(f" Status: {response.status_code}")
26+
time.sleep(0.5)
27+
return response
28+
29+
30+
def get_request_count():
31+
"""Return the current request count."""
32+
return _request_count
33+
34+
35+
def print_request_summary():
36+
"""Print the total request count in a parseable format."""
37+
print(f"\nTOTAL_REQUESTS_SENT:{_request_count}")

drift/instrumentation/fastapi/e2e-tests/src/test_requests.py

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,9 @@
11
"""Execute test requests against the FastAPI app."""
22

33
import json
4-
import os
5-
import time
64
from pathlib import Path
75

8-
import requests
9-
10-
PORT = os.getenv("PORT", "8000")
11-
BASE_URL = f"http://localhost:{PORT}"
12-
13-
14-
def make_request(method: str, endpoint: str, **kwargs):
15-
"""Make HTTP request and log result."""
16-
url = f"{BASE_URL}{endpoint}"
17-
print(f"→ {method} {endpoint}")
18-
19-
# Set default timeout if not provided
20-
kwargs.setdefault("timeout", 30)
21-
response = requests.request(method, url, **kwargs)
22-
print(f" Status: {response.status_code}")
23-
time.sleep(0.5) # Small delay between requests
24-
return response
6+
from drift.instrumentation.e2e_common.test_utils import make_request, print_request_summary
257

268

279
def verify_stack_traces():
@@ -180,3 +162,5 @@ def verify_context_propagation():
180162
print("\n" + "=" * 50)
181163
print("All requests completed successfully")
182164
print("=" * 50)
165+
166+
print_request_summary()
Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,6 @@
11
"""Execute test requests against the Flask app."""
22

3-
import time
4-
5-
import requests
6-
7-
BASE_URL = "http://localhost:8000"
8-
9-
10-
def make_request(method, endpoint, **kwargs):
11-
"""Make HTTP request and log result."""
12-
url = f"{BASE_URL}{endpoint}"
13-
print(f"→ {method} {endpoint}")
14-
15-
# Set default timeout if not provided
16-
kwargs.setdefault("timeout", 30)
17-
response = requests.request(method, url, **kwargs)
18-
print(f" Status: {response.status_code}")
19-
time.sleep(0.5) # Small delay between requests
20-
return response
21-
3+
from drift.instrumentation.e2e_common.test_utils import make_request, print_request_summary
224

235
if __name__ == "__main__":
246
print("Starting test request sequence...\n")
@@ -32,4 +14,4 @@ def make_request(method, endpoint, **kwargs):
3214
make_request("POST", "/api/post", json={"title": "Test Post", "body": "This is a test post", "userId": 1})
3315
make_request("DELETE", "/api/post/1")
3416

35-
print("\nAll requests completed successfully")
17+
print_request_summary()

drift/instrumentation/httpx/e2e-tests/src/test_requests.py

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,6 @@
11
"""Execute test requests against the Flask app to exercise the HTTPX instrumentation."""
22

3-
import time
4-
5-
import requests
6-
7-
BASE_URL = "http://localhost:8000"
8-
9-
10-
def make_request(method, endpoint, **kwargs):
11-
"""Make HTTP request and log result."""
12-
url = f"{BASE_URL}{endpoint}"
13-
print(f"-> {method} {endpoint}")
14-
15-
# Set default timeout if not provided
16-
kwargs.setdefault("timeout", 30)
17-
response = requests.request(method, url, **kwargs)
18-
print(f" Status: {response.status_code}")
19-
time.sleep(0.5) # Small delay between requests
20-
return response
21-
3+
from drift.instrumentation.e2e_common.test_utils import make_request, print_request_summary
224

235
if __name__ == "__main__":
246
print("Starting test request sequence for HTTPX instrumentation...\n")
@@ -124,4 +106,4 @@ def make_request(method, endpoint, **kwargs):
124106

125107
make_request("POST", "/test/file-like-body")
126108

127-
print("\nAll requests completed successfully")
109+
print_request_summary()

drift/instrumentation/httpx/instrumentation.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,7 @@ def _try_get_mock_from_request_sync(
823823
input_value=input_value,
824824
kind=SpanKind.CLIENT,
825825
input_schema_merges=input_schema_merges,
826+
is_pre_app_start=not sdk.app_ready,
826827
)
827828

828829
if not mock_response_output or not mock_response_output.found:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
-e /sdk # Mount point for drift SDK
22
Flask>=3.1.2
33
psycopg[binary]>=3.2.1
4+
psycopg-pool>=3.2.0
45
requests>=2.32.5

0 commit comments

Comments
 (0)