diff --git a/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs b/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs index d7b47456..225906fa 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs @@ -21,7 +21,7 @@ using Microsoft.SqlTools.ServiceLayer.LanguageServices; using Microsoft.SqlTools.ServiceLayer.Metadata; using Microsoft.SqlTools.ServiceLayer.Profiler; using Microsoft.SqlTools.ServiceLayer.QueryExecution; -using Microsoft.SqlTools.ServiceLayer.SchemaCopmare; +using Microsoft.SqlTools.ServiceLayer.SchemaCompare; using Microsoft.SqlTools.ServiceLayer.Scripting; using Microsoft.SqlTools.ServiceLayer.Security; using Microsoft.SqlTools.ServiceLayer.SqlContext; @@ -119,7 +119,7 @@ namespace Microsoft.SqlTools.ServiceLayer CmsService.Instance.InitializeService(serviceHost); serviceProvider.RegisterSingleService(CmsService.Instance); - SchemaCopmare.SchemaCompareService.Instance.InitializeService(serviceHost); + SchemaCompare.SchemaCompareService.Instance.InitializeService(serviceHost); serviceProvider.RegisterSingleService(SchemaCompareService.Instance); InitializeHostedServices(serviceProvider, serviceHost); diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs index 42f33f0b..ef2950c6 100755 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs @@ -2933,6 +2933,14 @@ namespace Microsoft.SqlTools.ServiceLayer } } + public static string OpenScmpConnectionBasedModelParsingError + { + get + { + return Keys.GetString(Keys.OpenScmpConnectionBasedModelParsingError); + } + } + public static string ConnectionServiceListDbErrorNotConnected(string uri) { return Keys.GetString(Keys.ConnectionServiceListDbErrorNotConnected, uri); @@ -4270,6 +4278,9 @@ namespace Microsoft.SqlTools.ServiceLayer public const string SchemaCompareExcludeIncludeNodeNotFound = "SchemaCompareExcludeIncludeNodeNotFound"; + public const string OpenScmpConnectionBasedModelParsingError = "OpenScmpConnectionBasedModelParsingError"; + + private Keys() { } diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx index 15589826..e156dcce 100755 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx @@ -1719,4 +1719,8 @@ Failed to find the specified change in the model + + Error encountered while trying to parse connection information for endpoint '{0}' with error message '{1}' + + diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings index 2760c71e..9b55edcd 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings @@ -796,4 +796,5 @@ ExtractInvalidVersion = Invalid version '{0}' passed. Version must be in the for ############################################################################ # Schema Compare PublishChangesTaskName = Apply schema compare changes -SchemaCompareExcludeIncludeNodeNotFound = Failed to find the specified change in the model \ No newline at end of file +SchemaCompareExcludeIncludeNodeNotFound = Failed to find the specified change in the model +OpenScmpConnectionBasedModelParsingError = Error encountered while trying to parse connection information for endpoint '{0}' with error message '{1}' \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf index 8d130e1f..524dc2d8 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf @@ -1996,6 +1996,11 @@ Failed to find the specified change in the model + + Error encountered while trying to parse connection information for endpoint '{0}' with error message '{1}' + Error encountered while trying to parse connection information for endpoint '{0}' with error message '{1}' + + \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs index c5e658da..5feeb904 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs @@ -232,5 +232,20 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts } } } + + public DeploymentOptions(DacDeployOptions options) + { + System.Reflection.PropertyInfo[] deploymentOptionsProperties = this.GetType().GetProperties(); + + foreach (var deployOptionsProp in deploymentOptionsProperties) + { + var prop = options.GetType().GetProperty(deployOptionsProp.Name); + + if (prop != null) + { + deployOptionsProp.SetValue(this, prop.GetValue(options)); + } + } + } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareOpenScmpRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareOpenScmpRequest.cs new file mode 100644 index 00000000..a644f3b5 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareOpenScmpRequest.cs @@ -0,0 +1,87 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +using Microsoft.SqlTools.Hosting.Protocol.Contracts; +using Microsoft.SqlTools.ServiceLayer.TaskServices; +using Microsoft.SqlTools.ServiceLayer.Utility; +using System.Collections.Generic; + +namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts +{ + public class SchemaCompareObjectId + { + /// + /// Name to create object identifier + /// + public string[] NameParts; + + /// + /// sql object type + /// + public string SqlObjectType; + } + + /// + /// Parameters for a schema compare open scmp file request. + /// + public class SchemaCompareOpenScmpParams + { + /// + /// filepath of scmp + /// + public string filePath { get; set; } + } + + /// + /// Parameters returned from a schema compare open scmp request. + /// + public class SchemaCompareOpenScmpResult : ResultStatus + { + /// + /// Gets or sets the current source endpoint info + /// + public SchemaCompareEndpointInfo SourceEndpointInfo { get; set; } + + /// + /// Gets or sets the current target endpoint info + /// + public SchemaCompareEndpointInfo TargetEndpointInfo { get; set; } + + /// + /// Gets or sets the original target name. This is the initial target name, not necessarily the same as TargetEndpointInfo if they were swapped + /// The original target name is used to determine whether to use ExcludedSourceElements or ExcludedTargetElements if source and target were swapped + /// + public string OriginalTargetName { get; set; } + + /// + /// Gets or sets the original target connection string. This is the initial target connection string, not necessarily the same as TargetEndpointInfo if they were swapped + /// The target connection string is necessary if the source and target are a dacpac and db with the same name + /// + public string OriginalTargetConnectionString { get; set; } + + /// + /// Gets or sets the deployment options + /// + public DeploymentOptions DeploymentOptions { get; set; } + + /// + /// Gets or sets the excluded source elements. This is based on the initial source, not necessarily the same as SourceEndpointInfo if they were swapped + /// + public List ExcludedSourceElements { get; set; } + + /// + /// Gets or sets the excluded target elements. This is based on the initial target, not necessarily the same as TargetEndpointInfo if they were swapped + /// + public List ExcludedTargetElements { get; set; } + } + + /// + /// Defines the Schema Compare open scmp request type + /// + class SchemaCompareOpenScmpRequest + { + public static readonly RequestType Type = + RequestType.Create("schemaCompare/openScmp"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareRequest.cs index ffd4e520..1431e9f1 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareRequest.cs @@ -4,6 +4,7 @@ // using Microsoft.SqlServer.Dac.Compare; using Microsoft.SqlTools.Hosting.Protocol.Contracts; +using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; using Microsoft.SqlTools.ServiceLayer.TaskServices; using Microsoft.SqlTools.ServiceLayer.Utility; using System.Collections.Generic; @@ -37,6 +38,11 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts /// Connection uri /// public string OwnerUri { get; set; } + + /// + /// Connection details + /// + public ConnectionDetails ConnectionDetails { get; set; } } /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareOpenScmpOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareOpenScmpOperation.cs new file mode 100644 index 00000000..7f6d9146 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareOpenScmpOperation.cs @@ -0,0 +1,183 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +using Microsoft.SqlServer.Dac; +using Microsoft.SqlServer.Dac.Compare; +using Microsoft.SqlTools.ServiceLayer.Connection; +using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts; +using Microsoft.SqlTools.ServiceLayer.TaskServices; +using Microsoft.SqlTools.Utility; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading; +using System.Xml; +using System.Xml.Linq; + +namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare +{ + /// + /// Schema compare load scmp operation + /// + class SchemaCompareOpenScmpOperation : ITaskOperation + { + private CancellationTokenSource cancellation = new CancellationTokenSource(); + private bool disposed = false; + + public SqlTask SqlTask { get; set; } + + public SchemaCompareOpenScmpParams Parameters { get; set; } + + public SchemaCompareOpenScmpResult Result { get; private set; } + + private XDocument scmpInfo { get; set; } + + public SchemaCompareOpenScmpOperation(SchemaCompareOpenScmpParams parameters) + { + Validate.IsNotNull("parameters", parameters); + this.Parameters = parameters; + } + + protected CancellationToken CancellationToken { get { return this.cancellation.Token; } } + + /// + /// The error occurred during operation + /// + public string ErrorMessage { get; set; } + + // The schema compare public api doesn't currently take a cancellation token so the operation can't be cancelled + public void Cancel() + { + } + + /// + /// Disposes the operation. + /// + public void Dispose() + { + if (!disposed) + { + this.Cancel(); + disposed = true; + } + } + + public void Execute(TaskExecutionMode mode) + { + if (this.CancellationToken.IsCancellationRequested) + { + throw new OperationCanceledException(this.CancellationToken); + } + + try + { + SchemaComparison compare = new SchemaComparison(this.Parameters.filePath); + + // load xml file because some parsing still needs to be done + this.scmpInfo = XDocument.Load(this.Parameters.filePath); + + this.Result = new SchemaCompareOpenScmpResult() + { + DeploymentOptions = new DeploymentOptions(compare.Options), + Success = true, + SourceEndpointInfo = this.GetEndpointInfo(true, compare.Source), + TargetEndpointInfo = this.GetEndpointInfo(false, compare.Target), + OriginalTargetName = this.GetOriginalTargetName(), + OriginalTargetConnectionString = this.GetOriginalTargetConnectionString(), + ExcludedSourceElements = this.GetExcludedElements(compare.ExcludedSourceObjects), + ExcludedTargetElements = this.GetExcludedElements(compare.ExcludedTargetObjects) + }; + } + catch (Exception e) + { + ErrorMessage = e.Message; + Logger.Write(TraceEventType.Error, string.Format("Schema compare open scmp operation failed with exception {0}", e)); + throw; + } + } + + private SchemaCompareEndpointInfo GetEndpointInfo(bool source, SchemaCompareEndpoint endpoint) + { + SchemaCompareEndpointInfo endpointInfo = new SchemaCompareEndpointInfo(); + + // if the endpoint is a dacpac we don't need to parse the xml + SchemaCompareDacpacEndpoint dacpacEndpoint = endpoint as SchemaCompareDacpacEndpoint; + if (dacpacEndpoint != null) + { + endpointInfo.EndpointType = SchemaCompareEndpointType.Dacpac; + endpointInfo.PackageFilePath = dacpacEndpoint.FilePath; + } + else + { + // need to parse xml to get connection string of database + var result = this.scmpInfo.Descendants("ConnectionBasedModelProvider"); + string searchingFor = source ? "Source" : "Target"; + + try + { + if (result != null) + { + foreach (XElement node in result) + { + if (node.Parent.Name.ToString().Contains(searchingFor)) + { + endpointInfo.ConnectionDetails = SchemaCompareService.ConnectionServiceInstance.ParseConnectionString(node.Value); + endpointInfo.ConnectionDetails.ConnectionString = node.Value; + endpointInfo.DatabaseName = endpointInfo.ConnectionDetails.DatabaseName; + endpointInfo.EndpointType = SchemaCompareEndpointType.Database; + } + } + } + } + catch (Exception e) + { + ErrorMessage = string.Format(SR.OpenScmpConnectionBasedModelParsingError, ((SchemaCompareDatabaseEndpoint)endpoint).DatabaseName,e.Message); + Logger.Write(TraceEventType.Error, string.Format("Schema compare open scmp operation failed during xml parsing with exception {0}", e.Message)); + throw; + } + } + + return endpointInfo; + } + + private List GetExcludedElements(IList excludedObjects) + { + List excludedElements = new List(); + + foreach (SchemaComparisonExcludedObjectId entry in excludedObjects) + { + excludedElements.Add(new SchemaCompareObjectId() + { + NameParts = entry.Identifier.Parts.Cast().ToArray(), + SqlObjectType = entry.TypeName + }); + } + + return excludedElements; + } + + + // The original target name is used to determine whether to use ExcludedSourceElements or ExcludedTargetElements if source and target were swapped + private string GetOriginalTargetName() + { + var result = this.scmpInfo.Descendants("PropertyElementName") + .Where(x => x.Element("Name").Value == "TargetDatabaseName") + .Select(x => x.Element("Value")).FirstOrDefault(); + + return result != null ? result.Value : string.Empty; + } + + // The original target connection string is used if comparing a dacpac and db with the same name + private string GetOriginalTargetConnectionString() + { + var result = this.scmpInfo.Descendants("PropertyElementName") + .Where(x => x.Element("Name").Value == "TargetConnectionString") + .Select(x => x.Element("Value")).FirstOrDefault(); + + return result != null ? result.Value : string.Empty; + } + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareService.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareService.cs index e7871f16..a9568563 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareService.cs @@ -10,11 +10,10 @@ using Microsoft.SqlTools.ServiceLayer.TaskServices; using System; using System.Collections.Concurrent; using System.Threading.Tasks; -using Microsoft.SqlTools.ServiceLayer.SchemaCompare; using Microsoft.SqlServer.Dac.Compare; using Microsoft.SqlTools.ServiceLayer.Utility; -namespace Microsoft.SqlTools.ServiceLayer.SchemaCopmare +namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare { /// /// Main class for SchemaCompare service @@ -27,6 +26,9 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCopmare private Lazy> schemaCompareResults = new Lazy>(() => new ConcurrentDictionary()); + // For testability + internal Task CurrentSchemaCompareTask; + /// /// Gets the singleton instance object /// @@ -46,8 +48,11 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCopmare serviceHost.SetRequestHandler(SchemaComparePublishChangesRequest.Type, this.HandleSchemaComparePublishChangesRequest); serviceHost.SetRequestHandler(SchemaCompareIncludeExcludeNodeRequest.Type, this.HandleSchemaCompareIncludeExcludeNodeRequest); serviceHost.SetRequestHandler(SchemaCompareGetDefaultOptionsRequest.Type, this.HandleSchemaCompareGetDefaultOptionsRequest); + serviceHost.SetRequestHandler(SchemaCompareOpenScmpRequest.Type, this.HandleSchemaCompareOpenScmpRequest); } + + /// /// Handles schema compare request /// @@ -86,7 +91,7 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCopmare Differences = operation.Differences }); } - catch(Exception e) + catch (Exception e) { await requestContext.SendResult(new SchemaCompareResult() { @@ -185,7 +190,7 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCopmare operation = new SchemaCompareIncludeExcludeNodeOperation(parameters, compareResult); operation.Execute(parameters.TaskExecutionMode); - + await requestContext.SendResult(new ResultStatus() { Success = true, @@ -227,6 +232,41 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCopmare } } + /// + /// Handles schema compare open SCMP request + /// + /// + public async Task HandleSchemaCompareOpenScmpRequest(SchemaCompareOpenScmpParams parameters, RequestContext requestContext) + { + try + { + CurrentSchemaCompareTask = Task.Run(async () => + { + SchemaCompareOpenScmpOperation operation = null; + + try + { + operation = new SchemaCompareOpenScmpOperation(parameters); + operation.Execute(TaskExecutionMode.Execute); + + await requestContext.SendResult(operation.Result); + } + catch (Exception e) + { + await requestContext.SendResult(new SchemaCompareOpenScmpResult() + { + Success = false, + ErrorMessage = operation == null ? e.Message : operation.ErrorMessage, + }); + } + }); + } + catch (Exception e) + { + await requestContext.SendError(e); + } + } + private SqlTaskManager SqlTaskManagerInstance { get diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs index aa37bb64..c17be9b4 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs @@ -13,7 +13,6 @@ using System; using System.IO; using System.Threading.Tasks; using Xunit; -using Microsoft.SqlTools.ServiceLayer.SchemaCopmare; namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SchemaCompare { diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs index e9c56fce..de70a874 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs @@ -2,6 +2,7 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Microsoft.SqlServer.Dac.Compare; using Microsoft.SqlTools.Hosting.Protocol; using Microsoft.SqlTools.ServiceLayer.SchemaCompare; using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts; @@ -495,5 +496,117 @@ CREATE TABLE [dbo].[table3] string afterIncludeScript = generateScriptOperation.ScriptGenerationResult.Script; Assert.True(initialScript.Length == afterIncludeScript.Length, $"Changes should be same as inital since we included what we excluded, before {initialScript}, now {afterIncludeScript}"); } + + /// + /// Verify opening an scmp comparing two databases + /// + [Fact] + public async void SchemaCompareOpenScmpDatabaseToDatabaseRequest() + { + await CreateAndOpenScmp(SchemaCompareEndpointType.Database, SchemaCompareEndpointType.Database); + } + + /// + /// Verify opening an scmp comparing a dacpac and database + /// + [Fact] + public async void SchemaCompareOpenScmpDacpacToDatabaseRequest() + { + await CreateAndOpenScmp(SchemaCompareEndpointType.Dacpac, SchemaCompareEndpointType.Database); + } + + /// + /// Verify opening an scmp comparing two dacpacs + /// + [Fact] + public async void SchemaCompareOpenScmpDacpacToDacpacRequest() + { + await CreateAndOpenScmp(SchemaCompareEndpointType.Dacpac, SchemaCompareEndpointType.Dacpac); + } + + private async Task CreateAndOpenScmp(SchemaCompareEndpointType sourceEndpointType, SchemaCompareEndpointType targetEndpointType) + { + SqlTestDb sourceDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, SourceScript, "SchemaCompareOpenScmpSource"); + SqlTestDb targetDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, TargetScript, "SchemaCompareOpenScmpTarget"); + + try + { + SchemaCompareEndpoint sourceEndpoint = CreateSchemaCompareEndpoint(sourceDb, sourceEndpointType); + SchemaCompareEndpoint targetEndpoint = CreateSchemaCompareEndpoint(targetDb, targetEndpointType); + + // create a comparison and exclude the first difference + SchemaComparison compare = new SchemaComparison(sourceEndpoint, targetEndpoint); + SchemaComparisonResult result = compare.Compare(); + Assert.NotEmpty(result.Differences); + SchemaDifference difference = result.Differences.First(); + if (difference.SourceObject != null) + { + compare.ExcludedSourceObjects.Add(new SchemaComparisonExcludedObjectId(difference.SourceObject.ObjectType, difference.SourceObject.Name)); + } + else + { + compare.ExcludedSourceObjects.Add(new SchemaComparisonExcludedObjectId(difference.TargetObject.ObjectType, difference.TargetObject.Name)); + } + + // save to scmp + string folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "SchemaCompareTest"); + Directory.CreateDirectory(folderPath); + string filePath = Path.Combine(folderPath, string.Format("SchemaCompareOpenScmpTest{0}.scmp", DateTime.Now.ToFileTime())); + compare.SaveToFile(filePath); + Assert.True(File.Exists(filePath)); + + var schemaCompareOpenScmpParams = new SchemaCompareOpenScmpParams + { + filePath = filePath + }; + + SchemaCompareOpenScmpOperation schemaCompareOpenScmpOperation = new SchemaCompareOpenScmpOperation(schemaCompareOpenScmpParams); + schemaCompareOpenScmpOperation.Execute(TaskExecutionMode.Execute); + + Assert.NotNull(schemaCompareOpenScmpOperation.Result); + Assert.True(schemaCompareOpenScmpOperation.Result.Success); + Assert.NotEmpty(schemaCompareOpenScmpOperation.Result.ExcludedSourceElements); + Assert.Equal(1, schemaCompareOpenScmpOperation.Result.ExcludedSourceElements.Count()); + Assert.Empty(schemaCompareOpenScmpOperation.Result.ExcludedTargetElements); + Assert.Equal(targetDb.DatabaseName, schemaCompareOpenScmpOperation.Result.OriginalTargetName); + ValidateResultEndpointInfo(sourceEndpoint, schemaCompareOpenScmpOperation.Result.SourceEndpointInfo, sourceDb.ConnectionString); + ValidateResultEndpointInfo(targetEndpoint, schemaCompareOpenScmpOperation.Result.TargetEndpointInfo, targetDb.ConnectionString); + + SchemaCompareTestUtils.VerifyAndCleanup(filePath); + } + finally + { + sourceDb.Cleanup(); + targetDb.Cleanup(); + } + } + + private SchemaCompareEndpoint CreateSchemaCompareEndpoint(SqlTestDb db, SchemaCompareEndpointType endpointType) + { + if (endpointType == SchemaCompareEndpointType.Dacpac) + { + string dacpacFilePath = SchemaCompareTestUtils.CreateDacpac(db); + return new SchemaCompareDacpacEndpoint(dacpacFilePath); + } + else + { + return new SchemaCompareDatabaseEndpoint(db.ConnectionString); + } + } + + private void ValidateResultEndpointInfo(SchemaCompareEndpoint originalEndpoint, SchemaCompareEndpointInfo resultEndpoint, string connectionString) + { + if (resultEndpoint.EndpointType == SchemaCompareEndpointType.Dacpac) + { + SchemaCompareDacpacEndpoint dacpacEndpoint = originalEndpoint as SchemaCompareDacpacEndpoint; + Assert.Equal(dacpacEndpoint.FilePath, resultEndpoint.PackageFilePath); + } + else + { + SchemaCompareDatabaseEndpoint databaseEndpoint = originalEndpoint as SchemaCompareDatabaseEndpoint; + Assert.Equal(databaseEndpoint.DatabaseName, resultEndpoint.DatabaseName); + Assert.Contains(resultEndpoint.ConnectionDetails.ConnectionString, connectionString); // connectionString has password but resultEndpoint doesn't + } + } } }