@@ -1004,6 +1004,124 @@ await VerifyOpenApiDocument(builder, document =>
10041004 } ) ;
10051005 }
10061006
1007+ // Test for: https://github.com/dotnet/aspnetcore/issues/63503
1008+ [ Fact ]
1009+ public async Task HandlesCircularReferencesRegardlessOfPropertyOrder_SelfFirst ( )
1010+ {
1011+ var builder = CreateBuilder ( ) ;
1012+ builder . MapPost ( "/" , ( DirectCircularModelSelfFirst dto ) => { } ) ;
1013+
1014+ // Assert
1015+ await VerifyOpenApiDocument ( builder , document =>
1016+ {
1017+ Assert . NotNull ( document . Components ? . Schemas ) ;
1018+ var schema = document . Components . Schemas [ "DirectCircularModelSelfFirst" ] ;
1019+ Assert . Equal ( JsonSchemaType . Object , schema . Type ) ;
1020+ Assert . NotNull ( schema . Properties ) ;
1021+ Assert . Collection ( schema . Properties ,
1022+ property =>
1023+ {
1024+ Assert . Equal ( "self" , property . Key ) ;
1025+ var reference = Assert . IsType < OpenApiSchemaReference > ( property . Value ) ;
1026+ Assert . Equal ( "#/components/schemas/DirectCircularModelSelfFirst" , reference . Reference . ReferenceV3 ) ;
1027+ } ,
1028+ property =>
1029+ {
1030+ Assert . Equal ( "referenced" , property . Key ) ;
1031+ var reference = Assert . IsType < OpenApiSchemaReference > ( property . Value ) ;
1032+ } ) ;
1033+
1034+ // Verify that it does not result in an empty schema for a referenced schema
1035+ var referencedSchema = document . Components . Schemas [ "ReferencedModel" ] ;
1036+ Assert . NotNull ( referencedSchema . Properties ) ;
1037+ Assert . NotEmpty ( referencedSchema . Properties ) ;
1038+ var idProperty = Assert . Single ( referencedSchema . Properties ) ;
1039+ Assert . Equal ( "id" , idProperty . Key ) ;
1040+ var idPropertySchema = Assert . IsType < OpenApiSchema > ( idProperty . Value ) ;
1041+ Assert . Equal ( JsonSchemaType . Integer , idPropertySchema . Type ) ;
1042+ } ) ;
1043+ }
1044+
1045+ // Test for: https://github.com/dotnet/aspnetcore/issues/63503
1046+ [ Fact ]
1047+ public async Task HandlesCircularReferencesRegardlessOfPropertyOrder_SelfLast ( )
1048+ {
1049+ var builder = CreateBuilder ( ) ;
1050+ builder . MapPost ( "/" , ( DirectCircularModelSelfLast dto ) => { } ) ;
1051+
1052+ await VerifyOpenApiDocument ( builder , document =>
1053+ {
1054+ Assert . NotNull ( document . Components ? . Schemas ) ;
1055+ var schema = document . Components . Schemas [ "DirectCircularModelSelfLast" ] ;
1056+ Assert . Equal ( JsonSchemaType . Object , schema . Type ) ;
1057+ Assert . NotNull ( schema . Properties ) ;
1058+ Assert . Collection ( schema . Properties ,
1059+ property =>
1060+ {
1061+ Assert . Equal ( "referenced" , property . Key ) ;
1062+ var reference = Assert . IsType < OpenApiSchemaReference > ( property . Value ) ;
1063+ } ,
1064+ property =>
1065+ {
1066+ Assert . Equal ( "self" , property . Key ) ;
1067+ var reference = Assert . IsType < OpenApiSchemaReference > ( property . Value ) ;
1068+ Assert . Equal ( "#/components/schemas/DirectCircularModelSelfLast" , reference . Reference . ReferenceV3 ) ;
1069+ } ) ;
1070+
1071+ // Verify that it does not result in an empty schema for a referenced schema
1072+ var referencedSchema = document . Components . Schemas [ "ReferencedModel" ] ;
1073+ Assert . NotNull ( referencedSchema . Properties ) ;
1074+ Assert . NotEmpty ( referencedSchema . Properties ) ;
1075+ var idProperty = Assert . Single ( referencedSchema . Properties ) ;
1076+ Assert . Equal ( "id" , idProperty . Key ) ;
1077+ var idPropertySchema = Assert . IsType < OpenApiSchema > ( idProperty . Value ) ;
1078+ Assert . Equal ( JsonSchemaType . Integer , idPropertySchema . Type ) ;
1079+ } ) ;
1080+ }
1081+
1082+ // Test for: https://github.com/dotnet/aspnetcore/issues/63503
1083+ [ Fact ]
1084+ public async Task HandlesCircularReferencesRegardlessOfPropertyOrder_MultipleSelf ( )
1085+ {
1086+ var builder = CreateBuilder ( ) ;
1087+ builder . MapPost ( "/" , ( DirectCircularModelMultiple dto ) => { } ) ;
1088+
1089+ await VerifyOpenApiDocument ( builder , document =>
1090+ {
1091+ Assert . NotNull ( document . Components ? . Schemas ) ;
1092+ var schema = document . Components . Schemas [ "DirectCircularModelMultiple" ] ;
1093+ Assert . Equal ( JsonSchemaType . Object , schema . Type ) ;
1094+ Assert . NotNull ( schema . Properties ) ;
1095+ Assert . Collection ( schema . Properties ,
1096+ property =>
1097+ {
1098+ Assert . Equal ( "selfFirst" , property . Key ) ;
1099+ var reference = Assert . IsType < OpenApiSchemaReference > ( property . Value ) ;
1100+ Assert . Equal ( "#/components/schemas/DirectCircularModelMultiple" , reference . Reference . ReferenceV3 ) ;
1101+ } ,
1102+ property =>
1103+ {
1104+ Assert . Equal ( "referenced" , property . Key ) ;
1105+ var reference = Assert . IsType < OpenApiSchemaReference > ( property . Value ) ;
1106+ } ,
1107+ property =>
1108+ {
1109+ Assert . Equal ( "selfLast" , property . Key ) ;
1110+ var reference = Assert . IsType < OpenApiSchemaReference > ( property . Value ) ;
1111+ Assert . Equal ( "#/components/schemas/DirectCircularModelMultiple" , reference . Reference . ReferenceV3 ) ;
1112+ } ) ;
1113+
1114+ // Verify that it does not result in an empty schema for a referenced schema
1115+ var referencedSchema = document . Components . Schemas [ "ReferencedModel" ] ;
1116+ Assert . NotNull ( referencedSchema . Properties ) ;
1117+ Assert . NotEmpty ( referencedSchema . Properties ) ;
1118+ var idProperty = Assert . Single ( referencedSchema . Properties ) ;
1119+ Assert . Equal ( "id" , idProperty . Key ) ;
1120+ var idPropertySchema = Assert . IsType < OpenApiSchema > ( idProperty . Value ) ;
1121+ Assert . Equal ( JsonSchemaType . Integer , idPropertySchema . Type ) ;
1122+ } ) ;
1123+ }
1124+
10071125 // Test models for issue 61194
10081126 private class Config
10091127 {
@@ -1060,5 +1178,30 @@ public sealed class RefUser
10601178 public string Name { get ; set ; } = "" ;
10611179 public string Email { get ; set ; } = "" ;
10621180 }
1181+
1182+ // Test models for issue 63503
1183+ private class DirectCircularModelSelfFirst
1184+ {
1185+ public DirectCircularModelSelfFirst Self { get ; set ; } = null ! ;
1186+ public ReferencedModel Referenced { get ; set ; } = null ! ;
1187+ }
1188+
1189+ private class DirectCircularModelSelfLast
1190+ {
1191+ public ReferencedModel Referenced { get ; set ; } = null ! ;
1192+ public DirectCircularModelSelfLast Self { get ; set ; } = null ! ;
1193+ }
1194+
1195+ private class DirectCircularModelMultiple
1196+ {
1197+ public DirectCircularModelMultiple SelfFirst { get ; set ; } = null ! ;
1198+ public ReferencedModel Referenced { get ; set ; } = null ! ;
1199+ public DirectCircularModelMultiple SelfLast { get ; set ; } = null ! ;
1200+ }
1201+
1202+ private class ReferencedModel
1203+ {
1204+ public int Id { get ; set ; }
1205+ }
10631206}
10641207#nullable restore
0 commit comments