// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Data.SqlClient; using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol; using Microsoft.SqlTools.ServiceLayer.Test.Utility; using Microsoft.SqlTools.Test.Utility; using Moq; using Moq.Protected; using Xunit; namespace Microsoft.SqlTools.ServiceLayer.Test.Connection { /// /// Tests for the ServiceHost Connection Service tests /// public class ConnectionServiceTests { /// /// Creates a mock db command that returns a predefined result set /// public static DbCommand CreateTestCommand(Dictionary[][] data) { var commandMock = new Mock { CallBase = true }; var commandMockSetup = commandMock.Protected() .Setup("ExecuteDbDataReader", It.IsAny()); commandMockSetup.Returns(() => new TestDbDataReader(data)); return commandMock.Object; } /// /// Creates a mock db connection that returns predefined data when queried for a result set /// public DbConnection CreateMockDbConnection(Dictionary[][] data) { var connectionMock = new Mock { CallBase = true }; connectionMock.Protected() .Setup("CreateDbCommand") .Returns(CreateTestCommand(data)); return connectionMock.Object; } [Fact] public void CanCancelConnectRequest() { var testFile = "file:///my/test/file.sql"; // Given a connection that times out and responds to cancellation var mockConnection = new Mock { CallBase = true }; CancellationToken token; bool ready = false; mockConnection.Setup(x => x.OpenAsync(Moq.It.IsAny())) .Callback(t => { // Pass the token to the return handler and signal the main thread to cancel token = t; ready = true; }) .Returns(() => { if (TestUtils.WaitFor(() => token.IsCancellationRequested)) { throw new OperationCanceledException(); } else { return Task.FromResult(true); } }); var mockFactory = new Mock(); mockFactory.Setup(factory => factory.CreateSqlConnection(It.IsAny())) .Returns(mockConnection.Object); var connectionService = new ConnectionService(mockFactory.Object); // Connect the connection asynchronously in a background thread var connectionDetails = TestObjects.GetTestConnectionDetails(); var connectTask = Task.Run(async () => { return await connectionService .Connect(new ConnectParams() { OwnerUri = testFile, Connection = connectionDetails }); }); // Wait for the connection to call OpenAsync() Assert.True(TestUtils.WaitFor(() => ready)); // Send a cancellation request var cancelResult = connectionService .CancelConnect(new CancelConnectParams() { OwnerUri = testFile }); // Wait for the connection task to finish connectTask.Wait(); // Verify that the connection was cancelled (no connection was created) Assert.Null(connectTask.Result.ConnectionId); // Verify that the cancel succeeded Assert.True(cancelResult); } [Fact] public async Task CanCancelConnectRequestByConnecting() { var testFile = "file:///my/test/file.sql"; // Given a connection that times out and responds to cancellation var mockConnection = new Mock { CallBase = true }; CancellationToken token; bool ready = false; mockConnection.Setup(x => x.OpenAsync(Moq.It.IsAny())) .Callback(t => { // Pass the token to the return handler and signal the main thread to cancel token = t; ready = true; }) .Returns(() => { if (TestUtils.WaitFor(() => token.IsCancellationRequested)) { throw new OperationCanceledException(); } else { return Task.FromResult(true); } }); // Given a second connection that succeeds var mockConnection2 = new Mock { CallBase = true }; mockConnection2.Setup(x => x.OpenAsync(Moq.It.IsAny())) .Returns(() => Task.Run(() => {})); var mockFactory = new Mock(); mockFactory.SetupSequence(factory => factory.CreateSqlConnection(It.IsAny())) .Returns(mockConnection.Object) .Returns(mockConnection2.Object); var connectionService = new ConnectionService(mockFactory.Object); // Connect the first connection asynchronously in a background thread var connectionDetails = TestObjects.GetTestConnectionDetails(); var connectTask = Task.Run(async () => { return await connectionService .Connect(new ConnectParams() { OwnerUri = testFile, Connection = connectionDetails }); }); // Wait for the connection to call OpenAsync() Assert.True(TestUtils.WaitFor(() => ready)); // Send a cancellation by trying to connect again var connectResult = await connectionService .Connect(new ConnectParams() { OwnerUri = testFile, Connection = connectionDetails }); // Wait for the first connection task to finish connectTask.Wait(); // Verify that the first connection was cancelled (no connection was created) Assert.Null(connectTask.Result.ConnectionId); // Verify that the second connection succeeded Assert.NotEmpty(connectResult.ConnectionId); } [Fact] public void CanCancelConnectRequestByDisconnecting() { var testFile = "file:///my/test/file.sql"; // Given a connection that times out and responds to cancellation var mockConnection = new Mock { CallBase = true }; CancellationToken token; bool ready = false; mockConnection.Setup(x => x.OpenAsync(Moq.It.IsAny())) .Callback(t => { // Pass the token to the return handler and signal the main thread to cancel token = t; ready = true; }) .Returns(() => { if (TestUtils.WaitFor(() => token.IsCancellationRequested)) { throw new OperationCanceledException(); } else { return Task.FromResult(true); } }); var mockFactory = new Mock(); mockFactory.Setup(factory => factory.CreateSqlConnection(It.IsAny())) .Returns(mockConnection.Object); var connectionService = new ConnectionService(mockFactory.Object); // Connect the first connection asynchronously in a background thread var connectionDetails = TestObjects.GetTestConnectionDetails(); var connectTask = Task.Run(async () => { return await connectionService .Connect(new ConnectParams() { OwnerUri = testFile, Connection = connectionDetails }); }); // Wait for the connection to call OpenAsync() Assert.True(TestUtils.WaitFor(() => ready)); // Send a cancellation by trying to disconnect var disconnectResult = connectionService .Disconnect(new DisconnectParams() { OwnerUri = testFile }); // Wait for the first connection task to finish connectTask.Wait(); // Verify that the first connection was cancelled (no connection was created) Assert.Null(connectTask.Result.ConnectionId); // Verify that the disconnect failed (since it caused a cancellation) Assert.False(disconnectResult); } /// /// Verify that we can connect to the default database when no database name is /// provided as a parameter. /// [Theory] [InlineDataAttribute(null)] [InlineDataAttribute("")] public async Task CanConnectWithEmptyDatabaseName(string databaseName) { // Connect var connectionDetails = TestObjects.GetTestConnectionDetails(); connectionDetails.DatabaseName = databaseName; var connectionResult = await TestObjects.GetTestConnectionService() .Connect(new ConnectParams() { OwnerUri = "file:///my/test/file.sql", Connection = connectionDetails }); // check that a connection was created Assert.NotEmpty(connectionResult.ConnectionId); } /// /// Verify that we can connect to the default database when no database name is /// provided as a parameter. /// [Theory] [InlineDataAttribute("master")] [InlineDataAttribute("nonMasterDb")] public async Task ConnectToDefaultDatabaseRespondsWithActualDbName(string expectedDbName) { // Given connecting with empty database name will return the expected DB name var connectionMock = new Mock { CallBase = true }; connectionMock.Setup(c => c.Database).Returns(expectedDbName); var mockFactory = new Mock(); mockFactory.Setup(factory => factory.CreateSqlConnection(It.IsAny())) .Returns(connectionMock.Object); var connectionService = new ConnectionService(mockFactory.Object); // When I connect with an empty DB name var connectionDetails = TestObjects.GetTestConnectionDetails(); connectionDetails.DatabaseName = string.Empty; var connectionResult = await connectionService .Connect(new ConnectParams() { OwnerUri = "file:///my/test/file.sql", Connection = connectionDetails }); // Then I expect connection to succeed and the Summary to include the correct DB name Assert.NotEmpty(connectionResult.ConnectionId); Assert.NotNull(connectionResult.ConnectionSummary); Assert.Equal(expectedDbName, connectionResult.ConnectionSummary.DatabaseName); } /// /// Verify that when a connection is started for a URI with an already existing /// connection, we disconnect first before connecting. /// [Fact] public async Task ConnectingWhenConnectionExistCausesDisconnectThenConnect() { bool callbackInvoked = false; // first connect string ownerUri = "file://my/sample/file.sql"; var connectionService = TestObjects.GetTestConnectionService(); var connectionResult = await connectionService .Connect(new ConnectParams() { OwnerUri = ownerUri, Connection = TestObjects.GetTestConnectionDetails() }); // verify that we are connected Assert.NotEmpty(connectionResult.ConnectionId); // register disconnect callback connectionService.RegisterOnDisconnectTask( (result, uri) => { callbackInvoked = true; Assert.True(uri.Equals(ownerUri)); return Task.FromResult(true); } ); // send annother connect request connectionResult = await connectionService .Connect(new ConnectParams() { OwnerUri = ownerUri, Connection = TestObjects.GetTestConnectionDetails() }); // verify that the event was fired (we disconnected first before connecting) Assert.True(callbackInvoked); // verify that we connected again Assert.NotEmpty(connectionResult.ConnectionId); } /// /// Verify that when connecting with invalid credentials, an error is thrown. /// [Fact] public async Task ConnectingWithInvalidCredentialsYieldsErrorMessage() { var testConnectionDetails = TestObjects.GetTestConnectionDetails(); var invalidConnectionDetails = new ConnectionDetails(); invalidConnectionDetails.ServerName = testConnectionDetails.ServerName; invalidConnectionDetails.DatabaseName = testConnectionDetails.DatabaseName; invalidConnectionDetails.UserName = "invalidUsername"; // triggers exception when opening mock connection invalidConnectionDetails.Password = "invalidPassword"; // Connect to test db with invalid credentials var connectionResult = await TestObjects.GetTestConnectionService() .Connect(new ConnectParams() { OwnerUri = "file://my/sample/file.sql", Connection = invalidConnectionDetails }); // check that an error was caught Assert.NotNull(connectionResult.Messages); Assert.NotEqual(String.Empty, connectionResult.Messages); } /// /// Verify that when connecting with invalid parameters, an error is thrown. /// [Theory] [InlineData("SqlLogin", null, "my-server", "test", "sa", "123456")] [InlineData("SqlLogin", "file://my/sample/file.sql", null, "test", "sa", "123456")] [InlineData("SqlLogin", "file://my/sample/file.sql", "my-server", "test", null, "123456")] [InlineData("SqlLogin", "file://my/sample/file.sql", "my-server", "test", "sa", null)] [InlineData("SqlLogin", "", "my-server", "test", "sa", "123456")] [InlineData("SqlLogin", "file://my/sample/file.sql", "", "test", "sa", "123456")] [InlineData("SqlLogin", "file://my/sample/file.sql", "my-server", "test", "", "123456")] [InlineData("SqlLogin", "file://my/sample/file.sql", "my-server", "test", "sa", "")] [InlineData("Integrated", null, "my-server", "test", "sa", "123456")] [InlineData("Integrated", "file://my/sample/file.sql", null, "test", "sa", "123456")] [InlineData("Integrated", "", "my-server", "test", "sa", "123456")] [InlineData("Integrated", "file://my/sample/file.sql", "", "test", "sa", "123456")] public async Task ConnectingWithInvalidParametersYieldsErrorMessage(string authType, string ownerUri, string server, string database, string userName, string password) { // Connect with invalid parameters var connectionResult = await TestObjects.GetTestConnectionService() .Connect(new ConnectParams() { OwnerUri = ownerUri, Connection = new ConnectionDetails() { ServerName = server, DatabaseName = database, UserName = userName, Password = password, AuthenticationType = authType } }); // check that an error was caught Assert.NotNull(connectionResult.Messages); Assert.NotEqual(String.Empty, connectionResult.Messages); } /// /// Verify that when using integrated authentication, the username and/or password can be empty. /// [Theory] [InlineData(null, null)] [InlineData(null, "")] [InlineData("", null)] [InlineData("", "")] [InlineData("sa", null)] [InlineData("sa", "")] [InlineData(null, "12345678")] [InlineData("", "12345678")] public async Task ConnectingWithNoUsernameOrPasswordWorksForIntegratedAuth(string userName, string password) { // Connect var connectionResult = await TestObjects.GetTestConnectionService() .Connect(new ConnectParams() { OwnerUri = "file:///my/test/file.sql", Connection = new ConnectionDetails() { ServerName = "my-server", DatabaseName = "test", UserName = userName, Password = password, AuthenticationType = "Integrated" } }); // check that the connection was successful Assert.NotEmpty(connectionResult.ConnectionId); } /// /// Verify that when connecting with a null parameters object, an error is thrown. /// [Fact] public async Task ConnectingWithNullParametersObjectYieldsErrorMessage() { // Connect with null parameters var connectionResult = await TestObjects.GetTestConnectionService() .Connect(null); // check that an error was caught Assert.NotNull(connectionResult.Messages); Assert.NotEqual(String.Empty, connectionResult.Messages); } /// /// Verify that optional parameters can be built into a connection string for connecting. /// [Theory] [InlineData("AuthenticationType", "Integrated", "Integrated Security")] [InlineData("AuthenticationType", "SqlLogin", "")] [InlineData("Encrypt", true, "Encrypt")] [InlineData("Encrypt", false, "Encrypt")] [InlineData("TrustServerCertificate", true, "TrustServerCertificate")] [InlineData("TrustServerCertificate", false, "TrustServerCertificate")] [InlineData("PersistSecurityInfo", true, "Persist Security Info")] [InlineData("PersistSecurityInfo", false, "Persist Security Info")] [InlineData("ConnectTimeout", 15, "Connect Timeout")] [InlineData("ConnectRetryCount", 1, "ConnectRetryCount")] [InlineData("ConnectRetryInterval", 10, "ConnectRetryInterval")] [InlineData("ApplicationName", "vscode-mssql", "Application Name")] [InlineData("WorkstationId", "mycomputer", "Workstation ID")] [InlineData("ApplicationIntent", "ReadWrite", "ApplicationIntent")] [InlineData("ApplicationIntent", "ReadOnly", "ApplicationIntent")] [InlineData("CurrentLanguage", "test", "Current Language")] [InlineData("Pooling", false, "Pooling")] [InlineData("Pooling", true, "Pooling")] [InlineData("MaxPoolSize", 100, "Max Pool Size")] [InlineData("MinPoolSize", 0, "Min Pool Size")] [InlineData("LoadBalanceTimeout", 0, "Load Balance Timeout")] [InlineData("Replication", true, "Replication")] [InlineData("Replication", false, "Replication")] [InlineData("AttachDbFilename", "myfile", "AttachDbFilename")] [InlineData("FailoverPartner", "partner", "Failover Partner")] [InlineData("MultiSubnetFailover", true, "MultiSubnetFailover")] [InlineData("MultiSubnetFailover", false, "MultiSubnetFailover")] [InlineData("MultipleActiveResultSets", false, "MultipleActiveResultSets")] [InlineData("MultipleActiveResultSets", true, "MultipleActiveResultSets")] [InlineData("PacketSize", 8192, "Packet Size")] [InlineData("TypeSystemVersion", "Latest", "Type System Version")] public void ConnectingWithOptionalParametersBuildsConnectionString(string propertyName, object propertyValue, string connectionStringMarker) { // Create a test connection details object and set the property to a specific value ConnectionDetails details = TestObjects.GetTestConnectionDetails(); PropertyInfo info = details.GetType().GetProperty(propertyName); info.SetValue(details, propertyValue); // Test that a connection string can be created without exceptions string connectionString = ConnectionService.BuildConnectionString(details); Assert.NotNull(connectionString); Assert.NotEmpty(connectionString); // Verify that the parameter is in the connection string Assert.True(connectionString.Contains(connectionStringMarker)); } /// /// Build connection string with an invalid auth type /// [Fact] public void BuildConnectionStringWithInvalidAuthType() { ConnectionDetails details = TestObjects.GetTestConnectionDetails(); details.AuthenticationType = "NotAValidAuthType"; Assert.Throws(() => ConnectionService.BuildConnectionString(details)); } /// /// Verify that a connection changed event is fired when the database context changes. /// [Fact] public async Task ConnectionChangedEventIsFiredWhenDatabaseContextChanges() { var serviceHostMock = new Mock(); var connectionService = TestObjects.GetTestConnectionService(); connectionService.ServiceHost = serviceHostMock.Object; // Set up an initial connection string ownerUri = "file://my/sample/file.sql"; var connectionResult = await connectionService .Connect(new ConnectParams() { OwnerUri = ownerUri, Connection = TestObjects.GetTestConnectionDetails() }); // verify that a valid connection id was returned Assert.NotEmpty(connectionResult.ConnectionId); ConnectionInfo info; Assert.True(connectionService.TryFindConnection(ownerUri, out info)); // Tell the connection manager that the database change ocurred connectionService.ChangeConnectionDatabaseContext(ownerUri, "myOtherDb"); // Verify that the connection changed event was fired serviceHostMock.Verify(x => x.SendEvent(ConnectionChangedNotification.Type, It.IsAny()), Times.Once()); } /// /// Verify that the SQL parser correctly detects errors in text /// [Fact] public async Task ConnectToDatabaseTest() { // connect to a database instance string ownerUri = "file://my/sample/file.sql"; var connectionResult = await TestObjects.GetTestConnectionService() .Connect(new ConnectParams() { OwnerUri = ownerUri, Connection = TestObjects.GetTestConnectionDetails() }); // verify that a valid connection id was returned Assert.NotEmpty(connectionResult.ConnectionId); } /// /// Verify that we can disconnect from an active connection succesfully /// [Fact] public async Task DisconnectFromDatabaseTest() { // first connect string ownerUri = "file://my/sample/file.sql"; var connectionService = TestObjects.GetTestConnectionService(); var connectionResult = await connectionService .Connect(new ConnectParams() { OwnerUri = ownerUri, Connection = TestObjects.GetTestConnectionDetails() }); // verify that we are connected Assert.NotEmpty(connectionResult.ConnectionId); // send disconnect request var disconnectResult = connectionService .Disconnect(new DisconnectParams() { OwnerUri = ownerUri }); Assert.True(disconnectResult); } /// /// Test that when a disconnect is performed, the callback event is fired /// [Fact] public async Task DisconnectFiresCallbackEvent() { bool callbackInvoked = false; // first connect string ownerUri = "file://my/sample/file.sql"; var connectionService = TestObjects.GetTestConnectionService(); var connectionResult = await connectionService .Connect(new ConnectParams() { OwnerUri = ownerUri, Connection = TestObjects.GetTestConnectionDetails() }); // verify that we are connected Assert.NotEmpty(connectionResult.ConnectionId); // register disconnect callback connectionService.RegisterOnDisconnectTask( (result, uri) => { callbackInvoked = true; Assert.True(uri.Equals(ownerUri)); return Task.FromResult(true); } ); // send disconnect request var disconnectResult = connectionService .Disconnect(new DisconnectParams() { OwnerUri = ownerUri }); Assert.True(disconnectResult); // verify that the event was fired Assert.True(callbackInvoked); } /// /// Test that disconnecting an active connection removes the Owner URI -> ConnectionInfo mapping /// [Fact] public async Task DisconnectRemovesOwnerMapping() { // first connect string ownerUri = "file://my/sample/file.sql"; var connectionService = TestObjects.GetTestConnectionService(); var connectionResult = await connectionService .Connect(new ConnectParams() { OwnerUri = ownerUri, Connection = TestObjects.GetTestConnectionDetails() }); // verify that we are connected Assert.NotEmpty(connectionResult.ConnectionId); // check that the owner mapping exists ConnectionInfo info; Assert.True(connectionService.TryFindConnection(ownerUri, out info)); // send disconnect request var disconnectResult = connectionService .Disconnect(new DisconnectParams() { OwnerUri = ownerUri }); Assert.True(disconnectResult); // check that the owner mapping no longer exists Assert.False(connectionService.TryFindConnection(ownerUri, out info)); } /// /// Test that disconnecting validates parameters and doesn't succeed when they are invalid /// [Theory] [InlineDataAttribute(null)] [InlineDataAttribute("")] public async Task DisconnectValidatesParameters(string disconnectUri) { // first connect string ownerUri = "file://my/sample/file.sql"; var connectionService = TestObjects.GetTestConnectionService(); var connectionResult = await connectionService .Connect(new ConnectParams() { OwnerUri = ownerUri, Connection = TestObjects.GetTestConnectionDetails() }); // verify that we are connected Assert.NotEmpty(connectionResult.ConnectionId); // send disconnect request var disconnectResult = connectionService .Disconnect(new DisconnectParams() { OwnerUri = disconnectUri }); // verify that disconnect failed Assert.False(disconnectResult); } /// /// Verifies the the list databases operation lists database names for the server used by a connection. /// [Fact] public async Task ListDatabasesOnServerForCurrentConnectionReturnsDatabaseNames() { // Result set for the query of database names Dictionary[] data = { new Dictionary { {"name", "master" } }, new Dictionary { {"name", "model" } }, new Dictionary { {"name", "msdb" } }, new Dictionary { {"name", "tempdb" } }, new Dictionary { {"name", "mydatabase" } }, }; // Setup mock connection factory to inject query results var mockFactory = new Mock(); mockFactory.Setup(factory => factory.CreateSqlConnection(It.IsAny())) .Returns(CreateMockDbConnection(new[] {data})); var connectionService = new ConnectionService(mockFactory.Object); // connect to a database instance string ownerUri = "file://my/sample/file.sql"; var connectionResult = await connectionService .Connect(new ConnectParams() { OwnerUri = ownerUri, Connection = TestObjects.GetTestConnectionDetails() }); // verify that a valid connection id was returned Assert.NotEmpty(connectionResult.ConnectionId); // list databases for the connection ListDatabasesParams parameters = new ListDatabasesParams(); parameters.OwnerUri = ownerUri; var listDatabasesResult = connectionService.ListDatabases(parameters); string[] databaseNames = listDatabasesResult.DatabaseNames; Assert.Equal(databaseNames.Length, 5); Assert.Equal(databaseNames[0], "master"); Assert.Equal(databaseNames[1], "model"); Assert.Equal(databaseNames[2], "msdb"); Assert.Equal(databaseNames[3], "tempdb"); Assert.Equal(databaseNames[4], "mydatabase"); } /// /// Verify that the SQL parser correctly detects errors in text /// [Fact] public async Task OnConnectionCallbackHandlerTest() { bool callbackInvoked = false; // setup connection service with callback var connectionService = TestObjects.GetTestConnectionService(); connectionService.RegisterOnConnectionTask( (sqlConnection) => { callbackInvoked = true; return Task.FromResult(true); } ); // connect to a database instance var connectionResult = await connectionService.Connect(TestObjects.GetTestConnectionParams()); // verify that a valid connection id was returned Assert.True(callbackInvoked); } /// /// Test ConnectionSummaryComparer /// [Fact] public void TestConnectionSummaryComparer() { var summary1 = new ConnectionSummary() { ServerName = "localhost", DatabaseName = "master", UserName = "user" }; var summary2 = new ConnectionSummary() { ServerName = "localhost", DatabaseName = "master", UserName = "user" }; var comparer = new ConnectionSummaryComparer(); Assert.True(comparer.Equals(summary1, summary2)); summary2.DatabaseName = "tempdb"; Assert.False(comparer.Equals(summary1, summary2)); Assert.False(comparer.Equals(null, summary2)); Assert.False(summary1.GetHashCode() == summary2.GetHashCode()); } /// /// Verify when a connection is created that the URI -> Connection mapping is created in the connection service. /// [Fact] public async Task TestConnectRequestRegistersOwner() { // Given a request to connect to a database var service = TestObjects.GetTestConnectionService(); var connectParams = TestObjects.GetTestConnectionParams(); // connect to a database instance var connectionResult = await service.Connect(connectParams); // verify that a valid connection id was returned Assert.NotNull(connectionResult.ConnectionId); Assert.NotEqual(String.Empty, connectionResult.ConnectionId); Assert.NotNull(new Guid(connectionResult.ConnectionId)); // verify that the (URI -> connection) mapping was created ConnectionInfo info; Assert.True(service.TryFindConnection(connectParams.OwnerUri, out info)); } /// /// Verify that Linux/OSX SqlExceptions thrown do not contain an error code. /// This is a bug in .NET core (see https://github.com/dotnet/corefx/issues/12472). /// If this test ever fails, it means that this bug has been fixed. When this is /// the case, look at RetryPolicyUtils.cs in IsRetryableNetworkConnectivityError(), /// and remove the code block specific to Linux/OSX. /// [Fact] public void TestThatLinuxAndOSXSqlExceptionHasNoErrorCode() { TestUtils.RunIfLinuxOrOSX(() => { try { SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(); builder.DataSource = "bad-server-name"; builder.UserID = "sa"; builder.Password = "bad password"; SqlConnection connection = new SqlConnection(builder.ConnectionString); connection.Open(); // This should fail } catch (SqlException ex) { // Error code should be 0 due to bug Assert.Equal(ex.Number, 0); } }); } // /// Test that cancel connection with a null connection parameter /// [Fact] public void TestCancelConnectionNullParam() { var service = TestObjects.GetTestConnectionService(); Assert.False(service.CancelConnect(null)); } // /// Test that cancel connection with a null connection parameter /// [Fact] public void TestListDatabasesInvalidParams() { var service = TestObjects.GetTestConnectionService(); var listParams = new ListDatabasesParams(); Assert.Throws(() => service.ListDatabases(listParams)); listParams.OwnerUri = "file://notmyfile.sql"; Assert.Throws(() => service.ListDatabases(listParams)); } /// /// Test that the connection complete notification type can be created. /// [Fact] public void TestConnectionCompleteNotificationIsCreated() { Assert.NotNull(ConnectionCompleteNotification.Type); } /// /// Test that the connection summary comparer creates a hash code correctly /// [Theory] [InlineData(true, null, null ,null)] [InlineData(false, null, null, null)] [InlineData(false, null, null, "sa")] [InlineData(false, null, "test", null)] [InlineData(false, null, "test", "sa")] [InlineData(false, "server", null, null)] [InlineData(false, "server", null, "sa")] [InlineData(false, "server", "test", null)] [InlineData(false, "server", "test", "sa")] public void TestConnectionSummaryComparerHashCode(bool objectNull, string serverName, string databaseName, string userName) { // Given a connection summary and comparer object ConnectionSummary summary = null; if (!objectNull) { summary = new ConnectionSummary() { ServerName = serverName, DatabaseName = databaseName, UserName = userName }; } ConnectionSummaryComparer comparer = new ConnectionSummaryComparer(); // If I compute a hash code int hashCode = comparer.GetHashCode(summary); if (summary == null || (serverName == null && databaseName == null && userName == null)) { // Then I expect it to be 31 for a null summary Assert.Equal(31, hashCode); } else { // And not 31 otherwise Assert.NotEqual(31, hashCode); } } [Fact] public void ConnectParamsAreInvalidIfConnectionIsNull() { // Given connection parameters where the connection property is null ConnectParams parameters = new ConnectParams(); parameters.OwnerUri = "my/sql/file.sql"; parameters.Connection = null; string errorMessage; // If I check if the parameters are valid Assert.False(parameters.IsValid(out errorMessage)); // Then I expect an error message Assert.NotNull(errorMessage); Assert.NotEmpty(errorMessage); } } }