6
6
from itertools import chain , zip_longest
7
7
from json import dumps , loads
8
8
from typing import Dict , List , Literal , Set , Tuple
9
+ from unittest .mock import call , patch
9
10
from uuid import UUID , uuid4
10
11
11
12
import pytest
20
21
from starlette .datastructures import QueryParams
21
22
22
23
from fastapi_jsonapi .api import RoutersJSONAPI
24
+ from fastapi_jsonapi .schema_builder import SchemaBuilder
23
25
from fastapi_jsonapi .views .view_base import ViewBase
24
26
from tests .common import is_postgres_tests
25
27
from tests .fixtures .app import build_alphabet_app , build_app_custom
52
54
CustomUUIDItemAttributesSchema ,
53
55
PostAttributesBaseSchema ,
54
56
PostCommentAttributesBaseSchema ,
57
+ PostCommentSchema ,
58
+ PostSchema ,
55
59
SelfRelationshipAttributesSchema ,
56
60
SelfRelationshipSchema ,
57
61
UserAttributesBaseSchema ,
@@ -360,6 +364,215 @@ async def test_select_custom_fields_for_includes_without_requesting_includes(
360
364
"meta" : {"count" : 1 , "totalPages" : 1 },
361
365
}
362
366
367
+ def _get_clear_mock_calls (self , mock_obj ) -> list [call ]:
368
+ mock_calls = mock_obj .mock_calls
369
+ return [call_ for call_ in mock_calls if call_ not in [call .__len__ (), call .__str__ ()]]
370
+
371
+ def _prepare_info_schema_calls_to_assert (self , mock_calls ) -> list [call ]:
372
+ calls_to_check = []
373
+ for wrapper_call in mock_calls :
374
+ kwargs = wrapper_call .kwargs
375
+ kwargs ["includes" ] = sorted (kwargs ["includes" ], key = lambda x : x )
376
+
377
+ calls_to_check .append (
378
+ call (
379
+ * wrapper_call .args ,
380
+ ** kwargs ,
381
+ ),
382
+ )
383
+
384
+ return sorted (
385
+ calls_to_check ,
386
+ key = lambda x : (x .kwargs ["base_name" ], x .kwargs ["includes" ]),
387
+ )
388
+
389
+ async def test_check_get_info_schema_cache (
390
+ self ,
391
+ user_1 : User ,
392
+ ):
393
+ resource_type = "user_with_cache"
394
+ with suppress (KeyError ):
395
+ RoutersJSONAPI .all_jsonapi_routers .pop (resource_type )
396
+
397
+ app_with_cache = build_app_custom (
398
+ model = User ,
399
+ schema = UserSchema ,
400
+ schema_in_post = UserInSchemaAllowIdOnPost ,
401
+ schema_in_patch = UserPatchSchema ,
402
+ resource_type = resource_type ,
403
+ # set cache size to enable caching
404
+ max_cache_size = 128 ,
405
+ )
406
+
407
+ target_func_name = "_get_info_from_schema_for_building"
408
+ url = app_with_cache .url_path_for (f"get_{ resource_type } _list" )
409
+ params = {
410
+ "include" : "posts,posts.comments" ,
411
+ }
412
+
413
+ expected_len_with_cache = 6
414
+ expected_len_without_cache = 10
415
+
416
+ with patch .object (
417
+ SchemaBuilder ,
418
+ target_func_name ,
419
+ wraps = app_with_cache .jsonapi_routers .schema_builder ._get_info_from_schema_for_building ,
420
+ ) as wrapped_func :
421
+ async with AsyncClient (app = app_with_cache , base_url = "http://test" ) as client :
422
+ response = await client .get (url , params = params )
423
+ assert response .status_code == status .HTTP_200_OK , response .text
424
+
425
+ calls_to_check = self ._prepare_info_schema_calls_to_assert (self ._get_clear_mock_calls (wrapped_func ))
426
+
427
+ # there are no duplicated calls
428
+ assert calls_to_check == sorted (
429
+ [
430
+ call (
431
+ base_name = "UserSchema" ,
432
+ schema = UserSchema ,
433
+ includes = ["posts" ],
434
+ non_optional_relationships = False ,
435
+ ),
436
+ call (
437
+ base_name = "UserSchema" ,
438
+ schema = UserSchema ,
439
+ includes = ["posts" , "posts.comments" ],
440
+ non_optional_relationships = False ,
441
+ ),
442
+ call (
443
+ base_name = "PostSchema" ,
444
+ schema = PostSchema ,
445
+ includes = [],
446
+ non_optional_relationships = False ,
447
+ ),
448
+ call (
449
+ base_name = "PostSchema" ,
450
+ schema = PostSchema ,
451
+ includes = ["comments" ],
452
+ non_optional_relationships = False ,
453
+ ),
454
+ call (
455
+ base_name = "PostCommentSchema" ,
456
+ schema = PostCommentSchema ,
457
+ includes = [],
458
+ non_optional_relationships = False ,
459
+ ),
460
+ call (
461
+ base_name = "PostCommentSchema" ,
462
+ schema = PostCommentSchema ,
463
+ includes = ["posts" ],
464
+ non_optional_relationships = False ,
465
+ ),
466
+ ],
467
+ key = lambda x : (x .kwargs ["base_name" ], x .kwargs ["includes" ]),
468
+ )
469
+ assert wrapped_func .call_count == expected_len_with_cache
470
+
471
+ response = await client .get (url , params = params )
472
+ assert response .status_code == status .HTTP_200_OK , response .text
473
+
474
+ # there are no new calls
475
+ assert wrapped_func .call_count == expected_len_with_cache
476
+
477
+ resource_type = "user_without_cache"
478
+ with suppress (KeyError ):
479
+ RoutersJSONAPI .all_jsonapi_routers .pop (resource_type )
480
+
481
+ app_without_cache = build_app_custom (
482
+ model = User ,
483
+ schema = UserSchema ,
484
+ schema_in_post = UserInSchemaAllowIdOnPost ,
485
+ schema_in_patch = UserPatchSchema ,
486
+ resource_type = resource_type ,
487
+ max_cache_size = 0 ,
488
+ )
489
+
490
+ with patch .object (
491
+ SchemaBuilder ,
492
+ target_func_name ,
493
+ wraps = app_without_cache .jsonapi_routers .schema_builder ._get_info_from_schema_for_building ,
494
+ ) as wrapped_func :
495
+ async with AsyncClient (app = app_without_cache , base_url = "http://test" ) as client :
496
+ response = await client .get (url , params = params )
497
+ assert response .status_code == status .HTTP_200_OK , response .text
498
+
499
+ calls_to_check = self ._prepare_info_schema_calls_to_assert (self ._get_clear_mock_calls (wrapped_func ))
500
+
501
+ # there are duplicated calls
502
+ assert calls_to_check == sorted (
503
+ [
504
+ call (
505
+ base_name = "UserSchema" ,
506
+ schema = UserSchema ,
507
+ includes = ["posts" ],
508
+ non_optional_relationships = False ,
509
+ ),
510
+ call (
511
+ base_name = "UserSchema" ,
512
+ schema = UserSchema ,
513
+ includes = ["posts" ],
514
+ non_optional_relationships = False ,
515
+ ), # duplicate
516
+ call (
517
+ base_name = "UserSchema" ,
518
+ schema = UserSchema ,
519
+ includes = ["posts" , "posts.comments" ],
520
+ non_optional_relationships = False ,
521
+ ),
522
+ call (
523
+ base_name = "PostSchema" ,
524
+ schema = PostSchema ,
525
+ includes = [],
526
+ non_optional_relationships = False ,
527
+ ),
528
+ call (
529
+ base_name = "PostSchema" ,
530
+ schema = PostSchema ,
531
+ includes = [],
532
+ non_optional_relationships = False ,
533
+ ), # duplicate
534
+ call (
535
+ base_name = "PostSchema" ,
536
+ schema = PostSchema ,
537
+ includes = [],
538
+ non_optional_relationships = False ,
539
+ ), # duplicate
540
+ call (
541
+ base_name = "PostSchema" ,
542
+ schema = PostSchema ,
543
+ includes = ["comments" ],
544
+ non_optional_relationships = False ,
545
+ ),
546
+ call (
547
+ base_name = "PostSchema" ,
548
+ schema = PostSchema ,
549
+ includes = ["comments" ],
550
+ non_optional_relationships = False ,
551
+ ), # duplicate
552
+ call (
553
+ base_name = "PostCommentSchema" ,
554
+ schema = PostCommentSchema ,
555
+ includes = [],
556
+ non_optional_relationships = False ,
557
+ ),
558
+ call (
559
+ base_name = "PostCommentSchema" ,
560
+ schema = PostCommentSchema ,
561
+ includes = ["posts" ],
562
+ non_optional_relationships = False ,
563
+ ), # duplicate
564
+ ],
565
+ key = lambda x : (x .kwargs ["base_name" ], x .kwargs ["includes" ]),
566
+ )
567
+
568
+ assert wrapped_func .call_count == expected_len_without_cache
569
+
570
+ response = await client .get (url , params = params )
571
+ assert response .status_code == status .HTTP_200_OK , response .text
572
+
573
+ # there are new calls
574
+ assert wrapped_func .call_count == expected_len_without_cache * 2
575
+
363
576
364
577
class TestCreatePostAndComments :
365
578
async def test_get_posts_with_users (
@@ -371,6 +584,13 @@ async def test_get_posts_with_users(
371
584
user_1_posts : List [Post ],
372
585
user_2_posts : List [Post ],
373
586
):
587
+ call (
588
+ base_name = "UserSchema" ,
589
+ schema = UserSchema ,
590
+ includes = ["posts" ],
591
+ non_optional_relationships = False ,
592
+ on_optional_relationships = False ,
593
+ )
374
594
url = app .url_path_for ("get_post_list" )
375
595
url = f"{ url } ?include=user"
376
596
response = await client .get (url )
0 commit comments