Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
68539fd
Negative test case for undeterminitstic primary key
Jul 13, 2022
952a7ec
Formatting fix
Jul 13, 2022
61caf2a
Trying to fix build error on remote
Jul 14, 2022
2151026
Ättempt 2
Jul 14, 2022
3f36b29
Attempting to fix test
Jul 14, 2022
5c66b49
Merge branch 'main' into dev/agarwalayush/BootStrapFailureTestCase
ayush3797 Jul 14, 2022
69b179f
Using Assert exception
Jul 19, 2022
33c84ea
Merge branch 'dev/agarwalayush/BootStrapFailureTestCase' of https://g…
Jul 19, 2022
3a1a9dd
Merge with main
Jul 19, 2022
45087ab
Fixing build failures
Jul 19, 2022
2794ce3
Using more appropriate statuscode
Jul 19, 2022
7c2bd09
Using test cleanup
Jul 19, 2022
fb44efd
Passing entity key as an argument rather than having special case
Jul 20, 2022
f83a948
Merge branch 'main' into dev/agarwalayush/BootStrapFailureTestCase
ayush3797 Jul 20, 2022
6cabf86
Sync with main
Jul 20, 2022
402951e
Assert on substatuscode
Jul 21, 2022
71dbdff
Merge branch 'main' into dev/agarwalayush/BootStrapFailureTestCase
ayush3797 Jul 21, 2022
a19c59e
Update DataGateway.Service.Tests/Unittests/BootStrapFailureTest.cs
ayush3797 Jul 21, 2022
58b67d9
Adding test cases for MySql/PostgreSql
Jul 22, 2022
0882d6c
pull with remote
Jul 22, 2022
51ad300
Removing previous unittest
Jul 22, 2022
1be3292
Attempting to fix build failures
Jul 22, 2022
a657cee
Merge branch 'main' into dev/agarwalayush/BootStrapFailureTestCase
ayush3797 Jul 22, 2022
3734aa3
Test restructuring
Jul 25, 2022
6b786b9
Merge branch 'dev/agarwalayush/BootStrapFailureTestCase' of https://g…
Jul 25, 2022
f329ae4
Formatting fix
Jul 25, 2022
e7dedc3
Merge remote-tracking branch 'origin/main' into dev/agarwalayush/Boot…
Jul 25, 2022
1b12e29
Moving test file to appropriate directory
Jul 26, 2022
18d23ac
Adding comments
Jul 27, 2022
44fa2b7
Adding find test for mysql view
Jul 27, 2022
790f551
Merge with main
Jul 27, 2022
2fe2038
Null check on custom entities
Jul 27, 2022
c4992a4
Merge branch 'main' into dev/agarwalayush/BootStrapFailureTestCase
ayush3797 Jul 27, 2022
704b075
Seperating db specific logic from addentity function
Jul 28, 2022
d574c2d
Removing schema parameter
Jul 28, 2022
5113972
Fixed method add entity
Jul 28, 2022
141456e
Merge branch 'main' into dev/agarwalayush/BootStrapFailureTestCase
ayush3797 Jul 28, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using Azure.DataGateway.Service.Controllers;
using Azure.DataGateway.Service.Exceptions;
using Azure.DataGateway.Service.Services;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Azure.DataGateway.Service.Tests.SqlTests.RestBootstrapTests
{
/// <summary>
/// Test class to perform tests on MsSql, MySql, PostgreSql for REST to check if the primary key
/// can be determined for a complex composite view. In case it cannot be determined, the runtime
/// would fail during boot up.
/// </summary>
Comment thread
ayush3797 marked this conversation as resolved.
[TestClass]
public class PrimaryKeyTestsForCompositeViews : SqlTestBase
{
private static readonly string _compositeViewName = "books_authors";
private static readonly string _compositeViewQuery = $"'CREATE VIEW {_compositeViewName} as SELECT books.title, authors.name, " +
$"authors.birthdate, books.id as book_id, authors.id as author_id " +
$"FROM books INNER JOIN book_author_link ON books.id = book_author_link.book_id " +
$"INNER JOIN authors ON authors.id = book_author_link.author_id'";

/// <summary>
/// Test to validate that the runtime fails and throws an exception during bootstrap when the primary
/// key cannot be determined for a complex composite view for MsSql.
/// </summary>
/// <returns></returns>
[TestMethod, TestCategory(TestCategory.MSSQL)]
public async Task MsSqlPrimaryKeyOnComplexCompositeView()
{
// Create query to be executed on the database to add the view.
string compositeViewDbQuery = $"EXEC(" +
_compositeViewQuery +
")";

await SetupDatabaseAsync(compositeViewDbQuery, TestCategory.MSSQL, true);
Comment thread
ayush3797 marked this conversation as resolved.
}

/// <summary>
/// Test to validate that the runtime fails and throws an exception during bootstrap when the primary
/// key cannot be determined for a complex composite view for PostgreSql.
/// </summary>
/// <returns></returns>
[TestMethod, TestCategory(TestCategory.POSTGRESQL)]
public async Task PostgreSqlPrimaryKeyOnComplexCompositeView()
{
// Create query to be executed on the database to add the view.
string compositeViewDbQuery = $"DO $do$ " +
$"BEGIN " +
$"EXECUTE(" +
_compositeViewQuery +
"); " +
"END " +
"$do$";

await SetupDatabaseAsync(compositeViewDbQuery, TestCategory.POSTGRESQL, true);
Comment thread
ayush3797 marked this conversation as resolved.
}

/// <summary>
/// Test to validate that the runtime boots up successfully when the primary
/// key can be determined for a complex composite view for MySql.
/// </summary>
/// <returns></returns>
[TestMethod, TestCategory(TestCategory.MYSQL)]
public async Task MySqlPrimaryKeyOnComplexCompositeView()
{
// Create query to be executed on the database to add the view.
string compositeViewDbQuery = $"prepare stmt4 from " +
_compositeViewQuery +
";" +
"execute stmt4";
await SetupDatabaseAsync(compositeViewDbQuery, TestCategory.MYSQL, false);
Comment thread
ayush3797 marked this conversation as resolved.
}

/// <summary>
/// Helper method to setup dependencies to perform tests and setup the database,
/// i.e. add all the tables and the complex view to the database.
/// </summary>
/// <param name="compositeDbViewquery">Query to add composite view to database.</param>
/// <param name="dbEngine">The database engine. For eg. MsSql.</param>
/// <param name="isExceptionExpected">Boolean value indicating whether boot up is expected to fail.</param>
/// <returns></returns>
private static async Task SetupDatabaseAsync(string compositeDbViewquery, string dbEngine, bool isExceptionExpected)
{
// Setup dependencies
DatabaseEngine = dbEngine;
string[] customEntity = { _compositeViewName, _compositeViewName, "" };
if (isExceptionExpected)
{
DataGatewayException ex = await Assert.ThrowsExceptionAsync<DataGatewayException>(() =>
InitializeTestFixture(null, new List<string> { compositeDbViewquery }, new List<string[]> { customEntity }));
Comment thread
ayush3797 marked this conversation as resolved.
Assert.AreEqual(HttpStatusCode.ServiceUnavailable, ex.StatusCode);
Assert.AreEqual($"Primary key not configured on the given database object {_compositeViewName}", ex.Message);
Assert.AreEqual(DataGatewayException.SubStatusCodes.ErrorInInitialization, ex.SubStatusCode);
}
else
{
await InitializeTestFixture(null, new List<string> { compositeDbViewquery },
Comment thread
ayush3797 marked this conversation as resolved.
new List<string[]> { customEntity });

// Perform a GET operation on the view to confirm that it is functional.
// Set up rest controller.
RestService _restService = new(_queryEngine,
_mutationEngine,
_sqlMetadataProvider,
_httpContextAccessor.Object,
_authorizationService.Object,
_authorizationResolver,
_runtimeConfigProvider);
RestController _restController = new(_restService);

// Query to validate the GET operation result.
string query = @"
SELECT JSON_OBJECT( 'title', title, 'name', name, 'birthdate',
birthdate, 'book_id', book_id, 'author_id', author_id) AS data
FROM (
SELECT *
FROM " + _compositeViewName + @"
WHERE book_id = 1 AND author_id = 123
) AS subq";

// Perform GET operation on the view.
await SetupAndRunRestApiTest(
primaryKeyRoute: "book_id/1/author_id/123",
queryString: string.Empty,
entity: _compositeViewName,
sqlQuery: query,
controller: _restController
);
Comment thread
ayush3797 marked this conversation as resolved.
}
}

/// <summary>
/// Runs after every test to reset the database state
/// </summary>
[TestCleanup]
public async Task TestCleanup()
{
string dropViewQuery = $"DROP VIEW IF EXISTS {_compositeViewName}";
await _queryExecutor.ExecuteQueryAsync(dropViewQuery, parameters: null);
}
}
}
58 changes: 56 additions & 2 deletions DataGateway.Service.Tests/SqlTests/SqlTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,30 @@ public abstract class SqlTestBase
/// this class.
/// </summary>
/// <param name="context"></param>
protected static async Task InitializeTestFixture(TestContext context)
/// <param name="customQueries">Test specific queries to be executed on database.</param>
/// <param name="customEntities">Test specific entities to be added to database.</param>
/// <returns></returns>
protected static async Task InitializeTestFixture(TestContext context, List<string> customQueries = null,
List<string[]> customEntities = null)
{
RuntimeConfigPath configPath = TestHelper.GetRuntimeConfigPath($"{DatabaseEngine}");
Mock<ILogger<RuntimeConfigProvider>> configProviderLogger = new();
RuntimeConfigProvider.ConfigProviderLogger = configProviderLogger.Object;
RuntimeConfigProvider.LoadRuntimeConfigValue(configPath, out _runtimeConfig);
TestHelper.AddMissingEntitiesToConfig(_runtimeConfig);

// Add magazines entity to the
if (TestCategory.MYSQL.Equals(DatabaseEngine))
{
TestHelper.AddMissingEntitiesToConfig(_runtimeConfig, "Magazine", "magazines");
}
else
{
TestHelper.AddMissingEntitiesToConfig(_runtimeConfig, "Magazine", "foo.magazines");
}

// Add custom entities for the test, if any.
AddCustomEntities(customEntities);

_runtimeConfigProvider = TestHelper.GetRuntimeConfigProvider(_runtimeConfig);

SetUpSQLMetadataProvider();
Expand All @@ -89,6 +106,10 @@ protected static async Task InitializeTestFixture(TestContext context)
_httpContextAccessor.Setup(x => x.HttpContext.User).Returns(new ClaimsPrincipal());

await ResetDbStateAsync();

//Execute additional queries, if any.
Comment thread
ayush3797 marked this conversation as resolved.
await ExecuteQueriesOnDbAsync(customQueries);

await _sqlMetadataProvider.InitializeAsync();

// sets the database name using the connection string
Expand Down Expand Up @@ -138,6 +159,39 @@ protected static async Task InitializeTestFixture(TestContext context)
HttpClient = _application.CreateClient();
}

/// <summary>
/// Helper method to add test specific entities to the entity mapping.
/// </summary>
/// <param name="customEntities">List of test specific entities.</param>
private static void AddCustomEntities(List<string[]> customEntities)
{
if (customEntities is not null)
{
foreach (string[] customEntity in customEntities)
{
string objectKey = customEntity[0];
string objectName = customEntity[1];
TestHelper.AddMissingEntitiesToConfig(_runtimeConfig, objectKey, objectName);
}
}
}

/// <summary>
/// Helper method to execute all the additional queries for a test on the database.
/// </summary>
/// <param name="customQueries"></param>
/// <returns></returns>
private static async Task ExecuteQueriesOnDbAsync(List<string> customQueries)
{
if (customQueries is not null)
{
foreach (string query in customQueries)
{
await _queryExecutor.ExecuteQueryAsync(query, parameters: null);
}
}
}

/// <summary>
/// Sets the database name based on the provided connection string.
/// If connection string has no database set, we set the default based on the db type.
Expand Down
31 changes: 15 additions & 16 deletions DataGateway.Service.Tests/TestHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Text.Json.Serialization;
using Azure.DataGateway.Config;
using Azure.DataGateway.Service.Configurations;
using Azure.DataGateway.Service.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
Expand Down Expand Up @@ -111,20 +112,18 @@ public static RuntimeConfig GetRuntimeConfig(RuntimeConfigProvider configProvide

/// <summary>
/// Temporary Helper function to ensure that in testing we have an entity
/// that can have a custom schema. We create a new entity of 'Magazine' with
/// a schema of 'foo' for table 'magazines', and then add this entity to our
/// runtime configuration. Because MySql will not have a schema we need a way
/// to customize this entity, which this helper function provides. Ultimately
/// this will be replaced with a JSON string in the tests that can be fully
/// customized for testing purposes.
/// that can have a custom schema. Ultimately this will be replaced with a JSON string
/// in the tests that can be fully customized for testing purposes.
/// </summary>
/// <param name="configPath"></param>
public static void AddMissingEntitiesToConfig(RuntimeConfig config)
/// <param name="config">Runtimeconfig object</param>
/// <param name="dbObjectKey">The key with which the entity is to be added.</param>
/// <param name="dbObjectName">The source name of the entity.</param>
Comment thread
ayush3797 marked this conversation as resolved.
public static void AddMissingEntitiesToConfig(RuntimeConfig config, string dbObjectKey, string dbObjectName)
{
string magazineSource = config.DatabaseType is DatabaseType.mysql ? "\"magazines\"" : "\"foo.magazines\"";
string magazineEntityJsonString =
@"{
""source"": " + magazineSource + @",
string source = "\"" + dbObjectName + "\"";
string entityJsonString =
@"{
""source"": " + source + @",
""graphql"": true,
""permissions"": [
{
Expand All @@ -133,8 +132,8 @@ public static void AddMissingEntitiesToConfig(RuntimeConfig config)
},
{
""role"": ""authenticated"",
""actions"": [ ""create"", ""update"", ""read"", ""delete"" ]
}
""actions"": [" + $" \"{ActionType.CREATE}\", \"{ActionType.READ}\", \"{ActionType.DELETE}\", \"{ActionType.UPDATE}\" ]" +
@"}
]
}";

Expand All @@ -147,8 +146,8 @@ public static void AddMissingEntitiesToConfig(RuntimeConfig config)
}
};

Entity magazineEntity = JsonSerializer.Deserialize<Entity>(magazineEntityJsonString, options);
config.Entities.Add("Magazine", magazineEntity);
Entity entity = JsonSerializer.Deserialize<Entity>(entityJsonString, options);
config.Entities.Add(dbObjectKey, entity);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ private async Task PopulateTableDefinitionAsync(
{
throw new DataGatewayException(
message: $"Primary key not configured on the given database object {tableName}",
statusCode: System.Net.HttpStatusCode.NotImplemented,
statusCode: System.Net.HttpStatusCode.ServiceUnavailable,
subStatusCode: DataGatewayException.SubStatusCodes.ErrorInInitialization);
}

Expand Down