OpenAPI.NET.OData/test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiSchemaGeneratorTests.cs
mispeer c25df986ac Users/mispeer/serialize nullable references for v2 (#34)
* Introduce setting hidden from UI, such that openApiSpec version 2 has the nullable references properly serialized.

AnyOf does not exist in v2, and those references are currently lost.

* Updating existing tests for nullable/schema areas where v2 and v3 differ

Making them theories to properly test both openApi specifications
Adding relevant yaml, json test files
Modifying code to ensure enums and entities also generate openApi with no anyOf for openApi v2 (not supported)

* Fix "Succeeded" typo in output for util

Fix tab spacing in util
Add version setting in util with documentation

* Remove tabs

* Refactor tests to take SpecVersion directly in InlineData

Modify 2 existing tests to test json schema generation by passed spec version
2019-06-19 14:27:48 -07:00

590 lines
19 KiB
C#

// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System;
using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Generator;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.OpenApi.OData.Tests
{
public class OpenApiSchemaGeneratorTest
{
private ITestOutputHelper _output;
public OpenApiSchemaGeneratorTest(ITestOutputHelper output)
{
_output = output;
}
[Fact]
public void CreateSchemasThrowArgumentNullContext()
{
// Arrange
ODataContext context = null;
// Act & Assert
Assert.Throws<ArgumentNullException>("context", () => context.CreateSchemas());
}
#region StructuredTypeSchema
[Fact]
public void CreateStructuredTypeSchemaThrowArgumentNullContext()
{
// Arrange
ODataContext context = null;
// Act & Assert
Assert.Throws<ArgumentNullException>("context", () => context.CreateStructuredTypeSchema(structuredType: null));
}
[Fact]
public void CreateStructuredTypeSchemaThrowArgumentNullEnumType()
{
// Arrange
ODataContext context = new ODataContext(EdmCoreModel.Instance);
// Act & Assert
Assert.Throws<ArgumentNullException>("structuredType", () => context.CreateStructuredTypeSchema(structuredType: null));
}
[Fact]
public void CreateComplexTypeWithoutBaseSchemaReturnCorrectSchema()
{
// Arrange
IEdmModel model = EdmModelHelper.MultipleInheritanceEdmModel;
ODataContext context = new ODataContext(model);
IEdmComplexType complex = model.SchemaElements.OfType<IEdmComplexType>().First(t => t.Name == "Address");
Assert.NotNull(complex); // Guard
// Act
var schema = context.CreateStructuredTypeSchema(complex);
// Assert
Assert.NotNull(schema);
Assert.Equal("object", schema.Type);
Assert.Null(schema.AllOf);
Assert.NotNull(schema.Properties);
Assert.Equal(2, schema.Properties.Count);
Assert.Equal(new string[] { "Street", "City" }, schema.Properties.Select(e => e.Key));
Assert.Equal("Complex type 'Address' description.", schema.Description);
Assert.Equal("Address", schema.Title);
// Act
string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
// Assert
Assert.NotNull(json);
Assert.Equal(@"{
""title"": ""Address"",
""type"": ""object"",
""properties"": {
""Street"": {
""type"": ""string"",
""nullable"": true
},
""City"": {
""type"": ""string"",
""nullable"": true
}
},
""description"": ""Complex type 'Address' description."",
""example"": {
""Street"": ""string"",
""City"": ""string""
}
}".ChangeLineBreaks(), json);
}
[Fact]
public void CreateComplexTypeWithBaseSchemaReturnCorrectSchema()
{
// Arrange
IEdmModel model = EdmModelHelper.MultipleInheritanceEdmModel;
ODataContext context = new ODataContext(model, new OpenApiConvertSettings
{
IEEE754Compatible = true
});
IEdmComplexType complex = model.SchemaElements.OfType<IEdmComplexType>().First(t => t.Name == "Tree");
Assert.NotNull(complex); // Guard
// Act
var schema = context.CreateStructuredTypeSchema(complex);
// Assert
Assert.NotNull(schema);
Assert.True(String.IsNullOrEmpty(schema.Type));
Assert.NotNull(schema.AllOf);
Assert.Null(schema.AnyOf);
Assert.Null(schema.OneOf);
Assert.Null(schema.Properties);
Assert.Equal(2, schema.AllOf.Count);
var baseSchema = schema.AllOf.First();
Assert.NotNull(baseSchema.Reference);
Assert.Equal(ReferenceType.Schema, baseSchema.Reference.Type);
Assert.Equal("NS.LandPlant", baseSchema.Reference.Id);
var declaredSchema = schema.AllOf.Last();
Assert.Equal("object", declaredSchema.Type);
Assert.Null(declaredSchema.AllOf);
Assert.Null(declaredSchema.AnyOf);
Assert.Null(declaredSchema.OneOf);
Assert.NotNull(declaredSchema.Properties);
Assert.Equal(1, declaredSchema.Properties.Count);
var property = Assert.Single(declaredSchema.Properties);
Assert.Equal("Price", property.Key);
Assert.Equal("decimal", property.Value.Format);
Assert.NotNull(property.Value.AnyOf);
Assert.Equal(new string[] { "number", "string" }, property.Value.AnyOf.Select(e => e.Type));
Assert.Equal("Complex type 'Tree' description.", declaredSchema.Description);
Assert.Equal("Tree", declaredSchema.Title);
// Act
string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
// Assert
Assert.NotNull(json);
Assert.Equal(@"{
""allOf"": [
{
""$ref"": ""#/components/schemas/NS.LandPlant""
},
{
""title"": ""Tree"",
""type"": ""object"",
""properties"": {
""Price"": {
""multipleOf"": 1,
""anyOf"": [
{
""type"": ""number""
},
{
""type"": ""string""
}
],
""format"": ""decimal""
}
},
""description"": ""Complex type 'Tree' description.""
}
],
""example"": {
""Color"": {
""@odata.type"": ""NS.Color""
},
""Continent"": {
""@odata.type"": ""NS.Continent""
},
""Name"": ""string"",
""Price"": ""decimal""
}
}"
.ChangeLineBreaks(), json);
}
[Fact]
public void CreateEntityTypeWithoutBaseSchemaReturnCorrectSchema()
{
// Arrange
IEdmModel model = EdmModelHelper.MultipleInheritanceEdmModel;
ODataContext context = new ODataContext(model);
IEdmEntityType entity = model.SchemaElements.OfType<IEdmEntityType>().First(t => t.Name == "Zoo");
Assert.NotNull(entity); // Guard
// Act
var schema = context.CreateStructuredTypeSchema(entity);
// Assert
Assert.NotNull(schema);
Assert.Equal("object", schema.Type);
Assert.Null(schema.AllOf);
Assert.NotNull(schema.Properties);
Assert.Equal(2, schema.Properties.Count);
Assert.Equal(new string[] { "Id", "Creatures" }, schema.Properties.Select(e => e.Key));
Assert.Equal("Entity type 'Zoo' description.", schema.Description);
Assert.Equal("Zoo", schema.Title);
// Act
string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
// Assert
Assert.NotNull(json);
Assert.Equal(@"{
""title"": ""Zoo"",
""type"": ""object"",
""properties"": {
""Id"": {
""maximum"": 2147483647,
""minimum"": -2147483648,
""type"": ""integer"",
""format"": ""int32""
},
""Creatures"": {
""type"": ""array"",
""items"": {
""$ref"": ""#/components/schemas/NS.Creature""
}
}
},
""description"": ""Entity type 'Zoo' description."",
""example"": {
""Id"": ""integer (identifier)"",
""Creatures"": [
{
""@odata.type"": ""NS.Creature""
}
]
}
}".ChangeLineBreaks(), json);
}
[Fact]
public void CreateEntityTypeWithBaseSchemaReturnCorrectSchema()
{
// Arrange
IEdmModel model = EdmModelHelper.MultipleInheritanceEdmModel;
ODataContext context = new ODataContext(model);
IEdmEntityType entity = model.SchemaElements.OfType<IEdmEntityType>().First(t => t.Name == "Human");
Assert.NotNull(entity); // Guard
// Act
var schema = context.CreateStructuredTypeSchema(entity);
// Assert
Assert.NotNull(schema);
Assert.True(String.IsNullOrEmpty(schema.Type));
Assert.NotNull(schema.AllOf);
Assert.Null(schema.AnyOf);
Assert.Null(schema.OneOf);
Assert.Null(schema.Properties);
Assert.Equal(2, schema.AllOf.Count);
var baseSchema = schema.AllOf.First();
Assert.NotNull(baseSchema.Reference);
Assert.Equal(ReferenceType.Schema, baseSchema.Reference.Type);
Assert.Equal("NS.Animal", baseSchema.Reference.Id);
var declaredSchema = schema.AllOf.Last();
Assert.Equal("object", declaredSchema.Type);
Assert.Null(declaredSchema.AllOf);
Assert.Null(declaredSchema.AnyOf);
Assert.Null(declaredSchema.OneOf);
Assert.NotNull(declaredSchema.Properties);
Assert.Equal(1, declaredSchema.Properties.Count);
var property = Assert.Single(declaredSchema.Properties);
Assert.Equal("Name", property.Key);
Assert.Equal("string", property.Value.Type);
Assert.Null(property.Value.OneOf);
Assert.Equal("Entity type 'Human' description.", declaredSchema.Description);
Assert.Equal("Human", declaredSchema.Title);
// Act
string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
_output.WriteLine(json);
// Assert
Assert.NotNull(json);
Assert.Equal(@"{
""allOf"": [
{
""$ref"": ""#/components/schemas/NS.Animal""
},
{
""title"": ""Human"",
""type"": ""object"",
""properties"": {
""Name"": {
""type"": ""string""
}
},
""description"": ""Entity type 'Human' description.""
}
],
""example"": {
""Id"": ""integer (identifier)"",
""Age"": ""integer"",
""Name"": ""string""
}
}"
.ChangeLineBreaks(), json);
}
#endregion
#region EnumTypeSchema
[Fact]
public void CreateEnumTypeSchemaThrowArgumentNullContext()
{
// Arrange
ODataContext context = null;
// Act & Assert
Assert.Throws<ArgumentNullException>("context", () => context.CreateEnumTypeSchema(enumType: null));
}
[Fact]
public void CreateEnumTypeSchemaThrowArgumentNullEnumType()
{
// Arrange
ODataContext context = new ODataContext(EdmCoreModel.Instance);
// Act & Assert
Assert.Throws<ArgumentNullException>("enumType", () => context.CreateEnumTypeSchema(enumType: null));
}
[Fact]
public void CreateEnumTypeSchemaReturnCorrectSchema()
{
// Arrange
IEdmModel model = EdmModelHelper.BasicEdmModel;
ODataContext context = new ODataContext(model);
IEdmEnumType enumType = model.SchemaElements.OfType<IEdmEnumType>().First(t => t.Name == "Color");
Assert.NotNull(enumType); // Guard
// Act
var schema = context.CreateEnumTypeSchema(enumType);
// Assert
Assert.NotNull(schema);
Assert.Equal("string", schema.Type);
Assert.Equal("Enum type 'Color' description.", schema.Description);
Assert.Equal("Color", schema.Title);
Assert.NotNull(schema.Enum);
Assert.Equal(2, schema.Enum.Count);
Assert.Equal(new string[] { "Blue", "White" }, schema.Enum.Select(e => ((OpenApiString)e).Value));
// Act
string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
// Assert
Assert.NotNull(json);
Assert.Equal(@"{
""title"": ""Color"",
""enum"": [
""Blue"",
""White""
],
""type"": ""string"",
""description"": ""Enum type 'Color' description.""
}".ChangeLineBreaks(), json);
}
#endregion
#region EdmPropertySchema
[Theory]
[InlineData(OpenApiSpecVersion.OpenApi2_0)]
[InlineData(OpenApiSpecVersion.OpenApi3_0)]
public void CreatePropertySchemaForNonNullableEnumPropertyReturnSchema(OpenApiSpecVersion specVersion)
{
// Arrange
IEdmModel model = EdmModelHelper.BasicEdmModel;
ODataContext context = new ODataContext(model);
context.Settings.OpenApiSpecVersion = specVersion;
IEdmEnumType enumType = model.SchemaElements.OfType<IEdmEnumType>().First(e => e.Name == "Color");
EdmEntityType entitType = new EdmEntityType("NS", "Entity");
IEdmProperty property = new EdmStructuralProperty(entitType, "ColorEnumValue", new EdmEnumTypeReference(enumType, false), "yellow");
// Act
var schema = context.CreatePropertySchema(property);
Assert.NotNull(schema);
string json = schema.SerializeAsJson(specVersion);
// Assert
if (specVersion == OpenApiSpecVersion.OpenApi2_0)
{
Assert.Equal(@"{
""$ref"": ""#/definitions/DefaultNs.Color""
}".ChangeLineBreaks(), json);
}
else
{
Assert.Equal(@"{
""anyOf"": [
{
""$ref"": ""#/components/schemas/DefaultNs.Color""
}
],
""default"": ""yellow""
}".ChangeLineBreaks(), json);
}
}
[Theory]
[InlineData(OpenApiSpecVersion.OpenApi3_0)]
[InlineData(OpenApiSpecVersion.OpenApi2_0)]
public void CreatePropertySchemaForNullableEnumPropertyReturnSchema(OpenApiSpecVersion specVersion)
{
// Arrange
IEdmModel model = EdmModelHelper.BasicEdmModel;
ODataContext context = new ODataContext(model);
context.Settings.OpenApiSpecVersion = specVersion;
IEdmEnumType enumType = model.SchemaElements.OfType<IEdmEnumType>().First(e => e.Name == "Color");
EdmEntityType entitType = new EdmEntityType("NS", "Entity");
IEdmProperty property = new EdmStructuralProperty(entitType, "ColorEnumValue", new EdmEnumTypeReference(enumType, true), "yellow");
// Act
var schema = context.CreatePropertySchema(property);
Assert.NotNull(schema);
string json = schema.SerializeAsJson(specVersion);
_output.WriteLine(json);
// Assert
if (specVersion == OpenApiSpecVersion.OpenApi2_0)
{
Assert.Equal(@"{
""$ref"": ""#/definitions/DefaultNs.Color""
}".ChangeLineBreaks(), json);
}
else
{
Assert.Equal(@"{
""anyOf"": [
{
""$ref"": ""#/components/schemas/DefaultNs.Color""
}
],
""default"": ""yellow"",
""nullable"": true
}".ChangeLineBreaks(), json);
}
}
#endregion
[Fact]
public void NonNullableBooleanPropertyWithDefaultValueWorks()
{
// Arrange
ODataContext context = new ODataContext(EdmModelHelper.BasicEdmModel);
EdmEntityType entitType = new EdmEntityType("NS", "Entity");
IEdmStructuralProperty property = new EdmStructuralProperty(
entitType, "BooleanValue", EdmCoreModel.Instance.GetBoolean(false), "false");
// Act
var schema = context.CreatePropertySchema(property);
// Assert
Assert.NotNull(schema);
Assert.Equal("boolean", schema.Type);
string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
Assert.Equal(@"{
""type"": ""boolean"",
""default"": false
}".ChangeLineBreaks(), json);
}
[Fact]
public void NonNullableBinaryPropertyWithBothMaxLengthAndDefaultValueWorks()
{
// Arrange
ODataContext context = new ODataContext(EdmModelHelper.BasicEdmModel);
EdmEntityType entitType = new EdmEntityType("NS", "Entity");
var binaryType = new EdmBinaryTypeReference(EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.Binary),
false, false, 44);
IEdmStructuralProperty property = new EdmStructuralProperty(
entitType, "BinaryValue", binaryType, "T0RhdGE");
// Act
var schema = context.CreatePropertySchema(property);
// Assert
Assert.NotNull(schema);
Assert.Equal("string", schema.Type);
string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
Assert.Equal(@"{
""maxLength"": 44,
""type"": ""string"",
""format"": ""base64url"",
""default"": ""T0RhdGE""
}".ChangeLineBreaks(), json);
}
[Fact]
public void NonNullableIntegerPropertyWithDefaultValueWorks()
{
// Arrange
ODataContext context = new ODataContext(EdmModelHelper.BasicEdmModel);
EdmEntityType entitType = new EdmEntityType("NS", "Entity");
IEdmStructuralProperty property = new EdmStructuralProperty(
entitType, "IntegerValue", EdmCoreModel.Instance.GetInt32(false), "-128");
// Act
var schema = context.CreatePropertySchema(property);
// Assert
Assert.NotNull(schema);
Assert.Equal("integer", schema.Type);
string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
Assert.Equal(@"{
""maximum"": 2147483647,
""minimum"": -2147483648,
""type"": ""integer"",
""format"": ""int32"",
""default"": -128
}".ChangeLineBreaks(), json);
}
[Fact]
public void NonNullableDoublePropertyWithDefaultStringWorks()
{
// Arrange
ODataContext context = new ODataContext(EdmModelHelper.BasicEdmModel);
EdmEntityType entitType = new EdmEntityType("NS", "Entity");
IEdmStructuralProperty property = new EdmStructuralProperty(
entitType, "DoubleValue", EdmCoreModel.Instance.GetDouble(false), "3.1415926535897931");
// Act
var schema = context.CreatePropertySchema(property);
// Assert
Assert.NotNull(schema);
Assert.Null(schema.Type);
string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
Assert.Equal(@"{
""anyOf"": [
{
""type"": ""number""
},
{
""type"": ""string""
},
{
""enum"": [
""-INF"",
""INF"",
""NaN""
]
}
],
""format"": ""double"",
""default"": ""3.1415926535897931""
}".ChangeLineBreaks(), json);
}
}
}