diff --git a/src/Microsoft.SqlTools.Hosting/Hosting/Contracts/ConnectionProviderOptions.cs b/src/Microsoft.SqlTools.Hosting/Hosting/Contracts/ConnectionProviderOptions.cs index 25532617..50089435 100644 --- a/src/Microsoft.SqlTools.Hosting/Hosting/Contracts/ConnectionProviderOptions.cs +++ b/src/Microsoft.SqlTools.Hosting/Hosting/Contracts/ConnectionProviderOptions.cs @@ -15,6 +15,13 @@ namespace Microsoft.SqlTools.Hosting.Contracts public ConnectionOption[] Options { get; set; } } + public class CategoryValue + { + public string DisplayName { get; set; } + + public string Name { get; set; } + } + public class ConnectionOption { public static readonly string ValueTypeString = "string"; @@ -48,10 +55,10 @@ namespace Microsoft.SqlTools.Hosting.Contracts /// /// Set of permitted values if ValueType is category. /// - public string[] CategoryValues { get; set; } + public CategoryValue[] CategoryValues { get; set; } /// - /// Determines if the parameter is one of the 'specical' known values. + /// Determines if the parameter is one of the 'special' known values. /// Can be either Server Name, Database Name, Authentication Type, /// User Name, or Password /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionDetails.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionDetails.cs index ce1c6208..095c46a4 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionDetails.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionDetails.cs @@ -3,6 +3,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using System.Collections.Generic; + namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts { /// @@ -13,120 +15,457 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts /// public class ConnectionDetails : ConnectionSummary { + public ConnectionDetails() + { + Options = new Dictionary(); + } + + /// + /// Gets or Sets the connection options + /// + public Dictionary Options { get; set; } + /// /// Gets or sets the connection password /// /// - public string Password { get; set; } + public string Password { + get + { + return GetOptionValue("password"); + } + set + { + SetOptionValue("password", value); + } + } + + /// + /// Gets or sets the connection server name + /// + public override string ServerName + { + get + { + return GetOptionValue("server"); + } + + set + { + SetOptionValue("server", value); + } + } + + /// + /// Gets or sets the connection database name + /// + public override string DatabaseName + { + get + { + return GetOptionValue("database"); + } + + set + { + SetOptionValue("database", value); + } + } + + /// + /// Gets or sets the connection user name + /// + public override string UserName + { + get + { + return GetOptionValue("user"); + } + + set + { + SetOptionValue("user", value); + } + } /// /// Gets or sets the authentication to use. /// - public string AuthenticationType { get; set; } + public string AuthenticationType + { + get + { + return GetOptionValue("authenticationType"); + } + + set + { + SetOptionValue("authenticationType", value); + } + } /// /// Gets or sets a Boolean value that indicates whether SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed. /// - public bool? Encrypt { get; set; } + public bool? Encrypt + { + get + { + return GetOptionValue("encrypt"); + } + + set + { + SetOptionValue("encrypt", value); + } + } /// /// Gets or sets a value that indicates whether the channel will be encrypted while bypassing walking the certificate chain to validate trust. /// - public bool? TrustServerCertificate { get; set; } + public bool? TrustServerCertificate + { + get + { + return GetOptionValue("trustServerCertificate"); + } + + set + { + SetOptionValue("trustServerCertificate", value); + } + } /// /// Gets or sets a Boolean value that indicates if security-sensitive information, such as the password, is not returned as part of the connection if the connection is open or has ever been in an open state. /// - public bool? PersistSecurityInfo { get; set; } + public bool? PersistSecurityInfo + { + get + { + return GetOptionValue("persistSecurityInfo"); + } + + set + { + SetOptionValue("persistSecurityInfo", value); + } + } /// /// Gets or sets the length of time (in seconds) to wait for a connection to the server before terminating the attempt and generating an error. /// - public int? ConnectTimeout { get; set; } + public int? ConnectTimeout + { + get + { + return GetOptionValue("connectTimeout"); + } + + set + { + SetOptionValue("connectTimeout", value); + } + } /// /// The number of reconnections attempted after identifying that there was an idle connection failure. /// - public int? ConnectRetryCount { get; set; } + public int? ConnectRetryCount + { + get + { + return GetOptionValue("connectRetryCount"); + } + + set + { + SetOptionValue("connectRetryCount", value); + } + } /// /// Amount of time (in seconds) between each reconnection attempt after identifying that there was an idle connection failure. /// - public int? ConnectRetryInterval { get; set; } + public int? ConnectRetryInterval + { + get + { + return GetOptionValue("connectRetryInterval"); + } + + set + { + SetOptionValue("connectRetryInterval", value); + } + } /// /// Gets or sets the name of the application associated with the connection string. /// - public string ApplicationName { get; set; } + public string ApplicationName + { + get + { + return GetOptionValue("applicationName"); + } + + set + { + SetOptionValue("applicationName", value); + } + } /// /// Gets or sets the name of the workstation connecting to SQL Server. /// - public string WorkstationId { get; set; } + public string WorkstationId + { + get + { + return GetOptionValue("workstationId"); + } + + set + { + SetOptionValue("workstationId", value); + } + } /// /// Declares the application workload type when connecting to a database in an SQL Server Availability Group. /// - public string ApplicationIntent { get; set; } + public string ApplicationIntent + { + get + { + return GetOptionValue("applicationIntent"); + } + + set + { + SetOptionValue("applicationIntent", value); + } + } /// /// Gets or sets the SQL Server Language record name. /// - public string CurrentLanguage { get; set; } + public string CurrentLanguage + { + get + { + return GetOptionValue("currentLanguage"); + } + + set + { + SetOptionValue("currentLanguage", value); + } + } /// /// Gets or sets a Boolean value that indicates whether the connection will be pooled or explicitly opened every time that the connection is requested. /// - public bool? Pooling { get; set; } + public bool? Pooling + { + get + { + return GetOptionValue("pooling"); + } + + set + { + SetOptionValue("pooling", value); + } + } /// /// Gets or sets the maximum number of connections allowed in the connection pool for this specific connection string. /// - public int? MaxPoolSize { get; set; } + public int? MaxPoolSize + { + get + { + return GetOptionValue("maxPoolSize"); + } + + set + { + SetOptionValue("maxPoolSize", value); + } + } /// /// Gets or sets the minimum number of connections allowed in the connection pool for this specific connection string. /// - public int? MinPoolSize { get; set; } + public int? MinPoolSize + { + get + { + return GetOptionValue("minPoolSize"); + } + + set + { + SetOptionValue("minPoolSize", value); + } + } /// /// Gets or sets the minimum time, in seconds, for the connection to live in the connection pool before being destroyed. /// - public int? LoadBalanceTimeout { get; set; } + public int? LoadBalanceTimeout + { + get + { + return GetOptionValue("loadBalanceTimeout"); + } + + set + { + SetOptionValue("loadBalanceTimeout", value); + } + } /// /// Gets or sets a Boolean value that indicates whether replication is supported using the connection. /// - public bool? Replication { get; set; } + public bool? Replication + { + get + { + return GetOptionValue("replication"); + } + + set + { + SetOptionValue("replication", value); + } + } /// /// Gets or sets a string that contains the name of the primary data file. This includes the full path name of an attachable database. /// - public string AttachDbFilename { get; set; } + public string AttachDbFilename + { + get + { + return GetOptionValue("attachDbFilename"); + } + + set + { + SetOptionValue("attachDbFilename", value); + } + } /// /// Gets or sets the name or address of the partner server to connect to if the primary server is down. /// - public string FailoverPartner { get; set; } + public string FailoverPartner + { + get + { + return GetOptionValue("failoverPartner"); + } + + set + { + SetOptionValue("failoverPartner", value); + } + } /// /// If your application is connecting to an AlwaysOn availability group (AG) on different subnets, setting MultiSubnetFailover=true provides faster detection of and connection to the (currently) active server. /// - public bool? MultiSubnetFailover { get; set; } + public bool? MultiSubnetFailover + { + get + { + return GetOptionValue("multiSubnetFailover"); + } + + set + { + SetOptionValue("multiSubnetFailover", value); + } + } /// /// When true, an application can maintain multiple active result sets (MARS). /// - public bool? MultipleActiveResultSets { get; set; } + public bool? MultipleActiveResultSets + { + get + { + return GetOptionValue("multipleActiveResultSets"); + } + + set + { + SetOptionValue("multipleActiveResultSets", value); + } + } /// /// Gets or sets the size in bytes of the network packets used to communicate with an instance of SQL Server. /// - public int? PacketSize { get; set; } + public int? PacketSize + { + get + { + return GetOptionValue("packetSize"); + } + + set + { + SetOptionValue("packetSize", value); + } + } /// /// Gets or sets a string value that indicates the type system the application expects. /// - public string TypeSystemVersion { get; set; } + public string TypeSystemVersion + { + get + { + return GetOptionValue("typeSystemVersion"); + } + + set + { + SetOptionValue("typeSystemVersion", value); + } + } + + private T GetOptionValue(string name) + { + T result = default(T); + if (Options != null && Options.ContainsKey(name)) + { + object value = Options[name]; + if(value != null && ( typeof(T) == typeof(int) || typeof(T) == typeof(int?))) + { + value = System.Convert.ToInt32(value); + } + result = value != null ? (T)value : default(T); + } + return result; + } + + private void SetOptionValue(string name, T value) + { + Options = Options ?? new Dictionary(); + if (Options.ContainsKey(name)) + { + Options[name] = value; + } + else + { + Options.Add(name, value); + } + } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionSummary.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionSummary.cs index 11549e85..fa99ab8a 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionSummary.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/ConnectionSummary.cs @@ -13,16 +13,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts /// /// Gets or sets the connection server name /// - public string ServerName { get; set; } + public virtual string ServerName { get; set; } /// /// Gets or sets the connection database name /// - public string DatabaseName { get; set; } + public virtual string DatabaseName { get; set; } /// /// Gets or sets the connection user name /// - public string UserName { get; set; } + public virtual string UserName { get; set; } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ServiceHost.cs b/src/Microsoft.SqlTools.ServiceLayer/ServiceHost.cs index 338ff0e6..c46f24c2 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ServiceHost.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ServiceHost.cs @@ -190,7 +190,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting ); } - private static ConnectionProviderOptions BuildConnectionProviderOptions() + internal static ConnectionProviderOptions BuildConnectionProviderOptions() { return new ConnectionProviderOptions { @@ -198,110 +198,278 @@ namespace Microsoft.SqlTools.ServiceLayer.Hosting { new ConnectionOption { - Name = "Server Name", + Name = "server", + DisplayName = "Server Name", Description = "Name of the SQL Server instance", ValueType = ConnectionOption.ValueTypeString, SpecialValueType = ConnectionOption.SpecialValueServerName, IsIdentity = true, - IsRequired = true + IsRequired = true, + GroupName = "Source" }, new ConnectionOption { - Name = "Initial Catalog", + Name = "database", DisplayName = "Database Name", - Description = "Name of the SQL Server database", + Description = "The name of the initial catalog or database int the data source", ValueType = ConnectionOption.ValueTypeString, SpecialValueType = ConnectionOption.SpecialValueDatabaseName, IsIdentity = true, - IsRequired = true + IsRequired = true, + GroupName = "Source" }, new ConnectionOption { - Name = "Auth Type", - Description = "Authentication type", + Name = "authenticationType", + DisplayName = "Authentication Type", + Description = "Specifies the method of authenticating with SQL Server", ValueType = ConnectionOption.ValueTypeCategory, SpecialValueType = ConnectionOption.SpecialValueAuthType, - CategoryValues = new string[] { "SQL Login", "Integrated Auth" }, + CategoryValues = new CategoryValue[] + { new CategoryValue {DisplayName = "SQL Login", Name = "SqlLogin" }, + new CategoryValue {DisplayName = "Integrated Auth", Name= "Integrated" } + }, IsIdentity = true, - IsRequired = true + IsRequired = true, + GroupName = "Security" }, new ConnectionOption { - Name = "Username", - Description = "SQL Login user name", + Name = "user", + DisplayName = "User Name", + Description = "Indicates the user ID to be used when connecting to the data source", ValueType = ConnectionOption.ValueTypeString, SpecialValueType = ConnectionOption.SpecialValueUserName, IsIdentity = true, - IsRequired = true + IsRequired = true, + GroupName = "Security" }, new ConnectionOption { - Name = "Password", - Description = "SQL Login password", + Name = "password", + DisplayName = "Password", + Description = "Indicates the password to be used when connecting to the data source", ValueType = ConnectionOption.ValueTypePassword, SpecialValueType = ConnectionOption.SpecialValuePasswordName, IsIdentity = true, - IsRequired = true + IsRequired = true, + GroupName = "Security" }, new ConnectionOption { - Name = "Application Intent", - Description = "Application intent", + Name = "applicationIntent", + DisplayName = "Application Intent", + Description = "Declares the application workload type when connecting to a server", ValueType = ConnectionOption.ValueTypeCategory, - CategoryValues = new string[] { "ReadWrite", "ReadOnly" } + CategoryValues = new CategoryValue[] { + new CategoryValue { Name = "ReadWrite", DisplayName = "ReadWrite" }, + new CategoryValue {Name = "ReadOnly", DisplayName = "ReadOnly" } + }, + GroupName = "Initialization" }, new ConnectionOption { - Name = "Asynchronous Processing", - Description = "Application processing enabled", - ValueType = ConnectionOption.ValueTypeBoolean + Name = "asynchronousProcessing", + DisplayName = "Asynchronous processing enabled", + Description = "When true, enables usage of the Asynchronous functionality in the .Net Framework Data Provider", + ValueType = ConnectionOption.ValueTypeBoolean, + GroupName = "Initialization" }, new ConnectionOption { - Name = "Connect Timeout", - Description = "Timeout in seconds when establishing a connection", + Name = "connectTimeout", + DisplayName = "Connect Timeout", + Description = + "The length of time (in seconds) to wait for a connection to the server before terminating the attempt and generating an error", ValueType = ConnectionOption.ValueTypeNumber, - DefaultValue = "15" + DefaultValue = "15", + GroupName = "Initialization" }, new ConnectionOption { - Name = "Current Language", - ValueType = ConnectionOption.ValueTypeString + Name = "currentLanguage", + DisplayName = "Current Language", + Description = "The SQL Server language record name", + ValueType = ConnectionOption.ValueTypeString, + GroupName = "Initialization" }, new ConnectionOption { - Name = "Column Encrytion Setting", + Name = "columnEncryptionSetting", + DisplayName = "Column Encryption Setting", + Description = "Default column encryption setting for all the commands on the connection", ValueType = ConnectionOption.ValueTypeCategory, GroupName = "Security", - CategoryValues = new string[] { "Disabled", "Enabled" } + CategoryValues = new CategoryValue[] { + new CategoryValue { Name = "Disabled" }, + new CategoryValue {Name = "Enabled" } + } }, new ConnectionOption { - Name = "Encrypt", + Name = "encrypt", + DisplayName = "Encrypt", + Description = + "When true, SQL Server uses SSL encryption for all data sent between the client and server if the servers has a certificate installed", GroupName = "Security", ValueType = ConnectionOption.ValueTypeBoolean }, new ConnectionOption { - Name = "Persist Security Info", + Name = "persistSecurityInfo", + DisplayName = "Persist Security Info", + Description = "When false, security-sensitive information, such as the password, is not returned as part of the connection", GroupName = "Security", ValueType = ConnectionOption.ValueTypeBoolean }, new ConnectionOption { - Name = "Trust Server Certificate", + Name = "trustServerCertificate", + DisplayName = "Trust Server Certificate", + Description = "When true (and encrypt=true), SQL Server uses SSL encryption for all data sent between the client and server without validating the server certificate", GroupName = "Security", ValueType = ConnectionOption.ValueTypeBoolean }, new ConnectionOption { - Name = "Attached DB File Name", + Name = "attachedDBFileName", + DisplayName = "Attached DB File Name", + Description = "The name of the primary file, including the full path name, of an attachable database", + ValueType = ConnectionOption.ValueTypeString, + GroupName = "Source" + }, + new ConnectionOption + { + Name = "contextConnection", + DisplayName = "Context Connection", + Description = "When true, indicates the connection should be from the SQL server context. Available only when running in the SQL Server process", + ValueType = ConnectionOption.ValueTypeBoolean, + GroupName = "Source" + }, + new ConnectionOption + { + Name = "port", + DisplayName = "Port", + ValueType = ConnectionOption.ValueTypeNumber + }, + new ConnectionOption + { + Name = "connectRetryCount", + DisplayName = "Connect Retry Count", + Description = "Number of attempts to restore connection", + ValueType = ConnectionOption.ValueTypeNumber, + DefaultValue = "1", + GroupName = "Connection Resiliency" + }, + new ConnectionOption + { + Name = "connectRetryInterval", + DisplayName = "Connect Retry Interval", + Description = "Delay between attempts to restore connection", + ValueType = ConnectionOption.ValueTypeNumber, + DefaultValue = "10", + GroupName = "Connection Resiliency" + + }, + new ConnectionOption + { + Name = "applicationName", + DisplayName = "Application Name", + Description = "The name of the application", + ValueType = ConnectionOption.ValueTypeString, + GroupName = "Context" + }, + new ConnectionOption + { + Name = "workstationId", + DisplayName = "Workstation Id", + Description = "The name of the workstation connecting to SQL Server", + ValueType = ConnectionOption.ValueTypeString, + GroupName = "Context" + }, + new ConnectionOption + { + Name = "pooling", + DisplayName = "Pooling", + Description = "When true, the connection object is drawn from the appropriate pool, or if necessary, is created and added to the appropriate pool", + ValueType = ConnectionOption.ValueTypeBoolean, + GroupName = "Pooling" + }, + new ConnectionOption + { + Name = "maxPoolSize", + DisplayName = "Max Pool Size", + Description = "The maximum number of connections allowed in the pool", + ValueType = ConnectionOption.ValueTypeNumber, + GroupName = "Pooling" + }, + new ConnectionOption + { + Name = "minPoolSize", + DisplayName = "Min Pool Size", + Description = "The minimum number of connections allowed in the pool", + ValueType = ConnectionOption.ValueTypeNumber, + GroupName = "Pooling" + }, + new ConnectionOption + { + Name = "loadBalanceTimeout", + DisplayName = "Load Balance Timeout", + Description = "The minimum amount of time (in seconds) for this connection to live in the pool before being destroyed", + ValueType = ConnectionOption.ValueTypeNumber, + GroupName = "Pooling" + }, + new ConnectionOption + { + Name = "replication", + DisplayName = "Replication", + Description = "Used by SQL Server in Replication", + ValueType = ConnectionOption.ValueTypeBoolean, + GroupName = "Replication" + }, + new ConnectionOption + { + Name = "attachDbFilename", + DisplayName = "Attach Db Filename", ValueType = ConnectionOption.ValueTypeString }, new ConnectionOption { - Name = "Context Connection", - ValueType = ConnectionOption.ValueTypeString + Name = "failoverPartner", + DisplayName = "Failover Partner", + Description = "the name or network address of the instance of SQL Server that acts as a failover partner", + ValueType = ConnectionOption.ValueTypeString, + GroupName = " Source" + }, + new ConnectionOption + { + Name = "multiSubnetFailover", + DisplayName = "Multi Subnet Failover", + ValueType = ConnectionOption.ValueTypeBoolean + }, + new ConnectionOption + { + Name = "multipleActiveResultSets", + DisplayName = "Multiple Active ResultSets", + Description = "When true, multiple result sets can be returned and read from one connection", + ValueType = ConnectionOption.ValueTypeBoolean, + GroupName = "Advanced" + }, + new ConnectionOption + { + Name = "packetSize", + DisplayName = "Packet Size", + Description = "Size in bytes of the network packets used to communicate with an instance of SQL Server", + ValueType = ConnectionOption.ValueTypeNumber, + GroupName = "Advanced" + }, + new ConnectionOption + { + Name = "typeSystemVersion", + DisplayName = "Type System Version", + Description = "Indicates which server type system then provider will expose through the DataReader", + ValueType = ConnectionOption.ValueTypeString, + GroupName = "Advanced" } } }; diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Connection/ConnectionDetailsTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Connection/ConnectionDetailsTests.cs new file mode 100644 index 00000000..58b3f924 --- /dev/null +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Connection/ConnectionDetailsTests.cs @@ -0,0 +1,211 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Linq; +using Microsoft.SqlTools.Hosting.Contracts; +using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; +using Xunit; + + +namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Connection +{ + /// + /// Tests for ConnectionDetails Class + /// + public class ConnectionDetailsTests + { + [Fact] + public void ConnectionDetailsWithoutAnyOptionShouldReturnNullOrDefaultForOptions() + { + ConnectionDetails details = new ConnectionDetails(); + + var expectedForStrings = default(string); + var expectedForInt = default(int?); + var expectedForBoolean = default(bool?); + + Assert.Equal(details.ApplicationIntent, expectedForStrings); + Assert.Equal(details.ApplicationName, expectedForStrings); + Assert.Equal(details.AttachDbFilename, expectedForStrings); + Assert.Equal(details.AuthenticationType, expectedForStrings); + Assert.Equal(details.CurrentLanguage, expectedForStrings); + Assert.Equal(details.DatabaseName, expectedForStrings); + Assert.Equal(details.FailoverPartner, expectedForStrings); + Assert.Equal(details.Password, expectedForStrings); + Assert.Equal(details.ServerName, expectedForStrings); + Assert.Equal(details.TypeSystemVersion, expectedForStrings); + Assert.Equal(details.UserName, expectedForStrings); + Assert.Equal(details.WorkstationId, expectedForStrings); + Assert.Equal(details.ConnectRetryInterval, expectedForInt); + Assert.Equal(details.ConnectRetryCount, expectedForInt); + Assert.Equal(details.ConnectTimeout, expectedForInt); + Assert.Equal(details.LoadBalanceTimeout, expectedForInt); + Assert.Equal(details.MaxPoolSize, expectedForInt); + Assert.Equal(details.MinPoolSize, expectedForInt); + Assert.Equal(details.PacketSize, expectedForInt); + Assert.Equal(details.Encrypt, expectedForBoolean); + Assert.Equal(details.MultipleActiveResultSets, expectedForBoolean); + Assert.Equal(details.MultiSubnetFailover, expectedForBoolean); + Assert.Equal(details.PersistSecurityInfo, expectedForBoolean); + Assert.Equal(details.Pooling, expectedForBoolean); + Assert.Equal(details.Replication, expectedForBoolean); + Assert.Equal(details.TrustServerCertificate, expectedForBoolean); + } + + [Fact] + public void ConnectionDetailsPropertySettersShouldSetOptionValuesCorrectly() + { + ConnectionDetails details = new ConnectionDetails(); + + var index = 0; + var expectedForStrings = "Value for strings"; + var expectedForInt = 345; + details.ApplicationIntent = expectedForStrings + index++; + details.ApplicationName = expectedForStrings + index++; + details.AttachDbFilename = expectedForStrings + index++; + details.AuthenticationType = expectedForStrings + index++; + details.CurrentLanguage = expectedForStrings + index++; + details.DatabaseName = expectedForStrings + index++; + details.FailoverPartner = expectedForStrings + index++; + details.Password = expectedForStrings + index++; + details.ServerName = expectedForStrings + index++; + details.TypeSystemVersion = expectedForStrings + index++; + details.UserName = expectedForStrings + index++; + details.WorkstationId = expectedForStrings + index++; + details.ConnectRetryInterval = expectedForInt + index++; + details.ConnectRetryCount = expectedForInt + index++; + details.ConnectTimeout = expectedForInt + index++; + details.LoadBalanceTimeout = expectedForInt + index++; + details.MaxPoolSize = expectedForInt + index++; + details.MinPoolSize = expectedForInt + index++; + details.PacketSize = expectedForInt + index++; + details.Encrypt = (index++ % 2 == 0); + details.MultipleActiveResultSets = (index++ % 2 == 0); + details.MultiSubnetFailover = (index++ % 2 == 0); + details.PersistSecurityInfo = (index++ % 2 == 0); + details.Pooling = (index++ % 2 == 0); + details.Replication = (index++ % 2 == 0); + details.TrustServerCertificate = (index++ % 2 == 0); + + index = 0; + Assert.Equal(details.ApplicationIntent, expectedForStrings + index++); + Assert.Equal(details.ApplicationName, expectedForStrings + index++); + Assert.Equal(details.AttachDbFilename, expectedForStrings + index++); + Assert.Equal(details.AuthenticationType, expectedForStrings + index++); + Assert.Equal(details.CurrentLanguage, expectedForStrings + index++); + Assert.Equal(details.DatabaseName, expectedForStrings + index++); + Assert.Equal(details.FailoverPartner, expectedForStrings + index++); + Assert.Equal(details.Password, expectedForStrings + index++); + Assert.Equal(details.ServerName, expectedForStrings + index++); + Assert.Equal(details.TypeSystemVersion, expectedForStrings + index++); + Assert.Equal(details.UserName, expectedForStrings + index++); + Assert.Equal(details.WorkstationId, expectedForStrings + index++); + Assert.Equal(details.ConnectRetryInterval, expectedForInt + index++); + Assert.Equal(details.ConnectRetryCount, expectedForInt + index++); + Assert.Equal(details.ConnectTimeout, expectedForInt + index++); + Assert.Equal(details.LoadBalanceTimeout, expectedForInt + index++); + Assert.Equal(details.MaxPoolSize, expectedForInt + index++); + Assert.Equal(details.MinPoolSize, expectedForInt + index++); + Assert.Equal(details.PacketSize, expectedForInt + index++); + Assert.Equal(details.Encrypt, (index++ % 2 == 0)); + Assert.Equal(details.MultipleActiveResultSets, (index++ % 2 == 0)); + Assert.Equal(details.MultiSubnetFailover, (index++ % 2 == 0)); + Assert.Equal(details.PersistSecurityInfo, (index++ % 2 == 0)); + Assert.Equal(details.Pooling, (index++ % 2 == 0)); + Assert.Equal(details.Replication, (index++ % 2 == 0)); + Assert.Equal(details.TrustServerCertificate, (index++ % 2 == 0)); + } + + [Fact] + public void ConnectionDetailsOptionsShouldBeDefinedInConnectionProviderOptions() + { + ConnectionDetails details = new ConnectionDetails(); + ConnectionProviderOptions optionMetadata = Hosting.ServiceHost.BuildConnectionProviderOptions(); + + var index = 0; + var expectedForStrings = "Value for strings"; + var expectedForInt = 345; + details.ApplicationIntent = expectedForStrings + index++; + details.ApplicationName = expectedForStrings + index++; + details.AttachDbFilename = expectedForStrings + index++; + details.AuthenticationType = expectedForStrings + index++; + details.CurrentLanguage = expectedForStrings + index++; + details.DatabaseName = expectedForStrings + index++; + details.FailoverPartner = expectedForStrings + index++; + details.Password = expectedForStrings + index++; + details.ServerName = expectedForStrings + index++; + details.TypeSystemVersion = expectedForStrings + index++; + details.UserName = expectedForStrings + index++; + details.WorkstationId = expectedForStrings + index++; + details.ConnectRetryInterval = expectedForInt + index++; + details.ConnectRetryCount = expectedForInt + index++; + details.ConnectTimeout = expectedForInt + index++; + details.LoadBalanceTimeout = expectedForInt + index++; + details.MaxPoolSize = expectedForInt + index++; + details.MinPoolSize = expectedForInt + index++; + details.PacketSize = expectedForInt + index++; + details.Encrypt = (index++ % 2 == 0); + details.MultipleActiveResultSets = (index++ % 2 == 0); + details.MultiSubnetFailover = (index++ % 2 == 0); + details.PersistSecurityInfo = (index++ % 2 == 0); + details.Pooling = (index++ % 2 == 0); + details.Replication = (index++ % 2 == 0); + details.TrustServerCertificate = (index++ % 2 == 0); + + if(optionMetadata.Options.Count() != details.Options.Count) + { + var optionsNotInMetadata = details.Options.Where(o => !optionMetadata.Options.Any(m => m.Name == o.Key)); + var optionNames = optionsNotInMetadata.Any() ? optionsNotInMetadata.Select(s => s.Key).Aggregate((i, j) => i + "," + j) : null; + Assert.True(string.IsNullOrEmpty(optionNames), "Options not in metadata: " + optionNames); + } + foreach (var option in details.Options) + { + var metadata = optionMetadata.Options.FirstOrDefault(x => x.Name == option.Key); + Assert.NotNull(metadata); + if(metadata.ValueType == ConnectionOption.ValueTypeString) + { + Assert.True(option.Value is string); + } + else if (metadata.ValueType == ConnectionOption.ValueTypeBoolean) + { + Assert.True(option.Value is bool?); + } + else if (metadata.ValueType == ConnectionOption.ValueTypeNumber) + { + Assert.True(option.Value is int?); + } + } + } + + + [Fact] + public void SettingConnectiomTimeoutToLongShouldStillReturnInt() + { + ConnectionDetails details = new ConnectionDetails(); + + long timeout = 30; + int? expectedValue = 30; + details.Options["connectTimeout"] = timeout; + + Assert.Equal(details.ConnectTimeout, expectedValue); + } + + [Fact] + public void ConnectTimeoutShouldReturnNullIfNotSet() + { + ConnectionDetails details = new ConnectionDetails(); + int? expectedValue = null; + Assert.Equal(details.ConnectTimeout, expectedValue); + } + + [Fact] + public void ConnectTimeoutShouldReturnNullIfSetToNull() + { + ConnectionDetails details = new ConnectionDetails(); + details.Options["connectTimeout"] = null; + int? expectedValue = null; + Assert.Equal(details.ConnectTimeout, expectedValue); + } + } +}