1
1
import asyncio
2
2
import os
3
+ from collections import namedtuple
4
+ from unittest .mock import patch
3
5
4
6
import asyncpg
5
7
@@ -20,7 +22,26 @@ def async_call(coro):
20
22
return loop .run_until_complete (coro )
21
23
22
24
23
- class TestFunctionalAsyncPG (TestBase ):
25
+ class CheckSpanMixin :
26
+ def check_span (self , span , expected_db_name = POSTGRES_DB_NAME ):
27
+ self .assertEqual (
28
+ span .attributes [SpanAttributes .DB_SYSTEM ], "postgresql"
29
+ )
30
+ self .assertEqual (
31
+ span .attributes [SpanAttributes .DB_NAME ], expected_db_name
32
+ )
33
+ self .assertEqual (
34
+ span .attributes [SpanAttributes .DB_USER ], POSTGRES_USER
35
+ )
36
+ self .assertEqual (
37
+ span .attributes [SpanAttributes .NET_PEER_NAME ], POSTGRES_HOST
38
+ )
39
+ self .assertEqual (
40
+ span .attributes [SpanAttributes .NET_PEER_PORT ], POSTGRES_PORT
41
+ )
42
+
43
+
44
+ class TestFunctionalAsyncPG (TestBase , CheckSpanMixin ):
24
45
def setUp (self ):
25
46
super ().setUp ()
26
47
self ._tracer = self .tracer_provider .get_tracer (__name__ )
@@ -39,25 +60,54 @@ def tearDown(self):
39
60
AsyncPGInstrumentor ().uninstrument ()
40
61
super ().tearDown ()
41
62
42
- def check_span (self , span ):
43
- self .assertEqual (
44
- span .attributes [SpanAttributes .DB_SYSTEM ], "postgresql"
45
- )
46
- self .assertEqual (
47
- span .attributes [SpanAttributes .DB_NAME ], POSTGRES_DB_NAME
48
- )
49
- self .assertEqual (
50
- span .attributes [SpanAttributes .DB_USER ], POSTGRES_USER
51
- )
63
+ def test_instrumented_execute_method_without_arguments (self , * _ , ** __ ):
64
+ """Should create a span for execute()."""
65
+ async_call (self ._connection .execute ("SELECT 42;" ))
66
+ spans = self .memory_exporter .get_finished_spans ()
67
+ self .assertEqual (len (spans ), 1 )
68
+ self .assertIs (StatusCode .UNSET , spans [0 ].status .status_code )
69
+ self .check_span (spans [0 ])
70
+ self .assertEqual (spans [0 ].name , "SELECT" )
52
71
self .assertEqual (
53
- span .attributes [SpanAttributes .NET_PEER_NAME ], POSTGRES_HOST
72
+ spans [ 0 ] .attributes [SpanAttributes .DB_STATEMENT ], "SELECT 42;"
54
73
)
74
+
75
+ def test_instrumented_execute_method_error (self , * _ , ** __ ):
76
+ """Should create an error span for execute() with the database name as the span name."""
77
+ with self .assertRaises (AttributeError ):
78
+ async_call (self ._connection .execute ("" ))
79
+ spans = self .memory_exporter .get_finished_spans ()
80
+ self .assertEqual (len (spans ), 1 )
81
+ self .assertIs (StatusCode .ERROR , spans [0 ].status .status_code )
82
+ self .check_span (spans [0 ])
83
+ self .assertEqual (spans [0 ].name , POSTGRES_DB_NAME )
84
+ self .assertEqual (spans [0 ].attributes [SpanAttributes .DB_STATEMENT ], "" )
85
+
86
+ def test_instrumented_fetch_method_without_arguments (self , * _ , ** __ ):
87
+ """Should create a span from fetch()."""
88
+ async_call (self ._connection .fetch ("SELECT 42;" ))
89
+ spans = self .memory_exporter .get_finished_spans ()
90
+ self .assertEqual (len (spans ), 1 )
91
+ self .assertIs (StatusCode .UNSET , spans [0 ].status .status_code )
92
+ self .check_span (spans [0 ])
93
+ self .assertEqual (spans [0 ].name , "SELECT" )
55
94
self .assertEqual (
56
- span .attributes [SpanAttributes .NET_PEER_PORT ], POSTGRES_PORT
95
+ spans [ 0 ] .attributes [SpanAttributes .DB_STATEMENT ], "SELECT 42;"
57
96
)
58
97
59
- def test_instrumented_execute_method_without_arguments (self , * _ , ** __ ):
60
- async_call (self ._connection .execute ("SELECT 42;" ))
98
+ def test_instrumented_fetch_method_empty_query (self , * _ , ** __ ):
99
+ """Should create an error span for fetch() with the database name as the span name."""
100
+ async_call (self ._connection .fetch ("" ))
101
+ spans = self .memory_exporter .get_finished_spans ()
102
+ self .assertEqual (len (spans ), 1 )
103
+ self .assertIs (StatusCode .UNSET , spans [0 ].status .status_code )
104
+ self .check_span (spans [0 ])
105
+ self .assertEqual (spans [0 ].name , POSTGRES_DB_NAME )
106
+ self .assertEqual (spans [0 ].attributes [SpanAttributes .DB_STATEMENT ], "" )
107
+
108
+ def test_instrumented_fetchval_method_without_arguments (self , * _ , ** __ ):
109
+ """Should create a span for fetchval()."""
110
+ async_call (self ._connection .fetchval ("SELECT 42;" ))
61
111
spans = self .memory_exporter .get_finished_spans ()
62
112
self .assertEqual (len (spans ), 1 )
63
113
self .assertIs (StatusCode .UNSET , spans [0 ].status .status_code )
@@ -67,17 +117,105 @@ def test_instrumented_execute_method_without_arguments(self, *_, **__):
67
117
spans [0 ].attributes [SpanAttributes .DB_STATEMENT ], "SELECT 42;"
68
118
)
69
119
70
- def test_instrumented_fetch_method_without_arguments (self , * _ , ** __ ):
71
- async_call (self ._connection .fetch ("SELECT 42;" ))
120
+ def test_instrumented_fetchval_method_empty_query (self , * _ , ** __ ):
121
+ """Should create an error span for fetchval() with the database name as the span name."""
122
+ async_call (self ._connection .fetchval ("" ))
72
123
spans = self .memory_exporter .get_finished_spans ()
73
124
self .assertEqual (len (spans ), 1 )
125
+ self .assertIs (StatusCode .UNSET , spans [0 ].status .status_code )
126
+ self .check_span (spans [0 ])
127
+ self .assertEqual (spans [0 ].name , POSTGRES_DB_NAME )
128
+ self .assertEqual (spans [0 ].attributes [SpanAttributes .DB_STATEMENT ], "" )
129
+
130
+ def test_instrumented_fetchrow_method_without_arguments (self , * _ , ** __ ):
131
+ """Should create a span for fetchrow()."""
132
+ async_call (self ._connection .fetchrow ("SELECT 42;" ))
133
+ spans = self .memory_exporter .get_finished_spans ()
134
+ self .assertEqual (len (spans ), 1 )
135
+ self .assertIs (StatusCode .UNSET , spans [0 ].status .status_code )
74
136
self .check_span (spans [0 ])
75
137
self .assertEqual (spans [0 ].name , "SELECT" )
76
138
self .assertEqual (
77
139
spans [0 ].attributes [SpanAttributes .DB_STATEMENT ], "SELECT 42;"
78
140
)
79
141
142
+ def test_instrumented_fetchrow_method_empty_query (self , * _ , ** __ ):
143
+ """Should create an error span for fetchrow() with the database name as the span name."""
144
+ async_call (self ._connection .fetchrow ("" ))
145
+ spans = self .memory_exporter .get_finished_spans ()
146
+ self .assertEqual (len (spans ), 1 )
147
+ self .assertIs (StatusCode .UNSET , spans [0 ].status .status_code )
148
+ self .check_span (spans [0 ])
149
+ self .assertEqual (spans [0 ].name , POSTGRES_DB_NAME )
150
+ self .assertEqual (spans [0 ].attributes [SpanAttributes .DB_STATEMENT ], "" )
151
+
152
+ def test_instrumented_cursor_execute_method_without_arguments (
153
+ self , * _ , ** __
154
+ ):
155
+ """Should create spans for the transaction as well as the cursor fetches."""
156
+
157
+ async def _cursor_execute ():
158
+ async with self ._connection .transaction ():
159
+ async for record in self ._connection .cursor (
160
+ "SELECT generate_series(0, 5);"
161
+ ):
162
+ pass
163
+
164
+ async_call (_cursor_execute ())
165
+ spans = self .memory_exporter .get_finished_spans ()
166
+
167
+ self .check_span (spans [0 ])
168
+ self .assertEqual (spans [0 ].name , "BEGIN;" )
169
+ self .assertEqual (
170
+ spans [0 ].attributes [SpanAttributes .DB_STATEMENT ], "BEGIN;"
171
+ )
172
+ self .assertIs (StatusCode .UNSET , spans [0 ].status .status_code )
173
+
174
+ for span in spans [1 :- 1 ]:
175
+ self .check_span (span )
176
+ self .assertEqual (span .name , "CURSOR: SELECT" )
177
+ self .assertEqual (
178
+ span .attributes [SpanAttributes .DB_STATEMENT ],
179
+ "SELECT generate_series(0, 5);" ,
180
+ )
181
+ self .assertIs (StatusCode .UNSET , span .status .status_code )
182
+
183
+ self .check_span (spans [- 1 ])
184
+ self .assertEqual (spans [- 1 ].name , "COMMIT;" )
185
+ self .assertEqual (
186
+ spans [- 1 ].attributes [SpanAttributes .DB_STATEMENT ], "COMMIT;"
187
+ )
188
+
189
+ def test_instrumented_cursor_execute_method_empty_query (self , * _ , ** __ ):
190
+ """Should create spans for the transaction and cursor fetches with the database name as the span name."""
191
+
192
+ async def _cursor_execute ():
193
+ async with self ._connection .transaction ():
194
+ async for record in self ._connection .cursor ("" ):
195
+ pass
196
+
197
+ async_call (_cursor_execute ())
198
+ spans = self .memory_exporter .get_finished_spans ()
199
+ self .assertEqual (len (spans ), 3 )
200
+
201
+ self .check_span (spans [0 ])
202
+ self .assertEqual (
203
+ spans [0 ].attributes [SpanAttributes .DB_STATEMENT ], "BEGIN;"
204
+ )
205
+ self .assertIs (StatusCode .UNSET , spans [0 ].status .status_code )
206
+
207
+ self .check_span (spans [1 ])
208
+ self .assertEqual (spans [1 ].name , f"CURSOR: { POSTGRES_DB_NAME } " )
209
+ self .assertEqual (spans [1 ].attributes [SpanAttributes .DB_STATEMENT ], "" )
210
+ self .assertIs (StatusCode .UNSET , spans [1 ].status .status_code )
211
+
212
+ self .check_span (spans [2 ])
213
+ self .assertEqual (
214
+ spans [2 ].attributes [SpanAttributes .DB_STATEMENT ], "COMMIT;"
215
+ )
216
+
80
217
def test_instrumented_remove_comments (self , * _ , ** __ ):
218
+ """Should remove comments from the query and set the span name correctly."""
81
219
async_call (self ._connection .fetch ("/* leading comment */ SELECT 42;" ))
82
220
async_call (
83
221
self ._connection .fetch (
@@ -88,25 +226,30 @@ def test_instrumented_remove_comments(self, *_, **__):
88
226
spans = self .memory_exporter .get_finished_spans ()
89
227
self .assertEqual (len (spans ), 3 )
90
228
self .check_span (spans [0 ])
229
+ self .assertIs (StatusCode .UNSET , spans [0 ].status .status_code )
91
230
self .assertEqual (spans [0 ].name , "SELECT" )
92
231
self .assertEqual (
93
232
spans [0 ].attributes [SpanAttributes .DB_STATEMENT ],
94
233
"/* leading comment */ SELECT 42;" ,
95
234
)
96
235
self .check_span (spans [1 ])
236
+ self .assertIs (StatusCode .UNSET , spans [1 ].status .status_code )
97
237
self .assertEqual (spans [1 ].name , "SELECT" )
98
238
self .assertEqual (
99
239
spans [1 ].attributes [SpanAttributes .DB_STATEMENT ],
100
240
"/* leading comment */ SELECT 42; /* trailing comment */" ,
101
241
)
102
242
self .check_span (spans [2 ])
243
+ self .assertIs (StatusCode .UNSET , spans [2 ].status .status_code )
103
244
self .assertEqual (spans [2 ].name , "SELECT" )
104
245
self .assertEqual (
105
246
spans [2 ].attributes [SpanAttributes .DB_STATEMENT ],
106
247
"SELECT 42; /* trailing comment */" ,
107
248
)
108
249
109
250
def test_instrumented_transaction_method (self , * _ , ** __ ):
251
+ """Should create spans for the transaction and the inner execute()."""
252
+
110
253
async def _transaction_execute ():
111
254
async with self ._connection .transaction ():
112
255
await self ._connection .execute ("SELECT 42;" )
@@ -134,6 +277,8 @@ async def _transaction_execute():
134
277
self .assertIs (StatusCode .UNSET , spans [2 ].status .status_code )
135
278
136
279
def test_instrumented_failed_transaction_method (self , * _ , ** __ ):
280
+ """Should create spans for the transaction as well as an error span for execute()."""
281
+
137
282
async def _transaction_execute ():
138
283
async with self ._connection .transaction ():
139
284
await self ._connection .execute ("SELECT 42::uuid;" )
@@ -164,6 +309,7 @@ async def _transaction_execute():
164
309
self .assertIs (StatusCode .UNSET , spans [2 ].status .status_code )
165
310
166
311
def test_instrumented_method_doesnt_capture_parameters (self , * _ , ** __ ):
312
+ """Should not capture parameters when capture_parameters is False."""
167
313
async_call (self ._connection .execute ("SELECT $1;" , "1" ))
168
314
spans = self .memory_exporter .get_finished_spans ()
169
315
self .assertEqual (len (spans ), 1 )
@@ -174,7 +320,7 @@ def test_instrumented_method_doesnt_capture_parameters(self, *_, **__):
174
320
)
175
321
176
322
177
- class TestFunctionalAsyncPG_CaptureParameters (TestBase ):
323
+ class TestFunctionalAsyncPG_CaptureParameters (TestBase , CheckSpanMixin ):
178
324
def setUp (self ):
179
325
super ().setUp ()
180
326
self ._tracer = self .tracer_provider .get_tracer (__name__ )
@@ -195,24 +341,8 @@ def tearDown(self):
195
341
AsyncPGInstrumentor ().uninstrument ()
196
342
super ().tearDown ()
197
343
198
- def check_span (self , span ):
199
- self .assertEqual (
200
- span .attributes [SpanAttributes .DB_SYSTEM ], "postgresql"
201
- )
202
- self .assertEqual (
203
- span .attributes [SpanAttributes .DB_NAME ], POSTGRES_DB_NAME
204
- )
205
- self .assertEqual (
206
- span .attributes [SpanAttributes .DB_USER ], POSTGRES_USER
207
- )
208
- self .assertEqual (
209
- span .attributes [SpanAttributes .NET_PEER_NAME ], POSTGRES_HOST
210
- )
211
- self .assertEqual (
212
- span .attributes [SpanAttributes .NET_PEER_PORT ], POSTGRES_PORT
213
- )
214
-
215
344
def test_instrumented_execute_method_with_arguments (self , * _ , ** __ ):
345
+ """Should create a span for execute() with captured parameters."""
216
346
async_call (self ._connection .execute ("SELECT $1;" , "1" ))
217
347
spans = self .memory_exporter .get_finished_spans ()
218
348
self .assertEqual (len (spans ), 1 )
@@ -228,6 +358,7 @@ def test_instrumented_execute_method_with_arguments(self, *_, **__):
228
358
)
229
359
230
360
def test_instrumented_fetch_method_with_arguments (self , * _ , ** __ ):
361
+ """Should create a span for fetch() with captured parameters."""
231
362
async_call (self ._connection .fetch ("SELECT $1;" , "1" ))
232
363
spans = self .memory_exporter .get_finished_spans ()
233
364
self .assertEqual (len (spans ), 1 )
@@ -242,10 +373,11 @@ def test_instrumented_fetch_method_with_arguments(self, *_, **__):
242
373
)
243
374
244
375
def test_instrumented_executemany_method_with_arguments (self , * _ , ** __ ):
376
+ """Should create a span for executemany with captured parameters."""
245
377
async_call (self ._connection .executemany ("SELECT $1;" , [["1" ], ["2" ]]))
246
378
spans = self .memory_exporter .get_finished_spans ()
247
379
self .assertEqual (len (spans ), 1 )
248
-
380
+ self . assertIs ( StatusCode . UNSET , spans [ 0 ]. status . status_code )
249
381
self .check_span (spans [0 ])
250
382
self .assertEqual (
251
383
spans [0 ].attributes [SpanAttributes .DB_STATEMENT ], "SELECT $1;"
@@ -255,15 +387,41 @@ def test_instrumented_executemany_method_with_arguments(self, *_, **__):
255
387
)
256
388
257
389
def test_instrumented_execute_interface_error_method (self , * _ , ** __ ):
390
+ """Should create an error span for execute() with captured parameters."""
258
391
with self .assertRaises (asyncpg .InterfaceError ):
259
392
async_call (self ._connection .execute ("SELECT 42;" , 1 , 2 , 3 ))
260
393
spans = self .memory_exporter .get_finished_spans ()
261
394
self .assertEqual (len (spans ), 1 )
262
-
395
+ self . assertIs ( StatusCode . ERROR , spans [ 0 ]. status . status_code )
263
396
self .check_span (spans [0 ])
264
397
self .assertEqual (
265
398
spans [0 ].attributes [SpanAttributes .DB_STATEMENT ], "SELECT 42;"
266
399
)
267
400
self .assertEqual (
268
401
spans [0 ].attributes ["db.statement.parameters" ], "(1, 2, 3)"
269
402
)
403
+
404
+ def test_instrumented_executemany_method_empty_query (self , * _ , ** __ ):
405
+ """Should create a span for executemany() with captured parameters."""
406
+ async_call (self ._connection .executemany ("" , []))
407
+ spans = self .memory_exporter .get_finished_spans ()
408
+ self .assertEqual (len (spans ), 1 )
409
+ self .assertIs (StatusCode .UNSET , spans [0 ].status .status_code )
410
+ self .check_span (spans [0 ])
411
+ self .assertEqual (spans [0 ].name , POSTGRES_DB_NAME )
412
+ self .assertEqual (spans [0 ].attributes [SpanAttributes .DB_STATEMENT ], "" )
413
+ self .assertEqual (
414
+ spans [0 ].attributes ["db.statement.parameters" ], "([],)"
415
+ )
416
+
417
+ def test_instrumented_fetch_method_broken_asyncpg (self , * _ , ** __ ):
418
+ """Should create a span for fetch() with "postgresql" as the span name."""
419
+ with patch .object (
420
+ self ._connection , "_params" , namedtuple ("ConnectionParams" , [])
421
+ ):
422
+ async_call (self ._connection .fetch ("" ))
423
+ spans = self .memory_exporter .get_finished_spans ()
424
+ self .assertEqual (len (spans ), 1 )
425
+ self .assertIs (StatusCode .UNSET , spans [0 ].status .status_code )
426
+ self .assertEqual (spans [0 ].name , "postgresql" )
427
+ self .assertEqual (spans [0 ].attributes [SpanAttributes .DB_STATEMENT ], "" )
0 commit comments