-
Notifications
You must be signed in to change notification settings - Fork 285
MsSql Integration Tests #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
5a7a2df
MsSql Passing Integration Tests + Integration Test Framework
seantleonard 669b588
Merge branch 'main' into dev/seleonar/mssql_integrationtests
seantleonard 7ad8f7b
Added LocalDB setup to Azure Pipeline (mssql) yml config. Removed Lin…
seantleonard e673151
Added named parameter for specific JSON serialization options in HotC…
seantleonard 677bcd7
remove unneccessary "using" directives in mssql integration test clas…
seantleonard efe99a8
Revert "remove unneccessary "using" directives in mssql integration t…
seantleonard 9608b30
Remove unneccessary "using" directives in mssqlintegration tests clas…
seantleonard 1953b4b
Added dedicated mssqlintegration test config file and added config se…
seantleonard 4cdc94e
Fixed build errors resulting from extra whitespaces. (analyzer issues)
seantleonard 800fe27
Merge branch 'main' into dev/seleonar/mssql_integrationtests
seantleonard 5ec8869
Address PR comments. MsSql named classes. namedParams for nulls, upda…
seantleonard ab3e65b
address whitespace issue in build pipeline format check
seantleonard 55d273d
Updating per PR comments. Refactored Testclass to fit MSTest paradigm…
bcfc05f
Updated config for test runs as Local test explorer cannot run both s…
999029e
Remove Creds from new config. and add linux testing back
22a1452
testing Pipeline run with outputting connectionstring to console.
23af474
More test of pipeline connection to sql db
9a58d0c
Removed Console logging for pipeline testing. tests now pass linux/wi…
29af7ea
Merge branch 'main' into dev/seleonar/mssql_integrationtests
7436b8d
Update Cosmos.GraphQL.Service/Cosmos.GraphQL.Service.Tests/MsSqlTests…
seantleonard File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
45 changes: 45 additions & 0 deletions
45
Cosmos.GraphQL.Service/Cosmos.GraphQL.Service.Tests/MsSqlTests/DatabaseInteractor.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| using Cosmos.GraphQL.Service.Resolvers; | ||
|
|
||
| namespace Cosmos.GraphQL.Service.Tests.MsSql | ||
| { | ||
| /// <summary> | ||
| /// Class that provides functions to interact with a database. | ||
| /// </summary> | ||
| public class DatabaseInteractor | ||
| { | ||
| public IQueryExecutor QueryExecutor { get; private set; } | ||
|
|
||
| public DatabaseInteractor(IQueryExecutor queryExecutor) | ||
| { | ||
| QueryExecutor = queryExecutor; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Inserts data into the database. | ||
| /// </summary> | ||
| public void InsertData(string tableName, string values) | ||
| { | ||
| _ = QueryExecutor.ExecuteQueryAsync($"INSERT INTO {tableName} VALUES({values});", null).Result; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Creates a table in the database with provided name and columns | ||
| /// </summary> | ||
| public void CreateTable(string tableName, string columns) | ||
| { | ||
| _ = QueryExecutor.ExecuteQueryAsync($"CREATE TABLE {tableName} ({columns});", null).Result; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Drops all tables in the database | ||
| /// </summary> | ||
| public void DropTable(string tableName) | ||
| { | ||
| // Drops all tables in the database. | ||
| string dropTable = string.Format( | ||
| @"DROP TABLE IF EXISTS {0};", tableName); | ||
|
|
||
| _ = QueryExecutor.ExecuteQueryAsync(sqltext: dropTable, parameters: null).Result; | ||
| } | ||
| } | ||
| } |
180 changes: 180 additions & 0 deletions
180
Cosmos.GraphQL.Service/Cosmos.GraphQL.Service.Tests/MsSqlTests/MsSqlQueryTests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,180 @@ | ||
| using System.Data.Common; | ||
| using System.IO; | ||
| using System.Text; | ||
| using System.Text.Json; | ||
| using System.Threading.Tasks; | ||
| using Cosmos.GraphQL.Service.Controllers; | ||
| using Cosmos.GraphQL.Service.Resolvers; | ||
| using Cosmos.GraphQL.Services; | ||
| using Microsoft.AspNetCore.Http; | ||
| using Microsoft.Data.SqlClient; | ||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
|
|
||
| namespace Cosmos.GraphQL.Service.Tests.MsSql | ||
| { | ||
| /// <summary> | ||
| /// Test GraphQL Queries validating proper resolver/engine operation. | ||
| /// </summary> | ||
| [TestClass, TestCategory(TestCategory.MsSql)] | ||
| public class MsSqlQueryTests | ||
| { | ||
| #region Test Fixture Setup | ||
| private static IMetadataStoreProvider _metadataStoreProvider; | ||
| private static IQueryExecutor _queryExecutor; | ||
| private static IQueryBuilder _queryBuilder; | ||
| private static IQueryEngine _queryEngine; | ||
| private static GraphQLService _graphQLService; | ||
| private static GraphQLController _graphQLController; | ||
| private static DatabaseInteractor _databaseInteractor; | ||
|
|
||
| public static string IntegrationTableName { get; } = "character"; | ||
|
|
||
| /// <summary> | ||
| /// Sets up test fixture for class, only to be run once per test run, as defined by | ||
| /// MSTest decorator. | ||
| /// </summary> | ||
| /// <param name="context"></param> | ||
| [ClassInitialize] | ||
| public static void InitializeTestFixure(TestContext context) | ||
| { | ||
| // Setup Schema and Resolvers | ||
| // | ||
| _metadataStoreProvider = new MetadataStoreProviderForTest(); | ||
| _metadataStoreProvider.StoreGraphQLSchema(MsSqlTestHelper.GraphQLSchema); | ||
| _metadataStoreProvider.StoreQueryResolver(MsSqlTestHelper.GetQueryResolverJson(MsSqlTestHelper.CharacterByIdResolver)); | ||
| _metadataStoreProvider.StoreQueryResolver(MsSqlTestHelper.GetQueryResolverJson(MsSqlTestHelper.CharacterListResolver)); | ||
|
|
||
| // Setup Database Components | ||
| // | ||
| _queryExecutor = new QueryExecutor<SqlConnection>(MsSqlTestHelper.DataGatewayConfig); | ||
| _queryBuilder = new MsSqlQueryBuilder(); | ||
| _queryEngine = new SqlQueryEngine(_metadataStoreProvider, _queryExecutor, _queryBuilder); | ||
|
|
||
| // Setup Integration DB Components | ||
| // | ||
| _databaseInteractor = new DatabaseInteractor(_queryExecutor); | ||
| CreateTable(); | ||
| InsertData(); | ||
|
|
||
| // Setup GraphQL Components | ||
| // | ||
| _graphQLService = new GraphQLService(_queryEngine, mutationEngine: null, _metadataStoreProvider); | ||
| _graphQLController = new GraphQLController(logger: null, _queryEngine, mutationEngine: null, _graphQLService); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Cleans up querying table used for Tests in this class. Only to be run once at | ||
| /// conclusion of test run, as defined by MSTest decorator. | ||
| /// </summary> | ||
| [ClassCleanup] | ||
| public static void CleanupTestFixture() | ||
| { | ||
| _databaseInteractor.DropTable(IntegrationTableName); | ||
| } | ||
| #endregion | ||
| #region Tests | ||
| /// <summary> | ||
| /// Get result of quering singular object | ||
| /// </summary> | ||
| /// <returns></returns> | ||
| [TestMethod] | ||
| public async Task SingleResultQuery() | ||
| { | ||
| string graphQLQueryName = "characterById"; | ||
| string graphQLQuery = "{\"query\":\"{\\n characterById(id:2){\\n name\\n primaryFunction\\n}\\n}\\n\"}"; | ||
| string msSqlQuery = $"SELECT name, primaryFunction FROM { IntegrationTableName} WHERE id = 2 FOR JSON PATH, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER"; | ||
|
|
||
| string actual = await getGraphQLResultAsync(graphQLQuery, graphQLQueryName); | ||
| string expected = await getDatabaseResultAsync(msSqlQuery); | ||
|
|
||
| Assert.AreEqual(actual, expected); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets array of results for querying more than one item. | ||
| /// </summary> | ||
| /// <returns></returns> | ||
| [TestMethod] | ||
| public async Task MultipleResultQuery() | ||
| { | ||
| string graphQLQueryName = "characterList"; | ||
| string graphQLQuery = "{\"query\":\"{\\n characterList {\\n name\\n primaryFunction\\n }\\n}\\n\"}"; | ||
| string msSqlQuery = $"SELECT name, primaryFunction FROM character FOR JSON PATH, INCLUDE_NULL_VALUES"; | ||
|
|
||
| string actual = await getGraphQLResultAsync(graphQLQuery, graphQLQueryName); | ||
| string expected = await getDatabaseResultAsync(msSqlQuery); | ||
|
|
||
| Assert.AreEqual(actual, expected); | ||
| } | ||
| #endregion | ||
| #region Query Test Helper Functions | ||
| /// <summary> | ||
| /// Sends graphQL query through graphQL service, consisting of gql engine processing (resolvers, object serialization) | ||
| /// returning JSON formatted result from 'data' property. | ||
| /// </summary> | ||
| /// <param name="graphQLQuery"></param> | ||
| /// <param name="graphQLQueryName"></param> | ||
| /// <returns>string in JSON format</returns> | ||
| public async Task<string> getGraphQLResultAsync(string graphQLQuery, string graphQLQueryName) | ||
| { | ||
| _graphQLController.ControllerContext.HttpContext = GetHttpContextWithBody(graphQLQuery); | ||
| JsonDocument graphQLResult = await _graphQLController.PostAsync(); | ||
| JsonElement graphQLResultData = graphQLResult.RootElement.GetProperty("data").GetProperty(graphQLQueryName); | ||
| return graphQLResultData.ToString(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Sends raw SQL query to database engine to retrieve expected result in JSON format | ||
| /// </summary> | ||
| /// <param name="queryText">raw database query</param> | ||
| /// <returns>string in JSON format</returns> | ||
| public async Task<string> getDatabaseResultAsync(string queryText) | ||
| { | ||
| JsonDocument sqlResult = JsonDocument.Parse("{ }"); | ||
| using DbDataReader reader = _databaseInteractor.QueryExecutor.ExecuteQueryAsync(queryText, parameters: null).Result; | ||
|
|
||
| if (await reader.ReadAsync()) | ||
| { | ||
| sqlResult = JsonDocument.Parse(reader.GetString(0)); | ||
| } | ||
|
|
||
| JsonElement sqlResultData = sqlResult.RootElement; | ||
|
|
||
| return sqlResultData.ToString(); | ||
| } | ||
| #endregion | ||
| #region Helper Functions | ||
| /// <summary> | ||
| /// Creates a default table | ||
| /// </summary> | ||
| private static void CreateTable() | ||
| { | ||
| _databaseInteractor.CreateTable(IntegrationTableName, "id int, name varchar(20), type varchar(20), homePlanet int, primaryFunction varchar(20)"); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Inserts some default data into the table | ||
| /// </summary> | ||
| private static void InsertData() | ||
| { | ||
| _databaseInteractor.InsertData(IntegrationTableName, "'1', 'Mace', 'Jedi','1','Master'"); | ||
| _databaseInteractor.InsertData(IntegrationTableName, "'2', 'Plo Koon', 'Jedi','2','Master'"); | ||
| _databaseInteractor.InsertData(IntegrationTableName, "'3', 'Yoda', 'Jedi','3','Master'"); | ||
| } | ||
| /// <summary> | ||
| /// returns httpcontext with body consisting of GraphQLQuery | ||
| /// </summary> | ||
| /// <param name="data">GraphQLQuery</param> | ||
| /// <returns>The http context with given data as stream of utf-8 bytes.</returns> | ||
| private DefaultHttpContext GetHttpContextWithBody(string data) | ||
| { | ||
| var stream = new MemoryStream(Encoding.UTF8.GetBytes(data)); | ||
| var httpContext = new DefaultHttpContext() | ||
| { | ||
| Request = { Body = stream, ContentLength = stream.Length } | ||
| }; | ||
| return httpContext; | ||
| } | ||
| #endregion | ||
| } | ||
| } | ||
69 changes: 69 additions & 0 deletions
69
Cosmos.GraphQL.Service/Cosmos.GraphQL.Service.Tests/MsSqlTests/MsSqlTestHelper.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| using Cosmos.GraphQL.Service.configurations; | ||
| using Cosmos.GraphQL.Service.Models; | ||
| using Microsoft.Extensions.Configuration; | ||
| using Microsoft.Extensions.Options; | ||
| using Newtonsoft.Json; | ||
| using System; | ||
| using System.IO; | ||
|
|
||
| namespace Cosmos.GraphQL.Service.Tests.MsSql | ||
| { | ||
| /// <summary> | ||
| /// Helper functions for setting up test scenarios | ||
| /// </summary> | ||
| public class MsSqlTestHelper | ||
| { | ||
| public static readonly string GraphQLSchema = @" | ||
| type Query { | ||
| characterList: [Character] | ||
| characterById (id : ID!): Character | ||
| } | ||
| type Character { | ||
| id : ID, | ||
| name : String, | ||
| type: String, | ||
| homePlanet: Int, | ||
| primaryFunction: String | ||
| } | ||
| "; | ||
|
|
||
| public static readonly string CharacterListResolver = "{\r\n \"id\": \"characterList\",\r\n \"parametrizedQuery\": \"SELECT id, name, type, homePlanet, primaryFunction FROM character\"\r\n }"; | ||
| public static readonly string CharacterByIdResolver = "{\r\n \"id\": \"characterById\",\r\n \"parametrizedQuery\": \"SELECT id, name, type, homePlanet, primaryFunction FROM character WHERE id = @id\"\r\n}"; | ||
| private static Lazy<IOptions<DataGatewayConfig>> _dataGatewayConfig = new Lazy<IOptions<DataGatewayConfig>>(() => MsSqlTestHelper.LoadConfig()); | ||
|
|
||
seantleonard marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /// <summary> | ||
| /// Converts Raw JSON resolver to Resolver class object | ||
| /// </summary> | ||
| /// <param name="rawResolverText">escaped JSON string</param> | ||
| /// <returns>GraphQLQueryResolver object</returns> | ||
| public static GraphQLQueryResolver GetQueryResolverJson(string rawResolverText) | ||
| { | ||
| return JsonConvert.DeserializeObject<GraphQLQueryResolver>(rawResolverText); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Sets up configuration object as defined by appsettings.ENV.json file | ||
| /// </summary> | ||
| /// <returns></returns> | ||
| private static IOptions<DataGatewayConfig> LoadConfig() | ||
| { | ||
| DataGatewayConfig datagatewayConfig = new DataGatewayConfig(); | ||
| IConfigurationRoot config = new ConfigurationBuilder() | ||
| .SetBasePath(Directory.GetCurrentDirectory()) | ||
| .AddJsonFile("appsettings.Test.json") | ||
| .Build(); | ||
|
|
||
| config.Bind(nameof(DataGatewayConfig), datagatewayConfig); | ||
|
|
||
| return Options.Create(datagatewayConfig); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns configuration value loaded from file. | ||
| /// </summary> | ||
| public static IOptions<DataGatewayConfig> DataGatewayConfig | ||
| { | ||
| get { return _dataGatewayConfig.Value; } | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
Cosmos.GraphQL.Service/Cosmos.GraphQL.Service/appsettings.MsSqlIntegrationTest.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
seantleonard marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "DataGatewayConfig": { | ||
| "DatabaseType": "MsSql", | ||
| "DatabaseConnection": { | ||
| "ConnectionString": "Server=tcp:127.0.0.1,1433;Persist Security Info=False;User ID=sa;Password=REPLACEME;MultipleActiveResultSets=False;Connection Timeout=5;" | ||
seantleonard marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.