public void DuplicatePropertyNamesTest() { PropertyInstance primitiveProperty = PayloadBuilder.PrimitiveProperty("DuplicateProperty", 42); PropertyInstance complexProperty = PayloadBuilder.Property("DuplicateProperty", PayloadBuilder.ComplexValue("TestModel.DuplicateComplexType").PrimitiveProperty("Name", "foo")); PropertyInstance collectionProperty = PayloadBuilder.Property("DuplicateProperty", PayloadBuilder.PrimitiveMultiValue(EntityModelUtils.GetCollectionTypeName("Edm.String")).WithTypeAnnotation(EdmCoreModel.GetCollection(EdmCoreModel.Instance.GetString(false)))); PropertyInstance[] allProperties = new[] { primitiveProperty, complexProperty, collectionProperty }; PropertyInstance[] propertiesWithPossibleDuplication = new[] { primitiveProperty, complexProperty }; PropertyInstance[] propertiesWithNoDuplication = new[] { collectionProperty }; IEnumerable<DuplicatePropertySet> duplicatePropertySets; // Those which may allow duplication duplicatePropertySets = propertiesWithPossibleDuplication .Variations(2).Select(properties => new DuplicatePropertySet { Properties = properties, DuplicationPotentiallyAllowed = true }); // Then for each in those which don't allow duplication try it against all the others duplicatePropertySets = duplicatePropertySets.Concat(propertiesWithNoDuplication.SelectMany( propertyWithNoDuplication => allProperties.SelectMany(otherProperty => new[] { new DuplicatePropertySet { Properties = new [] { propertyWithNoDuplication, otherProperty }, DuplicationPotentiallyAllowed = false }, new DuplicatePropertySet { Properties = new [] { otherProperty, propertyWithNoDuplication }, DuplicationPotentiallyAllowed = false }, }))); this.CombinatorialEngineProvider.RunCombinations( duplicatePropertySets, new bool[] { false, true }, new bool[] { true, false }, this.ReaderTestConfigurationProvider.ExplicitFormatConfigurations, (duplicatePropertySet, allowDuplicateProperties, useMetadata, testConfiguration) => { EdmModel model = new EdmModel(); var complexType = model.ComplexType("DuplicateComplexType"); complexType.AddStructuralProperty("Name", EdmPrimitiveTypeKind.String); model.Fixup(); PropertyInstance firstProperty = duplicatePropertySet.Properties.ElementAt(0); PropertyInstance secondProperty = duplicatePropertySet.Properties.ElementAt(1); // Non-metadata reading is not possible in JSON if (!useMetadata && (testConfiguration.Format == ODataFormat.Json)) { return; } // If we will have metadata then we can only allow combinations of the same kind if (useMetadata) { if (firstProperty.ElementType != secondProperty.ElementType) { return; } } // Copy the test config testConfiguration = new ReaderTestConfiguration(testConfiguration); if (allowDuplicateProperties) { testConfiguration.MessageReaderSettings.EnableODataServerBehavior(); } // Create a descriptor with the first property PayloadReaderTestDescriptor testDescriptor = new PayloadReaderTestDescriptor(this.Settings) { PayloadElement = firstProperty, PayloadEdmModel = useMetadata ? model : null }; // Now generate entity around it testDescriptor = testDescriptor.InComplexValue(5, 5); // Now add the second property to it ((ComplexInstance)testDescriptor.PayloadElement).Add(secondProperty); // [Astoria-ODataLib-Integration] Parsing of URLs on OData recognized places may fail, but Astoria server doesn't // Server does not read named stream links for Atom payload therefore the expected payload needs to be normalized if (testConfiguration.Format == ODataFormat.Atom) { testDescriptor.ExpectedResultNormalizers.Add(config => (payloadElement => WcfDsServerPayloadElementNormalizer.Normalize(payloadElement, ODataFormat.Atom, testDescriptor.PayloadEdmModel as EdmModel))); } // We expect failure only if we don't allow duplicates or if the property kind doesn't allow duplicates ever if ((!duplicatePropertySet.DuplicationPotentiallyAllowed || !allowDuplicateProperties)) { testDescriptor.ExpectedException = ODataExpectedExceptions.ODataException("DuplicatePropertyNamesChecker_DuplicatePropertyNamesNotAllowed", "DuplicateProperty"); } IEnumerable<PayloadReaderTestDescriptor> testDescriptors = new PayloadReaderTestDescriptor[] { testDescriptor.InProperty("TopLevelProperty"), testDescriptor.InProperty("ComplexProperty").InEntity(2, 2), testDescriptor.InCollection(5, 5).InProperty("TopLevelCollection"), }; this.CombinatorialEngineProvider.RunCombinations( testDescriptors, td => { var property = td.PayloadElement as PropertyInstance; if (property != null && testConfiguration.Format == ODataFormat.Atom) { property.Name = null; } td.RunTest(testConfiguration); }); }); }
public void DuplicatePropertyNamesTest() { EdmModel model = new EdmModel(); var entityType = new EdmEntityType("TestModel", "DuplicateEntityType"); entityType.AddKeys(entityType.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(false))); model.AddElement(entityType); var complexType = new EdmComplexType("TestModel", "DuplicateComplexType"); complexType.AddStructuralProperty("Name", EdmCoreModel.Instance.GetString(true)); model.AddElement(complexType); var container = new EdmEntityContainer("TestModel", "DefaultContainer"); container.AddEntitySet("ClosedEntitySet", entityType); model.AddElement(container); PropertyInstance primitiveProperty = PayloadBuilder.PrimitiveProperty("DuplicateProperty", 42); PropertyInstance complexProperty = PayloadBuilder.Property("DuplicateProperty", PayloadBuilder.ComplexValue("TestModel.DuplicateComplexType").PrimitiveProperty("Name", "foo")); PropertyInstance collectionProperty = PayloadBuilder.Property("DuplicateProperty", PayloadBuilder.PrimitiveMultiValue(EntityModelUtils.GetCollectionTypeName("Edm.String")).WithTypeAnnotation(EdmCoreModel.GetCollection(EdmCoreModel.Instance.GetString(false)))); PropertyInstance streamProperty = PayloadBuilder.StreamProperty("DuplicateProperty", "http://odata.org/readlink", "http://odata.org/editlink"); PropertyInstance navigationProperty = PayloadBuilder.NavigationProperty("DuplicateProperty", "http://odata.org/navlink") .IsCollection(true) .WithTypeAnnotation(entityType); PropertyInstance expandedFeedProperty = PayloadBuilder.ExpandedNavigationProperty("DuplicateProperty", PayloadBuilder.EntitySet(new EntityInstance[] { PayloadBuilder.Entity("TestModel.DuplicateEntityType").PrimitiveProperty("Id", 1) })) .IsCollection(true) .WithTypeAnnotation(entityType); PropertyInstance associationLinkProperty = PayloadBuilder.NavigationProperty("DuplicateProperty", null, "http://odata.org/assoclink"); PropertyInstance[] allProperties = new[] { primitiveProperty, complexProperty, collectionProperty, streamProperty, navigationProperty, expandedFeedProperty, associationLinkProperty }; PropertyInstance[] propertiesWithPossibleDuplication = new[] { primitiveProperty, complexProperty, navigationProperty, expandedFeedProperty }; PropertyInstance[] propertiesWithNoDuplication = new[] { collectionProperty, streamProperty, associationLinkProperty }; IEnumerable<DuplicatePropertySet> duplicatePropertySets; // Those which may allow duplication duplicatePropertySets = propertiesWithPossibleDuplication .Variations(2).Select(properties => new DuplicatePropertySet { Properties = properties, DuplicationPotentiallyAllowed = true }); // Then for each in those which don't allow duplication try it against all the others duplicatePropertySets = duplicatePropertySets.Concat(propertiesWithNoDuplication.SelectMany( propertyWithNoDuplication => allProperties.SelectMany(otherProperty => new[] { new DuplicatePropertySet { Properties = new [] { propertyWithNoDuplication, otherProperty }, DuplicationPotentiallyAllowed = false }, new DuplicatePropertySet { Properties = new [] { otherProperty, propertyWithNoDuplication }, DuplicationPotentiallyAllowed = false }, }))); this.CombinatorialEngineProvider.RunCombinations( duplicatePropertySets, new bool[] { false, true }, new bool[] { true, false }, this.ReaderTestConfigurationProvider.ExplicitFormatConfigurations, (duplicatePropertySet, useServerBehavior, useMetadata, testConfiguration) => { PropertyInstance firstProperty = duplicatePropertySet.Properties.ElementAt(0); PropertyInstance secondProperty = duplicatePropertySet.Properties.ElementAt(1); // Non-metadata parsing is not supported in JSON. if (!useMetadata && (testConfiguration.Format != ODataFormat.Atom)) { return; } // If we will have metadata then we can only allow combinations of the same kind if (useMetadata) { if (firstProperty.ElementType != secondProperty.ElementType) { return; } } // Association links are only recognized in response payloads and MPV >= V3 if ((testConfiguration.IsRequest) && duplicatePropertySet.Properties.Any(p => object.ReferenceEquals(p, associationLinkProperty))) { return; } // Steam properties are only recognized in response >=V3 payloads if ((testConfiguration.IsRequest) && duplicatePropertySet.Properties.Any(p => object.ReferenceEquals(p, streamProperty))) { return; } // Copy the test config testConfiguration = new ReaderTestConfiguration(testConfiguration); if (useServerBehavior) { testConfiguration.MessageReaderSettings.EnableODataServerBehavior(); } // Create a descriptor with the first property PayloadReaderTestDescriptor testDescriptor = new PayloadReaderTestDescriptor(this.Settings) { PayloadElement = firstProperty, PayloadEdmModel = useMetadata ? (EdmModel)Test.OData.Utils.Metadata.MetadataUtils.Clone(model) : null, }; // Now generate entity around it testDescriptor = testDescriptor.InEntity(2, 2); // Now add the second property to it ((EntityInstance)testDescriptor.PayloadElement).Add(secondProperty); // [Astoria-ODataLib-Integration] Parsing of URLs on OData recognized places may fail, but Astoria server doesn't // Server does not read named stream links for Atom payload therefore the expected payload needs to be normalized if (testConfiguration.Format == ODataFormat.Atom && useServerBehavior) { testDescriptor.ExpectedResultNormalizers.Add(config => (payloadElement => WcfDsServerPayloadElementNormalizer.Normalize(payloadElement, ODataFormat.Atom, testDescriptor.PayloadEdmModel as EdmModel))); } // We expect failure only if we don't allow duplicates or if the property kind doesn't allow duplicates ever. // In JSON with WCF DS Service behavior where duplicates are removed very soon and thus we never fail on them. if ((!duplicatePropertySet.DuplicationPotentiallyAllowed || !useServerBehavior)) { testDescriptor.ExpectedException = ODataExpectedExceptions.ODataException("DuplicatePropertyNamesChecker_DuplicatePropertyNamesNotAllowed", "DuplicateProperty"); } if (firstProperty.ElementType == ODataPayloadElementType.NavigationPropertyInstance && secondProperty.ElementType == ODataPayloadElementType.NavigationPropertyInstance) { NavigationPropertyInstance navigationFirstProperty = (NavigationPropertyInstance)firstProperty; NavigationPropertyInstance navigationSecondProperty = (NavigationPropertyInstance)secondProperty; // If one of the properties is an association link and the other is a navigation link, that combination is allowed and it's not an error. // Just skip that combination. if ((navigationFirstProperty.Value != null && navigationFirstProperty.AssociationLink == null && navigationSecondProperty.Value == null && navigationSecondProperty.AssociationLink != null) || (navigationFirstProperty.Value == null && navigationFirstProperty.AssociationLink != null && navigationSecondProperty.Value != null && navigationSecondProperty.AssociationLink == null)) { return; } if (testConfiguration.Format == ODataFormat.Json) { if ((navigationFirstProperty.Value is DeferredLink || navigationSecondProperty.Value is DeferredLink) && (navigationFirstProperty.Value is ExpandedLink || navigationSecondProperty.Value is ExpandedLink)) { testDescriptor.ExpectedException = null; testDescriptor.ExpectedResultNormalizers.Add( (tc) => (tc.IsRequest) ? (Func<ODataPayloadElement, ODataPayloadElement>)((payload) => EnsureAnnotationsBeforeProperties(DuplicateNavigationPropertiesToLinkCollection(payload))) : (payload) => EnsureAnnotationsBeforeProperties(DuplicateNavigationPropertiesToExpandedLink(payload))); } else if (navigationFirstProperty.AssociationLink != null && navigationSecondProperty.AssociationLink != null) { testDescriptor.ExpectedException = ODataExpectedExceptions.ODataException( "DuplicatePropertyNamesChecker_DuplicateAnnotationForPropertyNotAllowed", JsonLightConstants.ODataAssociationLinkUrlAnnotationName, "DuplicateProperty"); } } // In requests if both are navigation properties (not association links) // If both are expanded, then fail (that's not allowed), but if one is not expanded, that is allowed (binding in POST scenario) // Note that this only works because we use expanded feed, if we would add expanded entry, then the expanded entry can't be allowed along with a deferred link. else if (navigationFirstProperty.AssociationLink == null && navigationSecondProperty.AssociationLink == null && testConfiguration.IsRequest && (navigationFirstProperty.Value is DeferredLink || navigationSecondProperty.Value is DeferredLink)) { testDescriptor.ExpectedException = null; } } if (firstProperty.ElementType == ODataPayloadElementType.NamedStreamInstance && secondProperty.ElementType == ODataPayloadElementType.NamedStreamInstance) { if (testConfiguration.Format == ODataFormat.Atom) { // In ATOM stream properties are represented as pair of links and thus we first match the links together. // Thus the code finds the duplicate stream property before we get to the duplicate check // (since it actually finds a stream property which already has a link which it's trying to read) // As a result we get a different error message, which is OK. testDescriptor.ExpectedException = ODataExpectedExceptions.ODataException("ODataAtomEntryAndFeedDeserializer_StreamPropertyWithMultipleReadLinks", "DuplicateProperty"); } else if (testConfiguration.Format == ODataFormat.Json) { testDescriptor.ExpectedException = ODataExpectedExceptions.ODataException("DuplicatePropertyNamesChecker_DuplicateAnnotationForPropertyNotAllowed", JsonLightConstants.ODataMediaEditLinkAnnotationName, "DuplicateProperty"); } } if (testConfiguration.Format == ODataFormat.Json) { testDescriptor.PayloadElement = EnsureAnnotationsBeforeProperties(testDescriptor.PayloadElement); } testDescriptor.RunTest(testConfiguration); }); }
public void ResourceCollectionNamePropertyTests() { // The behavior of the Name property depends on the format. // ATOM: We use it to populate the <title> element if no title has been specified on the ATOM metadata annotation. If there is both a title annotation and a Name property, they must match or else we'll fail. // JSON Light: The Name property is required and written to the "name" property in the format. IList<CollectionInfo> interestingCollections = new[] { // Non null collection name new CollectionInfo { Name = "CollectionName", Url = "CollectionRelativeUrl", }, // No name or title annotation new CollectionInfo { Url = "CollectionRelativeUrl" }, // Name and title annotation are both non null and are equal. new CollectionInfo { Name = "CollectionName", Url = "CollectionRelativeUrl", TitleAnnotation = "CollectionName" }, // Name and title annotation differ (expect failure in ATOM). new CollectionInfo { Name = "CollectionName", Url = "CollectionRelativeUrl", TitleAnnotation = "CollectionTitle" } }; var collectionArrays = interestingCollections.Variations(0, 1, 3); EdmModel model=new EdmModel(); EdmEntityContainer edmEntityContainer = new EdmEntityContainer("DefaultNamespace", "DefaultContainer"); model.AddElement(edmEntityContainer); var testDescriptors = collectionArrays.Select(collectionArray => new PayloadWriterTestDescriptor<ODataServiceDocument>( this.Settings, CreateWorkspace( /*createMetadataFirst*/ false, /* workspaceName */ null, collectionArray), CreateExpectedResultCallback(baseUri, /* workspaceName */ null, collectionArray)) { Model = model, }); this.CombinatorialEngineProvider.RunCombinations( testDescriptors, this.WriterTestConfigurationProvider.ExplicitFormatConfigurationsWithIndent, (testDescriptor, testConfig) => { WriterTestConfiguration newConfiguration = testConfig.Clone(); newConfiguration.MessageWriterSettings.PayloadBaseUri = new Uri(baseUri); newConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri); TestWriterUtils.WriteAndVerifyTopLevelContent( testDescriptor, newConfiguration, (messageWriter) => messageWriter.WriteServiceDocument(testDescriptor.PayloadItems.Single()), this.Assert, baselineLogger: this.Logger); }); }
public void AssociationLinkTest() { string associationLinkName1 = "AssociationLinkOne"; string linkUrl1 = "http://odata.org/associationlink"; Uri linkUrlUri1 = new Uri(linkUrl1); string associationLinkName2 = "AssociationLinkTwo"; string linkUrl2 = "http://odata.org/associationlink2"; Uri linkUrlUri2 = new Uri(linkUrl2); EdmModel model = new EdmModel(); var edmEntityTypeOrderType = new EdmEntityType("TestModel", "OrderType"); model.AddElement(edmEntityTypeOrderType); var edmEntityTypeCustomerType = new EdmEntityType("TestModel", "CustomerType"); var edmNavigationPropertyAssociationLinkOne = edmEntityTypeCustomerType.AddUnidirectionalNavigation( new EdmNavigationPropertyInfo { Name = associationLinkName1, Target = edmEntityTypeOrderType, TargetMultiplicity = EdmMultiplicity.One }); var edmNavigationPropertyAssociationLinkTwo = edmEntityTypeCustomerType.AddUnidirectionalNavigation( new EdmNavigationPropertyInfo { Name = associationLinkName2, Target = edmEntityTypeOrderType, TargetMultiplicity = EdmMultiplicity.Many }); model.AddElement(edmEntityTypeCustomerType); var container = new EdmEntityContainer("TestModel", "Default"); model.AddElement(container); var customerSet = container.AddEntitySet("Customers", edmEntityTypeCustomerType); var orderSet = container.AddEntitySet("Orders", edmEntityTypeOrderType); customerSet.AddNavigationTarget(edmNavigationPropertyAssociationLinkOne, orderSet); customerSet.AddNavigationTarget(edmNavigationPropertyAssociationLinkTwo, orderSet); var testCases = new[] { new { NavigationLink = ObjectModelUtils.CreateDefaultNavigationLink(associationLinkName1, linkUrlUri1), Atom = BuildXmlAssociationLink(associationLinkName1, "application/xml", linkUrl1), JsonLight = (string)null, }, new { NavigationLink = ObjectModelUtils.CreateDefaultNavigationLink(associationLinkName2, linkUrlUri2), Atom = BuildXmlAssociationLink(associationLinkName2, "application/xml", linkUrl2), JsonLight = (string)null }, }; var testCasesWithMultipleLinks = testCases.Variations() .Select(tcs => new { NavigationLinks = tcs.Select(tc => tc.NavigationLink), Atom = string.Concat(tcs.Select(tc => tc.Atom)), JsonLight = string.Join(",", tcs.Where(tc => tc.JsonLight != null).Select(tc => tc.JsonLight)) }); var testDescriptors = testCasesWithMultipleLinks.Select(testCase => { ODataEntry entry = ObjectModelUtils.CreateDefaultEntry(); entry.TypeName = "TestModel.CustomerType"; List<ODataItem> items = new ODataItem[] { entry }.ToList(); foreach (var navLink in testCase.NavigationLinks) { items.Add(navLink); items.Add(null); } return new PayloadWriterTestDescriptor<ODataItem>( this.Settings, items, (testConfiguration) => { var firstAssocLink = testCase.NavigationLinks == null ? null : testCase.NavigationLinks.FirstOrDefault(); if (testConfiguration.Format == ODataFormat.Atom) { return new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings) { Xml = "<AssociationLinks>" + testCase.Atom + "</AssociationLinks>", FragmentExtractor = (result) => { var links = result.Elements(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomLinkElementName) .Where(l => l.Attribute(TestAtomConstants.AtomLinkRelationAttributeName).Value.StartsWith( TestAtomConstants.ODataNavigationPropertiesAssociationLinkRelationPrefix)); result = new XElement("AssociationLinks", links); if (result.FirstNode == null) { result.Add(""); } return result; }, ExpectedException2 = testConfiguration.IsRequest && firstAssocLink != null ? ODataExpectedExceptions.ODataException("ODataWriterCore_DeferredLinkInRequest") : null }; } else if (testConfiguration.Format == ODataFormat.Json) { return new JsonWriterTestExpectedResults(this.Settings.ExpectedResultSettings) { Json = string.Join( "$(NL)", "{", testCase.JsonLight, "}"), FragmentExtractor = (result) => { var associationLinks = result.Object().GetAnnotationsWithName("@" + JsonLightConstants.ODataAssociationLinkUrlAnnotationName).ToList(); var jsonResult = new JsonObject(); associationLinks.ForEach(l => { // NOTE we remove all annoatations here and in particular the text annotations to be able to easily compare // against the expected results. This however means that we do not distinguish between the indented and non-indented case here. l.RemoveAllAnnotations(true); jsonResult.Add(l); }); return jsonResult; }, }; } else { this.Settings.Assert.Fail("Unknown format '{0}'.", testConfiguration.Format); return null; } }) { Model = model, PayloadEdmElementContainer = customerSet }; }); // With and without model testDescriptors = testDescriptors.SelectMany(td => new[] { td, new PayloadWriterTestDescriptor<ODataItem>(td) { Model = null, PayloadEdmElementContainer = null } }); this.CombinatorialEngineProvider.RunCombinations( testDescriptors.PayloadCases(WriterPayloads.EntryPayloads), this.WriterTestConfigurationProvider.ExplicitFormatConfigurationsWithIndent, (testDescriptor, testConfiguration) => { testConfiguration = testConfiguration.Clone(); testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri); if (testDescriptor.Model == null && testConfiguration.Format == ODataFormat.Json) { return; } if (testDescriptor.IsGeneratedPayload && testDescriptor.Model != null) { return; } TestWriterUtils.WriteAndVerifyODataPayload(testDescriptor, testConfiguration, this.Assert, this.Logger); }); }