// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // #nullable disable using Microsoft.SqlTools.Hosting.Protocol; using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility; using Microsoft.SqlTools.ServiceLayer.Metadata; using Microsoft.SqlTools.ServiceLayer.Metadata.Contracts; using Microsoft.SqlTools.ServiceLayer.Test.Common; using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts; using Moq; using System; using System.Collections.Generic; using Microsoft.Data.SqlClient; using System.Linq; using System.Threading; using System.Threading.Tasks; using NUnit.Framework; using static Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility.LiveConnectionHelper; namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Metadata { /// /// Tests for the Metadata service component /// public class MetadataServiceTests { private string testTableSchema = "dbo"; private string testTableName = "MetadataTestTable"; private LiveConnectionHelper.TestConnectionResult GetLiveAutoCompleteTestObjects() { var textDocument = new TextDocumentPosition { TextDocument = new TextDocumentIdentifier { Uri = Test.Common.Constants.OwnerUri }, Position = new Position { Line = 0, Character = 0 } }; var result = LiveConnectionHelper.InitLiveConnectionInfo(); result.TextDocumentPosition = textDocument; return result; } private void CreateTestTable(SqlConnection sqlConn) { string sql = string.Format("IF OBJECT_ID('{0}.{1}', 'U') IS NULL CREATE TABLE {0}.{1}(id int)", this.testTableSchema, this.testTableName); using (var sqlCommand = new SqlCommand(sql, sqlConn)) { sqlCommand.ExecuteNonQuery(); } } private void DeleteTestTable(SqlConnection sqlConn) { string sql = string.Format("IF OBJECT_ID('{0}.{1}', 'U') IS NOT NULL DROP TABLE {0}.{1}", this.testTableSchema, this.testTableName); using (var sqlCommand = new SqlCommand(sql, sqlConn)) { sqlCommand.ExecuteNonQuery(); } } /// /// Verify that the metadata service correctly returns details for user tables /// [Test] public void MetadataReturnsUserTable() { this.testTableName += new Random().Next(1000000, 9999999).ToString(); var result = GetLiveAutoCompleteTestObjects(); var sqlConn = ConnectionService.OpenSqlConnection(result.ConnectionInfo); Assert.NotNull(sqlConn); CreateTestTable(sqlConn); var metadata = new List(); MetadataService.ReadMetadata(sqlConn, metadata); Assert.NotNull(metadata.Count > 0); bool foundTestTable = false; foreach (var item in metadata) { if (string.Equals(item.Schema, this.testTableSchema, StringComparison.OrdinalIgnoreCase) && string.Equals(item.Name, this.testTableName, StringComparison.OrdinalIgnoreCase)) { foundTestTable = true; break; } } Assert.True(foundTestTable); DeleteTestTable(sqlConn); } [Test] public async Task GetTableInfoReturnsValidResults() { this.testTableName += new Random().Next(1000000, 9999999).ToString(); var result = GetLiveAutoCompleteTestObjects(); var sqlConn = ConnectionService.OpenSqlConnection(result.ConnectionInfo); CreateTestTable(sqlConn); var requestContext = new Mock>(); requestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object())); var metadataParmas = new TableMetadataParams { OwnerUri = result.ConnectionInfo.OwnerUri, Schema = this.testTableSchema, ObjectName = this.testTableName }; await MetadataService.HandleGetTableRequest(metadataParmas, requestContext.Object); DeleteTestTable(sqlConn); requestContext.VerifyAll(); } [Test] public async Task GetViewInfoReturnsValidResults() { var result = GetLiveAutoCompleteTestObjects(); var requestContext = new Mock>(); requestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object())); var metadataParmas = new TableMetadataParams { OwnerUri = result.ConnectionInfo.OwnerUri, Schema = "sys", ObjectName = "all_objects" }; await MetadataService.HandleGetViewRequest(metadataParmas, requestContext.Object); requestContext.VerifyAll(); } [Test] public async Task VerifyMetadataList() { string query = @"CREATE TABLE testTable1 (c1 int) GO CREATE PROCEDURE testSp1 @StartProductID [int] AS BEGIN Select * from sys.all_columns END GO CREATE VIEW testView1 AS SELECT * from sys.all_columns GO CREATE FUNCTION testFun1() RETURNS [int] AS BEGIN RETURN 1 END GO CREATE FUNCTION [testFun2](@CityID int) RETURNS TABLE WITH SCHEMABINDING AS RETURN SELECT 1 AS AccessResult GO"; List expectedMetadataList = new List { new ObjectMetadata { MetadataType = MetadataType.Table, MetadataTypeName = "Table", Name = "testTable1", Schema = "dbo" }, new ObjectMetadata { MetadataType = MetadataType.SProc, MetadataTypeName = "StoredProcedure", Name = "testSp1", Schema = "dbo" }, new ObjectMetadata { MetadataType = MetadataType.View, MetadataTypeName = "View", Name = "testView1", Schema = "dbo" }, new ObjectMetadata { MetadataType = MetadataType.Function, MetadataTypeName = "UserDefinedFunction", Name = "testFun1", Schema = "dbo" }, new ObjectMetadata { MetadataType = MetadataType.Function, MetadataTypeName = "UserDefinedFunction", Name = "testFun2", Schema = "dbo" } }; await VerifyMetadataListCore(query, expectedMetadataList); } private async Task VerifyMetadataListCore(string query, List expectedMetadataList) { var testDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, query, "MetadataTests"); try { var requestContext = new Mock>(); requestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object())); ConnectionService connectionService = LiveConnectionHelper.GetLiveTestConnectionService(); using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile()) { //Opening a connection to db to lock the db TestConnectionResult connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync(testDb.DatabaseName, queryTempFile.FilePath, ConnectionType.Default); MetadataService service = new MetadataService(); await service.HandleMetadataListRequest(new MetadataQueryParams { OwnerUri = queryTempFile.FilePath }, requestContext.Object); Thread.Sleep(2000); await service.MetadataListTask; requestContext.Verify(x => x.SendResult(It.Is(r => VerifyResult(r, expectedMetadataList)))); connectionService.Disconnect(new ServiceLayer.Connection.Contracts.DisconnectParams { OwnerUri = queryTempFile.FilePath }); } } catch { throw; } finally { await testDb.CleanupAsync(); } } private static bool VerifyResult(MetadataQueryResult result, List expectedMetadataList) { if (expectedMetadataList == null) { return result.Metadata == null; } if(expectedMetadataList.Count != result.Metadata.Length) { return false; } foreach (ObjectMetadata expected in expectedMetadataList) { if (!result.Metadata.Any(x => x.MetadataType == expected.MetadataType && x.MetadataTypeName == expected.MetadataTypeName && x.Name == expected.Name && x.Schema == expected.Schema)) { return false; } } return true; } } }