diff --git a/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs b/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs index 5cb98a7b9a..9c71e32380 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs @@ -5,22 +5,19 @@ namespace JsonApiDotNetCore.OpenApi.Client; public interface IJsonApiClient { /// + /// Ensures correct serialization of JSON:API attributes in the request body of a POST/PATCH request at a resource endpoint. Properties with default + /// values are omitted, unless explicitly included using /// - /// Calling this method ensures that attributes containing a default value (null for reference types, 0 for integers, false for - /// booleans, etc) are omitted during serialization, except for those explicitly marked for inclusion in - /// . - /// - /// - /// This is sometimes required to ensure correct serialization of attributes during a POST/PATCH request. In JSON:API, an omitted attribute indicates to - /// ignore it, while an attribute that is set to "null" means to clear it. This poses a problem because the serializer cannot distinguish between "you - /// have explicitly set this .NET property to null" vs "you didn't touch it, so it is null by default" when converting an instance to JSON. + /// In JSON:API, an omitted attribute indicates to ignore it, while an attribute that is set to null means to clear it. This poses a problem, + /// because the serializer cannot distinguish between "you have explicitly set this .NET property to its default value" vs "you didn't touch it, so it + /// contains its default value" when converting to JSON. /// /// /// /// The request document instance for which default values should be omitted. /// /// - /// Optional. A list of expressions to indicate which properties to unconditionally include in the JSON request body. For example: + /// Optional. A list of lambda expressions that indicate which properties to always include in the JSON request body. For example: /// video.Title, video => video.Summary /// ]]> @@ -33,7 +30,8 @@ public interface IJsonApiClient /// /// /// An to clear the current registration. For efficient memory usage, it is recommended to wrap calls to this method in a - /// using statement, so the registrations are cleaned up after executing the request. + /// using statement, so the registrations are cleaned up after executing the request. After disposal, the client can be reused without the + /// registrations added earlier. /// IDisposable WithPartialAttributeSerialization(TRequestDocument requestDocument, params Expression>[] alwaysIncludedAttributeSelectors) diff --git a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs index f119396747..1c5bdec559 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs @@ -1,26 +1,25 @@ using System.Linq.Expressions; using System.Reflection; -using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; -using SysNotNull = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace JsonApiDotNetCore.OpenApi.Client; /// -/// Base class to inherit auto-generated client from. Enables to mark fields to be explicitly included in a request body, even if they are null or -/// default. +/// Base class to inherit auto-generated OpenAPI clients from. Provides support for partial POST/PATCH in JSON:API requests. /// -[PublicAPI] public abstract class JsonApiClient : IJsonApiClient { - private readonly JsonApiJsonConverter _jsonApiJsonConverter = new(); + private readonly DocumentJsonConverter _documentJsonConverter = new(); + /// + /// Initial setup. Call this from the UpdateJsonSerializerSettings partial method in the auto-generated OpenAPI client. + /// protected void SetSerializerSettingsForJsonApi(JsonSerializerSettings settings) { ArgumentGuard.NotNull(settings); - settings.Converters.Add(_jsonApiJsonConverter); + settings.Converters.Add(_documentJsonConverter); } /// @@ -40,14 +39,15 @@ public IDisposable WithPartialAttributeSerialization article.Title'."); + throw new ArgumentException($"The expression '{selector}' should select a single property. For example: 'article => article.Title'.", + nameof(alwaysIncludedAttributeSelectors)); } } - _jsonApiJsonConverter.RegisterDocument(requestDocument, new AttributeNamesContainer(attributeNames, typeof(TAttributesObject))); + var alwaysIncludedAttributes = new AlwaysIncludedAttributes(attributeNames, typeof(TAttributesObject)); + _documentJsonConverter.RegisterDocument(requestDocument, alwaysIncludedAttributes); - return new RequestDocumentRegistrationScope(_jsonApiJsonConverter, requestDocument); + return new DocumentRegistrationScope(_documentJsonConverter, requestDocument); } private static Expression RemoveConvert(Expression expression) @@ -67,40 +67,95 @@ private static Expression RemoveConvert(Expression expression) } } - private sealed class JsonApiJsonConverter : JsonConverter + /// + /// Tracks a JSON:API attributes registration for a JSON:API document instance in the serializer. Disposing removes the registration, so the client can + /// be reused. + /// + private sealed class DocumentRegistrationScope : IDisposable { - private readonly Dictionary _alwaysIncludedAttributesByRequestDocument = new(); - private readonly Dictionary> _requestDocumentsByType = new(); - private SerializationScope? _serializationScope; + private readonly DocumentJsonConverter _documentJsonConverter; + private readonly object _document; + + public DocumentRegistrationScope(DocumentJsonConverter documentJsonConverter, object document) + { + ArgumentGuard.NotNull(documentJsonConverter); + ArgumentGuard.NotNull(document); + + _documentJsonConverter = documentJsonConverter; + _document = document; + } + + public void Dispose() + { + _documentJsonConverter.UnRegisterDocument(_document); + } + } + + /// + /// Represents the set of JSON:API attributes to always send to the server, even if they are uninitialized (contain default value). + /// + private sealed class AlwaysIncludedAttributes + { + private readonly ISet _propertyNames; + private readonly Type _attributesObjectType; + + public AlwaysIncludedAttributes(ISet propertyNames, Type attributesObjectType) + { + ArgumentGuard.NotNull(propertyNames); + ArgumentGuard.NotNull(attributesObjectType); + + _propertyNames = propertyNames; + _attributesObjectType = attributesObjectType; + } + + public bool ContainsAttribute(string propertyName) + { + return _propertyNames.Contains(propertyName); + } + + public bool IsAttributesObjectType(Type type) + { + return _attributesObjectType == type; + } + } + + /// + /// A that acts on JSON:API documents. + /// + private sealed class DocumentJsonConverter : JsonConverter + { + private readonly Dictionary _alwaysIncludedAttributesByDocument = new(); + private readonly Dictionary> _documentsByType = new(); + private bool _isSerializing; public override bool CanRead => false; - public void RegisterDocument(object requestDocument, AttributeNamesContainer alwaysIncludedAttributes) + public void RegisterDocument(object document, AlwaysIncludedAttributes alwaysIncludedAttributes) { - _alwaysIncludedAttributesByRequestDocument[requestDocument] = alwaysIncludedAttributes; + _alwaysIncludedAttributesByDocument[document] = alwaysIncludedAttributes; - Type requestDocumentType = requestDocument.GetType(); + Type documentType = document.GetType(); - if (!_requestDocumentsByType.ContainsKey(requestDocumentType)) + if (!_documentsByType.ContainsKey(documentType)) { - _requestDocumentsByType[requestDocumentType] = new HashSet(); + _documentsByType[documentType] = new HashSet(); } - _requestDocumentsByType[requestDocumentType].Add(requestDocument); + _documentsByType[documentType].Add(document); } - public void RemoveRegistration(object requestDocument) + public void UnRegisterDocument(object document) { - if (_alwaysIncludedAttributesByRequestDocument.ContainsKey(requestDocument)) + if (_alwaysIncludedAttributesByDocument.ContainsKey(document)) { - _alwaysIncludedAttributesByRequestDocument.Remove(requestDocument); + _alwaysIncludedAttributesByDocument.Remove(document); - Type requestDocumentType = requestDocument.GetType(); - _requestDocumentsByType[requestDocumentType].Remove(requestDocument); + Type documentType = document.GetType(); + _documentsByType[documentType].Remove(document); - if (!_requestDocumentsByType[requestDocumentType].Any()) + if (!_documentsByType[documentType].Any()) { - _requestDocumentsByType.Remove(requestDocumentType); + _documentsByType.Remove(documentType); } } } @@ -109,12 +164,13 @@ public override bool CanConvert(Type objectType) { ArgumentGuard.NotNull(objectType); - if (_serializationScope == null) + if (_isSerializing) { - return _requestDocumentsByType.ContainsKey(objectType); + // Protect against infinite recursion. + return false; } - return _serializationScope.ShouldConvertAsAttributesObject(objectType); + return _documentsByType.ContainsKey(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) @@ -125,198 +181,147 @@ public override object ReadJson(JsonReader reader, Type objectType, object? exis public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { ArgumentGuard.NotNull(writer); - ArgumentGuard.NotNull(value); ArgumentGuard.NotNull(serializer); - if (_serializationScope == null) + if (value != null) { - AssertObjectIsRequestDocument(value); - - SerializeRequestDocument(writer, value, serializer); - } - else - { - AttributeNamesContainer? attributesObjectInfo = _serializationScope.AttributesObjectInScope; - - AssertObjectMatchesSerializationScope(attributesObjectInfo, value); + if (_alwaysIncludedAttributesByDocument.TryGetValue(value, out AlwaysIncludedAttributes? alwaysIncludedAttributes)) + { + var attributesJsonConverter = new AttributesJsonConverter(alwaysIncludedAttributes); + serializer.Converters.Add(attributesJsonConverter); + } - SerializeAttributesObject(attributesObjectInfo, writer, value, serializer); + try + { + _isSerializing = true; + serializer.Serialize(writer, value); + } + finally + { + _isSerializing = false; + } } } + } - private void AssertObjectIsRequestDocument(object value) - { - Type objectType = value.GetType(); + /// + /// A that acts on JSON:API attribute objects. + /// + private sealed class AttributesJsonConverter : JsonConverter + { + private readonly AlwaysIncludedAttributes _alwaysIncludedAttributes; + private bool _isSerializing; - if (!_requestDocumentsByType.ContainsKey(objectType)) - { - throw new UnreachableCodeException(); - } - } + public override bool CanRead => false; - private void SerializeRequestDocument(JsonWriter writer, object value, JsonSerializer serializer) + public AttributesJsonConverter(AlwaysIncludedAttributes alwaysIncludedAttributes) { - _serializationScope = new SerializationScope(); - - if (_alwaysIncludedAttributesByRequestDocument.TryGetValue(value, out AttributeNamesContainer? attributesObjectInfo)) - { - _serializationScope.AttributesObjectInScope = attributesObjectInfo; - } + ArgumentGuard.NotNull(alwaysIncludedAttributes); - try - { - serializer.Serialize(writer, value); - } - finally - { - _serializationScope = null; - } + _alwaysIncludedAttributes = alwaysIncludedAttributes; } - private static void AssertObjectMatchesSerializationScope([SysNotNull] AttributeNamesContainer? attributesObjectInfo, object value) + public override bool CanConvert(Type objectType) { - Type objectType = value.GetType(); + ArgumentGuard.NotNull(objectType); - if (attributesObjectInfo == null || !attributesObjectInfo.MatchesAttributesObjectType(objectType)) + if (_isSerializing) { - throw new UnreachableCodeException(); + // Protect against infinite recursion. + return false; } + + return _alwaysIncludedAttributes.IsAttributesObjectType(objectType); } - private static void SerializeAttributesObject(AttributeNamesContainer alwaysIncludedAttributes, JsonWriter writer, object value, - JsonSerializer serializer) + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { - AssertRequiredPropertiesAreNotExcluded(value, alwaysIncludedAttributes, writer); - - serializer.ContractResolver = new JsonApiAttributeContractResolver(alwaysIncludedAttributes); - serializer.Serialize(writer, value); + throw new UnreachableCodeException(); } - private static void AssertRequiredPropertiesAreNotExcluded(object value, AttributeNamesContainer alwaysIncludedAttributes, JsonWriter jsonWriter) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { - PropertyInfo[] propertyInfos = value.GetType().GetProperties(); + ArgumentGuard.NotNull(writer); + ArgumentGuard.NotNull(serializer); - foreach (PropertyInfo attributesPropertyInfo in propertyInfos) + if (value != null) { - bool isExplicitlyIncluded = alwaysIncludedAttributes.ContainsAttribute(attributesPropertyInfo.Name); - - if (isExplicitlyIncluded) + if (_alwaysIncludedAttributes.IsAttributesObjectType(value.GetType())) { - return; + AssertRequiredAttributesHaveNonDefaultValues(value, writer.Path); + + serializer.ContractResolver = new JsonApiAttributeContractResolver(_alwaysIncludedAttributes); } - AssertRequiredPropertyIsNotIgnored(value, attributesPropertyInfo, jsonWriter.Path); + try + { + _isSerializing = true; + serializer.Serialize(writer, value); + } + finally + { + _isSerializing = false; + } } } - private static void AssertRequiredPropertyIsNotIgnored(object value, PropertyInfo attribute, string path) + private void AssertRequiredAttributesHaveNonDefaultValues(object attributesObject, string jsonPath) { - JsonPropertyAttribute jsonPropertyForAttribute = attribute.GetCustomAttributes().Single(); - - if (jsonPropertyForAttribute.Required is not (Required.Always or Required.AllowNull)) - { - return; - } - - bool isPropertyIgnored = DefaultValueEqualsCurrentValue(attribute, value); - - if (isPropertyIgnored) + foreach (PropertyInfo propertyInfo in attributesObject.GetType().GetProperties()) { - throw new InvalidOperationException($"The following property should not be omitted: {path}.{jsonPropertyForAttribute.PropertyName}."); - } - } + bool isExplicitlyIncluded = _alwaysIncludedAttributes.ContainsAttribute(propertyInfo.Name); - private static bool DefaultValueEqualsCurrentValue(PropertyInfo propertyInfo, object instance) - { - object? currentValue = propertyInfo.GetValue(instance); - object? defaultValue = GetDefaultValue(propertyInfo.PropertyType); - - if (defaultValue == null) - { - return currentValue == null; + if (!isExplicitlyIncluded) + { + AssertPropertyHasNonDefaultValueIfRequired(attributesObject, propertyInfo, jsonPath); + } } - - return defaultValue.Equals(currentValue); } - private static object? GetDefaultValue(Type type) + private static void AssertPropertyHasNonDefaultValueIfRequired(object attributesObject, PropertyInfo propertyInfo, string jsonPath) { - return type.IsValueType ? Activator.CreateInstance(type) : null; - } - } + JsonPropertyAttribute jsonProperty = propertyInfo.GetCustomAttributes().Single(); - private sealed class SerializationScope - { - private bool _isFirstAttemptToConvertAttributes = true; - public AttributeNamesContainer? AttributesObjectInScope { get; set; } - - public bool ShouldConvertAsAttributesObject(Type type) - { - if (!_isFirstAttemptToConvertAttributes || AttributesObjectInScope == null) + if (jsonProperty.Required is Required.Always or Required.AllowNull) { - return false; - } + bool propertyHasDefaultValue = PropertyHasDefaultValue(propertyInfo, attributesObject); - if (!AttributesObjectInScope.MatchesAttributesObjectType(type)) - { - return false; + if (propertyHasDefaultValue) + { + throw new InvalidOperationException( + $"Required property '{propertyInfo.Name}' at JSON path '{jsonPath}.{jsonProperty.PropertyName}' is not set. If sending its default value is intended, include it explicitly."); + } } - - _isFirstAttemptToConvertAttributes = false; - return true; - } - } - - private sealed class AttributeNamesContainer - { - private readonly ISet _attributeNames; - private readonly Type _attributesObjectType; - - public AttributeNamesContainer(ISet attributeNames, Type attributesObjectType) - { - ArgumentGuard.NotNull(attributeNames); - ArgumentGuard.NotNull(attributesObjectType); - - _attributeNames = attributeNames; - _attributesObjectType = attributesObjectType; } - public bool ContainsAttribute(string name) + private static bool PropertyHasDefaultValue(PropertyInfo propertyInfo, object instance) { - return _attributeNames.Contains(name); - } - - public bool MatchesAttributesObjectType(Type type) - { - return _attributesObjectType == type; - } - } - - private sealed class RequestDocumentRegistrationScope : IDisposable - { - private readonly JsonApiJsonConverter _jsonApiJsonConverter; - private readonly object _requestDocument; - - public RequestDocumentRegistrationScope(JsonApiJsonConverter jsonApiJsonConverter, object requestDocument) - { - ArgumentGuard.NotNull(jsonApiJsonConverter); - ArgumentGuard.NotNull(requestDocument); + object? propertyValue = propertyInfo.GetValue(instance); + object? defaultValue = GetDefaultValue(propertyInfo.PropertyType); - _jsonApiJsonConverter = jsonApiJsonConverter; - _requestDocument = requestDocument; + return EqualityComparer.Default.Equals(propertyValue, defaultValue); } - public void Dispose() + private static object? GetDefaultValue(Type type) { - _jsonApiJsonConverter.RemoveRegistration(_requestDocument); + return type.IsValueType ? Activator.CreateInstance(type) : null; } } + /// + /// Corrects the and JSON annotations at runtime, which appear on the auto-generated + /// properties for JSON:API attributes. For example: + /// + /// + /// + /// private sealed class JsonApiAttributeContractResolver : DefaultContractResolver { - private readonly AttributeNamesContainer _alwaysIncludedAttributes; + private readonly AlwaysIncludedAttributes _alwaysIncludedAttributes; - public JsonApiAttributeContractResolver(AttributeNamesContainer alwaysIncludedAttributes) + public JsonApiAttributeContractResolver(AlwaysIncludedAttributes alwaysIncludedAttributes) { ArgumentGuard.NotNull(alwaysIncludedAttributes); @@ -325,25 +330,23 @@ public JsonApiAttributeContractResolver(AttributeNamesContainer alwaysIncludedAt protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { - JsonProperty property = base.CreateProperty(member, memberSerialization); - - bool canOmitAttribute = property.Required != Required.Always; + JsonProperty jsonProperty = base.CreateProperty(member, memberSerialization); - if (canOmitAttribute && _alwaysIncludedAttributes.MatchesAttributesObjectType(property.DeclaringType!)) + if (_alwaysIncludedAttributes.IsAttributesObjectType(jsonProperty.DeclaringType!)) { - if (_alwaysIncludedAttributes.ContainsAttribute(property.UnderlyingName!)) + if (_alwaysIncludedAttributes.ContainsAttribute(jsonProperty.UnderlyingName!)) { - property.NullValueHandling = NullValueHandling.Include; - property.DefaultValueHandling = DefaultValueHandling.Include; + jsonProperty.NullValueHandling = NullValueHandling.Include; + jsonProperty.DefaultValueHandling = DefaultValueHandling.Include; } else { - property.NullValueHandling = NullValueHandling.Ignore; - property.DefaultValueHandling = DefaultValueHandling.Ignore; + jsonProperty.NullValueHandling = NullValueHandling.Ignore; + jsonProperty.DefaultValueHandling = DefaultValueHandling.Ignore; } } - return property; + return jsonProperty; } } } diff --git a/src/JsonApiDotNetCore.OpenApi/ResourceFieldValidationMetadataProvider.cs b/src/JsonApiDotNetCore.OpenApi/ResourceFieldValidationMetadataProvider.cs index bfbe32bb31..e40ab57618 100644 --- a/src/JsonApiDotNetCore.OpenApi/ResourceFieldValidationMetadataProvider.cs +++ b/src/JsonApiDotNetCore.OpenApi/ResourceFieldValidationMetadataProvider.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCore.OpenApi; internal sealed class ResourceFieldValidationMetadataProvider { private readonly bool _validateModelState; - private readonly NullabilityInfoContext _nullabilityContext; + private readonly NullabilityInfoContext _nullabilityContext = new(); private readonly IModelMetadataProvider _modelMetadataProvider; public ResourceFieldValidationMetadataProvider(IJsonApiOptions options, IModelMetadataProvider modelMetadataProvider) @@ -20,7 +20,6 @@ public ResourceFieldValidationMetadataProvider(IJsonApiOptions options, IModelMe _validateModelState = options.ValidateModelState; _modelMetadataProvider = modelMetadataProvider; - _nullabilityContext = new NullabilityInfoContext(); } public bool IsNullable(ResourceFieldAttribute field) @@ -33,18 +32,13 @@ public bool IsNullable(ResourceFieldAttribute field) } bool hasRequiredAttribute = field.Property.HasAttribute(); - NullabilityInfo nullabilityInfo = _nullabilityContext.Create(field.Property); - - if (field is HasManyAttribute) - { - return false; - } - if (hasRequiredAttribute && _validateModelState && nullabilityInfo.ReadState != NullabilityState.NotNull) + if (_validateModelState && hasRequiredAttribute) { return false; } + NullabilityInfo nullabilityInfo = _nullabilityContext.Create(field.Property); return nullabilityInfo.ReadState != NullabilityState.NotNull; } @@ -64,11 +58,12 @@ public bool IsRequired(ResourceFieldAttribute field) return false; } - bool isNotNull = HasNullabilityStateNotNull(field); - bool isRequiredValueType = field.Property.PropertyType.IsValueType && hasRequiredAttribute && isNotNull; + NullabilityInfo nullabilityInfo = _nullabilityContext.Create(field.Property); + bool isRequiredValueType = field.Property.PropertyType.IsValueType && hasRequiredAttribute && nullabilityInfo.ReadState == NullabilityState.NotNull; if (isRequiredValueType) { + // Special case: ASP.NET ModelState Validation effectively ignores value types with [Required]. return false; } @@ -77,16 +72,9 @@ public bool IsRequired(ResourceFieldAttribute field) private bool IsModelStateValidationRequired(ResourceFieldAttribute field) { - ModelMetadata resourceFieldModelMetadata = _modelMetadataProvider.GetMetadataForProperties(field.Type.ClrType) - .Single(modelMetadata => modelMetadata.PropertyName! == field.Property.Name); + ModelMetadata modelMetadata = _modelMetadataProvider.GetMetadataForProperty(field.Type.ClrType, field.Property.Name); - return resourceFieldModelMetadata.ValidatorMetadata.Any(validatorMetadata => validatorMetadata is RequiredAttribute); - } - - private bool HasNullabilityStateNotNull(ResourceFieldAttribute field) - { - NullabilityInfo resourceFieldNullabilityInfo = _nullabilityContext.Create(field.Property); - bool hasNullabilityStateNotNull = resourceFieldNullabilityInfo is { ReadState: NullabilityState.NotNull, WriteState: NullabilityState.NotNull }; - return hasNullabilityStateNotNull; + // Non-nullable reference types are implicitly required, unless SuppressImplicitRequiredAttributeForNonNullableReferenceTypes is set. + return modelMetadata.ValidatorMetadata.Any(validatorMetadata => validatorMetadata is RequiredAttribute); } } diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs index 678229864c..9b31d2c31f 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs @@ -1,4 +1,4 @@ -using JsonApiDotNetCore.Configuration; +using System.Text.Json; using JsonApiDotNetCore.OpenApi.JsonApiObjects; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Relationships; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -34,14 +34,13 @@ internal sealed class ResourceFieldObjectSchemaBuilder private readonly RelationshipTypeFactory _relationshipTypeFactory; public ResourceFieldObjectSchemaBuilder(ResourceTypeInfo resourceTypeInfo, ISchemaRepositoryAccessor schemaRepositoryAccessor, - SchemaGenerator defaultSchemaGenerator, ResourceTypeSchemaGenerator resourceTypeSchemaGenerator, IJsonApiOptions options, + SchemaGenerator defaultSchemaGenerator, ResourceTypeSchemaGenerator resourceTypeSchemaGenerator, JsonNamingPolicy? namingPolicy, ResourceFieldValidationMetadataProvider resourceFieldValidationMetadataProvider) { ArgumentGuard.NotNull(resourceTypeInfo); ArgumentGuard.NotNull(schemaRepositoryAccessor); ArgumentGuard.NotNull(defaultSchemaGenerator); ArgumentGuard.NotNull(resourceTypeSchemaGenerator); - ArgumentGuard.NotNull(options); ArgumentGuard.NotNull(resourceFieldValidationMetadataProvider); _resourceTypeInfo = resourceTypeInfo; @@ -50,7 +49,7 @@ public ResourceFieldObjectSchemaBuilder(ResourceTypeInfo resourceTypeInfo, ISche _resourceTypeSchemaGenerator = resourceTypeSchemaGenerator; _resourceFieldValidationMetadataProvider = resourceFieldValidationMetadataProvider; - _nullableReferenceSchemaGenerator = new NullableReferenceSchemaGenerator(schemaRepositoryAccessor, options.SerializerOptions.PropertyNamingPolicy); + _nullableReferenceSchemaGenerator = new NullableReferenceSchemaGenerator(schemaRepositoryAccessor, namingPolicy); _relationshipTypeFactory = new RelationshipTypeFactory(resourceFieldValidationMetadataProvider); _schemasForResourceFields = GetFieldSchemas(); } diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs index 8e061fff57..ffa3729dcf 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs @@ -31,7 +31,7 @@ public ResourceObjectSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IRe _allowClientGeneratedIds = options.AllowClientGeneratedIds; _resourceFieldObjectSchemaBuilderFactory = resourceTypeInfo => new ResourceFieldObjectSchemaBuilder(resourceTypeInfo, schemaRepositoryAccessor, - defaultSchemaGenerator, _resourceTypeSchemaGenerator, options, resourceFieldValidationMetadataProvider); + defaultSchemaGenerator, _resourceTypeSchemaGenerator, options.SerializerOptions.PropertyNamingPolicy, resourceFieldValidationMetadataProvider); } public OpenApiSchema GenerateSchema(Type resourceObjectType) diff --git a/test/OpenApiClientTests/BaseOpenApiClientTests.cs b/test/OpenApiClientTests/BaseOpenApiClientTests.cs new file mode 100644 index 0000000000..a8ba84de9a --- /dev/null +++ b/test/OpenApiClientTests/BaseOpenApiClientTests.cs @@ -0,0 +1,86 @@ +using System.Linq.Expressions; +using System.Reflection; +using JsonApiDotNetCore.OpenApi.Client; + +namespace OpenApiClientTests; + +public abstract class BaseOpenApiClientTests +{ + private const string AttributesObjectParameterName = "attributesObject"; + + protected static Expression> CreateAttributeSelectorFor(string propertyName) + where TAttributesObject : class + { + Type attributesObjectType = typeof(TAttributesObject); + + ParameterExpression parameter = Expression.Parameter(attributesObjectType, AttributesObjectParameterName); + MemberExpression property = Expression.Property(parameter, propertyName); + UnaryExpression toObjectConversion = Expression.Convert(property, typeof(object)); + + return Expression.Lambda>(toObjectConversion, parameter); + } + + /// + /// Sets the property on the specified source to its default value (null for string, 0 for int, false for bool, etc). + /// + protected static void SetPropertyToDefaultValue(T source, string propertyName) + where T : class + { + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(propertyName); + + PropertyInfo property = GetExistingProperty(typeof(T), propertyName); + + object? defaultValue = property.PropertyType.IsValueType ? Activator.CreateInstance(property.PropertyType) : null; + property.SetValue(source, defaultValue); + } + + /// + /// Sets the property on the specified source to its initial value, when the type was constructed. This takes the presence of a type initializer into + /// account. + /// + protected static void SetPropertyToInitialValue(T source, string propertyName) + where T : class, new() + { + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(propertyName); + + var emptyRelationshipsObject = new T(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(propertyName); + + source.SetPropertyValue(propertyName, defaultValue); + } + + /// + /// Sets the 'Data' property of the specified relationship to null. + /// + protected static void SetDataPropertyToNull(T source, string relationshipPropertyName) + where T : class + { + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(relationshipPropertyName); + + PropertyInfo relationshipProperty = GetExistingProperty(typeof(T), relationshipPropertyName); + object? relationshipValue = relationshipProperty.GetValue(source); + + if (relationshipValue == null) + { + throw new InvalidOperationException($"Property '{typeof(T).Name}.{relationshipPropertyName}' is null."); + } + + PropertyInfo dataProperty = GetExistingProperty(relationshipProperty.PropertyType, "Data"); + dataProperty.SetValue(relationshipValue, null); + } + + private static PropertyInfo GetExistingProperty(Type type, string propertyName) + { + PropertyInfo? property = type.GetProperty(propertyName); + + if (property == null) + { + throw new InvalidOperationException($"Type '{type.Name}' does not contain a property named '{propertyName}'."); + } + + return property; + } +} diff --git a/test/OpenApiClientTests/FakeHttpClientWrapper.cs b/test/OpenApiClientTests/FakeHttpClientWrapper.cs index 8226a4e496..0d3e868384 100644 --- a/test/OpenApiClientTests/FakeHttpClientWrapper.cs +++ b/test/OpenApiClientTests/FakeHttpClientWrapper.cs @@ -23,11 +23,11 @@ private FakeHttpClientWrapper(HttpClient httpClient, FakeHttpMessageHandler hand _handler = handler; } - public JsonElement ParseRequestBody() + public JsonElement GetRequestBodyAsJson() { if (RequestBody == null) { - throw new InvalidOperationException(); + throw new InvalidOperationException("No body was provided with the request."); } using JsonDocument jsonDocument = JsonDocument.Parse(RequestBody); diff --git a/test/OpenApiClientTests/FakerFactory.cs b/test/OpenApiClientTests/FakerFactory.cs index c28adbaa60..db4fe3a406 100644 --- a/test/OpenApiClientTests/FakerFactory.cs +++ b/test/OpenApiClientTests/FakerFactory.cs @@ -1,5 +1,7 @@ using System.Reflection; using AutoBogus; +using JetBrains.Annotations; +using TestBuildingBlocks; namespace OpenApiClientTests; @@ -14,18 +16,27 @@ private FakerFactory() public AutoFaker Create() where TTarget : class { - return new AutoFaker(); + return GetDeterministicFaker(); + } + + private static AutoFaker GetDeterministicFaker() + where TTarget : class + { + var autoFaker = new AutoFaker(); + autoFaker.UseSeed(FakerContainer.GetFakerSeed()); + return autoFaker; } public AutoFaker CreateForObjectWithResourceId() where TTarget : class { - return new AutoFaker().Configure(builder => builder.WithOverride(new ResourceStringIdOverride())); + return GetDeterministicFaker().Configure(builder => builder.WithOverride(new ResourceStringIdOverride())); } private sealed class ResourceStringIdOverride : AutoGeneratorOverride { - private readonly IAutoFaker _idFaker = AutoFaker.Create(); + // AutoFaker has a class constraint, while TId has not, so we need to wrap it. + private readonly AutoFaker> _idContainerFaker = GetDeterministicFaker>(); public override bool CanOverride(AutoGenerateContext context) { @@ -35,7 +46,36 @@ public override bool CanOverride(AutoGenerateContext context) public override void Generate(AutoGenerateOverrideContext context) { - ((dynamic)context.Instance).Id = _idFaker.Generate()!.ToString()!; + object idValue = _idContainerFaker.Generate().Value!; + idValue = ToPositiveValue(idValue); + + ((dynamic)context.Instance).Id = idValue.ToString()!; + } + + private static object ToPositiveValue(object idValue) + { + if (idValue is short shortValue) + { + return Math.Abs(shortValue); + } + + if (idValue is int intValue) + { + return Math.Abs(intValue); + } + + if (idValue is long longValue) + { + return Math.Abs(longValue); + } + + return idValue; + } + + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + private sealed class ObjectContainer + { + public TValue? Value { get; set; } } } } diff --git a/test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs b/test/OpenApiClientTests/LegacyClient/PartialAttributeSerializationLifetimeTests.cs similarity index 93% rename from test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs rename to test/OpenApiClientTests/LegacyClient/PartialAttributeSerializationLifetimeTests.cs index b6855c06d2..be71dfa1e3 100644 --- a/test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs +++ b/test/OpenApiClientTests/LegacyClient/PartialAttributeSerializationLifetimeTests.cs @@ -6,10 +6,10 @@ namespace OpenApiClientTests.LegacyClient; -public sealed class RequestDocumentRegistrationLifetimeTests +public sealed class PartialAttributeSerializationLifetimeTests { [Fact] - public async Task Disposed_request_document_registration_does_not_affect_request() + public async Task Disposed_registration_does_not_affect_request() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -51,7 +51,7 @@ public async Task Disposed_request_document_registration_does_not_affect_request } [Fact] - public async Task Request_document_registration_can_be_used_for_multiple_requests() + public async Task Registration_can_be_used_for_multiple_requests() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -98,7 +98,7 @@ public async Task Request_document_registration_can_be_used_for_multiple_request } [Fact] - public async Task Request_is_unaffected_by_request_document_registration_of_different_request_document_of_same_type() + public async Task Request_is_unaffected_by_registration_for_different_document_of_same_type() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -153,7 +153,7 @@ public async Task Request_is_unaffected_by_request_document_registration_of_diff } [Fact] - public async Task Attribute_values_can_be_changed_after_request_document_registration() + public async Task Attribute_values_can_be_changed_after_registration() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -196,7 +196,7 @@ public async Task Attribute_values_can_be_changed_after_request_document_registr } [Fact] - public async Task Request_document_registration_is_unaffected_by_successive_registration_of_request_document_of_different_type() + public async Task Registration_is_unaffected_by_successive_registration_for_document_of_different_type() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -247,7 +247,7 @@ public async Task Request_document_registration_is_unaffected_by_successive_regi } [Fact] - public async Task Request_document_registration_is_unaffected_by_preceding_disposed_registration_of_different_request_document_of_same_type() + public async Task Registration_is_unaffected_by_preceding_disposed_registration_for_different_document_of_same_type() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -309,7 +309,7 @@ public async Task Request_document_registration_is_unaffected_by_preceding_dispo } [Fact] - public async Task Request_document_registration_is_unaffected_by_preceding_disposed_registration_of_request_document_of_different_type() + public async Task Registration_is_unaffected_by_preceding_disposed_registration_for_document_of_different_type() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -371,7 +371,7 @@ public async Task Request_document_registration_is_unaffected_by_preceding_dispo } [Fact] - public async Task Request_document_registration_is_unaffected_by_preceding_registration_of_different_request_document_of_same_type() + public async Task Registration_is_unaffected_by_preceding_registration_for_different_document_of_same_type() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); diff --git a/test/OpenApiClientTests/ObjectExtensions.cs b/test/OpenApiClientTests/ObjectExtensions.cs index 9bf15b6ac7..3f2633f5ff 100644 --- a/test/OpenApiClientTests/ObjectExtensions.cs +++ b/test/OpenApiClientTests/ObjectExtensions.cs @@ -10,8 +10,7 @@ internal static class ObjectExtensions ArgumentGuard.NotNull(source); ArgumentGuard.NotNull(propertyName); - PropertyInfo propertyInfo = source.GetType().GetProperties().Single(property => property.Name == propertyName); - + PropertyInfo propertyInfo = GetExistingProperty(source.GetType(), propertyName); return propertyInfo.GetValue(source); } @@ -20,18 +19,19 @@ public static void SetPropertyValue(this object source, string propertyName, obj ArgumentGuard.NotNull(source); ArgumentGuard.NotNull(propertyName); - PropertyInfo propertyInfo = source.GetType().GetProperties().Single(property => property.Name == propertyName); - + PropertyInfo propertyInfo = GetExistingProperty(source.GetType(), propertyName); propertyInfo.SetValue(source, value); } - public static object? GetDefaultValueForProperty(this object source, string propertyName) + private static PropertyInfo GetExistingProperty(Type type, string propertyName) { - ArgumentGuard.NotNull(source); - ArgumentGuard.NotNull(propertyName); + PropertyInfo? propertyInfo = type.GetProperty(propertyName); - PropertyInfo propertyInfo = source.GetType().GetProperties().Single(property => property.Name == propertyName); + if (propertyInfo == null) + { + throw new InvalidOperationException($"Type '{type}' does not contain a property named '{propertyName}'."); + } - return Activator.CreateInstance(propertyInfo.PropertyType); + return propertyInfo; } } diff --git a/test/OpenApiClientTests/OpenApiClientTests.cs b/test/OpenApiClientTests/OpenApiClientTests.cs deleted file mode 100644 index bb0b5d61a1..0000000000 --- a/test/OpenApiClientTests/OpenApiClientTests.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Linq.Expressions; - -namespace OpenApiClientTests; - -public class OpenApiClientTests -{ - private const string AttributesObjectParameterName = "attributesObject"; - - protected static Expression> CreateIncludedAttributeSelector(string propertyName) - where TAttributesObject : class - { - Type attributesObjectType = typeof(TAttributesObject); - - ParameterExpression parameter = Expression.Parameter(attributesObjectType, AttributesObjectParameterName); - MemberExpression property = Expression.Property(parameter, propertyName); - UnaryExpression toObjectConversion = Expression.Convert(property, typeof(object)); - - return Expression.Lambda>(toObjectConversion, parameter); - } -} diff --git a/test/OpenApiClientTests/OpenApiClientTests.csproj b/test/OpenApiClientTests/OpenApiClientTests.csproj index c5e7f0acdd..0052d618eb 100644 --- a/test/OpenApiClientTests/OpenApiClientTests.csproj +++ b/test/OpenApiClientTests/OpenApiClientTests.csproj @@ -53,34 +53,33 @@ NSwagCSharp /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions - + NrtOffMsvOffClient NrtOffMsvOffClient.cs NSwagCSharp - OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode + OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff.GeneratedCode /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:false - + NrtOffMsvOnClient NrtOffMsvOnClient.cs NSwagCSharp - OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode + OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn.GeneratedCode /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:false - + NrtOnMsvOffClient NrtOnMsvOffClient.cs NSwagCSharp - OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode + OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff.GeneratedCode /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:true - + NrtOnMsvOnClient NrtOnMsvOnClient.cs NSwagCSharp - OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode + OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn.GeneratedCode /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:true - diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs deleted file mode 100644 index 8079c4896c..0000000000 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Reflection; -using FluentAssertions; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; -using Xunit; - -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; - -public sealed class NullabilityTests -{ - [Theory] - [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), NullabilityState.Unknown)] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), NullabilityState.Unknown)] - [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), NullabilityState.NotNull)] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] - [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.Nullable)] - public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) - { - PropertyInfo[] properties = typeof(ResourceAttributesInPostRequest).GetProperties(); - PropertyInfo property = properties.Single(property => property.Name == propertyName); - property.Should().HaveNullabilityState(expectedState); - } -} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs deleted file mode 100644 index 53252ee439..0000000000 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Reflection; -using FluentAssertions; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; -using Xunit; - -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; - -public sealed class NullabilityTests -{ - [Theory] - [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), NullabilityState.Unknown)] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), NullabilityState.Unknown)] - [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), NullabilityState.NotNull)] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] - [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.NotNull)] - public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) - { - PropertyInfo[] properties = typeof(ResourceAttributesInPostRequest).GetProperties(); - PropertyInfo property = properties.Single(property => property.Name == propertyName); - property.Should().HaveNullabilityState(expectedState); - } -} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs deleted file mode 100644 index dc2bc84ccd..0000000000 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Reflection; -using FluentAssertions; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; -using Xunit; - -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; - -public sealed class NullabilityTests -{ - [Theory] - [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), NullabilityState.NotNull)] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), NullabilityState.NotNull)] - [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), NullabilityState.Nullable)] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), NullabilityState.Nullable)] - [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), NullabilityState.NotNull)] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] - [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.Nullable)] - public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) - { - PropertyInfo[] properties = typeof(ResourceAttributesInPostRequest).GetProperties(); - PropertyInfo property = properties.Single(property => property.Name == propertyName); - property.Should().HaveNullabilityState(expectedState); - } -} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs deleted file mode 100644 index e3c1a5ce31..0000000000 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Reflection; -using FluentAssertions; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; -using Xunit; - -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; - -public sealed class NullabilityTests -{ - [Theory] - [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), NullabilityState.NotNull)] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), NullabilityState.NotNull)] - [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), NullabilityState.Nullable)] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), NullabilityState.NotNull)] - [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), NullabilityState.NotNull)] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] - [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.NotNull)] - public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) - { - PropertyInfo[] properties = typeof(ResourceAttributesInPostRequest).GetProperties(); - PropertyInfo property = properties.Single(property => property.Name == propertyName); - property.Should().HaveNullabilityState(expectedState); - } -} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs similarity index 56% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/CreateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index 5dfa347b05..ef5ed004e0 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -4,15 +4,15 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff; -public sealed class CreateResourceTests : OpenApiClientTests +public sealed class CreateResourceTests : BaseOpenApiClientTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOffMsvOffFakers _fakers = new(); [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), "referenceType")] @@ -37,33 +37,32 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP } }; - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + CreateAttributeSelectorFor(attributePropertyName); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); }); } [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] - public async Task Can_set_default_value_to_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_set_attribute_to_default_value(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -81,27 +80,25 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam } }; - object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); - - Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + Expression> includeAttributeSelector = + CreateAttributeSelectorFor(attributePropertyName); + + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ShouldBeInteger(0)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(0)); }); } @@ -109,7 +106,7 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), "referenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] - public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -127,25 +124,22 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } @@ -153,7 +147,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), "requiredReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Cannot_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Cannot_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -171,31 +165,28 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - InvalidOperationException exception = assertion.Subject.Single(); - exception.Message.Should().Be($"The following property should not be omitted: data.attributes.{jsonPropertyName}."); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + InvalidOperationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be( + $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), nameof(ResourceRelationshipsInPostRequest.ToOne.Data), "toOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), nameof(ResourceRelationshipsInPostRequest.RequiredToOne.Data), "requiredToOne")] - public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] + public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -213,32 +204,29 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + // Act - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), nameof(ResourceRelationshipsInPostRequest.ToOne.Data), "toOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), nameof(ResourceRelationshipsInPostRequest.RequiredToOne.Data), "requiredToOne")] - public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] + public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -256,8 +244,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -266,19 +253,18 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -296,32 +282,28 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); - } + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -339,28 +321,26 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - public async Task Can_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -378,32 +358,29 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + // Act - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - public async Task Can_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -421,25 +398,20 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/GeneratedCode/NrtOffMsvOffClient.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/GeneratedCode/NrtOffMsvOffClient.cs similarity index 85% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/GeneratedCode/NrtOffMsvOffClient.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/GeneratedCode/NrtOffMsvOffClient.cs index 4bea9db090..f3ee9a9c53 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/GeneratedCode/NrtOffMsvOffClient.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/GeneratedCode/NrtOffMsvOffClient.cs @@ -1,7 +1,7 @@ using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff.GeneratedCode; internal partial class NrtOffMsvOffClient : JsonApiClient { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NrtOffMsvOffFakers.cs similarity index 57% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NrtOffMsvOffFakers.cs index 817344c11c..adb0e97833 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NrtOffMsvOffFakers.cs @@ -1,9 +1,9 @@ using Bogus; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff.GeneratedCode; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff; -internal sealed class ResourceFieldValidationFakers +internal sealed class NrtOffMsvOffFakers { private readonly Lazy> _lazyPostAttributesFaker = new(() => FakerFactory.Instance.Create()); @@ -11,14 +11,14 @@ internal sealed class ResourceFieldValidationFakers private readonly Lazy> _lazyPatchAttributesFaker = new(() => FakerFactory.Instance.Create()); - private readonly Lazy> _lazyNullableToOneFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyNullableToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); - private readonly Lazy> _lazyToManyFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyToManyFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); public Faker PostAttributes => _lazyPostAttributesFaker.Value; public Faker PatchAttributes => _lazyPatchAttributesFaker.Value; - public Faker NullableToOne => _lazyNullableToOneFaker.Value; - public Faker ToMany => _lazyToManyFaker.Value; + public Faker NullableToOne => _lazyNullableToOneFaker.Value; + public Faker ToMany => _lazyToManyFaker.Value; } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs new file mode 100644 index 0000000000..d15ba31e2b --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs @@ -0,0 +1,45 @@ +using System.Reflection; +using FluentAssertions; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff; + +public sealed class NullabilityTests +{ + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), NullabilityState.Unknown)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), NullabilityState.Unknown)] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.Nullable)] + public void Nullability_of_generated_attribute_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + // Act + PropertyInfo? property = typeof(ResourceAttributesInPostRequest).GetProperty(propertyName); + + // Assert + property.ShouldNotBeNull(); + property.Should().HaveNullabilityState(expectedState); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), NullabilityState.Unknown)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), NullabilityState.Unknown)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), NullabilityState.Unknown)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), NullabilityState.Unknown)] + public void Nullability_of_generated_relationship_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + // Act + PropertyInfo? relationshipProperty = typeof(ResourceRelationshipsInPostRequest).GetProperty(propertyName); + + // Assert + relationshipProperty.ShouldNotBeNull(); + + PropertyInfo? dataProperty = relationshipProperty.PropertyType.GetProperty("Data"); + dataProperty.ShouldNotBeNull(); + dataProperty.Should().HaveNullabilityState(expectedState); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs similarity index 62% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/UpdateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs index 68e71e97b9..0429245150 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs @@ -3,25 +3,24 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff; -public sealed class UpdateResourceTests +public sealed class UpdateResourceTests : BaseOpenApiClientTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOffMsvOffFakers _fakers = new(); [Fact] - public async Task Cannot_exclude_id() + public async Task Cannot_omit_Id() { // Arrange var requestDocument = new ResourcePatchRequestDocument { Data = new ResourceDataInPatchRequest { - Id = "1", Attributes = _fakers.PatchAttributes.Generate(), Relationships = new ResourceRelationshipsInPatchRequest { @@ -33,17 +32,13 @@ public async Task Cannot_exclude_id() } }; - ResourceDataInPatchRequest emptyDataObject = new(); - requestDocument.Data.Id = emptyDataObject.Id; - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); // Act - Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(999, requestDocument)); + Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); // Assert - await action.Should().ThrowAsync(); ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); @@ -57,7 +52,7 @@ public async Task Cannot_exclude_id() [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.NullableValueType), "nullableValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -76,25 +71,22 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } }; - ResourceAttributesInPatchRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } @@ -103,7 +95,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToOne), "requiredToOne")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -122,25 +114,22 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri } }; - ResourceRelationshipsInPatchRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json similarity index 87% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/swagger.g.json rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json index 572f1c7615..c0b2808012 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json @@ -5,10 +5,10 @@ "version": "1.0" }, "paths": { - "/Resource": { + "/resources": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceCollection", "responses": { @@ -26,7 +26,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceCollection", "responses": { @@ -44,7 +44,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResource", "requestBody": { @@ -73,10 +73,10 @@ } } }, - "/Resource/{id}": { + "/resources/{id}": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResource", "parameters": [ @@ -105,7 +105,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResource", "parameters": [ @@ -134,7 +134,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResource", "parameters": [ @@ -175,7 +175,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResource", "parameters": [ @@ -196,10 +196,10 @@ } } }, - "/Resource/{id}/requiredToMany": { + "/resources/{id}/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToMany", "parameters": [ @@ -219,7 +219,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -228,7 +228,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToMany", "parameters": [ @@ -248,7 +248,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -256,10 +256,10 @@ } } }, - "/Resource/{id}/relationships/requiredToMany": { + "/resources/{id}/relationships/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToManyRelationship", "parameters": [ @@ -279,7 +279,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -288,7 +288,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToManyRelationship", "parameters": [ @@ -308,7 +308,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -317,7 +317,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceRequiredToManyRelationship", "parameters": [ @@ -335,7 +335,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -348,7 +348,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredToManyRelationship", "parameters": [ @@ -366,7 +366,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -379,7 +379,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceRequiredToManyRelationship", "parameters": [ @@ -397,7 +397,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -409,10 +409,10 @@ } } }, - "/Resource/{id}/requiredToOne": { + "/resources/{id}/requiredToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToOne", "parameters": [ @@ -432,7 +432,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -441,7 +441,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToOne", "parameters": [ @@ -461,7 +461,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -469,10 +469,10 @@ } } }, - "/Resource/{id}/relationships/requiredToOne": { + "/resources/{id}/relationships/requiredToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToOneRelationship", "parameters": [ @@ -492,7 +492,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -501,7 +501,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToOneRelationship", "parameters": [ @@ -521,7 +521,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -530,7 +530,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredToOneRelationship", "parameters": [ @@ -548,7 +548,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" } } } @@ -560,10 +560,10 @@ } } }, - "/Resource/{id}/toMany": { + "/resources/{id}/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToMany", "parameters": [ @@ -583,7 +583,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -592,7 +592,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToMany", "parameters": [ @@ -612,7 +612,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -620,10 +620,10 @@ } } }, - "/Resource/{id}/relationships/toMany": { + "/resources/{id}/relationships/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToManyRelationship", "parameters": [ @@ -643,7 +643,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -652,7 +652,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToManyRelationship", "parameters": [ @@ -672,7 +672,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -681,7 +681,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceToManyRelationship", "parameters": [ @@ -699,7 +699,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -712,7 +712,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceToManyRelationship", "parameters": [ @@ -730,7 +730,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -743,7 +743,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceToManyRelationship", "parameters": [ @@ -761,7 +761,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -773,10 +773,10 @@ } } }, - "/Resource/{id}/toOne": { + "/resources/{id}/toOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToOne", "parameters": [ @@ -796,7 +796,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -805,7 +805,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToOne", "parameters": [ @@ -825,7 +825,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -833,10 +833,10 @@ } } }, - "/Resource/{id}/relationships/toOne": { + "/resources/{id}/relationships/toOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToOneRelationship", "parameters": [ @@ -856,7 +856,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -865,7 +865,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToOneRelationship", "parameters": [ @@ -885,7 +885,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -894,7 +894,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceToOneRelationship", "parameters": [ @@ -912,7 +912,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" } } } @@ -927,7 +927,7 @@ }, "components": { "schemas": { - "emptyResourceCollectionResponseDocument": { + "emptyCollectionResponseDocument": { "required": [ "data", "links" @@ -937,7 +937,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" } }, "meta": { @@ -953,7 +953,7 @@ }, "additionalProperties": false }, - "emptyResourceDataInResponse": { + "emptyDataInResponse": { "required": [ "id", "links", @@ -962,7 +962,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -978,7 +978,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifier": { + "emptyIdentifier": { "required": [ "id", "type" @@ -986,7 +986,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -995,7 +995,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifierCollectionResponseDocument": { + "emptyIdentifierCollectionResponseDocument": { "required": [ "data", "links" @@ -1005,7 +1005,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "meta": { @@ -1021,9 +1021,9 @@ }, "additionalProperties": false }, - "emptyResourceResourceType": { + "emptyResourceType": { "enum": [ - "emptyResources" + "empties" ], "type": "string" }, @@ -1208,7 +1208,7 @@ }, "nullable": true }, - "nullableEmptyResourceIdentifierResponseDocument": { + "nullableEmptyIdentifierResponseDocument": { "required": [ "data", "links" @@ -1218,7 +1218,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1238,7 +1238,7 @@ }, "additionalProperties": false }, - "nullableEmptyResourceSecondaryResponseDocument": { + "nullableEmptySecondaryResponseDocument": { "required": [ "data", "links" @@ -1248,7 +1248,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" }, { "$ref": "#/components/schemas/nullValue" @@ -1268,7 +1268,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInRequest": { + "nullableToOneEmptyInRequest": { "required": [ "data" ], @@ -1277,7 +1277,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1287,7 +1287,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInResponse": { + "nullableToOneEmptyInResponse": { "required": [ "links" ], @@ -1296,7 +1296,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1571,16 +1571,16 @@ "type": "object", "properties": { "toOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -1593,16 +1593,16 @@ "type": "object", "properties": { "toOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -1615,27 +1615,27 @@ "type": "object", "properties": { "toOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + "$ref": "#/components/schemas/nullableToOneEmptyInResponse" }, "requiredToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + "$ref": "#/components/schemas/nullableToOneEmptyInResponse" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" } }, "additionalProperties": false }, "resourceResourceType": { "enum": [ - "Resource" + "resources" ], "type": "string" }, - "toManyEmptyResourceInRequest": { + "toManyEmptyInRequest": { "required": [ "data" ], @@ -1644,13 +1644,13 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } } }, "additionalProperties": false }, - "toManyEmptyResourceInResponse": { + "toManyEmptyInResponse": { "required": [ "links" ], @@ -1659,7 +1659,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "links": { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs similarity index 55% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/CreateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index f3a5130486..806eb82d4a 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -4,15 +4,15 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn; -public sealed class CreateResourceTests : OpenApiClientTests +public sealed class CreateResourceTests : BaseOpenApiClientTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOffMsvOnFakers _fakers = new(); [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), "referenceType")] @@ -35,26 +35,25 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP } }; - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + CreateAttributeSelectorFor(attributePropertyName); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); }); } @@ -62,7 +61,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Can_set_default_value_to_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_set_attribute_to_default_value(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -80,33 +79,31 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam } }; - object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); - - Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + Expression> includeAttributeSelector = + CreateAttributeSelectorFor(attributePropertyName); + + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ShouldBeInteger(0)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(0)); }); } [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), "requiredReferenceType")] - public async Task Cannot_clear_attribute(string clrPropertyName, string jsonPropertyName) + public async Task Cannot_clear_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -124,26 +121,24 @@ public async Task Cannot_clear_attribute(string clrPropertyName, string jsonProp } }; - requestDocument.Data.Attributes.SetPropertyValue(clrPropertyName, null); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(clrPropertyName); + CreateAttributeSelectorFor(attributePropertyName); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - exception.Message.Should().Contain($"Cannot write a null value for property '{jsonPropertyName}'."); - } + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Cannot write a null value for property '{jsonPropertyName}'. Property requires a value. Path 'data.attributes'."); } [Theory] @@ -151,7 +146,7 @@ public async Task Cannot_clear_attribute(string clrPropertyName, string jsonProp [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] - public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -169,32 +164,29 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), "requiredReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Cannot_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Cannot_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -212,30 +204,27 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - InvalidOperationException exception = assertion.Subject.Single(); - exception.Message.Should().Be($"The following property should not be omitted: data.attributes.{jsonPropertyName}."); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + InvalidOperationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be( + $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), nameof(ResourceRelationshipsInPostRequest.ToOne.Data), "toOne")] - public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] + public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -253,31 +242,28 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + // Act - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), nameof(ResourceRelationshipsInPostRequest.ToOne.Data), "toOne")] - public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] + public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -295,8 +281,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -305,20 +290,19 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), nameof(ResourceRelationshipsInPostRequest.RequiredToOne.Data), "requiredToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -336,33 +320,29 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); - } + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), nameof(ResourceRelationshipsInPostRequest.RequiredToOne.Data), "requiredToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -380,29 +360,27 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -420,25 +398,22 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + // Act - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } @@ -446,7 +421,7 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -464,31 +439,26 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] - public async Task Cannot_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -506,31 +476,27 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); - } + exception.Message.Should().Be( + $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] - public async Task Cannot_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -548,22 +514,19 @@ public async Task Cannot_exclude_relationship_without_partial_attribute_serializ } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/GeneratedCode/NrtOffMsvOnClient.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/GeneratedCode/NrtOffMsvOnClient.cs similarity index 85% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/GeneratedCode/NrtOffMsvOnClient.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/GeneratedCode/NrtOffMsvOnClient.cs index 94bb5efc37..a722c5d49b 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/GeneratedCode/NrtOffMsvOnClient.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/GeneratedCode/NrtOffMsvOnClient.cs @@ -1,7 +1,7 @@ using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn.GeneratedCode; internal partial class NrtOffMsvOnClient : JsonApiClient { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NrtOffMsvOnFakers.cs similarity index 52% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NrtOffMsvOnFakers.cs index c032f0c073..14d23cb247 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NrtOffMsvOnFakers.cs @@ -1,9 +1,9 @@ using Bogus; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn.GeneratedCode; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn; -internal sealed class ResourceFieldValidationFakers +internal sealed class NrtOffMsvOnFakers { private readonly Lazy> _lazyPostAttributesFaker = new(() => FakerFactory.Instance.Create()); @@ -11,18 +11,18 @@ internal sealed class ResourceFieldValidationFakers private readonly Lazy> _lazyPatchAttributesFaker = new(() => FakerFactory.Instance.Create()); - private readonly Lazy> _lazyToOneFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); - private readonly Lazy> _lazyNullableToOneFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyNullableToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); - private readonly Lazy> _lazyToManyFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyToManyFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); public Faker PostAttributes => _lazyPostAttributesFaker.Value; public Faker PatchAttributes => _lazyPatchAttributesFaker.Value; - public Faker ToOne => _lazyToOneFaker.Value; - public Faker NullableToOne => _lazyNullableToOneFaker.Value; - public Faker ToMany => _lazyToManyFaker.Value; + public Faker ToOne => _lazyToOneFaker.Value; + public Faker NullableToOne => _lazyNullableToOneFaker.Value; + public Faker ToMany => _lazyToManyFaker.Value; } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs new file mode 100644 index 0000000000..11e082fdb9 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs @@ -0,0 +1,45 @@ +using System.Reflection; +using FluentAssertions; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn; + +public sealed class NullabilityTests +{ + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), NullabilityState.Unknown)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), NullabilityState.Unknown)] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.NotNull)] + public void Nullability_of_generated_attribute_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + // Act + PropertyInfo? property = typeof(ResourceAttributesInPostRequest).GetProperty(propertyName); + + // Assert + property.ShouldNotBeNull(); + property.Should().HaveNullabilityState(expectedState); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), NullabilityState.Unknown)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), NullabilityState.Unknown)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), NullabilityState.Unknown)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), NullabilityState.Unknown)] + public void Nullability_of_generated_relationship_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + // Act + PropertyInfo? relationshipProperty = typeof(ResourceRelationshipsInPostRequest).GetProperty(propertyName); + + // Assert + relationshipProperty.ShouldNotBeNull(); + + PropertyInfo? dataProperty = relationshipProperty.PropertyType.GetProperty("Data"); + dataProperty.ShouldNotBeNull(); + dataProperty.Should().HaveNullabilityState(expectedState); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs similarity index 62% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/UpdateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs index 81f54f6dd0..41ce44c4e0 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs @@ -3,25 +3,24 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn; -public sealed class UpdateResourceTests +public sealed class UpdateResourceTests : BaseOpenApiClientTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOffMsvOnFakers _fakers = new(); [Fact] - public async Task Cannot_exclude_id() + public async Task Cannot_omit_Id() { // Arrange var requestDocument = new ResourcePatchRequestDocument { Data = new ResourceDataInPatchRequest { - Id = "1", Attributes = _fakers.PatchAttributes.Generate(), Relationships = new ResourceRelationshipsInPatchRequest { @@ -33,17 +32,13 @@ public async Task Cannot_exclude_id() } }; - ResourceDataInPatchRequest emptyDataObject = new(); - requestDocument.Data.Id = emptyDataObject.Id; - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); // Act - Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(999, requestDocument)); + Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); // Assert - await action.Should().ThrowAsync(); ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); @@ -57,7 +52,7 @@ public async Task Cannot_exclude_id() [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.NullableValueType), "nullableValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -76,25 +71,22 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } }; - ResourceAttributesInPatchRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } @@ -103,7 +95,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToOne), "requiredToOne")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -122,25 +114,22 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri } }; - ResourceRelationshipsInPatchRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json similarity index 87% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/swagger.g.json rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json index f366cc19e2..cbf832157e 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json @@ -5,10 +5,10 @@ "version": "1.0" }, "paths": { - "/Resource": { + "/resources": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceCollection", "responses": { @@ -26,7 +26,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceCollection", "responses": { @@ -44,7 +44,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResource", "requestBody": { @@ -73,10 +73,10 @@ } } }, - "/Resource/{id}": { + "/resources/{id}": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResource", "parameters": [ @@ -105,7 +105,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResource", "parameters": [ @@ -134,7 +134,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResource", "parameters": [ @@ -175,7 +175,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResource", "parameters": [ @@ -196,10 +196,10 @@ } } }, - "/Resource/{id}/requiredToMany": { + "/resources/{id}/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToMany", "parameters": [ @@ -219,7 +219,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -228,7 +228,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToMany", "parameters": [ @@ -248,7 +248,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -256,10 +256,10 @@ } } }, - "/Resource/{id}/relationships/requiredToMany": { + "/resources/{id}/relationships/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToManyRelationship", "parameters": [ @@ -279,7 +279,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -288,7 +288,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToManyRelationship", "parameters": [ @@ -308,7 +308,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -317,7 +317,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceRequiredToManyRelationship", "parameters": [ @@ -335,7 +335,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -348,7 +348,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredToManyRelationship", "parameters": [ @@ -366,7 +366,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -379,7 +379,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceRequiredToManyRelationship", "parameters": [ @@ -397,7 +397,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -409,10 +409,10 @@ } } }, - "/Resource/{id}/requiredToOne": { + "/resources/{id}/requiredToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToOne", "parameters": [ @@ -432,7 +432,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -441,7 +441,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToOne", "parameters": [ @@ -461,7 +461,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -469,10 +469,10 @@ } } }, - "/Resource/{id}/relationships/requiredToOne": { + "/resources/{id}/relationships/requiredToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToOneRelationship", "parameters": [ @@ -492,7 +492,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -501,7 +501,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToOneRelationship", "parameters": [ @@ -521,7 +521,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -530,7 +530,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredToOneRelationship", "parameters": [ @@ -548,7 +548,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" } } } @@ -560,10 +560,10 @@ } } }, - "/Resource/{id}/toMany": { + "/resources/{id}/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToMany", "parameters": [ @@ -583,7 +583,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -592,7 +592,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToMany", "parameters": [ @@ -612,7 +612,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -620,10 +620,10 @@ } } }, - "/Resource/{id}/relationships/toMany": { + "/resources/{id}/relationships/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToManyRelationship", "parameters": [ @@ -643,7 +643,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -652,7 +652,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToManyRelationship", "parameters": [ @@ -672,7 +672,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -681,7 +681,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceToManyRelationship", "parameters": [ @@ -699,7 +699,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -712,7 +712,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceToManyRelationship", "parameters": [ @@ -730,7 +730,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -743,7 +743,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceToManyRelationship", "parameters": [ @@ -761,7 +761,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -773,10 +773,10 @@ } } }, - "/Resource/{id}/toOne": { + "/resources/{id}/toOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToOne", "parameters": [ @@ -796,7 +796,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -805,7 +805,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToOne", "parameters": [ @@ -825,7 +825,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -833,10 +833,10 @@ } } }, - "/Resource/{id}/relationships/toOne": { + "/resources/{id}/relationships/toOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToOneRelationship", "parameters": [ @@ -856,7 +856,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -865,7 +865,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToOneRelationship", "parameters": [ @@ -885,7 +885,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -894,7 +894,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceToOneRelationship", "parameters": [ @@ -912,7 +912,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" } } } @@ -927,7 +927,7 @@ }, "components": { "schemas": { - "emptyResourceCollectionResponseDocument": { + "emptyCollectionResponseDocument": { "required": [ "data", "links" @@ -937,7 +937,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" } }, "meta": { @@ -953,7 +953,7 @@ }, "additionalProperties": false }, - "emptyResourceDataInResponse": { + "emptyDataInResponse": { "required": [ "id", "links", @@ -962,7 +962,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -978,7 +978,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifier": { + "emptyIdentifier": { "required": [ "id", "type" @@ -986,7 +986,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -995,7 +995,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifierCollectionResponseDocument": { + "emptyIdentifierCollectionResponseDocument": { "required": [ "data", "links" @@ -1005,7 +1005,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "meta": { @@ -1021,7 +1021,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifierResponseDocument": { + "emptyIdentifierResponseDocument": { "required": [ "data", "links" @@ -1029,7 +1029,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, "meta": { "type": "object", @@ -1044,13 +1044,13 @@ }, "additionalProperties": false }, - "emptyResourceResourceType": { + "emptyResourceType": { "enum": [ - "emptyResources" + "empties" ], "type": "string" }, - "emptyResourceSecondaryResponseDocument": { + "emptySecondaryResponseDocument": { "required": [ "data", "links" @@ -1058,7 +1058,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" }, "meta": { "type": "object", @@ -1254,7 +1254,7 @@ }, "nullable": true }, - "nullableEmptyResourceIdentifierResponseDocument": { + "nullableEmptyIdentifierResponseDocument": { "required": [ "data", "links" @@ -1264,7 +1264,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1284,7 +1284,7 @@ }, "additionalProperties": false }, - "nullableEmptyResourceSecondaryResponseDocument": { + "nullableEmptySecondaryResponseDocument": { "required": [ "data", "links" @@ -1294,7 +1294,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" }, { "$ref": "#/components/schemas/nullValue" @@ -1314,7 +1314,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInRequest": { + "nullableToOneEmptyInRequest": { "required": [ "data" ], @@ -1323,7 +1323,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1333,7 +1333,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInResponse": { + "nullableToOneEmptyInResponse": { "required": [ "links" ], @@ -1342,7 +1342,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1609,16 +1609,16 @@ "type": "object", "properties": { "toOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -1630,16 +1630,16 @@ "type": "object", "properties": { "toOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -1651,27 +1651,27 @@ "type": "object", "properties": { "toOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + "$ref": "#/components/schemas/nullableToOneEmptyInResponse" }, "requiredToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + "$ref": "#/components/schemas/toOneEmptyInResponse" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" } }, "additionalProperties": false }, "resourceResourceType": { "enum": [ - "Resource" + "resources" ], "type": "string" }, - "toManyEmptyResourceInRequest": { + "toManyEmptyInRequest": { "required": [ "data" ], @@ -1680,13 +1680,13 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } } }, "additionalProperties": false }, - "toManyEmptyResourceInResponse": { + "toManyEmptyInResponse": { "required": [ "links" ], @@ -1695,7 +1695,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "links": { @@ -1708,26 +1708,26 @@ }, "additionalProperties": false }, - "toOneEmptyResourceInRequest": { + "toOneEmptyInRequest": { "required": [ "data" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "additionalProperties": false }, - "toOneEmptyResourceInResponse": { + "toOneEmptyInResponse": { "required": [ "links" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, "links": { "$ref": "#/components/schemas/linksInRelationshipObject" diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs similarity index 59% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/CreateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index 0ca3a6fb82..c3b777b726 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -4,15 +4,15 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff; -public sealed class CreateResourceTests : OpenApiClientTests +public sealed class CreateResourceTests : BaseOpenApiClientTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOnMsvOffFakers _fakers = new(); [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), "nullableReferenceType")] @@ -39,33 +39,32 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP } }; - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + CreateAttributeSelectorFor(attributePropertyName); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); }); } [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] - public async Task Can_set_default_value_to_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_set_attribute_to_default_value(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -85,34 +84,32 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam } }; - object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); - - Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + Expression> includeAttributeSelector = + CreateAttributeSelectorFor(attributePropertyName); + + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ShouldBeInteger(0)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(0)); }); } [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), "nonNullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), "requiredNonNullableReferenceType")] - public async Task Cannot_clear_attribute(string clrPropertyName, string jsonPropertyName) + public async Task Cannot_clear_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -132,26 +129,25 @@ public async Task Cannot_clear_attribute(string clrPropertyName, string jsonProp } }; - requestDocument.Data.Attributes.SetPropertyValue(clrPropertyName, null); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(clrPropertyName); + CreateAttributeSelectorFor(attributePropertyName); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - exception.Message.Should().Contain($"Cannot write a null value for property '{jsonPropertyName}'."); - } + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().StartWith($"Cannot write a null value for property '{jsonPropertyName}'."); + exception.Message.Should().EndWith("Path 'data.attributes'."); } [Theory] @@ -159,7 +155,7 @@ public async Task Cannot_clear_attribute(string clrPropertyName, string jsonProp [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), "nullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] - public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -179,25 +175,22 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } @@ -206,7 +199,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), "requiredNullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Cannot_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Cannot_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -226,32 +219,28 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - InvalidOperationException exception = assertion.Subject.Single(); - exception.Message.Should().Be($"The following property should not be omitted: data.attributes.{jsonPropertyName}."); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + InvalidOperationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be( + $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), nameof(ResourceRelationshipsInPostRequest.NullableToOne.Data), "nullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne.Data), - "requiredNullableToOne")] - public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] + public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -271,33 +260,29 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + // Act - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), nameof(ResourceRelationshipsInPostRequest.NullableToOne.Data), "nullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne.Data), - "requiredNullableToOne")] - public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] + public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -317,8 +302,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -327,23 +311,20 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), nameof(ResourceRelationshipsInPostRequest.NonNullableToOne.Data), - "nonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne.Data), - "requiredNonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -363,36 +344,30 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); - } + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), nameof(ResourceRelationshipsInPostRequest.NonNullableToOne.Data), - "nonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne.Data), - "requiredNonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -412,29 +387,27 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - public async Task Can_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -454,25 +427,22 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + // Act - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } @@ -480,7 +450,7 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - public async Task Can_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -500,31 +470,26 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] - public async Task Cannot_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -544,31 +509,27 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); - } + exception.Message.Should().Be( + $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] - public async Task Cannot_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -588,22 +549,19 @@ public async Task Cannot_exclude_relationship_without_partial_attribute_serializ } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/GeneratedCode/NrtOnMsvOffClient.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/GeneratedCode/NrtOnMsvOffClient.cs similarity index 85% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/GeneratedCode/NrtOnMsvOffClient.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/GeneratedCode/NrtOnMsvOffClient.cs index c44cdbe3d7..77b0854984 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/GeneratedCode/NrtOnMsvOffClient.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/GeneratedCode/NrtOnMsvOffClient.cs @@ -1,7 +1,7 @@ using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff.GeneratedCode; internal partial class NrtOnMsvOffClient : JsonApiClient { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NrtOnMsvOffFakers.cs similarity index 52% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NrtOnMsvOffFakers.cs index e3dbbd0d36..4b6365764e 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NrtOnMsvOffFakers.cs @@ -1,9 +1,9 @@ using Bogus; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff.GeneratedCode; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff; -internal sealed class ResourceFieldValidationFakers +internal sealed class NrtOnMsvOffFakers { private readonly Lazy> _lazyPostAttributesFaker = new(() => FakerFactory.Instance.Create()); @@ -11,18 +11,18 @@ internal sealed class ResourceFieldValidationFakers private readonly Lazy> _lazyPatchAttributesFaker = new(() => FakerFactory.Instance.Create()); - private readonly Lazy> _lazyToOneFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); - private readonly Lazy> _lazyNullableToOneFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyNullableToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); - private readonly Lazy> _lazyToManyFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyToManyFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); public Faker PostAttributes => _lazyPostAttributesFaker.Value; public Faker PatchAttributes => _lazyPatchAttributesFaker.Value; - public Faker ToOne => _lazyToOneFaker.Value; - public Faker NullableToOne => _lazyNullableToOneFaker.Value; - public Faker ToMany => _lazyToManyFaker.Value; + public Faker ToOne => _lazyToOneFaker.Value; + public Faker NullableToOne => _lazyNullableToOneFaker.Value; + public Faker ToMany => _lazyToManyFaker.Value; } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs new file mode 100644 index 0000000000..633133a63c --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs @@ -0,0 +1,49 @@ +using System.Reflection; +using FluentAssertions; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff; + +public sealed class NullabilityTests +{ + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), NullabilityState.Nullable)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), NullabilityState.Nullable)] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.Nullable)] + public void Nullability_of_generated_attribute_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + // Act + PropertyInfo? property = typeof(ResourceAttributesInPostRequest).GetProperty(propertyName); + + // Assert + property.ShouldNotBeNull(); + property.Should().HaveNullabilityState(expectedState); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), NullabilityState.NotNull)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), NullabilityState.NotNull)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), NullabilityState.Nullable)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), NullabilityState.Nullable)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), NullabilityState.NotNull)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), NullabilityState.NotNull)] + public void Nullability_of_generated_relationship_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + // Act + PropertyInfo? relationshipProperty = typeof(ResourceRelationshipsInPostRequest).GetProperty(propertyName); + + // Assert + relationshipProperty.ShouldNotBeNull(); + + PropertyInfo? dataProperty = relationshipProperty.PropertyType.GetProperty("Data"); + dataProperty.ShouldNotBeNull(); + dataProperty.Should().HaveNullabilityState(expectedState); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs similarity index 67% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/UpdateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs index 5596c17962..7685c56bc5 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs @@ -3,25 +3,24 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff; -public sealed class UpdateResourceTests +public sealed class UpdateResourceTests : BaseOpenApiClientTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOnMsvOffFakers _fakers = new(); [Fact] - public async Task Cannot_exclude_id() + public async Task Cannot_omit_Id() { // Arrange var requestDocument = new ResourcePatchRequestDocument { Data = new ResourceDataInPatchRequest { - Id = "1", Attributes = _fakers.PatchAttributes.Generate(), Relationships = new ResourceRelationshipsInPatchRequest { @@ -35,17 +34,13 @@ public async Task Cannot_exclude_id() } }; - ResourceDataInPatchRequest emptyDataObject = new(); - requestDocument.Data.Id = emptyDataObject.Id; - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); // Act - Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(999, requestDocument)); + Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); // Assert - await action.Should().ThrowAsync(); ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); @@ -61,7 +56,7 @@ public async Task Cannot_exclude_id() [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.NullableValueType), "nullableValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Can_exclude_attribute_that_is_required_in_create_resource(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -82,25 +77,22 @@ public async Task Can_exclude_attribute_that_is_required_in_create_resource(stri } }; - ResourceAttributesInPatchRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } @@ -111,7 +103,7 @@ public async Task Can_exclude_attribute_that_is_required_in_create_resource(stri [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredNullableToOne), "requiredNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship_that_is_required_in_create_resource(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -132,25 +124,22 @@ public async Task Can_exclude_relationship_that_is_required_in_create_resource(s } }; - ResourceRelationshipsInPatchRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json similarity index 86% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/swagger.g.json rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json index 8619a357c8..76623a9819 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json @@ -5,10 +5,10 @@ "version": "1.0" }, "paths": { - "/Resource": { + "/resources": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceCollection", "responses": { @@ -26,7 +26,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceCollection", "responses": { @@ -44,7 +44,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResource", "requestBody": { @@ -73,10 +73,10 @@ } } }, - "/Resource/{id}": { + "/resources/{id}": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResource", "parameters": [ @@ -105,7 +105,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResource", "parameters": [ @@ -134,7 +134,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResource", "parameters": [ @@ -175,7 +175,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResource", "parameters": [ @@ -196,10 +196,10 @@ } } }, - "/Resource/{id}/nonNullableToOne": { + "/resources/{id}/nonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNonNullableToOne", "parameters": [ @@ -219,7 +219,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -228,7 +228,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNonNullableToOne", "parameters": [ @@ -248,7 +248,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -256,10 +256,10 @@ } } }, - "/Resource/{id}/relationships/nonNullableToOne": { + "/resources/{id}/relationships/nonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNonNullableToOneRelationship", "parameters": [ @@ -279,7 +279,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -288,7 +288,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNonNullableToOneRelationship", "parameters": [ @@ -308,7 +308,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -317,7 +317,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceNonNullableToOneRelationship", "parameters": [ @@ -335,7 +335,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" } } } @@ -347,10 +347,10 @@ } } }, - "/Resource/{id}/nullableToOne": { + "/resources/{id}/nullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNullableToOne", "parameters": [ @@ -370,7 +370,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -379,7 +379,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNullableToOne", "parameters": [ @@ -399,7 +399,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -407,10 +407,10 @@ } } }, - "/Resource/{id}/relationships/nullableToOne": { + "/resources/{id}/relationships/nullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNullableToOneRelationship", "parameters": [ @@ -430,7 +430,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -439,7 +439,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNullableToOneRelationship", "parameters": [ @@ -459,7 +459,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -468,7 +468,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceNullableToOneRelationship", "parameters": [ @@ -486,7 +486,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" } } } @@ -498,10 +498,10 @@ } } }, - "/Resource/{id}/requiredNonNullableToOne": { + "/resources/{id}/requiredNonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNonNullableToOne", "parameters": [ @@ -521,7 +521,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -530,7 +530,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNonNullableToOne", "parameters": [ @@ -550,7 +550,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -558,10 +558,10 @@ } } }, - "/Resource/{id}/relationships/requiredNonNullableToOne": { + "/resources/{id}/relationships/requiredNonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNonNullableToOneRelationship", "parameters": [ @@ -581,7 +581,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -590,7 +590,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNonNullableToOneRelationship", "parameters": [ @@ -610,7 +610,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -619,7 +619,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredNonNullableToOneRelationship", "parameters": [ @@ -637,7 +637,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" } } } @@ -649,10 +649,10 @@ } } }, - "/Resource/{id}/requiredNullableToOne": { + "/resources/{id}/requiredNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNullableToOne", "parameters": [ @@ -672,7 +672,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -681,7 +681,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNullableToOne", "parameters": [ @@ -701,7 +701,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -709,10 +709,10 @@ } } }, - "/Resource/{id}/relationships/requiredNullableToOne": { + "/resources/{id}/relationships/requiredNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNullableToOneRelationship", "parameters": [ @@ -732,7 +732,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -741,7 +741,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNullableToOneRelationship", "parameters": [ @@ -761,7 +761,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -770,7 +770,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredNullableToOneRelationship", "parameters": [ @@ -788,7 +788,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" } } } @@ -800,10 +800,10 @@ } } }, - "/Resource/{id}/requiredToMany": { + "/resources/{id}/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToMany", "parameters": [ @@ -823,7 +823,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -832,7 +832,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToMany", "parameters": [ @@ -852,7 +852,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -860,10 +860,10 @@ } } }, - "/Resource/{id}/relationships/requiredToMany": { + "/resources/{id}/relationships/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToManyRelationship", "parameters": [ @@ -883,7 +883,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -892,7 +892,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToManyRelationship", "parameters": [ @@ -912,7 +912,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -921,7 +921,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceRequiredToManyRelationship", "parameters": [ @@ -939,7 +939,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -952,7 +952,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredToManyRelationship", "parameters": [ @@ -970,7 +970,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -983,7 +983,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceRequiredToManyRelationship", "parameters": [ @@ -1001,7 +1001,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1013,10 +1013,10 @@ } } }, - "/Resource/{id}/toMany": { + "/resources/{id}/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToMany", "parameters": [ @@ -1036,7 +1036,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -1045,7 +1045,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToMany", "parameters": [ @@ -1065,7 +1065,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -1073,10 +1073,10 @@ } } }, - "/Resource/{id}/relationships/toMany": { + "/resources/{id}/relationships/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToManyRelationship", "parameters": [ @@ -1096,7 +1096,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -1105,7 +1105,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToManyRelationship", "parameters": [ @@ -1125,7 +1125,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -1134,7 +1134,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceToManyRelationship", "parameters": [ @@ -1152,7 +1152,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1165,7 +1165,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceToManyRelationship", "parameters": [ @@ -1183,7 +1183,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1196,7 +1196,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceToManyRelationship", "parameters": [ @@ -1214,7 +1214,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1229,7 +1229,7 @@ }, "components": { "schemas": { - "emptyResourceCollectionResponseDocument": { + "emptyCollectionResponseDocument": { "required": [ "data", "links" @@ -1239,7 +1239,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" } }, "meta": { @@ -1255,7 +1255,7 @@ }, "additionalProperties": false }, - "emptyResourceDataInResponse": { + "emptyDataInResponse": { "required": [ "id", "links", @@ -1264,7 +1264,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -1280,7 +1280,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifier": { + "emptyIdentifier": { "required": [ "id", "type" @@ -1288,7 +1288,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -1297,7 +1297,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifierCollectionResponseDocument": { + "emptyIdentifierCollectionResponseDocument": { "required": [ "data", "links" @@ -1307,7 +1307,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "meta": { @@ -1323,7 +1323,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifierResponseDocument": { + "emptyIdentifierResponseDocument": { "required": [ "data", "links" @@ -1331,7 +1331,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, "meta": { "type": "object", @@ -1346,13 +1346,13 @@ }, "additionalProperties": false }, - "emptyResourceResourceType": { + "emptyResourceType": { "enum": [ - "emptyResources" + "empties" ], "type": "string" }, - "emptyResourceSecondaryResponseDocument": { + "emptySecondaryResponseDocument": { "required": [ "data", "links" @@ -1360,7 +1360,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" }, "meta": { "type": "object", @@ -1556,7 +1556,7 @@ }, "nullable": true }, - "nullableEmptyResourceIdentifierResponseDocument": { + "nullableEmptyIdentifierResponseDocument": { "required": [ "data", "links" @@ -1566,7 +1566,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1586,7 +1586,7 @@ }, "additionalProperties": false }, - "nullableEmptyResourceSecondaryResponseDocument": { + "nullableEmptySecondaryResponseDocument": { "required": [ "data", "links" @@ -1596,7 +1596,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" }, { "$ref": "#/components/schemas/nullValue" @@ -1616,7 +1616,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInRequest": { + "nullableToOneEmptyInRequest": { "required": [ "data" ], @@ -1625,7 +1625,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1635,7 +1635,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInResponse": { + "nullableToOneEmptyInResponse": { "required": [ "links" ], @@ -1644,7 +1644,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1942,22 +1942,22 @@ "type": "object", "properties": { "nonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "requiredNonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "nullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredNullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -1971,22 +1971,22 @@ "type": "object", "properties": { "nonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "requiredNonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "nullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredNullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -2000,33 +2000,33 @@ "type": "object", "properties": { "nonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + "$ref": "#/components/schemas/toOneEmptyInResponse" }, "requiredNonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + "$ref": "#/components/schemas/toOneEmptyInResponse" }, "nullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + "$ref": "#/components/schemas/nullableToOneEmptyInResponse" }, "requiredNullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + "$ref": "#/components/schemas/nullableToOneEmptyInResponse" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" } }, "additionalProperties": false }, "resourceResourceType": { "enum": [ - "Resource" + "resources" ], "type": "string" }, - "toManyEmptyResourceInRequest": { + "toManyEmptyInRequest": { "required": [ "data" ], @@ -2035,13 +2035,13 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } } }, "additionalProperties": false }, - "toManyEmptyResourceInResponse": { + "toManyEmptyInResponse": { "required": [ "links" ], @@ -2050,7 +2050,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "links": { @@ -2063,26 +2063,26 @@ }, "additionalProperties": false }, - "toOneEmptyResourceInRequest": { + "toOneEmptyInRequest": { "required": [ "data" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "additionalProperties": false }, - "toOneEmptyResourceInResponse": { + "toOneEmptyInResponse": { "required": [ "links" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, "links": { "$ref": "#/components/schemas/linksInRelationshipObject" diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs similarity index 59% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/CreateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index 44aa5da920..1685abd005 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -4,15 +4,15 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn; -public sealed class CreateResourceTests : OpenApiClientTests +public sealed class CreateResourceTests : BaseOpenApiClientTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOnMsvOnFakers _fakers = new(); [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), "nullableReferenceType")] @@ -37,26 +37,25 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP } }; - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + CreateAttributeSelectorFor(attributePropertyName); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); }); } @@ -64,7 +63,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Can_set_default_value_to_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_set_attribute_to_default_value(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -84,27 +83,25 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam } }; - object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); - - Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + Expression> includeAttributeSelector = + CreateAttributeSelectorFor(attributePropertyName); + + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ShouldBeInteger(0)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(0)); }); } @@ -112,7 +109,7 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), "nonNullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), "requiredNonNullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), "requiredNullableReferenceType")] - public async Task Cannot_clear_attribute(string clrPropertyName, string jsonPropertyName) + public async Task Cannot_clear_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -132,26 +129,24 @@ public async Task Cannot_clear_attribute(string clrPropertyName, string jsonProp } }; - requestDocument.Data.Attributes.SetPropertyValue(clrPropertyName, null); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(clrPropertyName); + CreateAttributeSelectorFor(attributePropertyName); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - exception.Message.Should().Contain($"Cannot write a null value for property '{jsonPropertyName}'."); - } + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Cannot write a null value for property '{jsonPropertyName}'. Property requires a value. Path 'data.attributes'."); } [Theory] @@ -159,7 +154,7 @@ public async Task Cannot_clear_attribute(string clrPropertyName, string jsonProp [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] - public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -179,25 +174,22 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } @@ -206,7 +198,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), "requiredNonNullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), "requiredNullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Cannot_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Cannot_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -226,30 +218,27 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - InvalidOperationException exception = assertion.Subject.Single(); - exception.Message.Should().Be($"The following property should not be omitted: data.attributes.{jsonPropertyName}."); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + InvalidOperationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be( + $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), nameof(ResourceRelationshipsInPostRequest.NullableToOne.Data), "nullableToOne")] - public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] + public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -269,31 +258,28 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + // Act - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), nameof(ResourceRelationshipsInPostRequest.NullableToOne.Data), "nullableToOne")] - public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] + public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -313,8 +299,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -323,25 +308,21 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), nameof(ResourceRelationshipsInPostRequest.NonNullableToOne.Data), - "nonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne.Data), - "requiredNonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne.Data), - "requiredNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -361,38 +342,31 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); - } + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), nameof(ResourceRelationshipsInPostRequest.NonNullableToOne.Data), - "nonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne.Data), - "requiredNonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne.Data), - "requiredNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -412,29 +386,27 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -454,25 +426,22 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + // Act - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } @@ -480,7 +449,7 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -500,25 +469,20 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } @@ -526,7 +490,7 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] - public async Task Cannot_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -546,33 +510,29 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); - } + exception.Message.Should().Be( + $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] - public async Task Cannot_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -592,22 +552,19 @@ public async Task Cannot_exclude_relationship_without_partial_attribute_serializ } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/GeneratedCode/NrtOnMsvOnClient.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/GeneratedCode/NrtOnMsvOnClient.cs similarity index 86% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/GeneratedCode/NrtOnMsvOnClient.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/GeneratedCode/NrtOnMsvOnClient.cs index 1eed829acb..6679b1c168 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/GeneratedCode/NrtOnMsvOnClient.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/GeneratedCode/NrtOnMsvOnClient.cs @@ -1,7 +1,7 @@ using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn.GeneratedCode; internal partial class NrtOnMsvOnClient : JsonApiClient { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NrtOnMsvOnFakers.cs similarity index 52% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NrtOnMsvOnFakers.cs index b0c9c260c7..9906627046 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NrtOnMsvOnFakers.cs @@ -1,9 +1,9 @@ using Bogus; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn.GeneratedCode; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn; -internal sealed class ResourceFieldValidationFakers +internal sealed class NrtOnMsvOnFakers { private readonly Lazy> _lazyPostAttributesFaker = new(() => FakerFactory.Instance.Create()); @@ -11,18 +11,18 @@ internal sealed class ResourceFieldValidationFakers private readonly Lazy> _lazyPatchAttributesFaker = new(() => FakerFactory.Instance.Create()); - private readonly Lazy> _lazyToOneFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); - private readonly Lazy> _lazyNullableToOneFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyNullableToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); - private readonly Lazy> _lazyToManyFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyToManyFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); public Faker PostAttributes => _lazyPostAttributesFaker.Value; public Faker PatchAttributes => _lazyPatchAttributesFaker.Value; - public Faker ToOne => _lazyToOneFaker.Value; - public Faker NullableToOne => _lazyNullableToOneFaker.Value; - public Faker ToMany => _lazyToManyFaker.Value; + public Faker ToOne => _lazyToOneFaker.Value; + public Faker NullableToOne => _lazyNullableToOneFaker.Value; + public Faker ToMany => _lazyToManyFaker.Value; } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs new file mode 100644 index 0000000000..83fce2a945 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs @@ -0,0 +1,49 @@ +using System.Reflection; +using FluentAssertions; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn; + +public sealed class NullabilityTests +{ + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), NullabilityState.Nullable)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.NotNull)] + public void Nullability_of_generated_attribute_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + // Act + PropertyInfo? property = typeof(ResourceAttributesInPostRequest).GetProperty(propertyName); + + // Assert + property.ShouldNotBeNull(); + property.Should().HaveNullabilityState(expectedState); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), NullabilityState.NotNull)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), NullabilityState.NotNull)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), NullabilityState.Nullable)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), NullabilityState.NotNull)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), NullabilityState.NotNull)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), NullabilityState.NotNull)] + public void Nullability_of_generated_relationship_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + // Act + PropertyInfo? relationshipProperty = typeof(ResourceRelationshipsInPostRequest).GetProperty(propertyName); + + // Assert + relationshipProperty.ShouldNotBeNull(); + + PropertyInfo? dataProperty = relationshipProperty.PropertyType.GetProperty("Data"); + dataProperty.ShouldNotBeNull(); + dataProperty.Should().HaveNullabilityState(expectedState); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs similarity index 67% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/UpdateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs index d0695d6cf7..d1dc00b8e8 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs @@ -3,25 +3,24 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn; -public sealed class UpdateResourceTests +public sealed class UpdateResourceTests : BaseOpenApiClientTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOnMsvOnFakers _fakers = new(); [Fact] - public async Task Cannot_exclude_id() + public async Task Cannot_omit_Id() { // Arrange var requestDocument = new ResourcePatchRequestDocument { Data = new ResourceDataInPatchRequest { - Id = "1", Attributes = _fakers.PatchAttributes.Generate(), Relationships = new ResourceRelationshipsInPatchRequest { @@ -35,17 +34,13 @@ public async Task Cannot_exclude_id() } }; - ResourceDataInPatchRequest emptyDataObject = new(); - requestDocument.Data.Id = emptyDataObject.Id; - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); // Act - Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(999, requestDocument)); + Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); // Assert - await action.Should().ThrowAsync(); ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); @@ -61,7 +56,7 @@ public async Task Cannot_exclude_id() [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.NullableValueType), "nullableValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -82,25 +77,22 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } }; - ResourceAttributesInPatchRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } @@ -111,7 +103,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredNullableToOne), "requiredNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -132,25 +124,22 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri } }; - ResourceRelationshipsInPatchRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json similarity index 86% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/swagger.g.json rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json index 897983660d..73f9c6048a 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json @@ -5,10 +5,10 @@ "version": "1.0" }, "paths": { - "/Resource": { + "/resources": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceCollection", "responses": { @@ -26,7 +26,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceCollection", "responses": { @@ -44,7 +44,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResource", "requestBody": { @@ -73,10 +73,10 @@ } } }, - "/Resource/{id}": { + "/resources/{id}": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResource", "parameters": [ @@ -105,7 +105,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResource", "parameters": [ @@ -134,7 +134,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResource", "parameters": [ @@ -175,7 +175,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResource", "parameters": [ @@ -196,10 +196,10 @@ } } }, - "/Resource/{id}/nonNullableToOne": { + "/resources/{id}/nonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNonNullableToOne", "parameters": [ @@ -219,7 +219,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -228,7 +228,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNonNullableToOne", "parameters": [ @@ -248,7 +248,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -256,10 +256,10 @@ } } }, - "/Resource/{id}/relationships/nonNullableToOne": { + "/resources/{id}/relationships/nonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNonNullableToOneRelationship", "parameters": [ @@ -279,7 +279,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -288,7 +288,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNonNullableToOneRelationship", "parameters": [ @@ -308,7 +308,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -317,7 +317,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceNonNullableToOneRelationship", "parameters": [ @@ -335,7 +335,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" } } } @@ -347,10 +347,10 @@ } } }, - "/Resource/{id}/nullableToOne": { + "/resources/{id}/nullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNullableToOne", "parameters": [ @@ -370,7 +370,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -379,7 +379,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNullableToOne", "parameters": [ @@ -399,7 +399,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -407,10 +407,10 @@ } } }, - "/Resource/{id}/relationships/nullableToOne": { + "/resources/{id}/relationships/nullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNullableToOneRelationship", "parameters": [ @@ -430,7 +430,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -439,7 +439,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNullableToOneRelationship", "parameters": [ @@ -459,7 +459,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -468,7 +468,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceNullableToOneRelationship", "parameters": [ @@ -486,7 +486,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" } } } @@ -498,10 +498,10 @@ } } }, - "/Resource/{id}/requiredNonNullableToOne": { + "/resources/{id}/requiredNonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNonNullableToOne", "parameters": [ @@ -521,7 +521,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -530,7 +530,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNonNullableToOne", "parameters": [ @@ -550,7 +550,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -558,10 +558,10 @@ } } }, - "/Resource/{id}/relationships/requiredNonNullableToOne": { + "/resources/{id}/relationships/requiredNonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNonNullableToOneRelationship", "parameters": [ @@ -581,7 +581,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -590,7 +590,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNonNullableToOneRelationship", "parameters": [ @@ -610,7 +610,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -619,7 +619,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredNonNullableToOneRelationship", "parameters": [ @@ -637,7 +637,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" } } } @@ -649,10 +649,10 @@ } } }, - "/Resource/{id}/requiredNullableToOne": { + "/resources/{id}/requiredNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNullableToOne", "parameters": [ @@ -672,7 +672,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -681,7 +681,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNullableToOne", "parameters": [ @@ -701,7 +701,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -709,10 +709,10 @@ } } }, - "/Resource/{id}/relationships/requiredNullableToOne": { + "/resources/{id}/relationships/requiredNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNullableToOneRelationship", "parameters": [ @@ -732,7 +732,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -741,7 +741,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNullableToOneRelationship", "parameters": [ @@ -761,7 +761,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -770,7 +770,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredNullableToOneRelationship", "parameters": [ @@ -788,7 +788,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" } } } @@ -800,10 +800,10 @@ } } }, - "/Resource/{id}/requiredToMany": { + "/resources/{id}/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToMany", "parameters": [ @@ -823,7 +823,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -832,7 +832,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToMany", "parameters": [ @@ -852,7 +852,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -860,10 +860,10 @@ } } }, - "/Resource/{id}/relationships/requiredToMany": { + "/resources/{id}/relationships/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToManyRelationship", "parameters": [ @@ -883,7 +883,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -892,7 +892,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToManyRelationship", "parameters": [ @@ -912,7 +912,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -921,7 +921,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceRequiredToManyRelationship", "parameters": [ @@ -939,7 +939,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -952,7 +952,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredToManyRelationship", "parameters": [ @@ -970,7 +970,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -983,7 +983,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceRequiredToManyRelationship", "parameters": [ @@ -1001,7 +1001,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1013,10 +1013,10 @@ } } }, - "/Resource/{id}/toMany": { + "/resources/{id}/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToMany", "parameters": [ @@ -1036,7 +1036,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -1045,7 +1045,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToMany", "parameters": [ @@ -1065,7 +1065,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -1073,10 +1073,10 @@ } } }, - "/Resource/{id}/relationships/toMany": { + "/resources/{id}/relationships/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToManyRelationship", "parameters": [ @@ -1096,7 +1096,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -1105,7 +1105,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToManyRelationship", "parameters": [ @@ -1125,7 +1125,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -1134,7 +1134,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceToManyRelationship", "parameters": [ @@ -1152,7 +1152,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1165,7 +1165,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceToManyRelationship", "parameters": [ @@ -1183,7 +1183,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1196,7 +1196,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceToManyRelationship", "parameters": [ @@ -1214,7 +1214,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1229,7 +1229,7 @@ }, "components": { "schemas": { - "emptyResourceCollectionResponseDocument": { + "emptyCollectionResponseDocument": { "required": [ "data", "links" @@ -1239,7 +1239,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" } }, "meta": { @@ -1255,7 +1255,7 @@ }, "additionalProperties": false }, - "emptyResourceDataInResponse": { + "emptyDataInResponse": { "required": [ "id", "links", @@ -1264,7 +1264,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -1280,7 +1280,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifier": { + "emptyIdentifier": { "required": [ "id", "type" @@ -1288,7 +1288,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -1297,7 +1297,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifierCollectionResponseDocument": { + "emptyIdentifierCollectionResponseDocument": { "required": [ "data", "links" @@ -1307,7 +1307,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "meta": { @@ -1323,7 +1323,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifierResponseDocument": { + "emptyIdentifierResponseDocument": { "required": [ "data", "links" @@ -1331,7 +1331,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, "meta": { "type": "object", @@ -1346,13 +1346,13 @@ }, "additionalProperties": false }, - "emptyResourceResourceType": { + "emptyResourceType": { "enum": [ - "emptyResources" + "empties" ], "type": "string" }, - "emptyResourceSecondaryResponseDocument": { + "emptySecondaryResponseDocument": { "required": [ "data", "links" @@ -1360,7 +1360,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" }, "meta": { "type": "object", @@ -1556,7 +1556,7 @@ }, "nullable": true }, - "nullableEmptyResourceIdentifierResponseDocument": { + "nullableEmptyIdentifierResponseDocument": { "required": [ "data", "links" @@ -1566,7 +1566,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1586,7 +1586,7 @@ }, "additionalProperties": false }, - "nullableEmptyResourceSecondaryResponseDocument": { + "nullableEmptySecondaryResponseDocument": { "required": [ "data", "links" @@ -1596,7 +1596,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" }, { "$ref": "#/components/schemas/nullValue" @@ -1616,7 +1616,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInRequest": { + "nullableToOneEmptyInRequest": { "required": [ "data" ], @@ -1625,7 +1625,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1635,7 +1635,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInResponse": { + "nullableToOneEmptyInResponse": { "required": [ "links" ], @@ -1644,7 +1644,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1936,22 +1936,22 @@ "type": "object", "properties": { "nonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "requiredNonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "nullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -1965,22 +1965,22 @@ "type": "object", "properties": { "nonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "requiredNonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "nullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -1994,33 +1994,33 @@ "type": "object", "properties": { "nonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + "$ref": "#/components/schemas/toOneEmptyInResponse" }, "requiredNonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + "$ref": "#/components/schemas/toOneEmptyInResponse" }, "nullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + "$ref": "#/components/schemas/nullableToOneEmptyInResponse" }, "requiredNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + "$ref": "#/components/schemas/toOneEmptyInResponse" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" } }, "additionalProperties": false }, "resourceResourceType": { "enum": [ - "Resource" + "resources" ], "type": "string" }, - "toManyEmptyResourceInRequest": { + "toManyEmptyInRequest": { "required": [ "data" ], @@ -2029,13 +2029,13 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } } }, "additionalProperties": false }, - "toManyEmptyResourceInResponse": { + "toManyEmptyInResponse": { "required": [ "links" ], @@ -2044,7 +2044,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "links": { @@ -2057,26 +2057,26 @@ }, "additionalProperties": false }, - "toOneEmptyResourceInRequest": { + "toOneEmptyInRequest": { "required": [ "data" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "additionalProperties": false }, - "toOneEmptyResourceInResponse": { + "toOneEmptyInResponse": { "required": [ "links" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, "links": { "$ref": "#/components/schemas/linksInRelationshipObject" diff --git a/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseTests.cs b/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseTests.cs index e0bbd5bad3..9a694feab5 100644 --- a/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseTests.cs +++ b/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseTests.cs @@ -25,34 +25,34 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets.get").With(getElement => + document.Should().ContainPath("paths./supermarkets.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarketCollection"); + operationElement.Should().Be("getSupermarketCollection"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarketCollectionResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceCollectionDocumentSchemaRefId = null; string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("jsonapi.$ref").ShouldBeSchemaReferenceId("jsonapiObject"); + propertiesElement.Should().ContainPath("jsonapi.$ref").ShouldBeSchemaReferenceId("jsonapiObject"); - linksInResourceCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceCollectionDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("linksInResourceCollectionDocument").SchemaReferenceId; - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.items.$ref").ShouldBeSchemaReferenceId("supermarketDataInResponse") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.items.$ref").ShouldBeSchemaReferenceId("supermarketDataInResponse") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceCollectionDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -67,68 +67,68 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() string? resourceAttributesInResponseSchemaRefId = null; string? resourceRelationshipInResponseSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - linksInResourceObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("linksInResourceObject") + linksInResourceObjectSchemaRefId = propertiesElement.Should().ContainPath("links.$ref").ShouldBeSchemaReferenceId("linksInResourceObject") .SchemaReferenceId; - primaryResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("supermarketResourceType") + primaryResourceTypeSchemaRefId = propertiesElement.Should().ContainPath("type.$ref").ShouldBeSchemaReferenceId("supermarketResourceType") .SchemaReferenceId; - resourceAttributesInResponseSchemaRefId = propertiesElement.ShouldContainPath("attributes.$ref") + resourceAttributesInResponseSchemaRefId = propertiesElement.Should().ContainPath("attributes.$ref") .ShouldBeSchemaReferenceId("supermarketAttributesInResponse").SchemaReferenceId; - resourceRelationshipInResponseSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + resourceRelationshipInResponseSchemaRefId = propertiesElement.Should().ContainPath("relationships.$ref") .ShouldBeSchemaReferenceId("supermarketRelationshipsInResponse").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceObjectSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceObjectSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); }); - schemasElement.ShouldContainPath($"{primaryResourceTypeSchemaRefId}.enum[0]").With(enumValueElement => + schemasElement.Should().ContainPath($"{primaryResourceTypeSchemaRefId}.enum[0]").With(enumValueElement => { - enumValueElement.ShouldBeString("supermarkets"); + enumValueElement.Should().Be("supermarkets"); }); - schemasElement.ShouldContainPath($"{resourceAttributesInResponseSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceAttributesInResponseSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("nameOfCity"); propertiesElement.Should().ContainProperty("kind"); - propertiesElement.ShouldContainPath("kind.$ref").ShouldBeSchemaReferenceId("supermarketType"); + propertiesElement.Should().ContainPath("kind.$ref").ShouldBeSchemaReferenceId("supermarketType"); }); string? nullableToOneResourceResponseDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceRelationshipInResponseSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceRelationshipInResponseSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("storeManager"); - propertiesElement.ShouldContainPath("storeManager.$ref").ShouldBeSchemaReferenceId("toOneStaffMemberInResponse"); + propertiesElement.Should().ContainPath("storeManager.$ref").ShouldBeSchemaReferenceId("toOneStaffMemberInResponse"); - nullableToOneResourceResponseDataSchemaRefId = propertiesElement.ShouldContainPath("backupStoreManager.$ref") + nullableToOneResourceResponseDataSchemaRefId = propertiesElement.Should().ContainPath("backupStoreManager.$ref") .ShouldBeSchemaReferenceId("nullableToOneStaffMemberInResponse").SchemaReferenceId; propertiesElement.Should().ContainProperty("cashiers"); - propertiesElement.ShouldContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("toManyStaffMemberInResponse"); + propertiesElement.Should().ContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("toManyStaffMemberInResponse"); }); string? linksInRelationshipObjectSchemaRefId = null; string? relatedResourceIdentifierSchemaRefId = null; - schemasElement.ShouldContainPath($"{nullableToOneResourceResponseDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{nullableToOneResourceResponseDataSchemaRefId}.properties").With(propertiesElement => { - linksInRelationshipObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("linksInRelationshipObject") - .SchemaReferenceId; + linksInRelationshipObjectSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") + .ShouldBeSchemaReferenceId("linksInRelationshipObject").SchemaReferenceId; - relatedResourceIdentifierSchemaRefId = propertiesElement.ShouldContainPath("data.oneOf[0].$ref") + relatedResourceIdentifierSchemaRefId = propertiesElement.Should().ContainPath("data.oneOf[0].$ref") .ShouldBeSchemaReferenceId("staffMemberIdentifier").SchemaReferenceId; - propertiesElement.ShouldContainPath("data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + propertiesElement.Should().ContainPath("data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); }); - schemasElement.ShouldContainPath($"{linksInRelationshipObjectSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInRelationshipObjectSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("related"); @@ -136,13 +136,13 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() string? relatedResourceTypeSchemaRefId = null; - schemasElement.ShouldContainPath($"{relatedResourceIdentifierSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{relatedResourceIdentifierSchemaRefId}.properties").With(propertiesElement => { - relatedResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("staffMemberResourceType") + relatedResourceTypeSchemaRefId = propertiesElement.Should().ContainPath("type.$ref").ShouldBeSchemaReferenceId("staffMemberResourceType") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{relatedResourceTypeSchemaRefId}.enum[0]").ShouldBeSchemaReferenceId("staffMembers"); + schemasElement.Should().ContainPath($"{relatedResourceTypeSchemaRefId}.enum[0]").ShouldBeSchemaReferenceId("staffMembers"); }); } @@ -155,28 +155,28 @@ public async Task Casing_convention_is_applied_to_GetSingle_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarket"); + operationElement.Should().Be("getSupermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarketPrimaryResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("linksInResourceDocument") + linksInResourceDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref").ShouldBeSchemaReferenceId("linksInResourceDocument") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -193,30 +193,30 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_sin // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}/storeManager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/storeManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarketStoreManager"); + operationElement.Should().Be("getSupermarketStoreManager"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staffMemberSecondaryResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("staffMemberDataInResponse") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("staffMemberDataInResponse") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("staffMemberAttributesInResponse"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("staffMemberAttributesInResponse"); }); }); } @@ -228,14 +228,14 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_nul JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/backupStoreManager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/backupStoreManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarketBackupStoreManager"); + operationElement.Should().Be("getSupermarketBackupStoreManager"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("nullableStaffMemberSecondaryResponseDocument"); }); } @@ -247,14 +247,14 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_res JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/cashiers.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/cashiers.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarketCashiers"); + operationElement.Should().Be("getSupermarketCashiers"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staffMemberCollectionResponseDocument"); }); } @@ -268,28 +268,28 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}/relationships/storeManager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/storeManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarketStoreManagerRelationship"); + operationElement.Should().Be("getSupermarketStoreManagerRelationship"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staffMemberIdentifierResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceIdentifierDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceIdentifierDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceIdentifierDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("linksInResourceIdentifierDocument").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceIdentifierDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceIdentifierDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -305,14 +305,14 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/backupStoreManager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/backupStoreManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarketBackupStoreManagerRelationship"); + operationElement.Should().Be("getSupermarketBackupStoreManagerRelationship"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("nullableStaffMemberIdentifierResponseDocument"); }); } @@ -326,28 +326,28 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarketCashiersRelationship"); + operationElement.Should().Be("getSupermarketCashiersRelationship"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staffMemberIdentifierCollectionResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceIdentifierCollectionDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceIdentifierCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceIdentifierCollectionDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("linksInResourceIdentifierCollectionDocument").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceIdentifierCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceIdentifierCollectionDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -369,47 +369,47 @@ public async Task Casing_convention_is_applied_to_Post_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets.post").With(getElement => + document.Should().ContainPath("paths./supermarkets.post").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("postSupermarket"); + operationElement.Should().Be("postSupermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarketPostRequestDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarketDataInPostRequest") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarketDataInPostRequest") .SchemaReferenceId; }); string? resourceRelationshipInPostRequestSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarketAttributesInPostRequest"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarketAttributesInPostRequest"); - resourceRelationshipInPostRequestSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + resourceRelationshipInPostRequestSchemaRefId = propertiesElement.Should().ContainPath("relationships.$ref") .ShouldBeSchemaReferenceId("supermarketRelationshipsInPostRequest").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceRelationshipInPostRequestSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceRelationshipInPostRequestSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("storeManager"); - propertiesElement.ShouldContainPath("storeManager.$ref").ShouldBeSchemaReferenceId("toOneStaffMemberInRequest"); + propertiesElement.Should().ContainPath("storeManager.$ref").ShouldBeSchemaReferenceId("toOneStaffMemberInRequest"); propertiesElement.Should().ContainProperty("backupStoreManager"); - propertiesElement.ShouldContainPath("backupStoreManager.$ref").ShouldBeSchemaReferenceId("nullableToOneStaffMemberInRequest"); + propertiesElement.Should().ContainPath("backupStoreManager.$ref").ShouldBeSchemaReferenceId("nullableToOneStaffMemberInRequest"); propertiesElement.Should().ContainProperty("cashiers"); - propertiesElement.ShouldContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("toManyStaffMemberInRequest"); + propertiesElement.Should().ContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("toManyStaffMemberInRequest"); }); }); } @@ -421,11 +421,11 @@ public async Task Casing_convention_is_applied_to_PostRelationship_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.post").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.post").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("postSupermarketCashiersRelationship"); + operationElement.Should().Be("postSupermarketCashiersRelationship"); }); }); } @@ -439,31 +439,31 @@ public async Task Casing_convention_is_applied_to_Patch_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patchSupermarket"); + operationElement.Should().Be("patchSupermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarketPatchRequestDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarketDataInPatchRequest") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarketDataInPatchRequest") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarketAttributesInPatchRequest"); - propertiesElement.ShouldContainPath("relationships.$ref").ShouldBeSchemaReferenceId("supermarketRelationshipsInPatchRequest"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarketAttributesInPatchRequest"); + propertiesElement.Should().ContainPath("relationships.$ref").ShouldBeSchemaReferenceId("supermarketRelationshipsInPatchRequest"); }); }); } @@ -475,11 +475,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/storeManager.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/storeManager.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patchSupermarketStoreManagerRelationship"); + operationElement.Should().Be("patchSupermarketStoreManagerRelationship"); }); }); } @@ -491,11 +491,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/backupStoreManager.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/backupStoreManager.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patchSupermarketBackupStoreManagerRelationship"); + operationElement.Should().Be("patchSupermarketBackupStoreManagerRelationship"); }); }); } @@ -507,11 +507,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patchSupermarketCashiersRelationship"); + operationElement.Should().Be("patchSupermarketCashiersRelationship"); }); }); } @@ -523,11 +523,11 @@ public async Task Casing_convention_is_applied_to_Delete_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}.delete").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}.delete").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("deleteSupermarket"); + operationElement.Should().Be("deleteSupermarket"); }); }); } @@ -539,11 +539,11 @@ public async Task Casing_convention_is_applied_to_DeleteRelationship_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.delete").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.delete").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("deleteSupermarketCashiersRelationship"); + operationElement.Should().Be("deleteSupermarketCashiersRelationship"); }); }); } diff --git a/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseTests.cs b/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseTests.cs index 0a532a6e3f..e0447fc482 100644 --- a/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseTests.cs +++ b/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseTests.cs @@ -25,34 +25,34 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets.get").With(getElement => + document.Should().ContainPath("paths./supermarkets.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket-collection"); + operationElement.Should().Be("get-supermarket-collection"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarket-collection-response-document").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceCollectionDocumentSchemaRefId = null; string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("jsonapi.$ref").ShouldBeSchemaReferenceId("jsonapi-object"); + propertiesElement.Should().ContainPath("jsonapi.$ref").ShouldBeSchemaReferenceId("jsonapi-object"); - linksInResourceCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceCollectionDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("links-in-resource-collection-document").SchemaReferenceId; - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.items.$ref").ShouldBeSchemaReferenceId("supermarket-data-in-response") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.items.$ref").ShouldBeSchemaReferenceId("supermarket-data-in-response") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceCollectionDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -67,68 +67,68 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() string? resourceAttributesInResponseSchemaRefId = null; string? resourceRelationshipInResponseSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - linksInResourceObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("links-in-resource-object") + linksInResourceObjectSchemaRefId = propertiesElement.Should().ContainPath("links.$ref").ShouldBeSchemaReferenceId("links-in-resource-object") .SchemaReferenceId; - primaryResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("supermarket-resource-type") + primaryResourceTypeSchemaRefId = propertiesElement.Should().ContainPath("type.$ref").ShouldBeSchemaReferenceId("supermarket-resource-type") .SchemaReferenceId; - resourceAttributesInResponseSchemaRefId = propertiesElement.ShouldContainPath("attributes.$ref") + resourceAttributesInResponseSchemaRefId = propertiesElement.Should().ContainPath("attributes.$ref") .ShouldBeSchemaReferenceId("supermarket-attributes-in-response").SchemaReferenceId; - resourceRelationshipInResponseSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + resourceRelationshipInResponseSchemaRefId = propertiesElement.Should().ContainPath("relationships.$ref") .ShouldBeSchemaReferenceId("supermarket-relationships-in-response").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceObjectSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceObjectSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); }); - schemasElement.ShouldContainPath($"{primaryResourceTypeSchemaRefId}.enum[0]").With(enumValueElement => + schemasElement.Should().ContainPath($"{primaryResourceTypeSchemaRefId}.enum[0]").With(enumValueElement => { - enumValueElement.ShouldBeString("supermarkets"); + enumValueElement.Should().Be("supermarkets"); }); - schemasElement.ShouldContainPath($"{resourceAttributesInResponseSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceAttributesInResponseSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("name-of-city"); propertiesElement.Should().ContainProperty("kind"); - propertiesElement.ShouldContainPath("kind.$ref").ShouldBeSchemaReferenceId("supermarket-type"); + propertiesElement.Should().ContainPath("kind.$ref").ShouldBeSchemaReferenceId("supermarket-type"); }); string? nullableToOneResourceResponseDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceRelationshipInResponseSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceRelationshipInResponseSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("store-manager"); - propertiesElement.ShouldContainPath("store-manager.$ref").ShouldBeSchemaReferenceId("to-one-staff-member-in-response"); + propertiesElement.Should().ContainPath("store-manager.$ref").ShouldBeSchemaReferenceId("to-one-staff-member-in-response"); - nullableToOneResourceResponseDataSchemaRefId = propertiesElement.ShouldContainPath("backup-store-manager.$ref") + nullableToOneResourceResponseDataSchemaRefId = propertiesElement.Should().ContainPath("backup-store-manager.$ref") .ShouldBeSchemaReferenceId("nullable-to-one-staff-member-in-response").SchemaReferenceId; propertiesElement.Should().ContainProperty("cashiers"); - propertiesElement.ShouldContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("to-many-staff-member-in-response"); + propertiesElement.Should().ContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("to-many-staff-member-in-response"); }); string? linksInRelationshipObjectSchemaRefId = null; string? relatedResourceIdentifierSchemaRefId = null; - schemasElement.ShouldContainPath($"{nullableToOneResourceResponseDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{nullableToOneResourceResponseDataSchemaRefId}.properties").With(propertiesElement => { - linksInRelationshipObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInRelationshipObjectSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("links-in-relationship-object").SchemaReferenceId; - relatedResourceIdentifierSchemaRefId = propertiesElement.ShouldContainPath("data.oneOf[0].$ref") + relatedResourceIdentifierSchemaRefId = propertiesElement.Should().ContainPath("data.oneOf[0].$ref") .ShouldBeSchemaReferenceId("staff-member-identifier").SchemaReferenceId; - propertiesElement.ShouldContainPath("data.oneOf[1].$ref").ShouldBeSchemaReferenceId("null-value"); + propertiesElement.Should().ContainPath("data.oneOf[1].$ref").ShouldBeSchemaReferenceId("null-value"); }); - schemasElement.ShouldContainPath($"{linksInRelationshipObjectSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInRelationshipObjectSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("related"); @@ -136,13 +136,13 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() string? relatedResourceTypeSchemaRefId = null; - schemasElement.ShouldContainPath($"{relatedResourceIdentifierSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{relatedResourceIdentifierSchemaRefId}.properties").With(propertiesElement => { - relatedResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("staff-member-resource-type") + relatedResourceTypeSchemaRefId = propertiesElement.Should().ContainPath("type.$ref").ShouldBeSchemaReferenceId("staff-member-resource-type") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{relatedResourceTypeSchemaRefId}.enum[0]").ShouldBeSchemaReferenceId("staff-members"); + schemasElement.Should().ContainPath($"{relatedResourceTypeSchemaRefId}.enum[0]").ShouldBeSchemaReferenceId("staff-members"); }); } @@ -155,28 +155,28 @@ public async Task Casing_convention_is_applied_to_GetSingle_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket"); + operationElement.Should().Be("get-supermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarket-primary-response-document").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("links-in-resource-document") - .SchemaReferenceId; + linksInResourceDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") + .ShouldBeSchemaReferenceId("links-in-resource-document").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -193,30 +193,30 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_sin // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}/store-manager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/store-manager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket-store-manager"); + operationElement.Should().Be("get-supermarket-store-manager"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staff-member-secondary-response-document").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("staff-member-data-in-response") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("staff-member-data-in-response") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("staff-member-attributes-in-response"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("staff-member-attributes-in-response"); }); }); } @@ -228,14 +228,14 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_nul JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/backup-store-manager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/backup-store-manager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket-backup-store-manager"); + operationElement.Should().Be("get-supermarket-backup-store-manager"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("nullable-staff-member-secondary-response-document"); }); } @@ -247,14 +247,14 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_res JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/cashiers.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/cashiers.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket-cashiers"); + operationElement.Should().Be("get-supermarket-cashiers"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staff-member-collection-response-document"); }); } @@ -268,28 +268,28 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}/relationships/store-manager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/store-manager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket-store-manager-relationship"); + operationElement.Should().Be("get-supermarket-store-manager-relationship"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staff-member-identifier-response-document").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceIdentifierDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceIdentifierDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceIdentifierDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("links-in-resource-identifier-document").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceIdentifierDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceIdentifierDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -305,14 +305,14 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/backup-store-manager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/backup-store-manager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket-backup-store-manager-relationship"); + operationElement.Should().Be("get-supermarket-backup-store-manager-relationship"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("nullable-staff-member-identifier-response-document"); }); } @@ -326,28 +326,28 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket-cashiers-relationship"); + operationElement.Should().Be("get-supermarket-cashiers-relationship"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staff-member-identifier-collection-response-document").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceIdentifierCollectionDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceIdentifierCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceIdentifierCollectionDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("links-in-resource-identifier-collection-document").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceIdentifierCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceIdentifierCollectionDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -369,47 +369,47 @@ public async Task Casing_convention_is_applied_to_Post_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets.post").With(getElement => + document.Should().ContainPath("paths./supermarkets.post").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("post-supermarket"); + operationElement.Should().Be("post-supermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarket-post-request-document").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarket-data-in-post-request") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarket-data-in-post-request") .SchemaReferenceId; }); string? resourceRelationshipInPostRequestSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarket-attributes-in-post-request"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarket-attributes-in-post-request"); - resourceRelationshipInPostRequestSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + resourceRelationshipInPostRequestSchemaRefId = propertiesElement.Should().ContainPath("relationships.$ref") .ShouldBeSchemaReferenceId("supermarket-relationships-in-post-request").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceRelationshipInPostRequestSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceRelationshipInPostRequestSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("store-manager"); - propertiesElement.ShouldContainPath("store-manager.$ref").ShouldBeSchemaReferenceId("to-one-staff-member-in-request"); + propertiesElement.Should().ContainPath("store-manager.$ref").ShouldBeSchemaReferenceId("to-one-staff-member-in-request"); propertiesElement.Should().ContainProperty("backup-store-manager"); - propertiesElement.ShouldContainPath("backup-store-manager.$ref").ShouldBeSchemaReferenceId("nullable-to-one-staff-member-in-request"); + propertiesElement.Should().ContainPath("backup-store-manager.$ref").ShouldBeSchemaReferenceId("nullable-to-one-staff-member-in-request"); propertiesElement.Should().ContainProperty("cashiers"); - propertiesElement.ShouldContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("to-many-staff-member-in-request"); + propertiesElement.Should().ContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("to-many-staff-member-in-request"); }); }); } @@ -421,11 +421,11 @@ public async Task Casing_convention_is_applied_to_PostRelationship_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.post").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.post").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("post-supermarket-cashiers-relationship"); + operationElement.Should().Be("post-supermarket-cashiers-relationship"); }); }); } @@ -439,31 +439,31 @@ public async Task Casing_convention_is_applied_to_Patch_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patch-supermarket"); + operationElement.Should().Be("patch-supermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarket-patch-request-document").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarket-data-in-patch-request") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarket-data-in-patch-request") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarket-attributes-in-patch-request"); - propertiesElement.ShouldContainPath("relationships.$ref").ShouldBeSchemaReferenceId("supermarket-relationships-in-patch-request"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarket-attributes-in-patch-request"); + propertiesElement.Should().ContainPath("relationships.$ref").ShouldBeSchemaReferenceId("supermarket-relationships-in-patch-request"); }); }); } @@ -475,11 +475,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/store-manager.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/store-manager.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patch-supermarket-store-manager-relationship"); + operationElement.Should().Be("patch-supermarket-store-manager-relationship"); }); }); } @@ -491,11 +491,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/backup-store-manager.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/backup-store-manager.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patch-supermarket-backup-store-manager-relationship"); + operationElement.Should().Be("patch-supermarket-backup-store-manager-relationship"); }); }); } @@ -507,11 +507,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patch-supermarket-cashiers-relationship"); + operationElement.Should().Be("patch-supermarket-cashiers-relationship"); }); }); } @@ -523,11 +523,11 @@ public async Task Casing_convention_is_applied_to_Delete_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}.delete").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}.delete").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("delete-supermarket"); + operationElement.Should().Be("delete-supermarket"); }); }); } @@ -539,11 +539,11 @@ public async Task Casing_convention_is_applied_to_DeleteRelationship_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.delete").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.delete").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("delete-supermarket-cashiers-relationship"); + operationElement.Should().Be("delete-supermarket-cashiers-relationship"); }); }); } diff --git a/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseTests.cs b/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseTests.cs index 73868dcdad..48497186c5 100644 --- a/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseTests.cs +++ b/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseTests.cs @@ -26,34 +26,34 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./Supermarkets.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarketCollection"); + operationElement.Should().Be("GetSupermarketCollection"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("SupermarketCollectionResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceCollectionDocumentSchemaRefId = null; string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("jsonapi.$ref").ShouldBeSchemaReferenceId("JsonapiObject"); + propertiesElement.Should().ContainPath("jsonapi.$ref").ShouldBeSchemaReferenceId("JsonapiObject"); - linksInResourceCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceCollectionDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("LinksInResourceCollectionDocument").SchemaReferenceId; - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.items.$ref").ShouldBeSchemaReferenceId("SupermarketDataInResponse") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.items.$ref").ShouldBeSchemaReferenceId("SupermarketDataInResponse") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceCollectionDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -68,68 +68,68 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() string? resourceAttributesInResponseSchemaRefId = null; string? resourceRelationshipInResponseSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - linksInResourceObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("LinksInResourceObject") + linksInResourceObjectSchemaRefId = propertiesElement.Should().ContainPath("links.$ref").ShouldBeSchemaReferenceId("LinksInResourceObject") .SchemaReferenceId; - primaryResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("SupermarketResourceType") + primaryResourceTypeSchemaRefId = propertiesElement.Should().ContainPath("type.$ref").ShouldBeSchemaReferenceId("SupermarketResourceType") .SchemaReferenceId; - resourceAttributesInResponseSchemaRefId = propertiesElement.ShouldContainPath("attributes.$ref") + resourceAttributesInResponseSchemaRefId = propertiesElement.Should().ContainPath("attributes.$ref") .ShouldBeSchemaReferenceId("SupermarketAttributesInResponse").SchemaReferenceId; - resourceRelationshipInResponseSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + resourceRelationshipInResponseSchemaRefId = propertiesElement.Should().ContainPath("relationships.$ref") .ShouldBeSchemaReferenceId("SupermarketRelationshipsInResponse").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceObjectSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceObjectSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); }); - schemasElement.ShouldContainPath($"{primaryResourceTypeSchemaRefId}.enum[0]").With(enumValueElement => + schemasElement.Should().ContainPath($"{primaryResourceTypeSchemaRefId}.enum[0]").With(enumValueElement => { - enumValueElement.ShouldBeString("Supermarkets"); + enumValueElement.Should().Be("Supermarkets"); }); - schemasElement.ShouldContainPath($"{resourceAttributesInResponseSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceAttributesInResponseSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("NameOfCity"); propertiesElement.Should().ContainProperty("Kind"); - propertiesElement.ShouldContainPath("Kind.$ref").ShouldBeSchemaReferenceId("SupermarketType"); + propertiesElement.Should().ContainPath("Kind.$ref").ShouldBeSchemaReferenceId("SupermarketType"); }); string? nullableToOneResourceResponseDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceRelationshipInResponseSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceRelationshipInResponseSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("StoreManager"); - propertiesElement.ShouldContainPath("StoreManager.$ref").ShouldBeSchemaReferenceId("ToOneStaffMemberInResponse"); + propertiesElement.Should().ContainPath("StoreManager.$ref").ShouldBeSchemaReferenceId("ToOneStaffMemberInResponse"); - nullableToOneResourceResponseDataSchemaRefId = propertiesElement.ShouldContainPath("BackupStoreManager.$ref") + nullableToOneResourceResponseDataSchemaRefId = propertiesElement.Should().ContainPath("BackupStoreManager.$ref") .ShouldBeSchemaReferenceId("NullableToOneStaffMemberInResponse").SchemaReferenceId; propertiesElement.Should().ContainProperty("Cashiers"); - propertiesElement.ShouldContainPath("Cashiers.$ref").ShouldBeSchemaReferenceId("ToManyStaffMemberInResponse"); + propertiesElement.Should().ContainPath("Cashiers.$ref").ShouldBeSchemaReferenceId("ToManyStaffMemberInResponse"); }); string? linksInRelationshipObjectSchemaRefId = null; string? relatedResourceIdentifierSchemaRefId = null; - schemasElement.ShouldContainPath($"{nullableToOneResourceResponseDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{nullableToOneResourceResponseDataSchemaRefId}.properties").With(propertiesElement => { - linksInRelationshipObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("LinksInRelationshipObject") - .SchemaReferenceId; + linksInRelationshipObjectSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") + .ShouldBeSchemaReferenceId("LinksInRelationshipObject").SchemaReferenceId; - relatedResourceIdentifierSchemaRefId = propertiesElement.ShouldContainPath("data.oneOf[0].$ref") + relatedResourceIdentifierSchemaRefId = propertiesElement.Should().ContainPath("data.oneOf[0].$ref") .ShouldBeSchemaReferenceId("StaffMemberIdentifier").SchemaReferenceId; - propertiesElement.ShouldContainPath("data.oneOf[1].$ref").ShouldBeSchemaReferenceId("NullValue"); + propertiesElement.Should().ContainPath("data.oneOf[1].$ref").ShouldBeSchemaReferenceId("NullValue"); }); - schemasElement.ShouldContainPath($"{linksInRelationshipObjectSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInRelationshipObjectSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("related"); @@ -137,13 +137,13 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() string? relatedResourceTypeSchemaRefId = null; - schemasElement.ShouldContainPath($"{relatedResourceIdentifierSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{relatedResourceIdentifierSchemaRefId}.properties").With(propertiesElement => { - relatedResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("StaffMemberResourceType") + relatedResourceTypeSchemaRefId = propertiesElement.Should().ContainPath("type.$ref").ShouldBeSchemaReferenceId("StaffMemberResourceType") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{relatedResourceTypeSchemaRefId}.enum[0]").ShouldBeSchemaReferenceId("StaffMembers"); + schemasElement.Should().ContainPath($"{relatedResourceTypeSchemaRefId}.enum[0]").ShouldBeSchemaReferenceId("StaffMembers"); }); } @@ -156,28 +156,28 @@ public async Task Casing_convention_is_applied_to_GetSingle_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./Supermarkets/{id}.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarket"); + operationElement.Should().Be("GetSupermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("SupermarketPrimaryResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("LinksInResourceDocument") + linksInResourceDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref").ShouldBeSchemaReferenceId("LinksInResourceDocument") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -194,30 +194,30 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_sin // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./Supermarkets/{id}/StoreManager.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/StoreManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarketStoreManager"); + operationElement.Should().Be("GetSupermarketStoreManager"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("StaffMemberSecondaryResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("StaffMemberDataInResponse") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("StaffMemberDataInResponse") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("StaffMemberAttributesInResponse"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("StaffMemberAttributesInResponse"); }); }); } @@ -229,14 +229,14 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_nul JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/BackupStoreManager.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/BackupStoreManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarketBackupStoreManager"); + operationElement.Should().Be("GetSupermarketBackupStoreManager"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("NullableStaffMemberSecondaryResponseDocument"); }); } @@ -248,14 +248,14 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_res JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/Cashiers.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/Cashiers.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarketCashiers"); + operationElement.Should().Be("GetSupermarketCashiers"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("StaffMemberCollectionResponseDocument"); }); } @@ -269,28 +269,28 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/StoreManager.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/StoreManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarketStoreManagerRelationship"); + operationElement.Should().Be("GetSupermarketStoreManagerRelationship"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("StaffMemberIdentifierResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceIdentifierDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceIdentifierDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceIdentifierDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("LinksInResourceIdentifierDocument").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceIdentifierDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceIdentifierDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -306,14 +306,14 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/BackupStoreManager.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/BackupStoreManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarketBackupStoreManagerRelationship"); + operationElement.Should().Be("GetSupermarketBackupStoreManagerRelationship"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("NullableStaffMemberIdentifierResponseDocument"); }); } @@ -327,28 +327,28 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/Cashiers.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/Cashiers.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarketCashiersRelationship"); + operationElement.Should().Be("GetSupermarketCashiersRelationship"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("StaffMemberIdentifierCollectionResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceIdentifierCollectionDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceIdentifierCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceIdentifierCollectionDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("LinksInResourceIdentifierCollectionDocument").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceIdentifierCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceIdentifierCollectionDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -370,47 +370,47 @@ public async Task Casing_convention_is_applied_to_Post_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./Supermarkets.post").With(getElement => + document.Should().ContainPath("paths./Supermarkets.post").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("PostSupermarket"); + operationElement.Should().Be("PostSupermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("SupermarketPostRequestDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("SupermarketDataInPostRequest") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("SupermarketDataInPostRequest") .SchemaReferenceId; }); string? resourceRelationshipInPostRequestSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("SupermarketAttributesInPostRequest"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("SupermarketAttributesInPostRequest"); - resourceRelationshipInPostRequestSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + resourceRelationshipInPostRequestSchemaRefId = propertiesElement.Should().ContainPath("relationships.$ref") .ShouldBeSchemaReferenceId("SupermarketRelationshipsInPostRequest").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceRelationshipInPostRequestSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceRelationshipInPostRequestSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("StoreManager"); - propertiesElement.ShouldContainPath("StoreManager.$ref").ShouldBeSchemaReferenceId("ToOneStaffMemberInRequest"); + propertiesElement.Should().ContainPath("StoreManager.$ref").ShouldBeSchemaReferenceId("ToOneStaffMemberInRequest"); propertiesElement.Should().ContainProperty("BackupStoreManager"); - propertiesElement.ShouldContainPath("BackupStoreManager.$ref").ShouldBeSchemaReferenceId("NullableToOneStaffMemberInRequest"); + propertiesElement.Should().ContainPath("BackupStoreManager.$ref").ShouldBeSchemaReferenceId("NullableToOneStaffMemberInRequest"); propertiesElement.Should().ContainProperty("Cashiers"); - propertiesElement.ShouldContainPath("Cashiers.$ref").ShouldBeSchemaReferenceId("ToManyStaffMemberInRequest"); + propertiesElement.Should().ContainPath("Cashiers.$ref").ShouldBeSchemaReferenceId("ToManyStaffMemberInRequest"); }); }); } @@ -422,11 +422,11 @@ public async Task Casing_convention_is_applied_to_PostRelationship_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/Cashiers.post").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/Cashiers.post").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("PostSupermarketCashiersRelationship"); + operationElement.Should().Be("PostSupermarketCashiersRelationship"); }); }); } @@ -440,31 +440,31 @@ public async Task Casing_convention_is_applied_to_Patch_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./Supermarkets/{id}.patch").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("PatchSupermarket"); + operationElement.Should().Be("PatchSupermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("SupermarketPatchRequestDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("SupermarketDataInPatchRequest") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("SupermarketDataInPatchRequest") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("SupermarketAttributesInPatchRequest"); - propertiesElement.ShouldContainPath("relationships.$ref").ShouldBeSchemaReferenceId("SupermarketRelationshipsInPatchRequest"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("SupermarketAttributesInPatchRequest"); + propertiesElement.Should().ContainPath("relationships.$ref").ShouldBeSchemaReferenceId("SupermarketRelationshipsInPatchRequest"); }); }); } @@ -476,11 +476,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/StoreManager.patch").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/StoreManager.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("PatchSupermarketStoreManagerRelationship"); + operationElement.Should().Be("PatchSupermarketStoreManagerRelationship"); }); }); } @@ -492,11 +492,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/BackupStoreManager.patch").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/BackupStoreManager.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("PatchSupermarketBackupStoreManagerRelationship"); + operationElement.Should().Be("PatchSupermarketBackupStoreManagerRelationship"); }); }); } @@ -508,11 +508,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/Cashiers.patch").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/Cashiers.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("PatchSupermarketCashiersRelationship"); + operationElement.Should().Be("PatchSupermarketCashiersRelationship"); }); }); } @@ -524,11 +524,11 @@ public async Task Casing_convention_is_applied_to_Delete_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}.delete").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}.delete").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("DeleteSupermarket"); + operationElement.Should().Be("DeleteSupermarket"); }); }); } @@ -540,11 +540,11 @@ public async Task Casing_convention_is_applied_to_DeleteRelationship_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/Cashiers.delete").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/Cashiers.delete").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("DeleteSupermarketCashiersRelationship"); + operationElement.Should().Be("DeleteSupermarketCashiersRelationship"); }); }); } diff --git a/test/OpenApiTests/ResourceFieldValidation/EmptyResource.cs b/test/OpenApiTests/ResourceFieldValidation/EmptyResource.cs deleted file mode 100644 index 0d7ddf5f1c..0000000000 --- a/test/OpenApiTests/ResourceFieldValidation/EmptyResource.cs +++ /dev/null @@ -1,9 +0,0 @@ -using JetBrains.Annotations; -using JsonApiDotNetCore.Resources; - -namespace OpenApiTests.ResourceFieldValidation; - -[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public class EmptyResource : Identifiable -{ -} diff --git a/test/OpenApiTests/ResourceFieldValidation/ModelStateValidationDisabledStartup.cs b/test/OpenApiTests/ResourceFieldValidation/MsvOffStartup.cs similarity index 81% rename from test/OpenApiTests/ResourceFieldValidation/ModelStateValidationDisabledStartup.cs rename to test/OpenApiTests/ResourceFieldValidation/MsvOffStartup.cs index 0bea8b4d58..838803f9c4 100644 --- a/test/OpenApiTests/ResourceFieldValidation/ModelStateValidationDisabledStartup.cs +++ b/test/OpenApiTests/ResourceFieldValidation/MsvOffStartup.cs @@ -5,7 +5,7 @@ namespace OpenApiTests.ResourceFieldValidation; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class ModelStateValidationDisabledStartup : OpenApiStartup +public sealed class MsvOffStartup : OpenApiStartup where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs deleted file mode 100644 index 40c6c5558a..0000000000 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; - -public sealed class NullabilityTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> - _testContext; - - public NullabilityTests( - OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled"; - } - - [Theory] - [InlineData("referenceType")] - [InlineData("requiredReferenceType")] - [InlineData("nullableValueType")] - [InlineData("requiredNullableValueType")] - public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); - }); - }); - } - - [Theory] - [InlineData("valueType")] - [InlineData("requiredValueType")] - public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Theory] - [InlineData("toOne")] - [InlineData("requiredToOne")] - public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); - }); - }); - } - - [Theory] - [InlineData("toMany")] - [InlineData("requiredToMany")] - public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } -} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/RequiredTests.cs deleted file mode 100644 index 5688462fb9..0000000000 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/RequiredTests.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System.Text.Json; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; - -public sealed class RequiredTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> - _testContext; - - public RequiredTests( - OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - } - - [Theory] - [InlineData("requiredReferenceType")] - [InlineData("requiredValueType")] - [InlineData("requiredNullableValueType")] - public async Task Schema_property_for_attribute_is_required_for_creating_resource(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); - }); - } - - [Theory] - [InlineData("referenceType")] - [InlineData("valueType")] - [InlineData("nullableValueType")] - public async Task Schema_property_for_attribute_is_not_required_for_creating_resource(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); - }); - } - - [Theory] - [InlineData("requiredToOne")] - [InlineData("requiredToMany")] - public async Task Schema_property_for_relationship_is_required_for_creating_resource(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => - { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); - }); - } - - [Theory] - [InlineData("toOne")] - [InlineData("toMany")] - public async Task Schema_property_for_relationship_is_not_required_for_creating_resource(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => - { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); - }); - } - - [Fact] - public async Task No_attribute_schema_properties_are_required_when_updating_resource() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); - } - - [Fact] - public async Task No_relationship_schema_properties_are_required_when_updating_resource() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); - } -} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs deleted file mode 100644 index dcbcc572ab..0000000000 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; - -public sealed class NullabilityTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; - - public NullabilityTests(OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled"; - } - - [Theory] - [InlineData("referenceType")] - [InlineData("nullableValueType")] - public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); - }); - }); - } - - [Theory] - [InlineData("requiredReferenceType")] - [InlineData("valueType")] - [InlineData("requiredValueType")] - [InlineData("requiredNullableValueType")] - public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Theory] - [InlineData("toOne")] - public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); - }); - }); - } - - [Theory] - [InlineData("requiredToOne")] - [InlineData("toMany")] - [InlineData("requiredToMany")] - public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } -} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/RequiredTests.cs deleted file mode 100644 index cba6e2035b..0000000000 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/RequiredTests.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System.Text.Json; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; - -public sealed class RequiredTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; - - public RequiredTests(OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - } - - [Theory] - [InlineData("requiredReferenceType")] - [InlineData("requiredNullableValueType")] - public async Task Schema_property_for_attribute_is_required_for_creating_resource(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); - }); - } - - [Theory] - [InlineData("referenceType")] - [InlineData("valueType")] - [InlineData("requiredValueType")] - [InlineData("nullableValueType")] - public async Task Schema_property_for_attribute_is_not_required_for_creating_resource(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); - }); - } - - [Theory] - [InlineData("requiredToOne")] - public async Task Schema_property_for_relationship_is_required_for_creating_resource(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => - { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); - }); - } - - [Theory] - [InlineData("toOne")] - [InlineData("toMany")] - [InlineData("requiredToMany")] - public async Task Schema_property_for_relationship_is_not_required_for_creating_resource(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => - { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); - }); - } - - [Fact] - public async Task No_attribute_schema_properties_are_required_when_updating_resource() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); - } - - [Fact] - public async Task No_relationship_schema_properties_are_required_when_updating_resource() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); - } -} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs deleted file mode 100644 index 1e8691d9de..0000000000 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs +++ /dev/null @@ -1,35 +0,0 @@ -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore; -using TestBuildingBlocks; - -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled; - -// @formatter:wrap_chained_method_calls chop_always - -[UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class NullableReferenceTypesDisabledDbContext : TestableDbContext -{ - public DbSet NrtDisabledResources => Set(); - - public NullableReferenceTypesDisabledDbContext(DbContextOptions options) - : base(options) - { - } - - protected override void OnModelCreating(ModelBuilder builder) - { - builder.Entity() - .HasOne(resource => resource.ToOne); - - builder.Entity() - .HasOne(resource => resource.RequiredToOne); - - builder.Entity() - .HasMany(resource => resource.ToMany); - - builder.Entity() - .HasMany(resource => resource.RequiredToMany); - - base.OnModelCreating(builder); - } -} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs deleted file mode 100644 index c31e4a9e42..0000000000 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; - -public sealed class NullabilityTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> - _testContext; - - public NullabilityTests( - OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled"; - } - - [Theory] - [InlineData("nullableReferenceType")] - [InlineData("requiredNullableReferenceType")] - [InlineData("nullableValueType")] - [InlineData("requiredNullableValueType")] - public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); - }); - }); - } - - [Theory] - [InlineData("nonNullableReferenceType")] - [InlineData("requiredNonNullableReferenceType")] - [InlineData("valueType")] - [InlineData("requiredValueType")] - public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Theory] - [InlineData("nullableToOne")] - [InlineData("requiredNullableToOne")] - public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); - }); - }); - } - - [Theory] - [InlineData("nonNullableToOne")] - [InlineData("requiredNonNullableToOne")] - [InlineData("toMany")] - [InlineData("requiredToMany")] - public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } -} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/RequiredTests.cs deleted file mode 100644 index 0cca901d44..0000000000 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/RequiredTests.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System.Text.Json; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; - -public sealed class RequiredTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> - _testContext; - - public RequiredTests( - OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - } - - [Theory] - [InlineData("requiredNonNullableReferenceType")] - [InlineData("requiredNullableReferenceType")] - [InlineData("requiredValueType")] - [InlineData("requiredNullableValueType")] - public async Task Schema_property_for_attribute_is_required_for_creating_resource(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); - }); - } - - [Theory] - [InlineData("nonNullableReferenceType")] - [InlineData("nullableReferenceType")] - [InlineData("valueType")] - [InlineData("nullableValueType")] - public async Task Schema_property_for_attribute_is_not_required_for_creating_resource(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); - }); - } - - [Theory] - [InlineData("requiredNonNullableToOne")] - [InlineData("requiredNullableToOne")] - [InlineData("requiredToMany")] - public async Task Schema_property_for_relationship_is_required_for_creating_resource(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => - { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); - }); - } - - [Theory] - [InlineData("nonNullableToOne")] - [InlineData("nullableToOne")] - [InlineData("toMany")] - public async Task Schema_property_for_relationship_is_not_required_for_creating_resource(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => - { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); - }); - } - - [Fact] - public async Task No_attribute_schema_properties_are_required_when_updating_resource() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); - } - - [Fact] - public async Task No_relationship_schema_properties_are_required_when_updating_resource() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); - } -} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs deleted file mode 100644 index b374162562..0000000000 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; - -public sealed class NullabilityTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; - - public NullabilityTests(OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled"; - } - - [Theory] - [InlineData("nullableReferenceType")] - [InlineData("nullableValueType")] - public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); - }); - }); - } - - [Theory] - [InlineData("nonNullableReferenceType")] - [InlineData("requiredNonNullableReferenceType")] - [InlineData("requiredNullableReferenceType")] - [InlineData("valueType")] - [InlineData("requiredValueType")] - [InlineData("requiredNullableValueType")] - public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Theory] - [InlineData("nullableToOne")] - public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); - }); - }); - } - - [Theory] - [InlineData("nonNullableToOne")] - [InlineData("requiredNonNullableToOne")] - [InlineData("requiredNullableToOne")] - [InlineData("toMany")] - [InlineData("requiredToMany")] - public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } -} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs deleted file mode 100644 index 54f1620db5..0000000000 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs +++ /dev/null @@ -1,41 +0,0 @@ -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore; -using TestBuildingBlocks; - -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled; - -// @formatter:wrap_chained_method_calls chop_always - -[UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class NullableReferenceTypesEnabledDbContext : TestableDbContext -{ - public DbSet NullableReferenceTypesEnabledResources => Set(); - - public NullableReferenceTypesEnabledDbContext(DbContextOptions options) - : base(options) - { - } - - protected override void OnModelCreating(ModelBuilder builder) - { - builder.Entity() - .HasOne(resource => resource.NonNullableToOne); - - builder.Entity() - .HasOne(resource => resource.RequiredNonNullableToOne); - - builder.Entity() - .HasOne(resource => resource.NullableToOne); - - builder.Entity() - .HasOne(resource => resource.RequiredNullableToOne); - - builder.Entity() - .HasMany(resource => resource.ToMany); - - builder.Entity() - .HasMany(resource => resource.RequiredToMany); - - base.OnModelCreating(builder); - } -} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs new file mode 100644 index 0000000000..d02338445e --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs @@ -0,0 +1,93 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff; + +public sealed class NullabilityTests : IClassFixture, NrtOffDbContext>> +{ + private readonly OpenApiTestContext, NrtOffDbContext> _testContext; + + public NullabilityTests(OpenApiTestContext, NrtOffDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff"; + } + + [Theory] + [InlineData("referenceType")] + [InlineData("requiredReferenceType")] + [InlineData("nullableValueType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => + { + schemaProperty.Should().ContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + }); + } + + [Theory] + [InlineData("valueType")] + [InlineData("requiredValueType")] + public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => + { + schemaProperty.Should().NotContainPath("nullable"); + }); + }); + } + + [Theory] + [InlineData("toOne")] + [InlineData("requiredToOne")] + public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + }); + }); + } + + [Theory] + [InlineData("toMany")] + [InlineData("requiredToMany")] + public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data").Should().NotContainPath("oneOf[1].$ref"); + }); + }); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/RequiredTests.cs new file mode 100644 index 0000000000..6974a721c2 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/RequiredTests.cs @@ -0,0 +1,103 @@ +using System.Text.Json; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff; + +public sealed class RequiredTests : IClassFixture, NrtOffDbContext>> +{ + private readonly OpenApiTestContext, NrtOffDbContext> _testContext; + + public RequiredTests(OpenApiTestContext, NrtOffDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + } + + [Theory] + [InlineData("requiredReferenceType")] + [InlineData("requiredValueType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_for_attribute_is_required_for_creating_resource(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); + }); + } + + [Theory] + [InlineData("referenceType")] + [InlineData("valueType")] + [InlineData("nullableValueType")] + public async Task Schema_property_for_attribute_is_not_required_for_creating_resource(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); + }); + } + + [Theory] + [InlineData("requiredToOne")] + [InlineData("requiredToMany")] + public async Task Schema_property_for_relationship_is_required_for_creating_resource(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); + }); + } + + [Theory] + [InlineData("toOne")] + [InlineData("toMany")] + public async Task Schema_property_for_relationship_is_not_required_for_creating_resource(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); + }); + } + + [Fact] + public async Task No_attribute_schema_properties_are_required_for_updating_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().NotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); + } + + [Fact] + public async Task No_relationship_schema_properties_are_required_for_updating_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().NotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs new file mode 100644 index 0000000000..185cd9a5e1 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs @@ -0,0 +1,93 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn; + +public sealed class NullabilityTests : IClassFixture, NrtOffDbContext>> +{ + private readonly OpenApiTestContext, NrtOffDbContext> _testContext; + + public NullabilityTests(OpenApiTestContext, NrtOffDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn"; + } + + [Theory] + [InlineData("referenceType")] + [InlineData("nullableValueType")] + public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => + { + schemaProperty.Should().ContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + }); + } + + [Theory] + [InlineData("requiredReferenceType")] + [InlineData("valueType")] + [InlineData("requiredValueType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => + { + schemaProperty.Should().NotContainPath("nullable"); + }); + }); + } + + [Theory] + [InlineData("toOne")] + public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + }); + }); + } + + [Theory] + [InlineData("requiredToOne")] + [InlineData("toMany")] + [InlineData("requiredToMany")] + public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data").Should().NotContainPath("oneOf[1].$ref"); + }); + }); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/RequiredTests.cs new file mode 100644 index 0000000000..bab06b8b23 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/RequiredTests.cs @@ -0,0 +1,103 @@ +using System.Text.Json; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn; + +public sealed class RequiredTests : IClassFixture, NrtOffDbContext>> +{ + private readonly OpenApiTestContext, NrtOffDbContext> _testContext; + + public RequiredTests(OpenApiTestContext, NrtOffDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + } + + [Theory] + [InlineData("requiredReferenceType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_for_attribute_is_required_for_creating_resource(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); + }); + } + + [Theory] + [InlineData("referenceType")] + [InlineData("valueType")] + [InlineData("requiredValueType")] + [InlineData("nullableValueType")] + public async Task Schema_property_for_attribute_is_not_required_for_creating_resource(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); + }); + } + + [Theory] + [InlineData("requiredToOne")] + public async Task Schema_property_for_relationship_is_required_for_creating_resource(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); + }); + } + + [Theory] + [InlineData("toOne")] + [InlineData("toMany")] + [InlineData("requiredToMany")] + public async Task Schema_property_for_relationship_is_not_required_for_creating_resource(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); + }); + } + + [Fact] + public async Task No_attribute_schema_properties_are_required_for_updating_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().NotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); + } + + [Fact] + public async Task No_relationship_schema_properties_are_required_for_updating_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().NotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffDbContext.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffDbContext.cs new file mode 100644 index 0000000000..0b6ea5d875 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffDbContext.cs @@ -0,0 +1,44 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff; + +// @formatter:wrap_chained_method_calls chop_always + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class NrtOffDbContext : TestableDbContext +{ + public DbSet Resources => Set(); + public DbSet Empties => Set(); + + public NrtOffDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity() + .HasOne(resource => resource.ToOne) + .WithOne() + .HasForeignKey("ToOneId"); + + builder.Entity() + .HasOne(resource => resource.RequiredToOne) + .WithOne() + .HasForeignKey("RequiredToOneId"); + + builder.Entity() + .HasMany(resource => resource.ToMany) + .WithOne() + .HasForeignKey("ToManyId"); + + builder.Entity() + .HasMany(resource => resource.RequiredToMany) + .WithOne() + .HasForeignKey("RequiredToManyId"); + + base.OnModelCreating(builder); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffEmpty.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffEmpty.cs new file mode 100644 index 0000000000..4df36bca01 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffEmpty.cs @@ -0,0 +1,13 @@ +#nullable disable + +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff; + +[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] +[Resource(PublicName = "empties", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] +public class NrtOffEmpty : Identifiable +{ +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NrtDisabledResource.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffResource.cs similarity index 63% rename from test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NrtDisabledResource.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffResource.cs index 4a297cf3e9..968d579d49 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NrtDisabledResource.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffResource.cs @@ -5,11 +5,11 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(PublicName = "Resource", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] -public sealed class NrtDisabledResource : Identifiable +[Resource(PublicName = "resources", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] +public sealed class NrtOffResource : Identifiable { [Attr] public string ReferenceType { get; set; } @@ -33,16 +33,16 @@ public sealed class NrtDisabledResource : Identifiable public int? RequiredNullableValueType { get; set; } [HasOne] - public EmptyResource ToOne { get; set; } + public NrtOffEmpty ToOne { get; set; } [Required] [HasOne] - public EmptyResource RequiredToOne { get; set; } + public NrtOffEmpty RequiredToOne { get; set; } [HasMany] - public ICollection ToMany { get; set; } = new HashSet(); + public ICollection ToMany { get; set; } = new HashSet(); [Required] [HasMany] - public ICollection RequiredToMany { get; set; } = new HashSet(); + public ICollection RequiredToMany { get; set; } = new HashSet(); } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs new file mode 100644 index 0000000000..a41c3f7508 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs @@ -0,0 +1,97 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff; + +public sealed class NullabilityTests : IClassFixture, NrtOnDbContext>> +{ + private readonly OpenApiTestContext, NrtOnDbContext> _testContext; + + public NullabilityTests(OpenApiTestContext, NrtOnDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff"; + } + + [Theory] + [InlineData("nullableReferenceType")] + [InlineData("requiredNullableReferenceType")] + [InlineData("nullableValueType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => + { + schemaProperty.Should().ContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + }); + } + + [Theory] + [InlineData("nonNullableReferenceType")] + [InlineData("requiredNonNullableReferenceType")] + [InlineData("valueType")] + [InlineData("requiredValueType")] + public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => + { + schemaProperty.Should().NotContainPath("nullable"); + }); + }); + } + + [Theory] + [InlineData("nullableToOne")] + [InlineData("requiredNullableToOne")] + public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + }); + }); + } + + [Theory] + [InlineData("nonNullableToOne")] + [InlineData("requiredNonNullableToOne")] + [InlineData("toMany")] + [InlineData("requiredToMany")] + public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data").Should().NotContainPath("oneOf[1].$ref"); + }); + }); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/RequiredTests.cs similarity index 50% rename from test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/RequiredTests.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/RequiredTests.cs index f0fbc13272..7020004021 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/RequiredTests.cs @@ -2,24 +2,23 @@ using TestBuildingBlocks; using Xunit; -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff; -public sealed class RequiredTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> +public sealed class RequiredTests : IClassFixture, NrtOnDbContext>> { - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; + private readonly OpenApiTestContext, NrtOnDbContext> _testContext; - public RequiredTests(OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + public RequiredTests(OpenApiTestContext, NrtOnDbContext> testContext) { _testContext = testContext; - testContext.UseController(); + testContext.UseController(); } [Theory] - [InlineData("nonNullableReferenceType")] [InlineData("requiredNonNullableReferenceType")] [InlineData("requiredNullableReferenceType")] + [InlineData("requiredValueType")] [InlineData("requiredNullableValueType")] public async Task Schema_property_for_attribute_is_required_for_creating_resource(string jsonPropertyName) { @@ -27,17 +26,17 @@ public async Task Schema_property_for_attribute_is_required_for_creating_resourc JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); }); } [Theory] + [InlineData("nonNullableReferenceType")] [InlineData("nullableReferenceType")] [InlineData("valueType")] - [InlineData("requiredValueType")] [InlineData("nullableValueType")] public async Task Schema_property_for_attribute_is_not_required_for_creating_resource(string jsonPropertyName) { @@ -45,64 +44,64 @@ public async Task Schema_property_for_attribute_is_not_required_for_creating_res JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); }); } [Theory] - [InlineData("nonNullableToOne")] [InlineData("requiredNonNullableToOne")] [InlineData("requiredNullableToOne")] + [InlineData("requiredToMany")] public async Task Schema_property_for_relationship_is_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); }); } [Theory] + [InlineData("nonNullableToOne")] [InlineData("nullableToOne")] [InlineData("toMany")] - [InlineData("requiredToMany")] public async Task Schema_property_for_relationship_is_not_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); }); } [Fact] - public async Task No_attribute_schema_properties_are_required_when_updating_resource() + public async Task No_attribute_schema_properties_are_required_for_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldNotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); + document.Should().NotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); } [Fact] - public async Task No_relationship_schema_properties_are_required_when_updating_resource() + public async Task No_relationship_schema_properties_are_required_for_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldNotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); + document.Should().NotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); } } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs new file mode 100644 index 0000000000..2d1cf8c117 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs @@ -0,0 +1,97 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn; + +public sealed class NullabilityTests : IClassFixture, NrtOnDbContext>> +{ + private readonly OpenApiTestContext, NrtOnDbContext> _testContext; + + public NullabilityTests(OpenApiTestContext, NrtOnDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn"; + } + + [Theory] + [InlineData("nullableReferenceType")] + [InlineData("nullableValueType")] + public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => + { + schemaProperty.Should().ContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + }); + } + + [Theory] + [InlineData("nonNullableReferenceType")] + [InlineData("requiredNonNullableReferenceType")] + [InlineData("requiredNullableReferenceType")] + [InlineData("valueType")] + [InlineData("requiredValueType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => + { + schemaProperty.Should().NotContainPath("nullable"); + }); + }); + } + + [Theory] + [InlineData("nullableToOne")] + public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + }); + }); + } + + [Theory] + [InlineData("nonNullableToOne")] + [InlineData("requiredNonNullableToOne")] + [InlineData("requiredNullableToOne")] + [InlineData("toMany")] + [InlineData("requiredToMany")] + public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data").Should().NotContainPath("oneOf[1].$ref"); + }); + }); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/RequiredTests.cs new file mode 100644 index 0000000000..e33a4ae3d7 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/RequiredTests.cs @@ -0,0 +1,107 @@ +using System.Text.Json; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn; + +public sealed class RequiredTests : IClassFixture, NrtOnDbContext>> +{ + private readonly OpenApiTestContext, NrtOnDbContext> _testContext; + + public RequiredTests(OpenApiTestContext, NrtOnDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + } + + [Theory] + [InlineData("nonNullableReferenceType")] + [InlineData("requiredNonNullableReferenceType")] + [InlineData("requiredNullableReferenceType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_for_attribute_is_required_for_creating_resource(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); + }); + } + + [Theory] + [InlineData("nullableReferenceType")] + [InlineData("valueType")] + [InlineData("requiredValueType")] + [InlineData("nullableValueType")] + public async Task Schema_property_for_attribute_is_not_required_for_creating_resource(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); + }); + } + + [Theory] + [InlineData("nonNullableToOne")] + [InlineData("requiredNonNullableToOne")] + [InlineData("requiredNullableToOne")] + public async Task Schema_property_for_relationship_is_required_for_creating_resource(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); + }); + } + + [Theory] + [InlineData("nullableToOne")] + [InlineData("toMany")] + [InlineData("requiredToMany")] + public async Task Schema_property_for_relationship_is_not_required_for_creating_resource(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); + }); + } + + [Fact] + public async Task No_attribute_schema_properties_are_required_for_updating_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().NotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); + } + + [Fact] + public async Task No_relationship_schema_properties_are_required_for_updating_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().NotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnDbContext.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnDbContext.cs new file mode 100644 index 0000000000..bf213ea812 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnDbContext.cs @@ -0,0 +1,54 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn; + +// @formatter:wrap_chained_method_calls chop_always + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class NrtOnDbContext : TestableDbContext +{ + public DbSet Resources => Set(); + public DbSet Empties => Set(); + + public NrtOnDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity() + .HasOne(resource => resource.NonNullableToOne) + .WithOne() + .HasForeignKey("NonNullableToOneId"); + + builder.Entity() + .HasOne(resource => resource.RequiredNonNullableToOne) + .WithOne() + .HasForeignKey("RequiredNonNullableToOneId"); + + builder.Entity() + .HasOne(resource => resource.NullableToOne) + .WithOne() + .HasForeignKey("NullableToOneId"); + + builder.Entity() + .HasOne(resource => resource.RequiredNullableToOne) + .WithOne() + .HasForeignKey("RequiredNullableToOneId"); + + builder.Entity() + .HasMany(resource => resource.ToMany) + .WithOne() + .HasForeignKey("ToManyId"); + + builder.Entity() + .HasMany(resource => resource.RequiredToMany) + .WithOne() + .HasForeignKey("RequiredToManyId"); + + base.OnModelCreating(builder); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnEmpty.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnEmpty.cs new file mode 100644 index 0000000000..65df582c71 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnEmpty.cs @@ -0,0 +1,11 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn; + +[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] +[Resource(PublicName = "empties", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] +public class NrtOnEmpty : Identifiable +{ +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NrtEnabledResource.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnResource.cs similarity index 62% rename from test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NrtEnabledResource.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnResource.cs index 8908f2a50f..efc897cea2 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NrtEnabledResource.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnResource.cs @@ -3,11 +3,11 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(PublicName = "Resource", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] -public sealed class NrtEnabledResource : Identifiable +[Resource(PublicName = "resources", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] +public sealed class NrtOnResource : Identifiable { [Attr] public string NonNullableReferenceType { get; set; } = null!; @@ -38,23 +38,23 @@ public sealed class NrtEnabledResource : Identifiable public int? RequiredNullableValueType { get; set; } [HasOne] - public EmptyResource NonNullableToOne { get; set; } = null!; + public NrtOnEmpty NonNullableToOne { get; set; } = null!; [Required] [HasOne] - public EmptyResource RequiredNonNullableToOne { get; set; } = null!; + public NrtOnEmpty RequiredNonNullableToOne { get; set; } = null!; [HasOne] - public EmptyResource? NullableToOne { get; set; } + public NrtOnEmpty? NullableToOne { get; set; } [Required] [HasOne] - public EmptyResource? RequiredNullableToOne { get; set; } + public NrtOnEmpty? RequiredNullableToOne { get; set; } [HasMany] - public ICollection ToMany { get; set; } = new HashSet(); + public ICollection ToMany { get; set; } = new HashSet(); [Required] [HasMany] - public ICollection RequiredToMany { get; set; } = new HashSet(); + public ICollection RequiredToMany { get; set; } = new HashSet(); } diff --git a/test/TestBuildingBlocks/FakerContainer.cs b/test/TestBuildingBlocks/FakerContainer.cs index 99cce6e04c..d9cc96e0b3 100644 --- a/test/TestBuildingBlocks/FakerContainer.cs +++ b/test/TestBuildingBlocks/FakerContainer.cs @@ -15,7 +15,7 @@ static FakerContainer() Date.SystemClock = () => 1.January(2020).AsUtc(); } - protected static int GetFakerSeed() + public static int GetFakerSeed() { // The goal here is to have stable data over multiple test runs, but at the same time different data per test case. diff --git a/test/TestBuildingBlocks/JsonElementExtensions.cs b/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs similarity index 56% rename from test/TestBuildingBlocks/JsonElementExtensions.cs rename to test/TestBuildingBlocks/JsonElementAssertionExtensions.cs index 6eafecc4ca..e73a217d81 100644 --- a/test/TestBuildingBlocks/JsonElementExtensions.cs +++ b/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; using BlushingPenguin.JsonPath; using FluentAssertions; using FluentAssertions.Execution; @@ -6,54 +6,14 @@ namespace TestBuildingBlocks; -public static class JsonElementExtensions +public static class JsonElementAssertionExtensions { public static JsonElementAssertions Should(this JsonElement source) { return new JsonElementAssertions(source); } - public static JsonElement ShouldContainPath(this JsonElement source, string path) - { - Func elementSelector = () => source.SelectToken(path, true)!.Value; - return elementSelector.Should().NotThrow().Subject; - } - - public static void ShouldNotContainPath(this JsonElement source, string path) - { - JsonElement? pathToken = source.SelectToken(path); - - pathToken.Should().BeNull(); - } - - public static void ShouldBeString(this JsonElement source, string value) - { - source.ValueKind.Should().Be(JsonValueKind.String); - source.GetString().Should().Be(value); - } - - public static void ShouldBeArrayWithElement(this JsonElement source, string value) - { - source.ValueKind.Should().Be(JsonValueKind.Array); - - var deserializedCollection = JsonSerializer.Deserialize>(source.GetRawText()); - deserializedCollection.Should().Contain(value); - } - - public static void ShouldBeArrayWithoutElement(this JsonElement source, string value) - { - source.ValueKind.Should().Be(JsonValueKind.Array); - - var deserializedCollection = JsonSerializer.Deserialize>(source.GetRawText()); - deserializedCollection.Should().NotContain(value); - } - - public static void ShouldBeInteger(this JsonElement source, int value) - { - source.ValueKind.Should().Be(JsonValueKind.Number); - source.GetInt32().Should().Be(value); - } - + [CustomAssertion] public static SchemaReferenceIdContainer ShouldBeSchemaReferenceId(this JsonElement source, string value) { string schemaReferenceId = GetSchemaReferenceId(source); @@ -62,6 +22,7 @@ public static SchemaReferenceIdContainer ShouldBeSchemaReferenceId(this JsonElem return new SchemaReferenceIdContainer(value); } + [CustomAssertion] private static string GetSchemaReferenceId(this JsonElement source) { source.ValueKind.Should().Be(JsonValueKind.String); @@ -69,10 +30,10 @@ private static string GetSchemaReferenceId(this JsonElement source) string? jsonElementValue = source.GetString(); jsonElementValue.ShouldNotBeNull(); - string schemaReferenceId = jsonElementValue.Split('/').Last(); - return schemaReferenceId; + return jsonElementValue.Split('/').Last(); } + [CustomAssertion] public static void WithSchemaReferenceId(this JsonElement subject, [InstantHandle] Action continuation) { string schemaReferenceId = GetSchemaReferenceId(subject); @@ -111,11 +72,54 @@ protected JsonElementAssertions(JsonElement subject) public void ContainProperty(string propertyName) { string json = _subject.ToString(); - string escapedJson = json.Replace("{", "{{").Replace("}", "}}"); Execute.Assertion.ForCondition(_subject.TryGetProperty(propertyName, out _)) .FailWith($"Expected JSON element '{escapedJson}' to contain a property named '{propertyName}'."); } + + public JsonElement ContainPath(string path) + { + Func elementSelector = () => _subject.SelectToken(path, true)!.Value; + return elementSelector.Should().NotThrow().Subject; + } + + public void NotContainPath(string path) + { + JsonElement? pathToken = _subject.SelectToken(path); + pathToken.Should().BeNull(); + } + + public void Be(string value) + { + _subject.ValueKind.Should().Be(JsonValueKind.String); + _subject.GetString().Should().Be(value); + } + + public void Be(int value) + { + _subject.ValueKind.Should().Be(JsonValueKind.Number); + _subject.GetInt32().Should().Be(value); + } + + public void ContainArrayElement(string value) + { + _subject.ValueKind.Should().Be(JsonValueKind.Array); + + string?[] stringValues = _subject.EnumerateArray().Where(element => element.ValueKind == JsonValueKind.String) + .Select(element => element.GetString()).ToArray(); + + stringValues.Should().Contain(value); + } + + public void NotContainArrayElement(string value) + { + _subject.ValueKind.Should().Be(JsonValueKind.Array); + + string?[] stringValues = _subject.EnumerateArray().Where(element => element.ValueKind == JsonValueKind.String) + .Select(element => element.GetString()).ToArray(); + + stringValues.Should().NotContain(value); + } } }