mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-14 01:25:40 -05:00
The main goal of this feature is to enable a command that will 1) Generate a parameterized command for each edit that is in the session 2) Execute that command against the server 3) Update the cached results of the table/view that's being edited with the committed changes (including computed/identity columns) There's some secret sauce in here where I cheated around worrying about gaps in the updated results. This was accomplished by implementing an IComparable for row edit objects that ensures deletes are the *last* actions to occur and that they occur from the bottom of the list up (highest row ID to lowest). Thus, all other actions that are dependent on the row ID are performed first, then the largest row ID is deleted, then next largest, etc. Nevertheless, by the end of a commit the associated ResultSet is still the source of truth. It is expected that the results grid will need updating once changes are committed. Also worth noting, although this pull request supports a "many edits, one commit" approach, it will work just fine for a "one edit, one commit" approach. * WIP * Adding basic commit support. Deletions work! * Nailing down the commit logic, insert commits work! * Updates work! * Fixing bug in DbColumnWrapper IsReadOnly setting * Comments * ResultSet unit tests, fixing issue with seeking in mock writers * Unit tests for RowCreate commands * Unit tests for RowDelete * RowUpdate unit tests * Session and edit base tests * Fixing broken unit tests * Moving constants to constants file * Addressing code review feedback * Fixes from merge issues, string consts * Removing ad-hoc code * fixing as per @abist requests * Fixing a couple more issues
221 lines
6.5 KiB
C#
221 lines
6.5 KiB
C#
//
|
|
// 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.Linq;
|
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
|
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
|
|
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
|
using Moq;
|
|
|
|
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
|
{
|
|
/// <summary>
|
|
/// Tests for the ServiceHost Connection Service tests
|
|
/// </summary>
|
|
public class TestObjects
|
|
{
|
|
|
|
public const string ScriptUri = "file://some/file.sql";
|
|
|
|
/// <summary>
|
|
/// Creates a test connection service
|
|
/// </summary>
|
|
public static ConnectionService GetTestConnectionService()
|
|
{
|
|
// use mock database connection
|
|
return new ConnectionService(new TestSqlConnectionFactory());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a test connection info instance.
|
|
/// </summary>
|
|
public static ConnectionInfo GetTestConnectionInfo()
|
|
{
|
|
return new ConnectionInfo(
|
|
new TestSqlConnectionFactory(),
|
|
ScriptUri,
|
|
GetTestConnectionDetails());
|
|
}
|
|
|
|
public static ConnectParams GetTestConnectionParams()
|
|
{
|
|
return new ConnectParams()
|
|
{
|
|
OwnerUri = ScriptUri,
|
|
Connection = GetTestConnectionDetails()
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a test connection details object
|
|
/// </summary>
|
|
public static ConnectionDetails GetTestConnectionDetails()
|
|
{
|
|
return new ConnectionDetails()
|
|
{
|
|
UserName = "user",
|
|
Password = "password",
|
|
DatabaseName = "databaseName",
|
|
ServerName = "serverName"
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a test language service instance
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static LanguageService GetTestLanguageService()
|
|
{
|
|
return new LanguageService();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates and returns a dummy TextDocumentPosition
|
|
/// </summary>
|
|
public static TextDocumentPosition GetTestDocPosition()
|
|
{
|
|
return new TextDocumentPosition
|
|
{
|
|
TextDocument = new TextDocumentIdentifier { Uri = ScriptUri },
|
|
Position = new Position
|
|
{
|
|
Line = 0,
|
|
Character = 0
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test mock class for IDbCommand
|
|
/// </summary>
|
|
public class TestSqlCommand : DbCommand
|
|
{
|
|
internal TestSqlCommand(TestResultSet[] data)
|
|
{
|
|
Data = data;
|
|
|
|
var mockParameterCollection = new Mock<DbParameterCollection>();
|
|
mockParameterCollection.Setup(c => c.Add(It.IsAny<object>()))
|
|
.Callback<object>(d => listParams.Add((DbParameter)d));
|
|
mockParameterCollection.Setup(c => c.AddRange(It.IsAny<Array>()))
|
|
.Callback<Array>(d => listParams.AddRange(d.Cast<DbParameter>()));
|
|
mockParameterCollection.Setup(c => c.Count)
|
|
.Returns(() => listParams.Count);
|
|
DbParameterCollection = mockParameterCollection.Object;
|
|
}
|
|
|
|
internal TestResultSet[] Data { get; set; }
|
|
|
|
public override void Cancel()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override int ExecuteNonQuery()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override object ExecuteScalar()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override void Prepare()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override string CommandText { get; set; }
|
|
public override int CommandTimeout { get; set; }
|
|
public override CommandType CommandType { get; set; }
|
|
public override UpdateRowSource UpdatedRowSource { get; set; }
|
|
protected override DbConnection DbConnection { get; set; }
|
|
protected override DbParameterCollection DbParameterCollection { get; }
|
|
protected override DbTransaction DbTransaction { get; set; }
|
|
public override bool DesignTimeVisible { get; set; }
|
|
|
|
protected override DbParameter CreateDbParameter()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
|
|
{
|
|
return new TestDbDataReader(Data);
|
|
}
|
|
|
|
private List<DbParameter> listParams = new List<DbParameter>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test mock class for SqlConnection wrapper
|
|
/// </summary>
|
|
public class TestSqlConnection : DbConnection
|
|
{
|
|
internal TestSqlConnection(TestResultSet[] data)
|
|
{
|
|
Data = data;
|
|
}
|
|
|
|
internal TestResultSet[] Data { get; set; }
|
|
|
|
protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override void Close()
|
|
{
|
|
// No Op
|
|
}
|
|
|
|
public override void Open()
|
|
{
|
|
// No Op, unless credentials are bad
|
|
if(ConnectionString.Contains("invalidUsername"))
|
|
{
|
|
throw new Exception("Invalid credentials provided");
|
|
}
|
|
}
|
|
|
|
public override string ConnectionString { get; set; }
|
|
public override string Database { get; }
|
|
public override ConnectionState State { get; }
|
|
public override string DataSource { get; }
|
|
public override string ServerVersion { get; }
|
|
|
|
protected override DbCommand CreateDbCommand()
|
|
{
|
|
return new TestSqlCommand(Data);
|
|
}
|
|
|
|
public override void ChangeDatabase(string databaseName)
|
|
{
|
|
// No Op
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test mock class for SqlConnection factory
|
|
/// </summary>
|
|
public class TestSqlConnectionFactory : ISqlConnectionFactory
|
|
{
|
|
public DbConnection CreateSqlConnection(string connectionString)
|
|
{
|
|
return new TestSqlConnection(null)
|
|
{
|
|
ConnectionString = connectionString
|
|
};
|
|
}
|
|
}
|
|
}
|