public void InvalidDocumentationShouldYieldFailure(
            string testCaseName,
            IList <string> inputXmlFiles,
            IList <string> inputBinaryFiles,
            int expectedOperationGenerationResultsCount,
            string expectedJsonFile,
            DocumentGenerationDiagnostic expectedDocumentGenerationResult,
            IList <OperationGenerationDiagnostic> expectedFailureOperationGenerationResults)
        {
            _output.WriteLine(testCaseName);

            var documents = new List <XDocument>();

            documents.AddRange(inputXmlFiles.Select(XDocument.Load));

            var input = new OpenApiGeneratorConfig(documents, inputBinaryFiles, "1.0.0", FilterSetVersion.V1);

            GenerationDiagnostic result;

            var generator = new OpenApiGenerator();

            var openApiDocuments = generator.GenerateDocuments(input, out result);

            openApiDocuments.Should().NotBeNull();

            result.DocumentGenerationDiagnostic.Should().BeEquivalentTo(expectedDocumentGenerationResult);
            result.OperationGenerationDiagnostics.Count.Should().Be(expectedOperationGenerationResultsCount);

            openApiDocuments[DocumentVariantInfo.Default].Should().NotBeNull();

            var failurePaths = result.OperationGenerationDiagnostics.Where(
                p => p.Errors.Count > 0)
                               .ToList();

            var actualDocument = openApiDocuments[DocumentVariantInfo.Default]
                                 .SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
            var expectedDocument = File.ReadAllText(expectedJsonFile);


            _output.WriteLine(actualDocument);

            failurePaths.Should().BeEquivalentTo(expectedFailureOperationGenerationResults);

            // We are doing serialization and deserialization to force the resulting actual document
            // to have the exact fields we will see in the resulting document based on the contract resolver.
            // Without serialization and deserialization, the actual document may have fields that should
            // not be present, such as empty list fields.
            var openApiStringReader = new OpenApiStringReader();

            var actualDeserializedDocument = openApiStringReader.Read(
                actualDocument,
                out OpenApiDiagnostic diagnostic);

            diagnostic.Errors.Count.Should().Be(0);

            actualDeserializedDocument
            .Should()
            .BeEquivalentTo(openApiStringReader.Read(expectedDocument, out var _));
        }
        /// <summary>
        /// Takes in annotation xml document and returns the OpenAPI document generation result
        /// which contains OpenAPI specification document(s).
        /// </summary>
        /// <param name="annotationXmlDocuments">The list of XDocuments representing annotation xmls.</param>
        /// <param name="contractAssemblyPaths">The contract assembly paths.</param>
        /// <param name="configurationXml">The serialized XDocument representing the generation configuration.</param>
        /// <param name="openApiDocumentVersion">The version of the OpenAPI document.</param>
        /// <param name="generationDiagnostic">A string representing serialized version of
        /// <see cref="GenerationDiagnostic"/>>
        /// </param>
        /// <returns>
        /// Dictionary mapping document variant metadata to their respective OpenAPI document.
        /// </returns>
        public IDictionary <DocumentVariantInfo, OpenApiDocument> GenerateOpenApiDocuments(
            IList <XDocument> annotationXmlDocuments,
            IList <string> contractAssemblyPaths,
            string configurationXml,
            string openApiDocumentVersion,
            out GenerationDiagnostic generationDiagnostic)
        {
            IDictionary <DocumentVariantInfo, OpenApiDocument> openApiDocuments
                = new Dictionary <DocumentVariantInfo, OpenApiDocument>();

            var operationElements = new List <XElement>();

            foreach (var annotationXmlDocument in annotationXmlDocuments)
            {
                operationElements.AddRange(
                    annotationXmlDocument.XPathSelectElements("//doc/members/member[url and verb]"));
            }

            XElement operationConfigElement = null;

            XElement documentConfigElement = null;

            var documentVariantElementNames = new List <string>();

            if (!string.IsNullOrWhiteSpace(configurationXml))
            {
                var configurationXmlDocument = XDocument.Parse(configurationXml);

                operationConfigElement      = configurationXmlDocument.XPathSelectElement("//configuration/operation");
                documentConfigElement       = configurationXmlDocument.XPathSelectElement("//configuration/document");
                documentVariantElementNames = configurationXmlDocument
                                              .XPathSelectElements("//configuration/document/variant/name")
                                              .Select(variantName => variantName.Value)
                                              .ToList();
            }

            if (!operationElements.Any())
            {
                generationDiagnostic = new GenerationDiagnostic
                {
                    DocumentGenerationDiagnostic = new DocumentGenerationDiagnostic
                    {
                        Errors =
                        {
                            new GenerationError
                            {
                                Message = SpecificationGenerationMessages.NoOperationElementFoundToParse
                            }
                        }
                    }
                };

                return(openApiDocuments);
            }

            try
            {
                generationDiagnostic = new GenerationDiagnostic();
                var documentGenerationDiagnostic = new DocumentGenerationDiagnostic();

                if (documentVariantElementNames?.Count > 1)
                {
                    documentGenerationDiagnostic.Errors.Add(new GenerationError
                    {
                        Message = string.Format(
                            SpecificationGenerationMessages.MoreThanOneVariantNameNotAllowed,
                            documentVariantElementNames.First())
                    });
                }

                var typeFetcher = new TypeFetcher(contractAssemblyPaths);

                var operationGenerationDiagnostics = GenerateSpecificationDocuments(
                    typeFetcher,
                    operationElements,
                    operationConfigElement,
                    documentVariantElementNames.FirstOrDefault(),
                    out var documents);

                foreach (var operationGenerationDiagnostic in operationGenerationDiagnostics)
                {
                    generationDiagnostic.OperationGenerationDiagnostics.Add(
                        new OperationGenerationDiagnostic(operationGenerationDiagnostic));
                }

                foreach (var variantInfoDocumentValuePair in documents)
                {
                    var openApiDocument = variantInfoDocumentValuePair.Value;

                    foreach (var documentFilter in _documentFilters)
                    {
                        try
                        {
                            documentFilter.Apply(
                                openApiDocument,
                                annotationXmlDocuments,
                                new DocumentFilterSettings
                            {
                                TypeFetcher            = typeFetcher,
                                OpenApiDocumentVersion = openApiDocumentVersion,
                            },
                                _openApiDocumentGenerationSettings);
                        }
                        catch (Exception e)
                        {
                            documentGenerationDiagnostic.Errors.Add(
                                new GenerationError
                            {
                                ExceptionType = e.GetType().Name,
                                Message       = e.Message
                            });
                        }
                    }

                    foreach (var filter in _postProcessingDocumentFilters)
                    {
                        filter.Apply(
                            openApiDocument,
                            new PostProcessingDocumentFilterSettings
                        {
                            OperationGenerationDiagnostics = operationGenerationDiagnostics
                        });
                    }
                }

                if (documentConfigElement != null)
                {
                    foreach (var documentConfigFilter in _documentConfigFilters)
                    {
                        try
                        {
                            documentConfigFilter.Apply(
                                documents,
                                documentConfigElement,
                                annotationXmlDocuments,
                                new DocumentConfigFilterSettings());
                        }
                        catch (Exception e)
                        {
                            documentGenerationDiagnostic.Errors.Add(
                                new GenerationError
                            {
                                ExceptionType = e.GetType().Name,
                                Message       = e.Message
                            });
                        }
                    }
                }

                var failedOperations = generationDiagnostic.OperationGenerationDiagnostics
                                       .Where(i => i.Errors.Count > 0);

                if (failedOperations.Any())
                {
                    var totalOperationsCount = generationDiagnostic.OperationGenerationDiagnostics.Count();

                    var exception = new UnableToGenerateAllOperationsException(
                        totalOperationsCount - failedOperations.Count(), totalOperationsCount);

                    documentGenerationDiagnostic.Errors.Add(
                        new GenerationError
                    {
                        ExceptionType = exception.GetType().Name,
                        Message       = exception.Message
                    });
                }

                generationDiagnostic.DocumentGenerationDiagnostic = documentGenerationDiagnostic;
                return(documents);
            }
            catch (Exception e)
            {
                generationDiagnostic = new GenerationDiagnostic
                {
                    DocumentGenerationDiagnostic =
                        new DocumentGenerationDiagnostic
                    {
                        Errors =
                        {
                            new GenerationError
                            {
                                ExceptionType = e.GetType().Name,
                                Message       = string.Format(SpecificationGenerationMessages.UnexpectedError, e)
                            }
                        }
                    }
                };

                return(openApiDocuments);
            }
        }
        /// <summary>
        /// Takes in annotation xml document and returns the OpenAPI document generation result
        /// which contains OpenAPI specification document(s).
        /// </summary>
        /// <param name="annotationXmlDocuments">The list of XDocuments representing annotation xmls.</param>
        /// <param name="contractAssemblyPaths">The contract assembly paths.</param>
        /// <param name="configurationXml">The serialized XDocument representing the generation configuration.</param>
        /// <param name="openApiDocumentVersion">The version of the OpenAPI document.</param>
        /// <param name="openApiInfoDescription">The description to use while populating OpenApiInfo.</param>
        /// <param name="generationDiagnostic">A string representing serialized version of
        /// <see cref="GenerationDiagnostic"/>>
        /// </param>
        /// <returns>
        /// Dictionary mapping document variant metadata to their respective OpenAPI document.
        /// </returns>
        public IDictionary <DocumentVariantInfo, OpenApiDocument> GenerateOpenApiDocuments(
            IList <XDocument> annotationXmlDocuments,
            IList <string> contractAssemblyPaths,
            string configurationXml,
            string openApiDocumentVersion,
            string openApiInfoDescription,
            out GenerationDiagnostic generationDiagnostic)
        {
            IDictionary <DocumentVariantInfo, OpenApiDocument> openApiDocuments
                = new Dictionary <DocumentVariantInfo, OpenApiDocument>();

            var operationElements = new List <XElement>();
            var propertyElements  = new List <XElement>();

            foreach (var annotationXmlDocument in annotationXmlDocuments)
            {
                operationElements.AddRange(
                    annotationXmlDocument.XPathSelectElements("//doc/members/member[url and verb]"));

                propertyElements.AddRange(annotationXmlDocument.XPathSelectElements("//doc/members/member")
                                          .Where(
                                              m => m.Attribute(KnownXmlStrings.Name) != null &&
                                              m.Attribute(KnownXmlStrings.Name).Value.StartsWith("P:")));
            }

            XElement operationConfigElement = null;

            XElement documentConfigElement = null;

            var documentVariantElementNames = new List <string>();

            if (!string.IsNullOrWhiteSpace(configurationXml))
            {
                var configurationXmlDocument = XDocument.Parse(configurationXml);

                operationConfigElement      = configurationXmlDocument.XPathSelectElement("//configuration/operation");
                documentConfigElement       = configurationXmlDocument.XPathSelectElement("//configuration/document");
                documentVariantElementNames = configurationXmlDocument
                                              .XPathSelectElements("//configuration/document/variant/name")
                                              .Select(variantName => variantName.Value)
                                              .ToList();
            }

            if (!operationElements.Any())
            {
                generationDiagnostic = new GenerationDiagnostic
                {
                    DocumentGenerationDiagnostic = new DocumentGenerationDiagnostic
                    {
                        Errors =
                        {
                            new GenerationError
                            {
                                Message = SpecificationGenerationMessages.NoOperationElementFoundToParse
                            }
                        }
                    }
                };

                return(openApiDocuments);
            }

            try
            {
                var propertyNameResolverTypeName = _openApiDocumentGenerationSettings.SchemaGenerationSettings
                                                   .PropertyNameResolver.GetType().FullName;

                var internalGenerationContext        = new InternalGenerationContext();
                var internalSchemaGenerationSettings = new InternalSchemaGenerationSettings()
                {
                    PropertyNameResolverName = propertyNameResolverTypeName
                };

                generationDiagnostic = new GenerationDiagnostic();
                var documentGenerationDiagnostic = new DocumentGenerationDiagnostic();

                if (documentVariantElementNames?.Count > 1)
                {
                    documentGenerationDiagnostic.Errors.Add(new GenerationError
                    {
                        Message = string.Format(
                            SpecificationGenerationMessages.MoreThanOneVariantNameNotAllowed,
                            documentVariantElementNames.First())
                    });
                }

                IList <string> serializedOperationElements = operationElements.Select(i => i.ToString()).ToList();

                // Operation config elements can contain the types that needs to be fetched too,
                // so add it to the list of operation elements which will be used to fetch type information.
                if (operationConfigElement != null)
                {
                    serializedOperationElements.Add(operationConfigElement.ToString());
                }

#if !NETFRAMEWORK
                var assemblyLoader = new AssemblyLoader.AssemblyLoader();
                assemblyLoader.RegisterAssemblyPaths(contractAssemblyPaths);
                var internalGenerationContextAsString = new AssemblyLoader.AssemblyLoader().BuildInternalGenerationContext(
                    contractAssemblyPaths,
                    serializedOperationElements,
                    propertyElements.Select(i => i.ToString()).ToList(),
                    documentVariantElementNames.FirstOrDefault(),
                    internalSchemaGenerationSettings);

                internalGenerationContext =
                    (InternalGenerationContext)JsonConvert.DeserializeObject(
                        internalGenerationContextAsString,
                        typeof(InternalGenerationContext));
#else
                using (var isolatedDomain = new AppDomainCreator <AssemblyLoader.AssemblyLoader>())
                {
                    isolatedDomain.Object.RegisterAssemblyPaths(contractAssemblyPaths);
                    var internalGenerationContextAsString = isolatedDomain.Object.BuildInternalGenerationContext(
                        contractAssemblyPaths,
                        serializedOperationElements,
                        propertyElements.Select(i => i.ToString()).ToList(),
                        documentVariantElementNames.FirstOrDefault(),
                        internalSchemaGenerationSettings);

                    internalGenerationContext =
                        (InternalGenerationContext)JsonConvert.DeserializeObject(
                            internalGenerationContextAsString,
                            typeof(InternalGenerationContext));
                }
#endif

                GenerationContext generationContext = internalGenerationContext.ToGenerationContext();

                var operationGenerationDiagnostics = GenerateSpecificationDocuments(
                    generationContext,
                    operationElements,
                    operationConfigElement,
                    documentVariantElementNames.FirstOrDefault(),
                    out var documents);

                foreach (var operationGenerationDiagnostic in operationGenerationDiagnostics)
                {
                    generationDiagnostic.OperationGenerationDiagnostics.Add(
                        new OperationGenerationDiagnostic(operationGenerationDiagnostic));
                }

                var referenceRegistryManager = new ReferenceRegistryManager(_openApiDocumentGenerationSettings);

                foreach (var variantInfoDocumentValuePair in documents)
                {
                    var openApiDocument = variantInfoDocumentValuePair.Value;

                    foreach (var documentFilter in _documentFilters)
                    {
                        var generationErrors = documentFilter.Apply(
                            openApiDocument,
                            annotationXmlDocuments,
                            new DocumentFilterSettings
                        {
                            OpenApiDocumentVersion   = openApiDocumentVersion,
                            OpenApiInfoDescription   = openApiInfoDescription,
                            ReferenceRegistryManager = referenceRegistryManager
                        },
                            _openApiDocumentGenerationSettings);

                        foreach (var error in generationErrors)
                        {
                            documentGenerationDiagnostic.Errors.Add(error);
                        }
                    }

                    foreach (var filter in _postProcessingDocumentFilters)
                    {
                        var generationErrors = filter.Apply(
                            openApiDocument,
                            new PostProcessingDocumentFilterSettings
                        {
                            OperationGenerationDiagnostics = operationGenerationDiagnostics
                        });

                        foreach (var error in generationErrors)
                        {
                            documentGenerationDiagnostic.Errors.Add(error);
                        }
                    }

                    referenceRegistryManager.SecuritySchemeReferenceRegistry.References.CopyInto(
                        openApiDocument.Components.SecuritySchemes);
                }

                if (documentConfigElement != null)
                {
                    foreach (var documentConfigFilter in _documentConfigFilters)
                    {
                        var generationErrors = documentConfigFilter.Apply(
                            documents,
                            documentConfigElement,
                            annotationXmlDocuments,
                            new DocumentConfigFilterSettings());

                        foreach (var error in generationErrors)
                        {
                            documentGenerationDiagnostic.Errors.Add(error);
                        }
                    }
                }

                var failedOperations = generationDiagnostic.OperationGenerationDiagnostics
                                       .Where(i => i.Errors.Count > 0);

                if (failedOperations.Any())
                {
                    var totalOperationsCount = generationDiagnostic.OperationGenerationDiagnostics.Count();

                    var exception = new UnableToGenerateAllOperationsException(
                        totalOperationsCount - failedOperations.Count(), totalOperationsCount);

                    documentGenerationDiagnostic.Errors.Add(
                        new GenerationError
                    {
                        ExceptionType = exception.GetType().Name,
                        Message       = exception.Message
                    });
                }

                generationDiagnostic.DocumentGenerationDiagnostic = documentGenerationDiagnostic;
                return(documents);
            }
            catch (Exception e)
            {
                generationDiagnostic = new GenerationDiagnostic
                {
                    DocumentGenerationDiagnostic =
                        new DocumentGenerationDiagnostic
                    {
                        Errors =
                        {
                            new GenerationError
                            {
                                ExceptionType = e.GetType().Name,
                                Message       = string.Format(SpecificationGenerationMessages.UnexpectedError, e)
                            }
                        }
                    }
                };

                return(openApiDocuments);
            }
        }
        public void GenerateDocumentMultipleVariantsShouldYieldFailure(
            string testCaseName,
            IList <string> inputXmlFiles,
            IList <string> inputBinaryFiles,
            string configXmlFile,
            int expectedOperationGenerationResultsCount,
            IDictionary <DocumentVariantInfo, string> documentVariantInfoToExpectedJsonFileMap,
            DocumentGenerationDiagnostic expectedDocumentGenerationResult,
            List <OperationGenerationDiagnostic> expectedOperationGenerationDiagnostic)
        {
            _output.WriteLine(testCaseName);

            // Arrange
            var documents = new List <XDocument>();

            documents.AddRange(inputXmlFiles.Select(XDocument.Load));

            var configPath     = configXmlFile;
            var configDocument = XDocument.Load(configPath);

            var generator = new OpenApiGenerator();

            var input = new OpenApiGeneratorConfig(documents, inputBinaryFiles, "1.0.0", FilterSetVersion.V1)
            {
                AdvancedConfigurationXmlDocument = configDocument
            };

            GenerationDiagnostic result;

            // Act
            var openApiDocuments = generator.GenerateDocuments(input, out result);

            result.Should().NotBeNull();

            // All operation generations should succeed.
            result.OperationGenerationDiagnostics.Count(r => r.Errors.Count == 0)
            .Should()
            .Be(expectedOperationGenerationResultsCount);

            // Document generation should yield failure as expected in the test cases.
            result.DocumentGenerationDiagnostic.Should().BeEquivalentTo(expectedDocumentGenerationResult);

            var failedPaths = result.OperationGenerationDiagnostics.Where(
                p => p.Errors.Count > 0)
                              .ToList();

            failedPaths.Should().BeEquivalentTo(expectedOperationGenerationDiagnostic);

            openApiDocuments.Keys.Should()
            .BeEquivalentTo(documentVariantInfoToExpectedJsonFileMap.Keys);

            var actualDocuments   = new List <OpenApiDocument>();
            var expectedDocuments = new List <OpenApiDocument>();

            foreach (var documentVariantInfoToExpectedJsonFile in documentVariantInfoToExpectedJsonFileMap)
            {
                // Verify each document variant against a json file content.
                var documentVariantInfo = documentVariantInfoToExpectedJsonFile.Key;
                var expectedJsonFile    = documentVariantInfoToExpectedJsonFile.Value;

                openApiDocuments.TryGetValue(documentVariantInfo, out var specificationDocument);

                var actualDocumentAsString = specificationDocument.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);

                _output.WriteLine(actualDocumentAsString);

                var openApiStringReader = new OpenApiStringReader();

                var actualDeserializedDocument = openApiStringReader.Read(
                    actualDocumentAsString,
                    out OpenApiDiagnostic diagnostic);

                diagnostic.Errors.Count.Should().Be(0);

                actualDeserializedDocument
                .Should()
                .BeEquivalentTo(openApiStringReader.Read(File.ReadAllText(expectedJsonFile), out var _));

                // Bug in fluent assertion method. Comparing the array of documents yields incorrect result.
                // Root cause unknown. This should be enabled once that bug is resolved.
                //actualDocuments.Add(
                //    openApiStringReader.Read(actualDocumentAsString, out var _));

                //expectedDocuments.Add(
                //    openApiStringReader.Read(File.ReadAllText(expectedJsonFile), out var _));
            }

            //actualDocuments.Should().BeEquivalentTo(expectedDocuments);
        }