11
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
# See the License for the specific language governing permissions and
13
13
# limitations under the License.
14
+ # pylint: disable=too-many-lines
15
+
14
16
import unittest
15
17
from collections .abc import Mapping
16
18
from timeit import default_timer
@@ -319,9 +321,56 @@ def test_metric_uninstrument(self):
319
321
if isinstance (point , NumberDataPoint ):
320
322
self .assertEqual (point .value , 0 )
321
323
324
+ def test_sub_app_fastapi_call (self ):
325
+ """
326
+ This test is to ensure that a span in case of a sub app targeted contains the correct server url
327
+
328
+ As this test case covers manual instrumentation, we won't see any additional spans for the sub app.
329
+ In this case all generated spans might suffice the requirements for the attributes already
330
+ (as the testcase is not setting a root_path for the outer app here)
331
+ """
332
+
333
+ self ._client .get ("/sub/home" )
334
+ spans = self .memory_exporter .get_finished_spans ()
335
+ self .assertEqual (len (spans ), 3 )
336
+ for span in spans :
337
+ # As we are only looking to the "outer" app, we would see only the "GET /sub" spans
338
+ self .assertIn ("GET /sub" , span .name )
339
+
340
+ # We now want to specifically test all spans including the
341
+ # - HTTP_TARGET
342
+ # - HTTP_URL
343
+ # attributes to be populated with the expected values
344
+ spans_with_http_attributes = [
345
+ span
346
+ for span in spans
347
+ if (
348
+ SpanAttributes .HTTP_URL in span .attributes
349
+ or SpanAttributes .HTTP_TARGET in span .attributes
350
+ )
351
+ ]
352
+
353
+ # We expect only one span to have the HTTP attributes set (the SERVER span from the app itself)
354
+ # the sub app is not instrumented with manual instrumentation tests.
355
+ self .assertEqual (1 , len (spans_with_http_attributes ))
356
+
357
+ for span in spans_with_http_attributes :
358
+ self .assertEqual (
359
+ "/sub/home" , span .attributes [SpanAttributes .HTTP_TARGET ]
360
+ )
361
+ self .assertEqual (
362
+ "https://testserver:443/sub/home" ,
363
+ span .attributes [SpanAttributes .HTTP_URL ],
364
+ )
365
+
322
366
@staticmethod
323
367
def _create_fastapi_app ():
324
368
app = fastapi .FastAPI ()
369
+ sub_app = fastapi .FastAPI ()
370
+
371
+ @sub_app .get ("/home" )
372
+ async def _ ():
373
+ return {"message" : "sub hi" }
325
374
326
375
@app .get ("/foobar" )
327
376
async def _ ():
@@ -339,6 +388,8 @@ async def _(param: str):
339
388
async def _ ():
340
389
return {"message" : "ok" }
341
390
391
+ app .mount ("/sub" , app = sub_app )
392
+
342
393
return app
343
394
344
395
@@ -453,6 +504,58 @@ def tearDown(self):
453
504
self ._instrumentor .uninstrument ()
454
505
super ().tearDown ()
455
506
507
+ def test_sub_app_fastapi_call (self ):
508
+ """
509
+ !!! Attention: we need to override this testcase for the auto-instrumented variant
510
+ The reason is, that with auto instrumentation, the sub app is instrumented as well
511
+ and therefore we would see the spans for the sub app as well
512
+
513
+ This test is to ensure that a span in case of a sub app targeted contains the correct server url
514
+ """
515
+
516
+ self ._client .get ("/sub/home" )
517
+ spans = self .memory_exporter .get_finished_spans ()
518
+ self .assertEqual (len (spans ), 6 )
519
+
520
+ for span in spans :
521
+ # As we are only looking to the "outer" app, we would see only the "GET /sub" spans
522
+ # -> the outer app is not aware of the sub_apps internal routes
523
+ sub_in = "GET /sub" in span .name
524
+ # The sub app spans are named GET /home as from the sub app perspective the request targets /home
525
+ # -> the sub app is technically not aware of the /sub prefix
526
+ home_in = "GET /home" in span .name
527
+
528
+ # We expect the spans to be either from the outer app or the sub app
529
+ self .assertTrue (
530
+ sub_in or home_in ,
531
+ f"Span { span .name } does not have /sub or /home in its name" ,
532
+ )
533
+
534
+ # We now want to specifically test all spans including the
535
+ # - HTTP_TARGET
536
+ # - HTTP_URL
537
+ # attributes to be populated with the expected values
538
+ spans_with_http_attributes = [
539
+ span
540
+ for span in spans
541
+ if (
542
+ SpanAttributes .HTTP_URL in span .attributes
543
+ or SpanAttributes .HTTP_TARGET in span .attributes
544
+ )
545
+ ]
546
+
547
+ # We now expect spans with attributes from both the app and its sub app
548
+ self .assertEqual (2 , len (spans_with_http_attributes ))
549
+
550
+ for span in spans_with_http_attributes :
551
+ self .assertEqual (
552
+ "/sub/home" , span .attributes [SpanAttributes .HTTP_TARGET ]
553
+ )
554
+ self .assertEqual (
555
+ "https://testserver:443/sub/home" ,
556
+ span .attributes [SpanAttributes .HTTP_URL ],
557
+ )
558
+
456
559
457
560
class TestAutoInstrumentationHooks (TestFastAPIManualInstrumentationHooks ):
458
561
"""
@@ -494,6 +597,58 @@ def tearDown(self):
494
597
self ._instrumentor .uninstrument ()
495
598
super ().tearDown ()
496
599
600
+ def test_sub_app_fastapi_call (self ):
601
+ """
602
+ !!! Attention: we need to override this testcase for the auto-instrumented variant
603
+ The reason is, that with auto instrumentation, the sub app is instrumented as well
604
+ and therefore we would see the spans for the sub app as well
605
+
606
+ This test is to ensure that a span in case of a sub app targeted contains the correct server url
607
+ """
608
+
609
+ self ._client .get ("/sub/home" )
610
+ spans = self .memory_exporter .get_finished_spans ()
611
+ self .assertEqual (len (spans ), 6 )
612
+
613
+ for span in spans :
614
+ # As we are only looking to the "outer" app, we would see only the "GET /sub" spans
615
+ # -> the outer app is not aware of the sub_apps internal routes
616
+ sub_in = "GET /sub" in span .name
617
+ # The sub app spans are named GET /home as from the sub app perspective the request targets /home
618
+ # -> the sub app is technically not aware of the /sub prefix
619
+ home_in = "GET /home" in span .name
620
+
621
+ # We expect the spans to be either from the outer app or the sub app
622
+ self .assertTrue (
623
+ sub_in or home_in ,
624
+ f"Span { span .name } does not have /sub or /home in its name" ,
625
+ )
626
+
627
+ # We now want to specifically test all spans including the
628
+ # - HTTP_TARGET
629
+ # - HTTP_URL
630
+ # attributes to be populated with the expected values
631
+ spans_with_http_attributes = [
632
+ span
633
+ for span in spans
634
+ if (
635
+ SpanAttributes .HTTP_URL in span .attributes
636
+ or SpanAttributes .HTTP_TARGET in span .attributes
637
+ )
638
+ ]
639
+
640
+ # We now expect spans with attributes from both the app and its sub app
641
+ self .assertEqual (2 , len (spans_with_http_attributes ))
642
+
643
+ for span in spans_with_http_attributes :
644
+ self .assertEqual (
645
+ "/sub/home" , span .attributes [SpanAttributes .HTTP_TARGET ]
646
+ )
647
+ self .assertEqual (
648
+ "https://testserver:443/sub/home" ,
649
+ span .attributes [SpanAttributes .HTTP_URL ],
650
+ )
651
+
497
652
498
653
class TestAutoInstrumentationLogic (unittest .TestCase ):
499
654
def test_instrumentation (self ):
0 commit comments