Skip to content

Commit cec524a

Browse files
authored
Merge pull request #771 from microsoft/copilot/sub-pr-770
Add unit tests for UseHttpPutForUpdate setting
2 parents 25df365 + 9e1b6bd commit cec524a

File tree

3 files changed

+280
-0
lines changed

3 files changed

+280
-0
lines changed

test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/ComplexPropertyPathItemHandlerTests.cs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,4 +263,101 @@ public void CreatesComplexPropertyPathsBasedOnTargetPathAnnotations(string reada
263263
Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Get));
264264
}
265265
}
266+
267+
[Theory]
268+
[InlineData(false, 2)]
269+
[InlineData(true, 2)]
270+
public void CreatesComplexPropertyPathItemUsesHttpPutForUpdateWhenSettingIsEnabled(bool useHttpPutForUpdate, int operationCount)
271+
{
272+
// Arrange
273+
var annotation = @"
274+
<Annotation Term=""Org.OData.Capabilities.V1.UpdateRestrictions"">
275+
<Record>
276+
<PropertyValue Property=""Updatable"" Bool=""true"" />
277+
</Record>
278+
</Annotation>
279+
<Annotation Term=""Org.OData.Capabilities.V1.ReadRestrictions"">
280+
<Record>
281+
<PropertyValue Property=""Readable"" Bool=""true"" />
282+
</Record>
283+
</Annotation>";
284+
var target = @"""NS.Customer/BillingAddress""";
285+
var model = EntitySetPathItemHandlerTests.GetEdmModel(annotation: annotation, target: target);
286+
var convertSettings = new OpenApiConvertSettings
287+
{
288+
UseHttpPutForUpdate = useHttpPutForUpdate
289+
};
290+
var context = new ODataContext(model, convertSettings);
291+
var entitySet = model.EntityContainer.FindEntitySet("Customers");
292+
Assert.NotNull(entitySet); // guard
293+
var entityType = entitySet.EntityType;
294+
var property = entityType.FindProperty("BillingAddress");
295+
Assert.NotNull(property); // guard
296+
var path = new ODataPath(new ODataNavigationSourceSegment(entitySet), new ODataKeySegment(entityType), new ODataComplexPropertySegment(property as IEdmStructuralProperty));
297+
Assert.Equal(ODataPathKind.ComplexProperty, path.Kind); // guard
298+
299+
// Act
300+
var pathItem = _pathItemHandler.CreatePathItem(context, path);
301+
302+
// Assert
303+
Assert.NotNull(pathItem);
304+
Assert.Equal(operationCount, pathItem.Operations?.Count ?? 0);
305+
306+
Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Get));
307+
if (useHttpPutForUpdate)
308+
{
309+
Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Put));
310+
Assert.False(pathItem.Operations.ContainsKey(HttpMethod.Patch));
311+
}
312+
else
313+
{
314+
Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Patch));
315+
Assert.False(pathItem.Operations.ContainsKey(HttpMethod.Put));
316+
}
317+
}
318+
319+
[Fact]
320+
public void CreateComplexPropertyPathItemPrefersUpdateMethodAnnotationOverUseHttpPutForUpdateSetting()
321+
{
322+
// Arrange - annotation specifies PUT explicitly, setting is disabled (default PATCH)
323+
var annotation = @"
324+
<Annotation Term=""Org.OData.Capabilities.V1.UpdateRestrictions"">
325+
<Record>
326+
<PropertyValue Property=""UpdateMethod"">
327+
<EnumMember>Org.OData.Capabilities.V1.HttpMethod/PUT</EnumMember>
328+
</PropertyValue>
329+
<PropertyValue Property=""Updatable"" Bool=""true"" />
330+
</Record>
331+
</Annotation>
332+
<Annotation Term=""Org.OData.Capabilities.V1.ReadRestrictions"">
333+
<Record>
334+
<PropertyValue Property=""Readable"" Bool=""true"" />
335+
</Record>
336+
</Annotation>";
337+
var target = @"""NS.Customer/BillingAddress""";
338+
var model = EntitySetPathItemHandlerTests.GetEdmModel(annotation: annotation, target: target);
339+
var convertSettings = new OpenApiConvertSettings
340+
{
341+
UseHttpPutForUpdate = false // Setting says use PATCH (default)
342+
};
343+
var context = new ODataContext(model, convertSettings);
344+
var entitySet = model.EntityContainer.FindEntitySet("Customers");
345+
Assert.NotNull(entitySet); // guard
346+
var entityType = entitySet.EntityType;
347+
var property = entityType.FindProperty("BillingAddress");
348+
Assert.NotNull(property); // guard
349+
var path = new ODataPath(new ODataNavigationSourceSegment(entitySet), new ODataKeySegment(entityType), new ODataComplexPropertySegment(property as IEdmStructuralProperty));
350+
Assert.Equal(ODataPathKind.ComplexProperty, path.Kind); // guard
351+
352+
// Act
353+
var pathItem = _pathItemHandler.CreatePathItem(context, path);
354+
355+
// Assert
356+
Assert.NotNull(pathItem);
357+
Assert.Equal(2, pathItem.Operations?.Count ?? 0);
358+
Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Get));
359+
// Should use PUT from annotation, not PATCH from setting
360+
Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Put));
361+
Assert.False(pathItem.Operations.ContainsKey(HttpMethod.Patch));
362+
}
266363
}

test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/EntityPathItemHandlerTests.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,67 @@ public void CreateEntityPathItemWorksForUpdateMethodRestrictionsCapabilities(boo
232232
VerifyPathItemOperations(annotation, expected);
233233
}
234234

235+
[Theory]
236+
[InlineData(false, new string[] { "get", "patch", "delete" })]
237+
[InlineData(true, new string[] { "get", "put", "delete" })]
238+
public void CreateEntityPathItemUsesHttpPutForUpdateWhenSettingIsEnabled(bool useHttpPutForUpdate, string[] expected)
239+
{
240+
// Arrange
241+
IEdmModel model = EntitySetPathItemHandlerTests.GetEdmModel(annotation: "");
242+
OpenApiConvertSettings settings = new OpenApiConvertSettings
243+
{
244+
UseHttpPutForUpdate = useHttpPutForUpdate
245+
};
246+
ODataContext context = new ODataContext(model, settings);
247+
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
248+
Assert.NotNull(entitySet); // guard
249+
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet), new ODataKeySegment(entitySet.EntityType));
250+
251+
// Act
252+
var pathItem = _pathItemHandler.CreatePathItem(context, path);
253+
254+
// Assert
255+
Assert.NotNull(pathItem);
256+
257+
Assert.NotNull(pathItem.Operations);
258+
Assert.NotEmpty(pathItem.Operations);
259+
Assert.Equal(expected, pathItem.Operations.Select(e => e.Key.ToString().ToLowerInvariant()));
260+
}
261+
262+
[Fact]
263+
public void CreateEntityPathItemPrefersUpdateMethodAnnotationOverUseHttpPutForUpdateSetting()
264+
{
265+
// Arrange - annotation specifies PUT explicitly, setting is disabled (default PATCH)
266+
string annotation = @"
267+
<Annotation Term=""Org.OData.Capabilities.V1.UpdateRestrictions"">
268+
<Record>
269+
<PropertyValue Property=""UpdateMethod"">
270+
<EnumMember>Org.OData.Capabilities.V1.HttpMethod/PUT</EnumMember>
271+
</PropertyValue>
272+
</Record>
273+
</Annotation>";
274+
275+
IEdmModel model = EntitySetPathItemHandlerTests.GetEdmModel(annotation);
276+
OpenApiConvertSettings settings = new OpenApiConvertSettings
277+
{
278+
UseHttpPutForUpdate = false // Setting says use PATCH (default)
279+
};
280+
ODataContext context = new ODataContext(model, settings);
281+
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
282+
Assert.NotNull(entitySet); // guard
283+
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet), new ODataKeySegment(entitySet.EntityType));
284+
285+
// Act
286+
var pathItem = _pathItemHandler.CreatePathItem(context, path);
287+
288+
// Assert
289+
Assert.NotNull(pathItem);
290+
Assert.NotNull(pathItem.Operations);
291+
Assert.NotEmpty(pathItem.Operations);
292+
// Should use PUT from annotation, not PATCH from setting
293+
Assert.Equal(new string[] { "get", "put", "delete" }, pathItem.Operations.Select(e => e.Key.ToString().ToLowerInvariant()));
294+
}
295+
235296
private void VerifyPathItemOperations(string annotation, string[] expected)
236297
{
237298
// Arrange

test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/NavigationPropertyPathItemHandlerTests.cs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,128 @@ public void CreateNavigationPropertyPathItemAddsCustomAttributeValuesToPathExten
602602
Assert.Equal("true", isHiddenValue);
603603
}
604604

605+
[Theory]
606+
[InlineData(false, new string[] { "get", "patch", "delete" })]
607+
[InlineData(true, new string[] { "get", "put", "delete" })]
608+
public void CreateSingleNavigationPropertyPathItemUsesHttpPutForUpdateWhenSettingIsEnabled(bool useHttpPutForUpdate, string[] expected)
609+
{
610+
// Arrange
611+
IEdmModel model = GetEdmModel("");
612+
OpenApiConvertSettings settings = new OpenApiConvertSettings
613+
{
614+
UseHttpPutForUpdate = useHttpPutForUpdate
615+
};
616+
ODataContext context = new ODataContext(model, settings);
617+
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
618+
Assert.NotNull(entitySet); // guard
619+
IEdmEntityType entityType = entitySet.EntityType;
620+
621+
IEdmNavigationProperty property = entityType.DeclaredNavigationProperties()
622+
.FirstOrDefault(c => c.ContainsTarget == true && c.TargetMultiplicity() != EdmMultiplicity.Many);
623+
Assert.NotNull(property);
624+
625+
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet),
626+
new ODataKeySegment(entityType),
627+
new ODataNavigationPropertySegment(property));
628+
629+
// Act
630+
var pathItem = _pathItemHandler.CreatePathItem(context, path);
631+
632+
// Assert
633+
Assert.NotNull(pathItem);
634+
Assert.NotNull(pathItem.Operations);
635+
Assert.NotEmpty(pathItem.Operations);
636+
Assert.Equal(expected, pathItem.Operations.Select(o => o.Key.ToString().ToLowerInvariant()));
637+
}
638+
639+
[Theory]
640+
[InlineData(false, new string[] { "get", "patch", "delete" })]
641+
[InlineData(true, new string[] { "get", "put", "delete" })]
642+
public void CreateCollectionNavigationPropertyPathItemUsesHttpPutForUpdateWhenSettingIsEnabled(bool useHttpPutForUpdate, string[] expected)
643+
{
644+
// Arrange
645+
IEdmModel model = GetEdmModel("");
646+
OpenApiConvertSettings settings = new OpenApiConvertSettings
647+
{
648+
UseHttpPutForUpdate = useHttpPutForUpdate
649+
};
650+
ODataContext context = new ODataContext(model, settings);
651+
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
652+
Assert.NotNull(entitySet); // guard
653+
IEdmEntityType entityType = entitySet.EntityType;
654+
655+
IEdmNavigationProperty property = entityType.DeclaredNavigationProperties()
656+
.FirstOrDefault(c => c.ContainsTarget == true && c.TargetMultiplicity() == EdmMultiplicity.Many);
657+
Assert.NotNull(property);
658+
659+
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet),
660+
new ODataKeySegment(entityType),
661+
new ODataNavigationPropertySegment(property),
662+
new ODataKeySegment(property.ToEntityType()));
663+
664+
// Act
665+
var pathItem = _pathItemHandler.CreatePathItem(context, path);
666+
667+
// Assert
668+
Assert.NotNull(pathItem);
669+
Assert.NotNull(pathItem.Operations);
670+
Assert.NotEmpty(pathItem.Operations);
671+
Assert.Equal(expected, pathItem.Operations.Select(o => o.Key.ToString().ToLowerInvariant()));
672+
}
673+
674+
[Fact]
675+
public void CreateNavigationPropertyPathItemPrefersUpdateMethodAnnotationOverUseHttpPutForUpdateSetting()
676+
{
677+
// Arrange - annotation specifies PUT explicitly, setting is disabled (default PATCH)
678+
string annotation = @"
679+
<Annotation Term=""Org.OData.Capabilities.V1.NavigationRestrictions"">
680+
<Record>
681+
<PropertyValue Property=""RestrictedProperties"" >
682+
<Collection>
683+
<Record>
684+
<PropertyValue Property=""NavigationProperty"" NavigationPropertyPath=""ContainedMyOrder"" />
685+
<PropertyValue Property=""UpdateRestrictions"" >
686+
<Record>
687+
<PropertyValue Property=""UpdateMethod"">
688+
<EnumMember>Org.OData.Capabilities.V1.HttpMethod/PUT</EnumMember>
689+
</PropertyValue>
690+
</Record>
691+
</PropertyValue>
692+
</Record>
693+
</Collection>
694+
</PropertyValue>
695+
</Record>
696+
</Annotation>";
697+
698+
IEdmModel model = GetEdmModel(annotation);
699+
OpenApiConvertSettings settings = new OpenApiConvertSettings
700+
{
701+
UseHttpPutForUpdate = false // Setting says use PATCH (default)
702+
};
703+
ODataContext context = new ODataContext(model, settings);
704+
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
705+
Assert.NotNull(entitySet); // guard
706+
IEdmEntityType entityType = entitySet.EntityType;
707+
708+
IEdmNavigationProperty property = entityType.DeclaredNavigationProperties()
709+
.FirstOrDefault(c => c.ContainsTarget == true && c.Name == "ContainedMyOrder");
710+
Assert.NotNull(property);
711+
712+
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet),
713+
new ODataKeySegment(entityType),
714+
new ODataNavigationPropertySegment(property));
715+
716+
// Act
717+
var pathItem = _pathItemHandler.CreatePathItem(context, path);
718+
719+
// Assert
720+
Assert.NotNull(pathItem);
721+
Assert.NotNull(pathItem.Operations);
722+
Assert.NotEmpty(pathItem.Operations);
723+
// Should use PUT from annotation, not PATCH from setting
724+
Assert.Equal(new string[] { "get", "put", "delete" }, pathItem.Operations.Select(o => o.Key.ToString().ToLowerInvariant()));
725+
}
726+
605727
public static IEdmModel GetEdmModel(string annotation, string annotation2 = "")
606728
{
607729
const string template = @"<edmx:Edmx Version=""4.0"" xmlns:edmx=""http://docs.oasis-open.org/odata/ns/edmx"" xmlns:ags=""http://aggregator.microsoft.com/internal"">

0 commit comments

Comments
 (0)