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 = @"