From 9e143362933e45f507f0156b7b4442560cab41dc Mon Sep 17 00:00:00 2001 From: udeeshagautam <46980425+udeeshagautam@users.noreply.github.com> Date: Thu, 18 Apr 2019 12:52:10 -0700 Subject: [PATCH] Feature/schemacompare options (#798) * Initial working code for schema compare options * Removing the unnecessary default value attribute * Cleaning up tests * Taking PR comments * Taking name change for Schema Compare Options --> Deployment Options * Remove parent to avoid circular reference (to avoid issues with serialization) --- .../Contracts/DeploymentOptions.cs | 222 ++++++++++ .../Contracts/SchemaCompareRequest.cs | 5 + .../SchemaCompare/SchemaCompareOperation.cs | 47 +- .../SchemaCompareServiceOptionsTests.cs | 400 ++++++++++++++++++ .../SchemaCompareServiceTests.cs | 82 +--- .../SchemaCompare/SchemaCompareTestUtils.cs | 57 +++ 6 files changed, 741 insertions(+), 72 deletions(-) create mode 100644 src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs create mode 100644 test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs create mode 100644 test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareTestUtils.cs diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs new file mode 100644 index 00000000..59cb3b94 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs @@ -0,0 +1,222 @@ +// +// 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 System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts +{ + /// + /// Class to define deployment options. + /// Keeping the order and defaults same as DacFx + /// The default values here should also match the default values in ADS UX + /// + public class DeploymentOptions + { + public bool IgnoreTableOptions { get; set; } + + public bool IgnoreSemicolonBetweenStatements { get; set; } + + public bool IgnoreRouteLifetime { get; set; } + + public bool IgnoreRoleMembership { get; set; } + + public bool IgnoreQuotedIdentifiers { get; set; } + + public bool IgnorePermissions { get; set; } + + public bool IgnorePartitionSchemes { get; set; } + + public bool IgnoreObjectPlacementOnPartitionScheme { get; set; } + + public bool IgnoreNotForReplication { get; set; } + + public bool IgnoreLoginSids { get; set; } + + public bool IgnoreLockHintsOnIndexes { get; set; } + + public bool IgnoreKeywordCasing { get; set; } + + public bool IgnoreIndexPadding { get; set; } + + public bool IgnoreIndexOptions { get; set; } + + public bool IgnoreIncrement { get; set; } + + public bool IgnoreIdentitySeed { get; set; } + + public bool IgnoreUserSettingsObjects { get; set; } + + public bool IgnoreFullTextCatalogFilePath { get; set; } + + public bool IgnoreWhitespace { get; set; } + + public bool IgnoreWithNocheckOnForeignKeys { get; set; } + + public bool VerifyCollationCompatibility { get; set; } + + public bool UnmodifiableObjectWarnings { get; set; } + + public bool TreatVerificationErrorsAsWarnings { get; set; } + + public bool ScriptRefreshModule { get; set; } + + public bool ScriptNewConstraintValidation { get; set; } + + public bool ScriptFileSize { get; set; } + + public bool ScriptDeployStateChecks { get; set; } + + public bool ScriptDatabaseOptions { get; set; } + + public bool ScriptDatabaseCompatibility { get; set; } + + public bool ScriptDatabaseCollation { get; set; } + + public bool RunDeploymentPlanExecutors { get; set; } + + public bool RegisterDataTierApplication { get; set; } + + public bool PopulateFilesOnFileGroups { get; set; } + + public bool NoAlterStatementsToChangeClrTypes { get; set; } + + public bool IncludeTransactionalScripts { get; set; } + + public bool IncludeCompositeObjects { get; set; } + + public bool AllowUnsafeRowLevelSecurityDataMovement { get; set; } + + public bool IgnoreWithNocheckOnCheckConstraints { get; set; } + + public bool IgnoreFillFactor { get; set; } + + public bool IgnoreFileSize { get; set; } + + public bool IgnoreFilegroupPlacement { get; set; } + + public bool DoNotAlterReplicatedObjects { get; set; } + + public bool DoNotAlterChangeDataCaptureObjects { get; set; } + + public bool DisableAndReenableDdlTriggers { get; set; } + + public bool DeployDatabaseInSingleUserMode { get; set; } + + public bool CreateNewDatabase { get; set; } + + public bool CompareUsingTargetCollation { get; set; } + + public bool CommentOutSetVarDeclarations { get; set; } + + public int CommandTimeout { get; set; } = 120; + + public bool BlockWhenDriftDetected { get; set; } + + public bool BlockOnPossibleDataLoss { get; set; } + + public bool BackupDatabaseBeforeChanges { get; set; } + + public bool AllowIncompatiblePlatform { get; set; } + + public bool AllowDropBlockingAssemblies { get; set; } + + public string AdditionalDeploymentContributorArguments { get; set; } + + public string AdditionalDeploymentContributors { get; set; } + + public bool DropConstraintsNotInSource { get; set; } + + public bool DropDmlTriggersNotInSource { get; set; } + + public bool DropExtendedPropertiesNotInSource { get; set; } + + public bool DropIndexesNotInSource { get; set; } + + public bool IgnoreFileAndLogFilePath { get; set; } + + public bool IgnoreExtendedProperties { get; set; } + + public bool IgnoreDmlTriggerState { get; set; } + + public bool IgnoreDmlTriggerOrder { get; set; } + + public bool IgnoreDefaultSchema { get; set; } + + public bool IgnoreDdlTriggerState { get; set; } + + public bool IgnoreDdlTriggerOrder { get; set; } + + public bool IgnoreCryptographicProviderFilePath { get; set; } + + public bool VerifyDeployment { get; set; } + + public bool IgnoreComments { get; set; } + + public bool IgnoreColumnCollation { get; set; } + + public bool IgnoreAuthorizer { get; set; } + + public bool IgnoreAnsiNulls { get; set; } + + public bool GenerateSmartDefaults { get; set; } + + public bool DropStatisticsNotInSource { get; set; } + + public bool DropRoleMembersNotInSource { get; set; } + + public bool DropPermissionsNotInSource { get; set; } + + public bool DropObjectsNotInSource { get; set; } + + public bool IgnoreColumnOrder { get; set; } + + public ObjectType[] DoNotDropObjectTypes { get; set; } = null; + + public ObjectType[] ExcludeObjectTypes { get; set; } = + { + ObjectType.ServerTriggers, + ObjectType.Routes, + ObjectType.LinkedServerLogins, + ObjectType.Endpoints, + ObjectType.ErrorMessages, + ObjectType.Filegroups, + ObjectType.Logins, + ObjectType.LinkedServers, + ObjectType.Credentials, + ObjectType.DatabaseScopedCredentials, + ObjectType.DatabaseEncryptionKeys, + ObjectType.MasterKeys, + ObjectType.DatabaseAuditSpecifications, + ObjectType.Audits, + ObjectType.ServerAuditSpecifications, + ObjectType.CryptographicProviders, + ObjectType.ServerRoles, + ObjectType.EventSessions, + ObjectType.DatabaseOptions, + ObjectType.EventNotifications, + ObjectType.ServerRoleMembership, + ObjectType.AssemblyFiles, + }; + + public DeploymentOptions() + { + DacDeployOptions options = new DacDeployOptions(); + 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/SchemaCompareRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareRequest.cs index 0cd43561..4203f127 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareRequest.cs @@ -58,6 +58,11 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts /// Executation mode for the operation. Default is execution /// public TaskExecutionMode TaskExecutionMode { get; set; } + + /// + /// gets or sets the deployment options for schema compare + /// + public DeploymentOptions DeploymentOptions { get; set; } } /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareOperation.cs index 61b043c7..1de38af5 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareOperation.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareOperation.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; using Microsoft.SqlServer.Dac.Compare; using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts; @@ -40,6 +41,8 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare public List Differences; + public DacDeployOptions DefaultOptions; + public SchemaCompareOperation(SchemaCompareParams parameters, ConnectionInfo sourceConnInfo, ConnectionInfo targetConnInfo) { Validate.IsNotNull("parameters", parameters); @@ -86,10 +89,16 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare SchemaCompareEndpoint targetEndpoint = CreateSchemaCompareEndpoint(this.Parameters.TargetEndpointInfo, this.TargetConnectionString); SchemaComparison comparison = new SchemaComparison(sourceEndpoint, targetEndpoint); + + if (this.Parameters.DeploymentOptions != null) + { + comparison.Options = this.CreateSchemaCompareOptions(this.Parameters.DeploymentOptions); + } + this.ComparisonResult = comparison.Compare(); // try one more time if it didn't work the first time - if(!this.ComparisonResult.IsValid) + if (!this.ComparisonResult.IsValid) { this.ComparisonResult = comparison.Compare(); } @@ -109,6 +118,22 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare } } + private DacDeployOptions CreateSchemaCompareOptions(DeploymentOptions deploymentOptions) + { + System.Reflection.PropertyInfo[] deploymentOptionsProperties = deploymentOptions.GetType().GetProperties(); + + DacDeployOptions dacOptions = new DacDeployOptions(); + foreach (var deployOptionsProp in deploymentOptionsProperties) + { + var prop = dacOptions.GetType().GetProperty(deployOptionsProp.Name); + if (prop != null) + { + prop.SetValue(dacOptions, deployOptionsProp.GetValue(deploymentOptions)); + } + } + return dacOptions; + } + private DiffEntry CreateDiffEntry(SchemaDifference difference, DiffEntry parent) { DiffEntry diffEntry = new DiffEntry(); @@ -141,7 +166,7 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare diffEntry.TargetScript = RemoveExcessWhitespace(targetScript); } } - + diffEntry.Children = new List(); foreach (SchemaDifference child in difference.Children) @@ -157,17 +182,17 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare switch (endpointInfo.EndpointType) { case SchemaCompareEndpointType.Dacpac: - { - return new SchemaCompareDacpacEndpoint(endpointInfo.PackageFilePath); - } + { + return new SchemaCompareDacpacEndpoint(endpointInfo.PackageFilePath); + } case SchemaCompareEndpointType.Database: - { - return new SchemaCompareDatabaseEndpoint(connectionString); - } + { + return new SchemaCompareDatabaseEndpoint(connectionString); + } default: - { - return null; - } + { + return null; + } } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs new file mode 100644 index 00000000..6f2bd153 --- /dev/null +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs @@ -0,0 +1,400 @@ +// +// 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; +using Microsoft.SqlTools.ServiceLayer.DacFx; +using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts; +using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility; +using Microsoft.SqlTools.ServiceLayer.SchemaCompare; +using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts; +using Microsoft.SqlTools.ServiceLayer.TaskServices; +using Microsoft.SqlTools.ServiceLayer.Test.Common; +using Microsoft.SqlServer.Dac; +using Moq; +using System; +using System.IO; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SchemaCompare +{ + /// + /// Group of tests to test non-default options and included items for schema comapre + /// Note - adding it to new class for easy findability + /// + public class SchemaCompareServiceOptionsTests + { + private const string Source1 = @"CREATE TABLE [dbo].[table1] +( + [ID] INT NOT NULL PRIMARY KEY, + [Date] DATE NOT NULL, +)"; + private const string Target1 = @"CREATE TABLE [dbo].[table1] +( + [Date] DATE NOT NULL, + [ID] INT NOT NULL PRIMARY KEY, +)"; + + private const string Source2 = @" +CREATE FUNCTION [dbo].[Function1] +( + @param1 int, + @param2 char(5) +) +RETURNS @returntable TABLE +( + c1 int, + c2 char(5) +) +AS +BEGIN + INSERT @returntable + SELECT @param1, @param2 + RETURN +END" +; + private const string Target2 = @"CREATE FUNCTION [dbo].[Function1] +( + @param1 int, + @param2 char(5) +) +RETURNS @returntable TABLE +( + x1 int, + x2 char(5) +) +AS +BEGIN + INSERT @returntable + SELECT @param1, @param2 + RETURN +END +"; + private DeploymentOptions GetIgnoreColumnOptions() + { + var options = new DeploymentOptions(); + options.IgnoreColumnOrder = true; + return options; + } + + private DeploymentOptions GetExcludeTableValuedFunctionOptions() + { + var options = new DeploymentOptions(); + options.ExcludeObjectTypes = new ObjectType[]{ + ObjectType.ServerTriggers, + ObjectType.Routes, + ObjectType.LinkedServerLogins, + ObjectType.Endpoints, + ObjectType.ErrorMessages, + ObjectType.Filegroups, + ObjectType.Logins, + ObjectType.LinkedServers, + ObjectType.Credentials, + ObjectType.DatabaseScopedCredentials, + ObjectType.DatabaseEncryptionKeys, + ObjectType.MasterKeys, + ObjectType.DatabaseAuditSpecifications, + ObjectType.Audits, + ObjectType.ServerAuditSpecifications, + ObjectType.CryptographicProviders, + ObjectType.ServerRoles, + ObjectType.EventSessions, + ObjectType.DatabaseOptions, + ObjectType.EventNotifications, + ObjectType.ServerRoleMembership, + ObjectType.AssemblyFiles, + ObjectType.TableValuedFunctions, //added Functions to excluded types + }; + return options; + } + + private async Task>> SendAndValidateSchemaCompareRequestDacpacToDacpacWithOptions(string sourceScript, string targetScript, DeploymentOptions nodiffOption, DeploymentOptions shouldDiffOption) + { + + var result = SchemaCompareTestUtils.GetLiveAutoCompleteTestObjects(); + var schemaCompareRequestContext = new Mock>(); + schemaCompareRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object())); + + // create dacpacs from databases + SqlTestDb sourceDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, sourceScript, "SchemaCompareSource"); + SqlTestDb targetDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, targetScript, "SchemaCompareTarget"); + try + { + string sourceDacpacFilePath = SchemaCompareTestUtils.CreateDacpac(sourceDb); + string targetDacpacFilePath = SchemaCompareTestUtils.CreateDacpac(targetDb); + + SchemaCompareEndpointInfo sourceInfo = new SchemaCompareEndpointInfo(); + SchemaCompareEndpointInfo targetInfo = new SchemaCompareEndpointInfo(); + + sourceInfo.EndpointType = SchemaCompareEndpointType.Dacpac; + sourceInfo.PackageFilePath = sourceDacpacFilePath; + targetInfo.EndpointType = SchemaCompareEndpointType.Dacpac; + targetInfo.PackageFilePath = targetDacpacFilePath; + + var schemaCompareParams1 = new SchemaCompareParams + { + SourceEndpointInfo = sourceInfo, + TargetEndpointInfo = targetInfo, + DeploymentOptions = nodiffOption + }; + + SchemaCompareOperation schemaCompareOperation1 = new SchemaCompareOperation(schemaCompareParams1, null, null); + schemaCompareOperation1.Execute(TaskExecutionMode.Execute); + Assert.True(schemaCompareOperation1.ComparisonResult.IsEqual); + + var schemaCompareParams2 = new SchemaCompareParams + { + SourceEndpointInfo = sourceInfo, + TargetEndpointInfo = targetInfo, + DeploymentOptions = shouldDiffOption, + }; + + SchemaCompareOperation schemaCompareOperation2 = new SchemaCompareOperation(schemaCompareParams2, null, null); + schemaCompareOperation2.Execute(TaskExecutionMode.Execute); + Assert.False(schemaCompareOperation2.ComparisonResult.IsEqual); + Assert.NotNull(schemaCompareOperation2.ComparisonResult.Differences); + + // cleanup + SchemaCompareTestUtils.VerifyAndCleanup(sourceDacpacFilePath); + SchemaCompareTestUtils.VerifyAndCleanup(targetDacpacFilePath); + } + finally + { + sourceDb.Cleanup(); + targetDb.Cleanup(); + } + + return schemaCompareRequestContext; + } + + private async Task>> SendAndValidateSchemaCompareRequestDatabaseToDatabaseWithOptions(string sourceScript, string targetScript, DeploymentOptions nodiffOption, DeploymentOptions shouldDiffOption) + { + var result = SchemaCompareTestUtils.GetLiveAutoCompleteTestObjects(); + var schemaCompareRequestContext = new Mock>(); + schemaCompareRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object())); + + SqlTestDb sourceDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, sourceScript, "SchemaCompareSource"); + SqlTestDb targetDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, targetScript, "SchemaCompareTarget"); + string folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "SchemaCompareTest"); + Directory.CreateDirectory(folderPath); + + try + { + SchemaCompareEndpointInfo sourceInfo = new SchemaCompareEndpointInfo(); + SchemaCompareEndpointInfo targetInfo = new SchemaCompareEndpointInfo(); + + sourceInfo.EndpointType = SchemaCompareEndpointType.Database; + sourceInfo.DatabaseName = sourceDb.DatabaseName; + targetInfo.EndpointType = SchemaCompareEndpointType.Database; + targetInfo.DatabaseName = targetDb.DatabaseName; + + var schemaCompareParams1 = new SchemaCompareParams + { + SourceEndpointInfo = sourceInfo, + TargetEndpointInfo = targetInfo, + DeploymentOptions = nodiffOption + }; + + SchemaCompareOperation schemaCompareOperation1 = new SchemaCompareOperation(schemaCompareParams1, result.ConnectionInfo, result.ConnectionInfo); + schemaCompareOperation1.Execute(TaskExecutionMode.Execute); + + Assert.True(schemaCompareOperation1.ComparisonResult.IsValid); + Assert.True(schemaCompareOperation1.ComparisonResult.IsEqual); + Assert.NotNull(schemaCompareOperation1.ComparisonResult.Differences); + + var schemaCompareParams2 = new SchemaCompareParams + { + SourceEndpointInfo = sourceInfo, + TargetEndpointInfo = targetInfo, + DeploymentOptions = shouldDiffOption, + }; + + SchemaCompareOperation schemaCompareOperation2 = new SchemaCompareOperation(schemaCompareParams2, result.ConnectionInfo, result.ConnectionInfo); + schemaCompareOperation2.Execute(TaskExecutionMode.Execute); + Assert.False(schemaCompareOperation2.ComparisonResult.IsEqual); + Assert.NotNull(schemaCompareOperation2.ComparisonResult.Differences); + } + finally + { + // cleanup + sourceDb.Cleanup(); + targetDb.Cleanup(); + } + return schemaCompareRequestContext; + } + + private async Task>> SendAndValidateSchemaCompareGenerateScriptRequestDacpacToDatabaseWithOptions(string sourceScript, string targetScript, DeploymentOptions nodiffOption, DeploymentOptions shouldDiffOption) + { + var result = SchemaCompareTestUtils.GetLiveAutoCompleteTestObjects(); + var schemaCompareRequestContext = new Mock>(); + schemaCompareRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object())); + + SqlTestDb sourceDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, sourceScript, "SchemaCompareSource"); + SqlTestDb targetDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, targetScript, "SchemaCompareTarget"); + string folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "SchemaCompareTest"); + Directory.CreateDirectory(folderPath); + + try + { + string sourceDacpacFilePath = SchemaCompareTestUtils.CreateDacpac(sourceDb); + + SchemaCompareEndpointInfo sourceInfo = new SchemaCompareEndpointInfo(); + SchemaCompareEndpointInfo targetInfo = new SchemaCompareEndpointInfo(); + + sourceInfo.EndpointType = SchemaCompareEndpointType.Dacpac; + sourceInfo.PackageFilePath = sourceDacpacFilePath; + targetInfo.EndpointType = SchemaCompareEndpointType.Database; + targetInfo.DatabaseName = targetDb.DatabaseName; + + var schemaCompareParams1 = new SchemaCompareParams + { + SourceEndpointInfo = sourceInfo, + TargetEndpointInfo = targetInfo, + DeploymentOptions = nodiffOption, + }; + + SchemaCompareOperation schemaCompareOperation1 = new SchemaCompareOperation(schemaCompareParams1, result.ConnectionInfo, result.ConnectionInfo); + schemaCompareOperation1.Execute(TaskExecutionMode.Execute); + + Assert.True(schemaCompareOperation1.ComparisonResult.IsValid); + Assert.True(schemaCompareOperation1.ComparisonResult.IsEqual); + Assert.NotNull(schemaCompareOperation1.ComparisonResult.Differences); + + // generate script + var generateScriptParams1 = new SchemaCompareGenerateScriptParams + { + TargetDatabaseName = targetDb.DatabaseName, + OperationId = schemaCompareOperation1.OperationId, + ScriptFilePath = Path.Combine(folderPath, string.Concat(sourceDb.DatabaseName, "_", "Update.publish1.sql")) + }; + + SchemaCompareGenerateScriptOperation generateScriptOperation1 = new SchemaCompareGenerateScriptOperation(generateScriptParams1, schemaCompareOperation1.ComparisonResult); + generateScriptOperation1.Execute(TaskExecutionMode.Execute); + + // validate script + var filePath1 = Path.Combine(folderPath, string.Concat(sourceDb.DatabaseName, "_", "Update.publish1.sql")); + Assert.True(File.Exists(filePath1) && string.IsNullOrEmpty(File.ReadAllText(filePath1)), "Should not be any differences"); + + var schemaCompareParams2 = new SchemaCompareParams + { + SourceEndpointInfo = sourceInfo, + TargetEndpointInfo = targetInfo, + DeploymentOptions = shouldDiffOption, + }; + + SchemaCompareOperation schemaCompareOperation2 = new SchemaCompareOperation(schemaCompareParams2, result.ConnectionInfo, result.ConnectionInfo); + schemaCompareOperation2.Execute(TaskExecutionMode.Execute); + + Assert.True(schemaCompareOperation2.ComparisonResult.IsValid); + Assert.False(schemaCompareOperation2.ComparisonResult.IsEqual); + Assert.NotNull(schemaCompareOperation2.ComparisonResult.Differences); + + // generate script + var generateScriptParams2 = new SchemaCompareGenerateScriptParams + { + TargetDatabaseName = targetDb.DatabaseName, + OperationId = schemaCompareOperation1.OperationId, + ScriptFilePath = Path.Combine(folderPath, string.Concat(sourceDb.DatabaseName, "_", "Update.publish2.sql")) + }; + + SchemaCompareGenerateScriptOperation generateScriptOperation2 = new SchemaCompareGenerateScriptOperation(generateScriptParams2, schemaCompareOperation2.ComparisonResult); + generateScriptOperation2.Execute(TaskExecutionMode.Execute); + + // validate script + var filePath2 = Path.Combine(folderPath, string.Concat(sourceDb.DatabaseName, "_", "Update.publish2.sql")); + Assert.True(File.Exists(filePath2) && !string.IsNullOrEmpty(File.ReadAllText(filePath2)), "Should have differences differences"); + + // cleanup + SchemaCompareTestUtils.VerifyAndCleanup(generateScriptParams1.ScriptFilePath); + SchemaCompareTestUtils.VerifyAndCleanup(generateScriptParams2.ScriptFilePath); + SchemaCompareTestUtils.VerifyAndCleanup(sourceDacpacFilePath); + } + finally + { + sourceDb.Cleanup(); + targetDb.Cleanup(); + } + return schemaCompareRequestContext; + } + + /// + /// Verify the schema compare request comparing two dacpacs with and without ignore column option + /// + [Fact] + public async void SchemaCompareDacpacToDacpacOptions() + { + Assert.NotNull(await SendAndValidateSchemaCompareRequestDacpacToDacpacWithOptions(Source1, Target1, GetIgnoreColumnOptions(), new DeploymentOptions())); + } + + /// + /// Verify the schema compare request comparing two dacpacs with and excluding table valued functions + /// + [Fact] + public async void SchemaCompareDacpacToDacpacObjectTypes() + { + Assert.NotNull(await SendAndValidateSchemaCompareRequestDacpacToDacpacWithOptions(Source2, Target2, GetExcludeTableValuedFunctionOptions(), new DeploymentOptions())); + } + + /// + /// Verify the schema compare request comparing two databases with and without ignore column option + /// + [Fact] + public async void SchemaCompareDatabaseToDatabaseOptions() + { + Assert.NotNull(await SendAndValidateSchemaCompareRequestDatabaseToDatabaseWithOptions(Source1, Target1, GetIgnoreColumnOptions(), new DeploymentOptions())); + } + + /// + /// Verify the schema compare request comparing two databases with and excluding table valued functions + /// + [Fact] + public async void SchemaCompareDatabaseToDatabaseObjectTypes() + { + Assert.NotNull(await SendAndValidateSchemaCompareRequestDatabaseToDatabaseWithOptions(Source2, Target2, GetExcludeTableValuedFunctionOptions(), new DeploymentOptions())); + } + + /// + /// Verify the schema compare script generation comparing dacpac and db with and without ignore column option + /// + [Fact] + public async void SchemaCompareGenerateScriptDacpacToDatabaseOptions() + { + Assert.NotNull(await SendAndValidateSchemaCompareGenerateScriptRequestDacpacToDatabaseWithOptions(Source1, Target1, GetIgnoreColumnOptions(), new DeploymentOptions())); + } + + /// + /// Verify the schema compare script generation comparing dacpac and db with and excluding table valued function + /// + [Fact] + public async void SchemaCompareGenerateScriptDacpacToDatabaseObjectTypes() + { + Assert.NotNull(await SendAndValidateSchemaCompareGenerateScriptRequestDacpacToDatabaseWithOptions(Source2, Target2, GetExcludeTableValuedFunctionOptions(), new DeploymentOptions())); + } + + /// + /// Verify the schema compare script generation comparing dacpac and db with and excluding table valued function + /// + [Fact] + public void ValidateSchemaCompareOptionsDefault() + { + DeploymentOptions deployOptions = new DeploymentOptions(); + DacDeployOptions dacOptions = new DacDeployOptions(); + + System.Reflection.PropertyInfo[] deploymentOptionsProperties = deployOptions.GetType().GetProperties(); + System.Reflection.PropertyInfo[] ddProperties = dacOptions.GetType().GetProperties(); + + foreach (var deployOptionsProp in deploymentOptionsProperties) + { + var dacProp = dacOptions.GetType().GetProperty(deployOptionsProp.Name); + Assert.True(dacProp != null, $"DacDeploy property not present for {deployOptionsProp.Name}"); + + var deployOptionsValue = deployOptionsProp.GetValue(deployOptions); + var dacValue = dacProp.GetValue(dacOptions); + + Assert.True((deployOptionsValue == null && dacValue == null) || deployOptionsValue.Equals(dacValue), $"DacFx DacDeploy property not equal to Tools Service DeploymentOptions for { deployOptionsProp.Name}, SchemaCompareOptions value: {deployOptionsValue} and DacDeployOptions value: {dacValue} "); + } + } + + } +} diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs index 6643a8fb..baa0b71b 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs @@ -43,16 +43,10 @@ CREATE TABLE [dbo].[table3] [col1] INT NULL, )"; - private LiveConnectionHelper.TestConnectionResult GetLiveAutoCompleteTestObjects() - { - var result = LiveConnectionHelper.InitLiveConnectionInfo(); - return result; - } - private async Task>> SendAndValidateSchemaCompareRequestDacpacToDacpac() { - var result = GetLiveAutoCompleteTestObjects(); + var result = SchemaCompareTestUtils.GetLiveAutoCompleteTestObjects(); var schemaCompareRequestContext = new Mock>(); schemaCompareRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object())); @@ -61,8 +55,8 @@ CREATE TABLE [dbo].[table3] SqlTestDb targetDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, TargetScript, "SchemaCompareTarget"); try { - string sourceDacpacFilePath = CreateDacpac(sourceDb); - string targetDacpacFilePath = CreateDacpac(targetDb); + string sourceDacpacFilePath = SchemaCompareTestUtils.CreateDacpac(sourceDb); + string targetDacpacFilePath = SchemaCompareTestUtils.CreateDacpac(targetDb); SchemaCompareEndpointInfo sourceInfo = new SchemaCompareEndpointInfo(); SchemaCompareEndpointInfo targetInfo = new SchemaCompareEndpointInfo(); @@ -86,8 +80,8 @@ CREATE TABLE [dbo].[table3] Assert.NotNull(schemaCompareOperation.ComparisonResult.Differences); // cleanup - VerifyAndCleanup(sourceDacpacFilePath); - VerifyAndCleanup(targetDacpacFilePath); + SchemaCompareTestUtils.VerifyAndCleanup(sourceDacpacFilePath); + SchemaCompareTestUtils.VerifyAndCleanup(targetDacpacFilePath); } finally { @@ -100,7 +94,7 @@ CREATE TABLE [dbo].[table3] private async Task>> SendAndValidateSchemaCompareRequestDatabaseToDatabase() { - var result = GetLiveAutoCompleteTestObjects(); + var result = SchemaCompareTestUtils.GetLiveAutoCompleteTestObjects(); var schemaCompareRequestContext = new Mock>(); schemaCompareRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object())); @@ -143,7 +137,7 @@ CREATE TABLE [dbo].[table3] private async Task>> SendAndValidateSchemaCompareRequestDatabaseToDacpac() { - var result = GetLiveAutoCompleteTestObjects(); + var result = SchemaCompareTestUtils.GetLiveAutoCompleteTestObjects(); var schemaCompareRequestContext = new Mock>(); schemaCompareRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object())); @@ -152,7 +146,7 @@ CREATE TABLE [dbo].[table3] try { - string targetDacpacFilePath = CreateDacpac(targetDb); + string targetDacpacFilePath = SchemaCompareTestUtils.CreateDacpac(targetDb); SchemaCompareEndpointInfo sourceInfo = new SchemaCompareEndpointInfo(); SchemaCompareEndpointInfo targetInfo = new SchemaCompareEndpointInfo(); @@ -176,7 +170,7 @@ CREATE TABLE [dbo].[table3] Assert.NotNull(schemaCompareOperation.ComparisonResult.Differences); // cleanup - VerifyAndCleanup(targetDacpacFilePath); + SchemaCompareTestUtils.VerifyAndCleanup(targetDacpacFilePath); } finally { @@ -188,7 +182,7 @@ CREATE TABLE [dbo].[table3] private async Task>> SendAndValidateSchemaCompareGenerateScriptRequestDatabaseToDatabase() { - var result = GetLiveAutoCompleteTestObjects(); + var result = SchemaCompareTestUtils.GetLiveAutoCompleteTestObjects(); var schemaCompareRequestContext = new Mock>(); schemaCompareRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object())); @@ -232,7 +226,7 @@ CREATE TABLE [dbo].[table3] generateScriptOperation.Execute(TaskExecutionMode.Execute); // cleanup - VerifyAndCleanup(generateScriptParams.ScriptFilePath); + SchemaCompareTestUtils.VerifyAndCleanup(generateScriptParams.ScriptFilePath); } finally { @@ -244,7 +238,7 @@ CREATE TABLE [dbo].[table3] private async Task>> SendAndValidateSchemaCompareGenerateScriptRequestDacpacToDatabase() { - var result = GetLiveAutoCompleteTestObjects(); + var result = SchemaCompareTestUtils.GetLiveAutoCompleteTestObjects(); var schemaCompareRequestContext = new Mock>(); schemaCompareRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object())); @@ -255,7 +249,7 @@ CREATE TABLE [dbo].[table3] try { - string sourceDacpacFilePath = CreateDacpac(sourceDb); + string sourceDacpacFilePath = SchemaCompareTestUtils.CreateDacpac(sourceDb); SchemaCompareEndpointInfo sourceInfo = new SchemaCompareEndpointInfo(); SchemaCompareEndpointInfo targetInfo = new SchemaCompareEndpointInfo(); @@ -290,8 +284,8 @@ CREATE TABLE [dbo].[table3] generateScriptOperation.Execute(TaskExecutionMode.Execute); // cleanup - VerifyAndCleanup(generateScriptParams.ScriptFilePath); - VerifyAndCleanup(sourceDacpacFilePath); + SchemaCompareTestUtils.VerifyAndCleanup(generateScriptParams.ScriptFilePath); + SchemaCompareTestUtils.VerifyAndCleanup(sourceDacpacFilePath); } finally { @@ -303,7 +297,7 @@ CREATE TABLE [dbo].[table3] private async Task>> SendAndValidateSchemaComparePublishChangesRequestDacpacToDatabase() { - var result = GetLiveAutoCompleteTestObjects(); + var result = SchemaCompareTestUtils.GetLiveAutoCompleteTestObjects(); var schemaCompareRequestContext = new Mock>(); schemaCompareRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object())); @@ -314,7 +308,7 @@ CREATE TABLE [dbo].[table3] try { - string sourceDacpacFilePath = CreateDacpac(sourceDb); + string sourceDacpacFilePath = SchemaCompareTestUtils.CreateDacpac(sourceDb); SchemaCompareEndpointInfo sourceInfo = new SchemaCompareEndpointInfo(); SchemaCompareEndpointInfo targetInfo = new SchemaCompareEndpointInfo(); @@ -362,7 +356,7 @@ CREATE TABLE [dbo].[table3] Assert.Empty(schemaCompareOperation.ComparisonResult.Differences); // cleanup - VerifyAndCleanup(sourceDacpacFilePath); + SchemaCompareTestUtils.VerifyAndCleanup(sourceDacpacFilePath); } finally { @@ -374,7 +368,7 @@ CREATE TABLE [dbo].[table3] private async Task>> SendAndValidateSchemaComparePublishChangesRequestDatabaseToDatabase() { - var result = GetLiveAutoCompleteTestObjects(); + var result = SchemaCompareTestUtils.GetLiveAutoCompleteTestObjects(); var schemaCompareRequestContext = new Mock>(); schemaCompareRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object())); @@ -440,9 +434,9 @@ CREATE TABLE [dbo].[table3] /// Verify the schema compare request comparing two dacpacs /// [Fact] - public void SchemaCompareDacpacToDacpac() + public async void SchemaCompareDacpacToDacpac() { - Assert.NotNull(SendAndValidateSchemaCompareRequestDacpacToDacpac()); + Assert.NotNull(await SendAndValidateSchemaCompareRequestDacpacToDacpac()); } /// @@ -490,7 +484,6 @@ CREATE TABLE [dbo].[table3] Assert.NotNull(await SendAndValidateSchemaComparePublishChangesRequestDacpacToDatabase()); } - /// /// Verify the schema compare publish changes request comparing a database to a database /// @@ -499,38 +492,5 @@ CREATE TABLE [dbo].[table3] { Assert.NotNull(await SendAndValidateSchemaComparePublishChangesRequestDatabaseToDatabase()); } - - private void VerifyAndCleanup(string filePath) - { - // Verify it was created - Assert.True(File.Exists(filePath)); - - // Remove the file - if (File.Exists(filePath)) - { - File.Delete(filePath); - } - } - - private string CreateDacpac(SqlTestDb testdb) - { - var result = GetLiveAutoCompleteTestObjects(); - string folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "SchemaCompareTest"); - Directory.CreateDirectory(folderPath); - - var extractParams = new ExtractParams - { - DatabaseName = testdb.DatabaseName, - PackageFilePath = Path.Combine(folderPath, string.Format("{0}.dacpac", testdb.DatabaseName)), - ApplicationName = "test", - ApplicationVersion = "1.0.0.0" - }; - - DacFxService service = new DacFxService(); - ExtractOperation operation = new ExtractOperation(extractParams, result.ConnectionInfo); - service.PerformOperation(operation); - - return extractParams.PackageFilePath; - } } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareTestUtils.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareTestUtils.cs new file mode 100644 index 00000000..d9c7a0b6 --- /dev/null +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareTestUtils.cs @@ -0,0 +1,57 @@ +// +// 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.ServiceLayer.DacFx; +using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts; +using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility; +using Microsoft.SqlTools.ServiceLayer.Test.Common; +using NUnit.Framework; +using System; +using System.IO; + +namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SchemaCompare +{ + internal static class SchemaCompareTestUtils + { + internal static void VerifyAndCleanup(string filePath) + { + // Verify it was created + Assert.True(File.Exists(filePath)); + + // Remove the file + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + } + + internal static string CreateDacpac(SqlTestDb testdb) + { + var result = GetLiveAutoCompleteTestObjects(); + string folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "SchemaCompareTest"); + Directory.CreateDirectory(folderPath); + + var extractParams = new ExtractParams + { + DatabaseName = testdb.DatabaseName, + PackageFilePath = Path.Combine(folderPath, string.Format("{0}.dacpac", testdb.DatabaseName)), + ApplicationName = "test", + ApplicationVersion = "1.0.0.0" + }; + + DacFxService service = new DacFxService(); + ExtractOperation operation = new ExtractOperation(extractParams, result.ConnectionInfo); + service.PerformOperation(operation); + + return extractParams.PackageFilePath; + } + + internal static LiveConnectionHelper.TestConnectionResult GetLiveAutoCompleteTestObjects() + { + var result = LiveConnectionHelper.InitLiveConnectionInfo(); + return result; + } + } +}