From d9195fbeb44b8cb56ebaa608a923009a322f1bf2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 13 Jan 2026 12:52:06 +0000
Subject: [PATCH 1/2] Initial plan
From 9e1b6bd1f53b0cf8ebd15335e261f6edc34cc1b1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 13 Jan 2026 12:59:30 +0000
Subject: [PATCH 2/2] Add unit tests for UseHttpPutForUpdate feature
Co-authored-by: baywet <7905502+baywet@users.noreply.github.com>
---
.../ComplexPropertyPathItemHandlerTests.cs | 97 ++++++++++++++
.../PathItem/EntityPathItemHandlerTests.cs | 61 +++++++++
.../NavigationPropertyPathItemHandlerTests.cs | 122 ++++++++++++++++++
3 files changed, 280 insertions(+)
diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/ComplexPropertyPathItemHandlerTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/ComplexPropertyPathItemHandlerTests.cs
index 0a413cd1..28345303 100644
--- a/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/ComplexPropertyPathItemHandlerTests.cs
+++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/ComplexPropertyPathItemHandlerTests.cs
@@ -263,4 +263,101 @@ public void CreatesComplexPropertyPathsBasedOnTargetPathAnnotations(string reada
Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Get));
}
}
+
+ [Theory]
+ [InlineData(false, 2)]
+ [InlineData(true, 2)]
+ public void CreatesComplexPropertyPathItemUsesHttpPutForUpdateWhenSettingIsEnabled(bool useHttpPutForUpdate, int operationCount)
+ {
+ // Arrange
+ var annotation = @"
+
+
+
+
+
+
+
+
+
+";
+ var target = @"""NS.Customer/BillingAddress""";
+ var model = EntitySetPathItemHandlerTests.GetEdmModel(annotation: annotation, target: target);
+ var convertSettings = new OpenApiConvertSettings
+ {
+ UseHttpPutForUpdate = useHttpPutForUpdate
+ };
+ var context = new ODataContext(model, convertSettings);
+ var entitySet = model.EntityContainer.FindEntitySet("Customers");
+ Assert.NotNull(entitySet); // guard
+ var entityType = entitySet.EntityType;
+ var property = entityType.FindProperty("BillingAddress");
+ Assert.NotNull(property); // guard
+ var path = new ODataPath(new ODataNavigationSourceSegment(entitySet), new ODataKeySegment(entityType), new ODataComplexPropertySegment(property as IEdmStructuralProperty));
+ Assert.Equal(ODataPathKind.ComplexProperty, path.Kind); // guard
+
+ // Act
+ var pathItem = _pathItemHandler.CreatePathItem(context, path);
+
+ // Assert
+ Assert.NotNull(pathItem);
+ Assert.Equal(operationCount, pathItem.Operations?.Count ?? 0);
+
+ Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Get));
+ if (useHttpPutForUpdate)
+ {
+ Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Put));
+ Assert.False(pathItem.Operations.ContainsKey(HttpMethod.Patch));
+ }
+ else
+ {
+ Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Patch));
+ Assert.False(pathItem.Operations.ContainsKey(HttpMethod.Put));
+ }
+ }
+
+ [Fact]
+ public void CreateComplexPropertyPathItemPrefersUpdateMethodAnnotationOverUseHttpPutForUpdateSetting()
+ {
+ // Arrange - annotation specifies PUT explicitly, setting is disabled (default PATCH)
+ var annotation = @"
+
+
+
+ Org.OData.Capabilities.V1.HttpMethod/PUT
+
+
+
+
+
+
+
+
+";
+ var target = @"""NS.Customer/BillingAddress""";
+ var model = EntitySetPathItemHandlerTests.GetEdmModel(annotation: annotation, target: target);
+ var convertSettings = new OpenApiConvertSettings
+ {
+ UseHttpPutForUpdate = false // Setting says use PATCH (default)
+ };
+ var context = new ODataContext(model, convertSettings);
+ var entitySet = model.EntityContainer.FindEntitySet("Customers");
+ Assert.NotNull(entitySet); // guard
+ var entityType = entitySet.EntityType;
+ var property = entityType.FindProperty("BillingAddress");
+ Assert.NotNull(property); // guard
+ var path = new ODataPath(new ODataNavigationSourceSegment(entitySet), new ODataKeySegment(entityType), new ODataComplexPropertySegment(property as IEdmStructuralProperty));
+ Assert.Equal(ODataPathKind.ComplexProperty, path.Kind); // guard
+
+ // Act
+ var pathItem = _pathItemHandler.CreatePathItem(context, path);
+
+ // Assert
+ Assert.NotNull(pathItem);
+ Assert.Equal(2, pathItem.Operations?.Count ?? 0);
+ Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Get));
+ // Should use PUT from annotation, not PATCH from setting
+ Assert.True(pathItem.Operations.ContainsKey(HttpMethod.Put));
+ Assert.False(pathItem.Operations.ContainsKey(HttpMethod.Patch));
+ }
}
\ No newline at end of file
diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/EntityPathItemHandlerTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/EntityPathItemHandlerTests.cs
index 7d6c6c61..92885c16 100644
--- a/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/EntityPathItemHandlerTests.cs
+++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/EntityPathItemHandlerTests.cs
@@ -232,6 +232,67 @@ public void CreateEntityPathItemWorksForUpdateMethodRestrictionsCapabilities(boo
VerifyPathItemOperations(annotation, expected);
}
+ [Theory]
+ [InlineData(false, new string[] { "get", "patch", "delete" })]
+ [InlineData(true, new string[] { "get", "put", "delete" })]
+ public void CreateEntityPathItemUsesHttpPutForUpdateWhenSettingIsEnabled(bool useHttpPutForUpdate, string[] expected)
+ {
+ // Arrange
+ IEdmModel model = EntitySetPathItemHandlerTests.GetEdmModel(annotation: "");
+ OpenApiConvertSettings settings = new OpenApiConvertSettings
+ {
+ UseHttpPutForUpdate = useHttpPutForUpdate
+ };
+ ODataContext context = new ODataContext(model, settings);
+ IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
+ Assert.NotNull(entitySet); // guard
+ ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet), new ODataKeySegment(entitySet.EntityType));
+
+ // Act
+ var pathItem = _pathItemHandler.CreatePathItem(context, path);
+
+ // Assert
+ Assert.NotNull(pathItem);
+
+ Assert.NotNull(pathItem.Operations);
+ Assert.NotEmpty(pathItem.Operations);
+ Assert.Equal(expected, pathItem.Operations.Select(e => e.Key.ToString().ToLowerInvariant()));
+ }
+
+ [Fact]
+ public void CreateEntityPathItemPrefersUpdateMethodAnnotationOverUseHttpPutForUpdateSetting()
+ {
+ // Arrange - annotation specifies PUT explicitly, setting is disabled (default PATCH)
+ string annotation = @"
+
+
+
+ Org.OData.Capabilities.V1.HttpMethod/PUT
+
+
+";
+
+ IEdmModel model = EntitySetPathItemHandlerTests.GetEdmModel(annotation);
+ OpenApiConvertSettings settings = new OpenApiConvertSettings
+ {
+ UseHttpPutForUpdate = false // Setting says use PATCH (default)
+ };
+ ODataContext context = new ODataContext(model, settings);
+ IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
+ Assert.NotNull(entitySet); // guard
+ ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet), new ODataKeySegment(entitySet.EntityType));
+
+ // Act
+ var pathItem = _pathItemHandler.CreatePathItem(context, path);
+
+ // Assert
+ Assert.NotNull(pathItem);
+ Assert.NotNull(pathItem.Operations);
+ Assert.NotEmpty(pathItem.Operations);
+ // Should use PUT from annotation, not PATCH from setting
+ Assert.Equal(new string[] { "get", "put", "delete" }, pathItem.Operations.Select(e => e.Key.ToString().ToLowerInvariant()));
+ }
+
private void VerifyPathItemOperations(string annotation, string[] expected)
{
// Arrange
diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/NavigationPropertyPathItemHandlerTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/NavigationPropertyPathItemHandlerTests.cs
index 32c456d7..76b43541 100644
--- a/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/NavigationPropertyPathItemHandlerTests.cs
+++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/PathItem/NavigationPropertyPathItemHandlerTests.cs
@@ -602,6 +602,128 @@ public void CreateNavigationPropertyPathItemAddsCustomAttributeValuesToPathExten
Assert.Equal("true", isHiddenValue);
}
+ [Theory]
+ [InlineData(false, new string[] { "get", "patch", "delete" })]
+ [InlineData(true, new string[] { "get", "put", "delete" })]
+ public void CreateSingleNavigationPropertyPathItemUsesHttpPutForUpdateWhenSettingIsEnabled(bool useHttpPutForUpdate, string[] expected)
+ {
+ // Arrange
+ IEdmModel model = GetEdmModel("");
+ OpenApiConvertSettings settings = new OpenApiConvertSettings
+ {
+ UseHttpPutForUpdate = useHttpPutForUpdate
+ };
+ ODataContext context = new ODataContext(model, settings);
+ IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
+ Assert.NotNull(entitySet); // guard
+ IEdmEntityType entityType = entitySet.EntityType;
+
+ IEdmNavigationProperty property = entityType.DeclaredNavigationProperties()
+ .FirstOrDefault(c => c.ContainsTarget == true && c.TargetMultiplicity() != EdmMultiplicity.Many);
+ Assert.NotNull(property);
+
+ ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet),
+ new ODataKeySegment(entityType),
+ new ODataNavigationPropertySegment(property));
+
+ // Act
+ var pathItem = _pathItemHandler.CreatePathItem(context, path);
+
+ // Assert
+ Assert.NotNull(pathItem);
+ Assert.NotNull(pathItem.Operations);
+ Assert.NotEmpty(pathItem.Operations);
+ Assert.Equal(expected, pathItem.Operations.Select(o => o.Key.ToString().ToLowerInvariant()));
+ }
+
+ [Theory]
+ [InlineData(false, new string[] { "get", "patch", "delete" })]
+ [InlineData(true, new string[] { "get", "put", "delete" })]
+ public void CreateCollectionNavigationPropertyPathItemUsesHttpPutForUpdateWhenSettingIsEnabled(bool useHttpPutForUpdate, string[] expected)
+ {
+ // Arrange
+ IEdmModel model = GetEdmModel("");
+ OpenApiConvertSettings settings = new OpenApiConvertSettings
+ {
+ UseHttpPutForUpdate = useHttpPutForUpdate
+ };
+ ODataContext context = new ODataContext(model, settings);
+ IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
+ Assert.NotNull(entitySet); // guard
+ IEdmEntityType entityType = entitySet.EntityType;
+
+ IEdmNavigationProperty property = entityType.DeclaredNavigationProperties()
+ .FirstOrDefault(c => c.ContainsTarget == true && c.TargetMultiplicity() == EdmMultiplicity.Many);
+ Assert.NotNull(property);
+
+ ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet),
+ new ODataKeySegment(entityType),
+ new ODataNavigationPropertySegment(property),
+ new ODataKeySegment(property.ToEntityType()));
+
+ // Act
+ var pathItem = _pathItemHandler.CreatePathItem(context, path);
+
+ // Assert
+ Assert.NotNull(pathItem);
+ Assert.NotNull(pathItem.Operations);
+ Assert.NotEmpty(pathItem.Operations);
+ Assert.Equal(expected, pathItem.Operations.Select(o => o.Key.ToString().ToLowerInvariant()));
+ }
+
+ [Fact]
+ public void CreateNavigationPropertyPathItemPrefersUpdateMethodAnnotationOverUseHttpPutForUpdateSetting()
+ {
+ // Arrange - annotation specifies PUT explicitly, setting is disabled (default PATCH)
+ string annotation = @"
+
+
+
+
+
+
+
+
+
+ Org.OData.Capabilities.V1.HttpMethod/PUT
+
+
+
+
+
+
+
+";
+
+ IEdmModel model = GetEdmModel(annotation);
+ OpenApiConvertSettings settings = new OpenApiConvertSettings
+ {
+ UseHttpPutForUpdate = false // Setting says use PATCH (default)
+ };
+ ODataContext context = new ODataContext(model, settings);
+ IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
+ Assert.NotNull(entitySet); // guard
+ IEdmEntityType entityType = entitySet.EntityType;
+
+ IEdmNavigationProperty property = entityType.DeclaredNavigationProperties()
+ .FirstOrDefault(c => c.ContainsTarget == true && c.Name == "ContainedMyOrder");
+ Assert.NotNull(property);
+
+ ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet),
+ new ODataKeySegment(entityType),
+ new ODataNavigationPropertySegment(property));
+
+ // Act
+ var pathItem = _pathItemHandler.CreatePathItem(context, path);
+
+ // Assert
+ Assert.NotNull(pathItem);
+ Assert.NotNull(pathItem.Operations);
+ Assert.NotEmpty(pathItem.Operations);
+ // Should use PUT from annotation, not PATCH from setting
+ Assert.Equal(new string[] { "get", "put", "delete" }, pathItem.Operations.Select(o => o.Key.ToString().ToLowerInvariant()));
+ }
+
public static IEdmModel GetEdmModel(string annotation, string annotation2 = "")
{
const string template = @"