/// <summary> /// Because the test deserializer used to parse the actual payload will not have metadata, we fix it up here. /// Note: this should probably move to happen just before comparison, rather than when generating expectations. /// </summary> /// <param name="payloadElement">The payload element</param> private void SortAndNormalizeProperties(ComplexInstance payloadElement) { var sortedProperties = payloadElement.Properties.OrderBy(p => p.Name).ToList(); foreach (var p in sortedProperties) { var property = p; payloadElement.Remove(property); // If we have a primitive property with null value, we write a type name for the null value // in V1 and V2. If not type name is available or we are in V3, we don't do that and thus expect a null property instance. var primitiveProperty = property as PrimitiveProperty; if (primitiveProperty != null && primitiveProperty.Value.IsNull && (this.dsv >= DataServiceProtocolVersion.V4 || string.IsNullOrEmpty(primitiveProperty.Value.FullTypeName))) { property = new NullPropertyInstance(property.Name, null).WithAnnotations(property.Annotations); } var complexProperty = property as ComplexProperty; if (complexProperty != null && complexProperty.Value.IsNull) { property = new NullPropertyInstance(property.Name, complexProperty.Value.FullTypeName).WithAnnotations(property.Annotations); } var complexCollectionProperty = property as ComplexMultiValueProperty; if (complexCollectionProperty != null && complexCollectionProperty.Value.Count == 0 && complexCollectionProperty.Value.FullTypeName == null) { property = new PrimitiveProperty(complexCollectionProperty.Name, complexCollectionProperty.Value.FullTypeName, string.Empty); } payloadElement.Add(property); } }
/// <summary> /// Removes duplicate properties from the specified complex or entity value. /// </summary> /// <param name="payloadElement"></param> private void DeduplicateProperties(ComplexInstance payloadElement) { Dictionary<string, PropertyInstance> properties = new Dictionary<string, PropertyInstance>(); foreach (PropertyInstance propertyInstance in payloadElement.Properties.ToList()) { PropertyInstance firstPropertyInstance; if (properties.TryGetValue(propertyInstance.Name, out firstPropertyInstance)) { payloadElement.Remove(propertyInstance); payloadElement.Replace(firstPropertyInstance, propertyInstance); properties[propertyInstance.Name] = propertyInstance; } else { properties.Add(propertyInstance.Name, propertyInstance); } } }
/// <summary> /// Removes duplicate properties from the specified complex or entity value. /// </summary> /// <param name="payloadElement"></param> private void DeduplicateProperties(ComplexInstance payloadElement) { Dictionary <string, PropertyInstance> properties = new Dictionary <string, PropertyInstance>(); foreach (PropertyInstance propertyInstance in payloadElement.Properties.ToList()) { PropertyInstance firstPropertyInstance; if (properties.TryGetValue(propertyInstance.Name, out firstPropertyInstance)) { payloadElement.Remove(propertyInstance); payloadElement.Replace(firstPropertyInstance, propertyInstance); properties[propertyInstance.Name] = propertyInstance; } else { properties.Add(propertyInstance.Name, propertyInstance); } } }
public void ComplexValueIgnorePropertyNullValuesTest() { var versions = new Version[] { null, new Version(4, 0), }; EdmModel edmModel = new EdmModel(); IEdmComplexType countryRegionType = edmModel.ComplexType("CountryRegion") .Property("Name", EdmPrimitiveTypeKind.String) .Property("CountryRegionCode", EdmPrimitiveTypeKind.String); IEdmComplexType countryRegionNullType = edmModel.ComplexType("CountryRegionNull") .Property("Name", EdmPrimitiveTypeKind.String) .Property("CountryRegionCode", EdmPrimitiveTypeKind.String); IEdmComplexType addressType = edmModel.ComplexType("Address") .Property("Street", EdmPrimitiveTypeKind.String) .Property("StreetNull", EdmCoreModel.Instance.GetString(true) as EdmTypeReference) .Property("Numbers", EdmCoreModel.GetCollection(EdmCoreModel.Instance.GetInt32(false)) as EdmTypeReference) .Property("CountryRegion", new EdmComplexTypeReference(countryRegionType, false)) .Property("CountryRegionNull", new EdmComplexTypeReference(countryRegionNullType, true)); edmModel.EntityType("Customer") .KeyProperty("ID", EdmCoreModel.Instance.GetInt32(false) as EdmTypeReference) .Property("Address", new EdmComplexTypeReference(addressType, false)); edmModel.Fixup(); this.CombinatorialEngineProvider.RunCombinations( new ODataNullValueBehaviorKind[] { ODataNullValueBehaviorKind.Default, ODataNullValueBehaviorKind.DisableValidation, ODataNullValueBehaviorKind.IgnoreValue }, versions, versions, TestReaderUtils.ODataBehaviorKinds, (nullPropertyValueReaderBehavior, dataServiceVersion, edmVersion, behaviorKind) => { edmModel.SetEdmVersion(edmVersion); // Now we set the 'IgnoreNullValues' annotation on all properties IEdmComplexType edmAddressType = (IEdmComplexType)edmModel.FindType("TestModel.Address"); foreach (IEdmStructuralProperty edmProperty in edmAddressType.StructuralProperties()) { edmModel.SetNullValueReaderBehavior(edmProperty, nullPropertyValueReaderBehavior); } EntityInstance customerPayload = PayloadBuilder.Entity("TestModel.Customer") .PrimitiveProperty("ID", 1) .Property("Address", PayloadBuilder.ComplexValue("TestModel.Address") .PrimitiveProperty("Street", "One Microsoft Way") .PrimitiveProperty("StreetNull", "One Microsoft Way") .Property("Numbers", PayloadBuilder.PrimitiveMultiValue("Collection(Edm.Int32)").Item(1).Item(2)) .Property("CountryRegion", PayloadBuilder.ComplexValue("TestModel.CountryRegion") .PrimitiveProperty("Name", "Austria") .PrimitiveProperty("CountryRegionCode", "AUT")) .Property("CountryRegionNull", PayloadBuilder.ComplexValue("TestModel.CountryRegionNull") .PrimitiveProperty("Name", "Austria") .PrimitiveProperty("CountryRegionCode", "AUT"))); var testCases = new[] { // Complex types that are not nullable should not allow null values. // Null primitive property in the payload and non-nullable property in the model new IgnoreNullValueTestCase { PropertyName = "Street", ExpectedResponseException = ODataExpectedExceptions.ODataException("ReaderValidationUtils_NullNamedValueForNonNullableType", "Street", "Edm.String"), }, // Null complex property in the payload and non-nullable property in the model new IgnoreNullValueTestCase { PropertyName = "CountryRegion", ExpectedResponseException = ODataExpectedExceptions.ODataException("ReaderValidationUtils_NullNamedValueForNonNullableType", "CountryRegion", "TestModel.CountryRegion"), }, // Null collection property in the payload and non-nullable property in the model new IgnoreNullValueTestCase { PropertyName = "Numbers", ExpectedResponseException = ODataExpectedExceptions.ODataException("ReaderValidationUtils_NullNamedValueForNonNullableType", "Numbers", "Collection(Edm.Int32)"), }, // Complex types that are nullable should allow null values. // Null primitive property in the payload and nullable property in the model new IgnoreNullValueTestCase { PropertyName = "StreetNull", }, // Null complex property in the payload and nullable property in the model new IgnoreNullValueTestCase { PropertyName = "CountryRegionNull", }, }; Func <IgnoreNullValueTestCase, ReaderTestConfiguration, PayloadReaderTestDescriptor> createTestDescriptor = (testCase, testConfig) => { EntityInstance payloadValue = customerPayload.DeepCopy(); ComplexInstance payloadAddressValue = ((ComplexProperty)payloadValue.GetProperty("Address")).Value; SetToNull(payloadAddressValue, testCase.PropertyName); ComplexInstance resultValue = payloadValue; if (testConfig.IsRequest && nullPropertyValueReaderBehavior == ODataNullValueBehaviorKind.IgnoreValue) { resultValue = customerPayload.DeepCopy(); ComplexInstance resultAddressValue = ((ComplexProperty)resultValue.GetProperty("Address")).Value; resultAddressValue.Remove(resultAddressValue.GetProperty(testCase.PropertyName)); } return(new PayloadReaderTestDescriptor(this.Settings) { PayloadElement = payloadValue, PayloadEdmModel = edmModel, ExpectedResultPayloadElement = tc => { if (tc.Format == ODataFormat.Json) { // under the client knob ODL will compute edit links, ids, etc // so we need to update the expected payload if (tc.RunBehaviorKind == TestODataBehaviorKind.WcfDataServicesClient) { var entity = resultValue as EntityInstance; if (entity != null) { if (!tc.IsRequest) { entity.Id = "http://odata.org/test/Customer(1)"; entity.EditLink = "http://odata.org/test/Customer(1)"; entity.WithSelfLink("http://odata.org/test/Customer(1)"); } } } var tempDescriptor = new PayloadReaderTestDescriptor(this.Settings) { PayloadElement = resultValue, PayloadEdmModel = edmModel, }; JsonLightPayloadElementFixup.Fixup(tempDescriptor); return tempDescriptor.PayloadElement; } return resultValue; }, ExpectedException = (testConfig.IsRequest && nullPropertyValueReaderBehavior != ODataNullValueBehaviorKind.Default) ? null : testCase.ExpectedResponseException }); }; this.CombinatorialEngineProvider.RunCombinations( testCases, this.ReaderTestConfigurationProvider.ExplicitFormatConfigurations, (testCase, testConfiguration) => { testConfiguration = testConfiguration.CloneAndApplyBehavior(behaviorKind); testConfiguration.MessageReaderSettings.BaseUri = null; PayloadReaderTestDescriptor testDescriptor = createTestDescriptor(testCase, testConfiguration); testDescriptor.RunTest(testConfiguration); }); }); }