@@ -522,3 +522,133 @@ def test_validators_use_proper_draft():
522
522
}
523
523
cc = canonicalish (schema )
524
524
jsonschema .validators .validator_for (cc ).check_schema (cc )
525
+
526
+
527
+ # Reference to itself
528
+ ROOT_REFERENCE = {"$ref" : "#" }
529
+ # One extra nesting level
530
+ NESTED = {"not" : {"$ref" : "#/not" }}
531
+ # The same as above, but includes "$id".
532
+ NESTED_WITH_ID = {
533
+ "not" : {"$ref" : "#/not" },
534
+ "$id" : "http://json-schema.org/draft-07/schema#" ,
535
+ }
536
+ SELF_REFERENTIAL = {"foo" : {"$ref" : "#foo" }, "not" : {"$ref" : "#foo" }}
537
+
538
+
539
+ @pytest .mark .parametrize (
540
+ "schema, expected" ,
541
+ (
542
+ (ROOT_REFERENCE , ROOT_REFERENCE ),
543
+ (NESTED , NESTED ),
544
+ (NESTED_WITH_ID , NESTED_WITH_ID ),
545
+ # "foo" content should be inlined as is, because "#" is recursive (special case)
546
+ (
547
+ {"foo" : {"$ref" : "#" }, "not" : {"$ref" : "#foo" }},
548
+ {"foo" : {"$ref" : "#" }, "not" : {"$ref" : "#" }},
549
+ ),
550
+ # "foo" content should be inlined as is, because it points to itself
551
+ (
552
+ SELF_REFERENTIAL ,
553
+ SELF_REFERENTIAL ,
554
+ ),
555
+ # The same as above, but with one extra nesting level
556
+ (
557
+ {"foo" : {"not" : {"$ref" : "#foo" }}, "not" : {"$ref" : "#foo" }},
558
+ # 1. We start from resolving "$ref" in "not"
559
+ # 2. at this point we don't know this path is recursive, so we follow to "foo"
560
+ # 3. inside "foo" we found a reference to "foo", which means it is recursive
561
+ {"foo" : {"not" : {"$ref" : "#foo" }}, "not" : {"not" : {"$ref" : "#foo" }}},
562
+ ),
563
+ # Circular reference between two schemas
564
+ (
565
+ {"foo" : {"$ref" : "#bar" }, "bar" : {"$ref" : "#foo" }, "not" : {"$ref" : "#foo" }},
566
+ # 1. We start in "not" and follow to "foo"
567
+ # 2. In "foo" we follow to "bar"
568
+ # 3. Here we see a reference to previously seen scope, which means it is a recursive path
569
+ # We take the schema where we stop and inline it to the starting point (therefore it is `{"$ref": "#foo"}`)
570
+ {"foo" : {"$ref" : "#bar" }, "bar" : {"$ref" : "#foo" }, "not" : {"$ref" : "#foo" }},
571
+ ),
572
+ ),
573
+ )
574
+ def test_skip_recursive_references_simple_schemas (schema , expected ):
575
+ # When there is a recursive reference, it should not be resolved
576
+ assert resolve_all_refs (schema ) == expected
577
+
578
+
579
+ @pytest .mark .parametrize (
580
+ "schema, resolved" ,
581
+ (
582
+ # NOTE. The `resolved` fixture does not include "definitions" to save visual space here, but it is extended
583
+ # with it in the test body.
584
+ # The reference target is behind two references, that share the same definition path. Not a recursive reference
585
+ (
586
+ {
587
+ "definitions" : {
588
+ "properties" : {
589
+ "foo" : {"type" : "string" },
590
+ "bar" : {"$ref" : "#/definitions/properties/foo" },
591
+ },
592
+ },
593
+ "not" : {"$ref" : "#/definitions/properties/bar" },
594
+ },
595
+ {
596
+ "not" : {"type" : "string" },
597
+ },
598
+ ),
599
+ # Here we need to resolve multiple references while being on the same resolution scope:
600
+ # "#/definitions/foo" contains two references
601
+ (
602
+ {
603
+ "definitions" : {
604
+ "foo" : {
605
+ "properties" : {
606
+ "bar" : {"$ref" : "#/definitions/spam" },
607
+ "baz" : {"$ref" : "#/definitions/spam" },
608
+ }
609
+ },
610
+ "spam" : {"type" : "string" },
611
+ },
612
+ "properties" : {"foo" : {"$ref" : "#/definitions/foo" }},
613
+ },
614
+ {
615
+ "properties" : {
616
+ "foo" : {
617
+ "properties" : {
618
+ "bar" : {"type" : "string" },
619
+ "baz" : {"type" : "string" },
620
+ }
621
+ }
622
+ },
623
+ },
624
+ ),
625
+ # Similar to the one above, but recursive
626
+ (
627
+ {
628
+ "definitions" : {
629
+ "foo" : {
630
+ "properties" : {
631
+ "bar" : {"$ref" : "#/definitions/spam" },
632
+ "baz" : {"$ref" : "#/definitions/spam" },
633
+ }
634
+ },
635
+ "spam" : {"$ref" : "#/definitions/foo" },
636
+ },
637
+ "properties" : {"foo" : {"$ref" : "#/definitions/foo" }},
638
+ },
639
+ {
640
+ "properties" : {
641
+ "foo" : {
642
+ "properties" : {
643
+ "bar" : {"$ref" : "#/definitions/foo" },
644
+ "baz" : {"$ref" : "#/definitions/foo" },
645
+ }
646
+ }
647
+ },
648
+ },
649
+ ),
650
+ ),
651
+ )
652
+ def test_skip_recursive_references_complex_schemas (schema , resolved ):
653
+ resolved ["definitions" ] = schema ["definitions" ]
654
+ assert resolve_all_refs (schema ) == resolved
0 commit comments