You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by cu...@apache.org on 2024/03/22 18:23:16 UTC
(arrow-adbc) branch main updated: test(csharp/test/Drivers/Interop/Snowflake): test error handling (#1592)
This is an automated email from the ASF dual-hosted git repository.
curth pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git
The following commit(s) were added to refs/heads/main by this push:
new ff866d9e2 test(csharp/test/Drivers/Interop/Snowflake): test error handling (#1592)
ff866d9e2 is described below
commit ff866d9e2a696accbff3de3157eff70b3509fa79
Author: Bruce Irschick <br...@improving.com>
AuthorDate: Fri Mar 22 11:23:11 2024 -0700
test(csharp/test/Drivers/Interop/Snowflake): test error handling (#1592)
Add tests to check for error handling of the following invalid input.
- invalid SQL syntax
- invalid column or table names
- invalid connection information (user, password)
- invalid driver information (location, entry point)
- invalid database, warehouse
---
csharp/test/Drivers/Interop/Snowflake/CastTests.cs | 11 +-
.../Interop/Snowflake/ErrorHandlingTests.cs | 284 +++++++++++++++++++++
.../Interop/Snowflake/SnowflakeTestingUtils.cs | 15 ++
3 files changed, 300 insertions(+), 10 deletions(-)
diff --git a/csharp/test/Drivers/Interop/Snowflake/CastTests.cs b/csharp/test/Drivers/Interop/Snowflake/CastTests.cs
index aa4d3bada..867db8b7a 100644
--- a/csharp/test/Drivers/Interop/Snowflake/CastTests.cs
+++ b/csharp/test/Drivers/Interop/Snowflake/CastTests.cs
@@ -276,16 +276,7 @@ namespace Apache.Arrow.Adbc.Tests.Drivers.Interop.Snowflake
string[] expectedExceptionTextContains = null)
{
Exception actualException = Assert.Throws(expectedExceptionType, () => PerformQuery(table, castFunction, castExpression));
- AssertContainsAll(expectedExceptionTextContains, actualException.Message);
- }
-
- private static void AssertContainsAll(string[] expectedTexts, string actualText)
- {
- if (expectedTexts == null) { return; };
- foreach (string text in expectedTexts)
- {
- Assert.Contains(text, actualText);
- }
+ SnowflakeTestingUtils.AssertContainsAll(expectedExceptionTextContains, actualException.Message);
}
private QueryResult PerformQuery(string table, string castFunction, string castExpression)
diff --git a/csharp/test/Drivers/Interop/Snowflake/ErrorHandlingTests.cs b/csharp/test/Drivers/Interop/Snowflake/ErrorHandlingTests.cs
new file mode 100644
index 000000000..9d4a427c5
--- /dev/null
+++ b/csharp/test/Drivers/Interop/Snowflake/ErrorHandlingTests.cs
@@ -0,0 +1,284 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Apache.Arrow.Adbc.Tests.Drivers.Interop.Snowflake
+{
+ // TODO: When supported, use prepared statements instead of SQL string literals
+ // Which will better test how the driver handles values sent/received
+
+ public class ErrorHandlingTests : IDisposable
+ {
+ private const string TestTablePrefix = "ADBCERRORSTEST_"; // Make configurable? Also; must be all caps if not double quoted
+ private const string DefaultColumnName = "COLUMN_NAME";
+ private const string DefaultColumnNameLower = "column_name";
+ readonly SnowflakeTestConfiguration _snowflakeTestConfiguration;
+ readonly AdbcConnection _connection;
+ readonly AdbcStatement _statement;
+ readonly string _catalogSchema;
+ private readonly ITestOutputHelper _output;
+ private bool _disposedValue = false;
+
+ /// <summary>
+ /// Validates that specific errors are generated as expected.
+ /// </summary>
+ public ErrorHandlingTests(ITestOutputHelper output)
+ {
+ Skip.IfNot(Utils.CanExecuteTestConfig(SnowflakeTestingUtils.SNOWFLAKE_TEST_CONFIG_VARIABLE));
+ _snowflakeTestConfiguration = SnowflakeTestingUtils.TestConfiguration;
+ Dictionary<string, string> options = new();
+ AdbcDriver snowflakeDriver = SnowflakeTestingUtils.GetSnowflakeAdbcDriver(_snowflakeTestConfiguration, out Dictionary<string, string> parameters);
+ AdbcDatabase adbcDatabase = snowflakeDriver.Open(parameters);
+ _connection = adbcDatabase.Connect(options);
+ _statement = _connection.CreateStatement();
+ _catalogSchema = string.Format("{0}.{1}", _snowflakeTestConfiguration.Metadata.Catalog, _snowflakeTestConfiguration.Metadata.Schema);
+ _output = output;
+ }
+
+ /// <summary>
+ /// Tests for invalid table and column names
+ /// </summary>
+ [SkippableTheory]
+ [InlineData("NUMERIC", "0", "INVALID_TABLE_NAME", null, new[] { "002003", "42S02" })]
+ [InlineData("NUMERIC", "0", null, "INVALID_" + DefaultColumnName, new[] { "000904", "42000" })]
+ [InlineData("NUMERIC", "0", null, "\"" + DefaultColumnNameLower + "\"", new[] { "000904", "42000" })]
+ public void TestInvalidObjectName(string columnSpecification, string sourceValue, string overrideTableName, string overrideColumnName, string[] expectedExceptionMessage = null)
+ {
+ InitializeTest(columnSpecification, sourceValue, out string columnName, out string tableName);
+ SelectAndValidateException(overrideTableName ?? tableName, overrideColumnName ?? columnName, typeof(AdbcException), expectedExceptionMessage);
+ }
+
+ /// <summary>
+ /// Tests for invalid syntax.
+ /// </summary>
+ [SkippableTheory]
+ [InlineData("NUMERIC", "0", null, DefaultColumnName + ",", new[] { "001003", "42000" })]
+ [InlineData("NUMERIC", "0", null, "," + DefaultColumnName, new[] { "001003", "42000" })]
+ [InlineData("NUMERIC", "0", null, "'" + DefaultColumnName, new[] { "001003", "42000" })]
+ [InlineData("NUMERIC", "0", null, DefaultColumnName + "'", new[] { "001003", "42000" })]
+ [InlineData("NUMERIC", "0", null, "\"" + DefaultColumnName, new[] { "001003", "42000" })]
+ [InlineData("NUMERIC", "0", null, DefaultColumnName + "\"", new[] { "001003", "42000" })]
+ public void TestInvalidSyntax(string columnSpecification, string sourceValue, string overrideTableName, string overrideColumnName, string[] expectedExceptionMessage = null)
+ {
+ InitializeTest(columnSpecification, sourceValue, out string columnName, out string tableName);
+ SelectAndValidateException(overrideTableName ?? tableName, overrideColumnName ?? columnName, typeof(AdbcException), expectedExceptionMessage);
+ }
+
+ /// <summary>
+ /// Tests for invalid driver path
+ /// </summary>
+ [SkippableFact]
+ public void TestDriverLoadInvalidPath()
+ {
+ Dictionary<string, string> parameters = new();
+ SnowflakeTestConfiguration testConfiguration = Utils.LoadTestConfiguration<SnowflakeTestConfiguration>(SnowflakeTestingUtils.SNOWFLAKE_TEST_CONFIG_VARIABLE);
+ string driverFilename = Guid.NewGuid().ToString();
+ testConfiguration.DriverPath = Path.Combine(Environment.CurrentDirectory, driverFilename + ".dll");
+ Exception actualException = Assert.Throws<ArgumentException>(() => SnowflakeTestingUtils.GetSnowflakeAdbcDriver(testConfiguration, out parameters));
+ SnowflakeTestingUtils.AssertContainsAll(new[] { "file does not exist (Parameter 'file')" }, actualException.Message);
+ }
+
+ /// <summary>
+ /// Tests for invalid driver entry point
+ /// </summary>
+ [SkippableFact]
+ public void TestDriverLoadInvalidEntryPoint()
+ {
+ Dictionary<string, string> parameters = new();
+ SnowflakeTestConfiguration testConfiguration = Utils.LoadTestConfiguration<SnowflakeTestConfiguration>(SnowflakeTestingUtils.SNOWFLAKE_TEST_CONFIG_VARIABLE);
+ string entryPoint = Guid.NewGuid().ToString();
+ testConfiguration.DriverEntryPoint = entryPoint;
+ Exception actualException = Assert.Throws<EntryPointNotFoundException>(() => SnowflakeTestingUtils.GetSnowflakeAdbcDriver(testConfiguration, out parameters));
+ SnowflakeTestingUtils.AssertContainsAll(new[] { "Unable to find an entry point named", entryPoint }, actualException.Message);
+ }
+
+ /// <summary>
+ /// Tests for various invalid connection properties.
+ /// </summary>
+ /// <param name="test">A test for an invalid configuration checking the exception type and keywords in the error message.</param>
+ [SkippableTheory]
+ [MemberData(nameof(GenerateTestConfigurationData))]
+ public void TestDriverConnectionInvalidConnection(SnowflakeTestConfigurationTest test)
+ {
+ Dictionary<string, string> options = new();
+ AdbcDriver snowflakeDriver = SnowflakeTestingUtils.GetSnowflakeAdbcDriver(
+ test.TestConfiguration as SnowflakeTestConfiguration,
+ out Dictionary<string, string> parameters);
+ AdbcDatabase adbcDatabase = snowflakeDriver.Open(parameters);
+ Exception actualException = Assert.Throws(test.ExceptionType, () => adbcDatabase.Connect(options));
+ SnowflakeTestingUtils.AssertContainsAll(test.ExceptionMessageComponents, actualException.Message);
+ }
+
+ /// <summary>
+ /// Generates test configuration for testing invalid properties in the connection.
+ /// </summary>
+ /// <returns>The list of tests.</returns>
+ public static IEnumerable<object[]> GenerateTestConfigurationData()
+ {
+ string property = Guid.NewGuid().ToString();
+ SnowflakeTestConfiguration testConfiguration;
+
+ // Note: Providing an invalid Host will hang the test.
+ //testConfiguration = Utils.LoadTestConfiguration<SnowflakeTestConfiguration>(SnowflakeTestingUtils.SNOWFLAKE_TEST_CONFIG_VARIABLE);
+ //testConfiguration.Host = property;
+ //yield return new object[] { new SnowflakeTestConfigurationTest(testConfiguration, typeof(AdbcException), ["Unknown"]) };
+
+ testConfiguration = Utils.LoadTestConfiguration<SnowflakeTestConfiguration>(SnowflakeTestingUtils.SNOWFLAKE_TEST_CONFIG_VARIABLE);
+ testConfiguration.Authentication.Default.User = property;
+ yield return new object[] { new SnowflakeTestConfigurationTest(testConfiguration, typeof(AdbcException), new[] { "[Snowflake] 390100 (08004)", "Incorrect username or password was specified" }) };
+
+ testConfiguration = Utils.LoadTestConfiguration<SnowflakeTestConfiguration>(SnowflakeTestingUtils.SNOWFLAKE_TEST_CONFIG_VARIABLE);
+ testConfiguration.Authentication.Default.Password = property;
+ yield return new object[] { new SnowflakeTestConfigurationTest(testConfiguration, typeof(AdbcException), new[] { "[Snowflake] 390100 (08004)", "Incorrect username or password was specified" }) };
+
+ testConfiguration = Utils.LoadTestConfiguration<SnowflakeTestConfiguration>(SnowflakeTestingUtils.SNOWFLAKE_TEST_CONFIG_VARIABLE);
+ testConfiguration.Database = property;
+ yield return new object[] { new SnowflakeTestConfigurationTest(testConfiguration, typeof(AdbcException), new[] { "[Snowflake] 390201 (08004)", "The requested database does not exist or not authorized." }) };
+
+ testConfiguration = Utils.LoadTestConfiguration<SnowflakeTestConfiguration>(SnowflakeTestingUtils.SNOWFLAKE_TEST_CONFIG_VARIABLE);
+ testConfiguration.Warehouse = property;
+ yield return new object[] { new SnowflakeTestConfigurationTest(testConfiguration, typeof(AdbcException), new[] { "[Snowflake] 390201 (08004)", "The requested warehouse does not exist or not authorized." }) };
+ }
+
+ /// <summary>
+ /// A test.
+ /// </summary>
+ public readonly struct SnowflakeTestConfigurationTest
+ {
+ public TestConfiguration TestConfiguration { get; }
+
+ public Type ExceptionType { get; }
+
+ public string[] ExceptionMessageComponents { get; }
+
+ /// <param name="testConfiguration">A test configuration.</param>
+ /// <param name="exceptionType">Expected exception type.</param>
+ /// <param name="exceptionMessageComponents">Expected keywords/phrases to be found in exception message.</param>
+ public SnowflakeTestConfigurationTest(
+ TestConfiguration testConfiguration,
+ Type exceptionType,
+ string[] exceptionMessageComponents)
+ {
+ TestConfiguration = testConfiguration;
+ ExceptionType = exceptionType;
+ ExceptionMessageComponents = exceptionMessageComponents;
+ }
+ }
+
+ private void SelectAndValidateException(
+ string table,
+ string projection,
+ Type expectedExceptionType,
+ string[] expectedExceptionTextContains = null)
+ {
+ Exception actualException = Assert.Throws(expectedExceptionType, () => PerformQuery(table, projection));
+ SnowflakeTestingUtils.AssertContainsAll(expectedExceptionTextContains, actualException.Message);
+ }
+
+ private QueryResult PerformQuery(string table, string projection)
+ {
+ string selectStatement = string.Format(
+ "SELECT {0} AS CASTRESULT FROM {1};",
+ projection,
+ table);
+ _output.WriteLine(selectStatement);
+ _statement.SqlQuery = selectStatement;
+ return _statement.ExecuteQuery();
+ }
+
+ private void InsertSingleValue(string table, string columnName, string value)
+ {
+ string insertNumberStatement = string.Format("INSERT INTO {0} ({1}) VALUES ({2});", table, columnName, value);
+ Console.WriteLine(insertNumberStatement);
+ _statement.SqlQuery = insertNumberStatement;
+ UpdateResult updateResult = _statement.ExecuteUpdate();
+ Assert.Equal(1, updateResult.AffectedRows);
+ }
+
+ private void InitializeTest(
+ string columnSpecification,
+ string sourceValue,
+ out string columnName,
+ out string tableName,
+ bool useSelectSyntax = false)
+ {
+ columnName = DefaultColumnName;
+ tableName = CreateTemporaryTable(
+ _statement,
+ TestTablePrefix,
+ _catalogSchema,
+ string.Format("{0} {1}", columnName, columnSpecification));
+ if (useSelectSyntax)
+ {
+ InsertIntoFromSelect(tableName, columnName, sourceValue);
+ }
+ else
+ {
+ InsertSingleValue(tableName, columnName, sourceValue);
+ }
+ }
+
+ private static string CreateTemporaryTable(
+ AdbcStatement statement,
+ string testTablePrefix,
+ string catalogSchema,
+ string columns)
+ {
+ string tableName = string.Format("{0}.{1}{2}", catalogSchema, testTablePrefix, Guid.NewGuid().ToString().Replace("-", ""));
+ string createTableStatement = string.Format("CREATE TEMPORARY TABLE {0} ({1})", tableName, columns);
+ statement.SqlQuery = createTableStatement;
+ statement.ExecuteUpdate();
+ return tableName;
+ }
+
+ private void InsertIntoFromSelect(string table, string columnName, string selectQuery, long expectedAffectedRows = 1)
+ {
+ string insertStatement = string.Format("INSERT INTO {0} ({1}) {2};", table, columnName, selectQuery);
+ _output.WriteLine(insertStatement);
+ _statement.SqlQuery = insertStatement;
+ UpdateResult updateResult = _statement.ExecuteUpdate();
+ Assert.Equal(expectedAffectedRows, updateResult.AffectedRows);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ // Protected implementation of Dispose pattern.
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
+ {
+ if (disposing)
+ {
+
+ _connection?.Dispose();
+ _statement?.Dispose();
+ }
+
+ _disposedValue = true;
+ }
+ }
+ }
+}
diff --git a/csharp/test/Drivers/Interop/Snowflake/SnowflakeTestingUtils.cs b/csharp/test/Drivers/Interop/Snowflake/SnowflakeTestingUtils.cs
index 2f394a89a..3aa57345e 100644
--- a/csharp/test/Drivers/Interop/Snowflake/SnowflakeTestingUtils.cs
+++ b/csharp/test/Drivers/Interop/Snowflake/SnowflakeTestingUtils.cs
@@ -21,6 +21,7 @@ using System.IO;
using System.Linq;
using System.Text;
using Apache.Arrow.Adbc.Drivers.Interop.Snowflake;
+using Xunit;
namespace Apache.Arrow.Adbc.Tests.Drivers.Interop.Snowflake
{
@@ -165,5 +166,19 @@ namespace Apache.Arrow.Adbc.Tests.Drivers.Interop.Snowflake
return queries;
}
+
+ /// <summary>
+ /// Assert that all expected texts in collection appear in the value.
+ /// </summary>
+ /// <param name="expectedTexts"></param>
+ /// <param name="value"></param>
+ internal static void AssertContainsAll(string[] expectedTexts, string value)
+ {
+ if (expectedTexts == null) { return; };
+ foreach (string text in expectedTexts)
+ {
+ Assert.Contains(text, value);
+ }
+ }
}
}