1717using TestBuildingBlocks ;
1818using Xunit ;
1919
20+ #pragma warning disable AV1561 // Signature contains too many parameters
21+
2022namespace UnitTests . Middleware
2123{
2224 public sealed class JsonApiRequestTests
2325 {
26+ // @formatter:wrap_lines false
2427 [ Theory ]
25- [ InlineData ( "HEAD" , "/todoItems" , true , EndpointKind . Primary , null , true ) ]
26- [ InlineData ( "HEAD" , "/todoItems/1" , false , EndpointKind . Primary , null , true ) ]
27- [ InlineData ( "HEAD" , "/todoItems/1/owner" , false , EndpointKind . Secondary , null , true ) ]
28- [ InlineData ( "HEAD" , "/todoItems/1/tags" , true , EndpointKind . Secondary , null , true ) ]
29- [ InlineData ( "HEAD" , "/todoItems/1/relationships/owner" , false , EndpointKind . Relationship , null , true ) ]
30- [ InlineData ( "HEAD" , "/todoItems/1/relationships/tags" , true , EndpointKind . Relationship , null , true ) ]
31- [ InlineData ( "GET" , "/todoItems" , true , EndpointKind . Primary , null , true ) ]
32- [ InlineData ( "GET" , "/todoItems/1" , false , EndpointKind . Primary , null , true ) ]
33- [ InlineData ( "GET" , "/todoItems/1/owner" , false , EndpointKind . Secondary , null , true ) ]
34- [ InlineData ( "GET" , "/todoItems/1/tags" , true , EndpointKind . Secondary , null , true ) ]
35- [ InlineData ( "GET" , "/todoItems/1/relationships/owner" , false , EndpointKind . Relationship , null , true ) ]
36- [ InlineData ( "GET" , "/todoItems/1/relationships/tags" , true , EndpointKind . Relationship , null , true ) ]
37- [ InlineData ( "POST" , "/todoItems" , false , EndpointKind . Primary , WriteOperationKind . CreateResource , false ) ]
38- [ InlineData ( "POST" , "/todoItems/1/relationships/tags" , true , EndpointKind . Relationship , WriteOperationKind . AddToRelationship , false ) ]
39- [ InlineData ( "PATCH" , "/todoItems/1" , false , EndpointKind . Primary , WriteOperationKind . UpdateResource , false ) ]
40- [ InlineData ( "PATCH" , "/todoItems/1/relationships/owner" , false , EndpointKind . Relationship , WriteOperationKind . SetRelationship , false ) ]
41- [ InlineData ( "PATCH" , "/todoItems/1/relationships/tags" , true , EndpointKind . Relationship , WriteOperationKind . SetRelationship , false ) ]
42- [ InlineData ( "DELETE" , "/todoItems/1" , false , EndpointKind . Primary , WriteOperationKind . DeleteResource , false ) ]
43- [ InlineData ( "DELETE" , "/todoItems/1/relationships/tags" , true , EndpointKind . Relationship , WriteOperationKind . RemoveFromRelationship , false ) ]
44- public async Task Sets_request_properties_correctly ( string requestMethod , string requestPath , bool expectIsCollection , EndpointKind expectKind ,
45- WriteOperationKind ? expectWriteOperation , bool expectIsReadOnly )
28+ [ InlineData ( "HEAD" , "/todoItems" , EndpointKind . Primary , null , "todoItems" , null , null , IsCollection . Yes , IsReadOnly . Yes , null ) ]
29+ [ InlineData ( "HEAD" , "/people/1" , EndpointKind . Primary , "1" , "people" , null , null , IsCollection . No , IsReadOnly . Yes , null ) ]
30+ [ InlineData ( "HEAD" , "/todoItems/2/owner" , EndpointKind . Secondary , "2" , "todoItems" , "people" , "owner" , IsCollection . No , IsReadOnly . Yes , null ) ]
31+ [ InlineData ( "HEAD" , "/todoItems/3/tags" , EndpointKind . Secondary , "3" , "todoItems" , "itemTags" , "tags" , IsCollection . Yes , IsReadOnly . Yes , null ) ]
32+ [ InlineData ( "HEAD" , "/todoItems/ABC/relationships/owner" , EndpointKind . Relationship , "ABC" , "todoItems" , "people" , "owner" , IsCollection . No , IsReadOnly . Yes , null ) ]
33+ [ InlineData ( "HEAD" , "/todoItems/ABC/relationships/tags" , EndpointKind . Relationship , "ABC" , "todoItems" , "itemTags" , "tags" , IsCollection . Yes , IsReadOnly . Yes , null ) ]
34+ [ InlineData ( "GET" , "/todoItems" , EndpointKind . Primary , null , "todoItems" , null , null , IsCollection . Yes , IsReadOnly . Yes , null ) ]
35+ [ InlineData ( "GET" , "/todoItems/-1" , EndpointKind . Primary , "-1" , "todoItems" , null , null , IsCollection . No , IsReadOnly . Yes , null ) ]
36+ [ InlineData ( "GET" , "/todoItems/1/owner" , EndpointKind . Secondary , "1" , "todoItems" , "people" , "owner" , IsCollection . No , IsReadOnly . Yes , null ) ]
37+ [ InlineData ( "GET" , "/todoItems/1/tags" , EndpointKind . Secondary , "1" , "todoItems" , "itemTags" , "tags" , IsCollection . Yes , IsReadOnly . Yes , null ) ]
38+ [ InlineData ( "GET" , "/todoItems/1/relationships/owner" , EndpointKind . Relationship , "1" , "todoItems" , "people" , "owner" , IsCollection . No , IsReadOnly . Yes , null ) ]
39+ [ InlineData ( "GET" , "/todoItems/1/relationships/tags" , EndpointKind . Relationship , "1" , "todoItems" , "itemTags" , "tags" , IsCollection . Yes , IsReadOnly . Yes , null ) ]
40+ [ InlineData ( "POST" , "/todoItems" , EndpointKind . Primary , null , "todoItems" , null , null , IsCollection . No , IsReadOnly . No , WriteOperationKind . CreateResource ) ]
41+ [ InlineData ( "POST" , "/todoItems/1/relationships/tags" , EndpointKind . Relationship , "1" , "todoItems" , "itemTags" , "tags" , IsCollection . Yes , IsReadOnly . No , WriteOperationKind . AddToRelationship ) ]
42+ [ InlineData ( "PATCH" , "/itemTags/1" , EndpointKind . Primary , "1" , "itemTags" , null , null , IsCollection . No , IsReadOnly . No , WriteOperationKind . UpdateResource ) ]
43+ [ InlineData ( "PATCH" , "/todoItems/1/relationships/owner" , EndpointKind . Relationship , "1" , "todoItems" , "people" , "owner" , IsCollection . No , IsReadOnly . No , WriteOperationKind . SetRelationship ) ]
44+ [ InlineData ( "PATCH" , "/todoItems/1/relationships/tags" , EndpointKind . Relationship , "1" , "todoItems" , "itemTags" , "tags" , IsCollection . Yes , IsReadOnly . No , WriteOperationKind . SetRelationship ) ]
45+ [ InlineData ( "DELETE" , "/todoItems/1" , EndpointKind . Primary , "1" , "todoItems" , null , null , IsCollection . No , IsReadOnly . No , WriteOperationKind . DeleteResource ) ]
46+ [ InlineData ( "DELETE" , "/todoItems/1/relationships/tags" , EndpointKind . Relationship , "1" , "todoItems" , "itemTags" , "tags" , IsCollection . Yes , IsReadOnly . No , WriteOperationKind . RemoveFromRelationship ) ]
47+ [ InlineData ( "POST" , "/operations" , EndpointKind . AtomicOperations , null , null , null , null , IsCollection . No , IsReadOnly . No , null ) ]
48+ // @formatter:wrap_lines restore
49+ public async Task Sets_request_properties_correctly ( string requestMethod , string requestPath , EndpointKind expectKind , string ? expectPrimaryId ,
50+ string ? expectPrimaryResourceType , string ? expectSecondaryResourceType , string ? expectRelationshipName , IsCollection expectIsCollection ,
51+ IsReadOnly expectIsReadOnly , WriteOperationKind ? expectWriteOperation )
4652 {
4753 // Arrange
48- var options = new JsonApiOptions
49- {
50- UseRelativeLinks = true
51- } ;
52-
53- var graphBuilder = new ResourceGraphBuilder ( options , NullLoggerFactory . Instance ) ;
54- graphBuilder . Add < TodoItem , int > ( ) ;
55- graphBuilder . Add < Person , int > ( ) ;
56- graphBuilder . Add < Tag , int > ( ) ;
54+ var options = new JsonApiOptions ( ) ;
55+ var request = new JsonApiRequest ( ) ;
5756
58- IResourceGraph resourceGraph = graphBuilder . Build ( ) ;
57+ // @formatter:keep_existing_linebreaks true
5958
60- var controllerResourceMappingMock = new Mock < IControllerResourceMapping > ( ) ;
59+ IResourceGraph resourceGraph = new ResourceGraphBuilder ( options , NullLoggerFactory . Instance )
60+ . Add < TodoItem , int > ( )
61+ . Add < Person , int > ( )
62+ . Add < ItemTag , int > ( )
63+ . Build ( ) ;
6164
62- ResourceType todoItemType = resourceGraph . GetResourceType < TodoItem > ( ) ;
63- controllerResourceMappingMock . Setup ( mapping => mapping . GetResourceTypeForController ( It . IsAny < Type > ( ) ) ) . Returns ( todoItemType ) ;
65+ // @formatter:keep_existing_linebreaks restore
6466
6567 var httpContext = new DefaultHttpContext ( ) ;
66- SetupRoutes ( httpContext , requestMethod , requestPath ) ;
67-
68- var request = new JsonApiRequest ( ) ;
68+ IControllerResourceMapping controllerResourceMapping = SetupRoutes ( httpContext , resourceGraph , requestMethod , requestPath ) ;
6969
70- var middleware = new JsonApiMiddleware ( _ => Task . CompletedTask , new HttpContextAccessor ( ) ) ;
70+ var middleware = new JsonApiMiddleware ( _ => Task . CompletedTask , new HttpContextAccessor
71+ {
72+ HttpContext = httpContext
73+ } ) ;
7174
7275 // Act
73- await middleware . InvokeAsync ( httpContext , controllerResourceMappingMock . Object , options , request , NullLogger < JsonApiMiddleware > . Instance ) ;
76+ await middleware . InvokeAsync ( httpContext , controllerResourceMapping , options , request , NullLogger < JsonApiMiddleware > . Instance ) ;
7477
7578 // Assert
76- request . IsCollection . Should ( ) . Be ( expectIsCollection ) ;
7779 request . Kind . Should ( ) . Be ( expectKind ) ;
80+ request . PrimaryId . Should ( ) . Be ( expectPrimaryId ) ;
81+
82+ if ( expectPrimaryResourceType == null )
83+ {
84+ request . PrimaryResourceType . Should ( ) . BeNull ( ) ;
85+ }
86+ else
87+ {
88+ request . PrimaryResourceType . ShouldNotBeNull ( ) ;
89+ request . PrimaryResourceType . PublicName . Should ( ) . Be ( expectPrimaryResourceType ) ;
90+ }
91+
92+ if ( expectSecondaryResourceType == null )
93+ {
94+ request . SecondaryResourceType . Should ( ) . BeNull ( ) ;
95+ }
96+ else
97+ {
98+ request . SecondaryResourceType . ShouldNotBeNull ( ) ;
99+ request . SecondaryResourceType . PublicName . Should ( ) . Be ( expectSecondaryResourceType ) ;
100+ }
101+
102+ if ( expectRelationshipName == null )
103+ {
104+ request . Relationship . Should ( ) . BeNull ( ) ;
105+ }
106+ else
107+ {
108+ request . Relationship . ShouldNotBeNull ( ) ;
109+ request . Relationship . PublicName . Should ( ) . Be ( expectRelationshipName ) ;
110+ }
111+
112+ request . IsCollection . Should ( ) . Be ( expectIsCollection == IsCollection . Yes ) ;
113+ request . IsReadOnly . Should ( ) . Be ( expectIsReadOnly == IsReadOnly . Yes ) ;
78114 request . WriteOperation . Should ( ) . Be ( expectWriteOperation ) ;
79- request . IsReadOnly . Should ( ) . Be ( expectIsReadOnly ) ;
80- request . PrimaryResourceType . ShouldNotBeNull ( ) ;
81- request . PrimaryResourceType . PublicName . Should ( ) . Be ( "todoItems" ) ;
82115 }
83116
84- private static void SetupRoutes ( HttpContext httpContext , string requestMethod , string requestPath )
117+ private static IControllerResourceMapping SetupRoutes ( HttpContext httpContext , IResourceGraph resourceGraph , string requestMethod , string requestPath )
85118 {
86119 httpContext . Request . Method = requestMethod ;
87120
@@ -110,6 +143,10 @@ private static void SetupRoutes(HttpContext httpContext, string requestMethod, s
110143 {
111144 feature . RouteValues [ "action" ] = "Relationship" ;
112145 }
146+ else if ( pathSegments . Contains ( "operations" ) )
147+ {
148+ feature . RouteValues [ "action" ] = "PostOperations" ;
149+ }
113150
114151 httpContext . Features . Set < IRouteValuesFeature > ( feature ) ;
115152
@@ -118,7 +155,30 @@ private static void SetupRoutes(HttpContext httpContext, string requestMethod, s
118155 ControllerTypeInfo = ( TypeInfo ) typeof ( object )
119156 } ;
120157
158+ var controllerResourceMappingMock = new Mock < IControllerResourceMapping > ( ) ;
159+
160+ controllerResourceMappingMock . Setup ( mapping => mapping . GetResourceTypeForController ( It . IsAny < Type > ( ) ) ) . Returns ( ( ) =>
161+ {
162+ return pathSegments . Length > 0
163+ ? resourceGraph . GetResourceTypes ( ) . FirstOrDefault ( resourceType => resourceType . PublicName == pathSegments [ 0 ] )
164+ : null ;
165+ } ) ;
166+
121167 httpContext . SetEndpoint ( new Endpoint ( _ => Task . CompletedTask , new EndpointMetadataCollection ( controllerActionDescriptor ) , null ) ) ;
168+
169+ return controllerResourceMappingMock . Object ;
170+ }
171+
172+ public enum IsReadOnly
173+ {
174+ Yes ,
175+ No
176+ }
177+
178+ public enum IsCollection
179+ {
180+ Yes ,
181+ No
122182 }
123183
124184 [ UsedImplicitly ( ImplicitUseTargetFlags . Itself ) ]
@@ -127,7 +187,7 @@ private sealed class Person : Identifiable<int>
127187 }
128188
129189 [ UsedImplicitly ( ImplicitUseTargetFlags . Members ) ]
130- private sealed class Tag : Identifiable < int >
190+ private sealed class ItemTag : Identifiable < int >
131191 {
132192 [ HasMany ]
133193 public ISet < TodoItem > TodoItems { get ; set ; } = new HashSet < TodoItem > ( ) ;
@@ -140,7 +200,7 @@ private sealed class TodoItem : Identifiable<int>
140200 public Person ? Owner { get ; set ; }
141201
142202 [ HasMany ]
143- public ISet < Tag > Tags { get ; set ; } = new HashSet < Tag > ( ) ;
203+ public ISet < ItemTag > Tags { get ; set ; } = new HashSet < ItemTag > ( ) ;
144204 }
145205 }
146206}
0 commit comments