diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandSet.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandSet.cs index b26af7fc69..7265d82175 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandSet.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandSet.cs @@ -30,7 +30,7 @@ internal SqlCommandSet() : base() _commandList = new List(); } - private SqlCommand BatchCommand + internal SqlCommand BatchCommand { get { @@ -45,7 +45,7 @@ private SqlCommand BatchCommand internal int CommandCount => CommandList.Count; - private List CommandList + internal List CommandList { get { @@ -98,24 +98,16 @@ internal void Append(SqlCommand command) { ADP.CheckArgumentNull(command, nameof(command)); SqlClientEventSource.Log.TryTraceEvent("SqlCommandSet.Append | API | Object Id {0}, Command '{1}', Parameter Count {2}", ObjectID, command.ObjectID, command.Parameters.Count); + + // SqlCommandSet only supports commands of type Text or StoredProcedure. This aligns with SqlCommand (validated by its CommandType setter.) + Debug.Assert(command.CommandType is CommandType.Text or CommandType.StoredProcedure); + string cmdText = command.CommandText; if (string.IsNullOrEmpty(cmdText)) { throw ADP.CommandTextRequired(nameof(Append)); } - CommandType commandType = command.CommandType; - switch (commandType) - { - case CommandType.Text: - case CommandType.StoredProcedure: - break; - case CommandType.TableDirect: - throw SQL.NotSupportedCommandType(commandType); - default: - throw ADP.InvalidCommandType(commandType); - } - SqlParameterCollection parameters = null; SqlParameterCollection collection = command.Parameters; @@ -279,7 +271,7 @@ internal bool GetBatchedAffected(int commandIdentifier, out int recordsAffected, internal int GetParameterCount(int commandIndex) => CommandList[commandIndex].Parameters.Count; - private void ValidateCommandBehavior(string method, CommandBehavior behavior) + internal void ValidateCommandBehavior(string method, CommandBehavior behavior) { if (0 != (behavior & ~(CommandBehavior.SequentialAccess | CommandBehavior.CloseConnection))) { diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/LocalAppContextSwitchesTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/LocalAppContextSwitchesTests.cs deleted file mode 100644 index 170e39a322..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/LocalAppContextSwitchesTests.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Reflection; -using Xunit; - -namespace Microsoft.Data.SqlClient.Tests -{ - public class LocalAppContextSwitchesTests - { - [Theory] - [InlineData("LegacyRowVersionNullBehavior", false)] - [InlineData("SuppressInsecureTlsWarning", false)] - [InlineData("MakeReadAsyncBlocking", false)] - [InlineData("UseMinimumLoginTimeout", true)] - [InlineData("LegacyVarTimeZeroScaleBehaviour", true)] - [InlineData("UseCompatibilityProcessSni", false)] - [InlineData("UseCompatibilityAsyncBehaviour", false)] - [InlineData("UseConnectionPoolV2", false)] - #if NETFRAMEWORK - [InlineData("DisableTnirByDefault", false)] - #endif - public void DefaultSwitchValue(string property, bool expectedDefaultValue) - { - var switchesType = typeof(SqlCommand).Assembly.GetType("Microsoft.Data.SqlClient.LocalAppContextSwitches"); - - var switchValue = (bool)switchesType.GetProperty(property, BindingFlags.Public | BindingFlags.Static).GetValue(null); - - Assert.Equal(expectedDefaultValue, switchValue); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj index 56265208b4..91a5a505b9 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj @@ -30,10 +30,7 @@ - - - diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBufferTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBufferTests.cs deleted file mode 100644 index 2be8f5bd3b..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBufferTests.cs +++ /dev/null @@ -1,253 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Data.SqlTypes; -using System.Linq; -using System.Reflection; -using Xunit; - -namespace Microsoft.Data.SqlClient.Tests -{ - public sealed class SqlBufferTests - { - static SqlBufferTests() - { - const string sqlBufferTypeFullName = "Microsoft.Data.SqlClient.SqlBuffer"; - const string storageTypeName = nameof(SqlBufferProxy.StorageType); - - var assembly = typeof(SqlClientFactory).Assembly; - _sqlBufferType = assembly.GetType(sqlBufferTypeFullName) - ?? throw new Exception($"Type not found [{sqlBufferTypeFullName}]"); - _storageTypeType = _sqlBufferType.GetNestedTypes(BindingFlags.NonPublic) - .FirstOrDefault(x => x.Name == storageTypeName) - ?? throw new Exception($"Type not found [{sqlBufferTypeFullName}+{storageTypeName}]"); - } - - private static readonly Type _sqlBufferType; - private static readonly Type _storageTypeType; - private readonly SqlBufferProxy _target = new(); - - public static IEnumerable GetStorageTypeValues() - { -#if NET - return Enum.GetValues() - .Select(x => new object[] { x }); -#else - return Enum.GetValues(typeof(SqlBufferProxy.StorageType)) - .OfType() - .Select(x => new object[] { x }); -#endif - } - - [Theory] - [MemberData(nameof(GetStorageTypeValues))] - public void StorageTypeInProxyShouldHaveTheSameValueAsOriginal(SqlBufferProxy.StorageType expected) - { - var originalEnumName = Enum.GetName(_storageTypeType, (int)expected); - - Assert.Equal(expected.ToString(), originalEnumName); - } - - [Fact] - public void GuidShouldThrowWhenSqlGuidNullIsSet() - { - _target.SqlGuid = SqlGuid.Null; - - Assert.Throws(() => _target.Guid); - } - - [Theory] - [InlineData(SqlBufferProxy.StorageType.Guid)] - [InlineData(SqlBufferProxy.StorageType.SqlGuid)] - public void GuidShouldThrowWhenSetToNullOfTypeIsCalled(SqlBufferProxy.StorageType storageType) - { - _target.SetToNullOfType(storageType); - - Assert.Throws(() => _target.Guid); - } - - [Fact] - public void GuidShouldReturnWhenGuidIsSet() - { - var expected = Guid.NewGuid(); - _target.Guid = expected; - - Assert.Equal(expected, _target.Guid); - } - - [Fact] - public void GuidShouldReturnExpectedWhenSqlGuidIsSet() - { - var expected = Guid.NewGuid(); - _target.SqlGuid = expected; - - Assert.Equal(expected, _target.Guid); - } - - [Theory] - [InlineData(SqlBufferProxy.StorageType.Guid)] - [InlineData(SqlBufferProxy.StorageType.SqlGuid)] - public void SqlGuidShouldReturnSqlNullWhenSetToNullOfTypeIsCalled(SqlBufferProxy.StorageType storageType) - { - _target.SetToNullOfType(storageType); - - Assert.Equal(SqlGuid.Null, _target.SqlGuid); - } - - [Fact] - public void SqlGuidShouldReturnSqlGuidNullWhenSqlGuidNullIsSet() - { - _target.SqlGuid = SqlGuid.Null; - - Assert.Equal(SqlGuid.Null, _target.SqlGuid); - } - - [Fact] - public void SqlGuidShouldReturnExpectedWhenGuidIsSet() - { - var guid = Guid.NewGuid(); - SqlGuid expected = guid; - _target.Guid = guid; - - Assert.Equal(expected, _target.SqlGuid); - } - - [Fact] - public void SqlGuidShouldReturnExpectedWhenSqlGuidIsSet() - { - SqlGuid expected = Guid.NewGuid(); - _target.SqlGuid = expected; - - Assert.Equal(expected, _target.SqlGuid); - } - - [Fact] - public void SqlValueShouldReturnExpectedWhenGuidIsSet() - { - var guid = Guid.NewGuid(); - SqlGuid expected = guid; - _target.Guid = guid; - - Assert.Equal(expected, _target.SqlValue); - } - - [Fact] - public void SqlValueShouldReturnExpectedWhenSqlGuidIsSet() - { - SqlGuid expected = Guid.NewGuid(); - _target.SqlGuid = expected; - - Assert.Equal(expected, _target.SqlValue); - } - - public sealed class SqlBufferProxy - { - public enum StorageType - { - Empty = 0, - Boolean, - Byte, - DateTime, - Decimal, - Double, - Int16, - Int32, - Int64, - Guid, - Money, - Single, - String, - SqlBinary, - SqlCachedBuffer, - SqlGuid, - SqlXml, - Date, - DateTime2, - DateTimeOffset, - Time, - } - - private static readonly PropertyInfo _guidProperty; - private static readonly PropertyInfo _sqlGuidProperty; - private static readonly PropertyInfo _sqlValueProperty; - private static readonly MethodInfo _setToNullOfTypeMethod; - private readonly object _instance; - - static SqlBufferProxy() - { - var flags = BindingFlags.NonPublic | BindingFlags.Instance; - _guidProperty = _sqlBufferType.GetProperty(nameof(Guid), flags); - _sqlGuidProperty = _sqlBufferType.GetProperty(nameof(SqlGuid), flags); - _sqlValueProperty = _sqlBufferType.GetProperty(nameof(SqlValue), flags); - _setToNullOfTypeMethod = _sqlBufferType.GetMethod(nameof(SetToNullOfType), flags); - } - - public SqlBufferProxy() - { - _instance = Activator.CreateInstance(_sqlBufferType, true); - } - - public Guid Guid - { - get => GetPropertyValue(_guidProperty); - set => SetPropertyValue(_guidProperty, value); - } - - public SqlGuid SqlGuid - { - get => GetPropertyValue(_sqlGuidProperty); - set => SetPropertyValue(_sqlGuidProperty, value); - } - - public object SqlValue - { - get => GetPropertyValue(_sqlValueProperty); - } - - public void SetToNullOfType(StorageType storageType) - { -#if NET - _setToNullOfTypeMethod - .Invoke(_instance, BindingFlags.DoNotWrapExceptions, null, new object[] { (int)storageType }, null); -#else - _setToNullOfTypeMethod.Invoke(_instance, new object[] { (int)storageType }); -#endif - } - - private T GetPropertyValue(PropertyInfo property) - { -#if NET - return (T)property.GetValue(_instance, BindingFlags.DoNotWrapExceptions, null, null, null); -#else - try - { - return (T)property.GetValue(_instance); - } - catch (TargetInvocationException e) - { - throw e.InnerException!; - } -#endif - } - - private void SetPropertyValue(PropertyInfo property, object value) - { -#if NET - property.SetValue(_instance, value, BindingFlags.DoNotWrapExceptions, null, null, null); -#else - try - { - property.SetValue(_instance, value); - } - catch (TargetInvocationException e) - { - throw e.InnerException!; - } -#endif - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlCommandSetTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlCommandSetTest.cs deleted file mode 100644 index 7e8a3f0377..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlCommandSetTest.cs +++ /dev/null @@ -1,200 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.Reflection; -using Xunit; - -namespace Microsoft.Data.SqlClient.Tests -{ - public class SqlCommandSetTest - { - private static Assembly mds = Assembly.GetAssembly(typeof(SqlConnection)); - - [Theory] - [InlineData("BatchCommand")] - [InlineData("CommandList")] - public void GetDisposedProperty_Throws(string propertyName) - { - var cmdSet = CreateInstance(); - CallMethod(cmdSet, "Dispose"); - Exception ex = GetProperty_Throws(cmdSet, propertyName); - VerifyException(ex, "disposed"); - } - - [Fact] - public void AppendCommandWithEmptyString_Throws() - { - var cmdSet = CreateInstance(); - SqlCommand cmd = new SqlCommand(""); - Exception ex = CallMethod_Throws(cmdSet, "Append", cmd); - VerifyException(ex, "CommandText property has not been initialized"); - } - - public static IEnumerable CommandTypeData() - { - return new object[][] - { - new object[] { CommandType.TableDirect }, - new object[] { (CommandType)5 } - }; - } - - [Theory] - [MemberData( - nameof(CommandTypeData) -#if NETFRAMEWORK - // .NET Framework puts system enums in something called the Global - // Assembly Cache (GAC), and xUnit refuses to serialize enums that - // live there. So for .NET Framework, we disable enumeration of the - // test data to avoid warnings on the console when running tests. - , DisableDiscoveryEnumeration = true -#endif - )] - public void AppendBadCommandType_Throws(CommandType commandType) - { - var cmdSet = CreateInstance(); - SqlCommand cmd = GenerateBadCommand(commandType); - Exception ex = CallMethod_Throws(cmdSet, "Append", cmd); - VerifyException(ex, "CommandType"); - } - - [Fact] - public void AppendBadParameterName_Throws() - { - var cmdSet = CreateInstance(); - SqlCommand cmd = new SqlCommand("Test"); - cmd.CommandType = CommandType.Text; - cmd.Parameters.Add(new SqlParameter("Test1;=", "1")); - Exception ex = CallMethod_Throws(cmdSet, "Append", cmd); - VerifyException(ex, "not valid"); - } - - [Theory] - [InlineData(new byte[] { 1, 2, 3 })] - [InlineData(new char[] { '1', '2', '3' })] - public void AppendParameterArrayWithSize(object array) - { - var cmdSet = CreateInstance(); - SqlCommand cmd = new SqlCommand("Test"); - cmd.CommandType = CommandType.StoredProcedure; - SqlParameter parameter = new SqlParameter("@array", array); - parameter.Size = 2; - cmd.Parameters.Add(parameter); - CallMethod(cmdSet, "Append", cmd); - object p = CallMethod(cmdSet, "GetParameter", 0, 0); - SqlParameter result = p as SqlParameter; - Assert.NotNull(result); - Assert.Equal("@array", result.ParameterName); - Assert.Equal(2, result.Size); - } - - [Fact] - public void GetParameter() - { - var cmdSet = CreateInstance(); - SqlCommand cmd = new SqlCommand("Test"); - cmd.CommandType = CommandType.Text; - cmd.Parameters.Add(new SqlParameter("@text", "value")); - CallMethod(cmdSet, "Append", cmd); - object p = CallMethod(cmdSet, "GetParameter", 0, 0); - SqlParameter result = p as SqlParameter; - Assert.NotNull(result); - Assert.Equal("@text", result.ParameterName); - Assert.Equal("value", (string)result.Value); - } - - [Fact] - public void GetParameterCount() - { - var commandSetType = mds.GetType("Microsoft.Data.SqlClient.SqlCommandSet"); - var cmdSet = Activator.CreateInstance(commandSetType, true); - SqlCommand cmd = new SqlCommand("Test"); - cmd.CommandType = CommandType.Text; - cmd.Parameters.Add(new SqlParameter("@abc", "1")); - cmd.Parameters.Add(new SqlParameter("@test", "2")); - commandSetType.GetMethod("Append", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Invoke(cmdSet, new object[] { cmd }); - int index = 0; - int count = (int)commandSetType.GetMethod("GetParameterCount", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Invoke(cmdSet, new object[] { index }); - Assert.Equal(2, count); - } - - [Fact] - public void InvalidCommandBehaviorValidateCommandBehavior_Throws() - { - var cmdSet = CreateInstance(); - Exception ex = CallMethod_Throws(cmdSet, "ValidateCommandBehavior", "ExecuteNonQuery", (CommandBehavior)64); - VerifyException(ex, "CommandBehavior"); - } - - [Fact] - public void NotSupportedCommandBehaviorValidateCommandBehavior_Throws() - { - var cmdSet = CreateInstance(); - Exception ex = CallMethod_Throws(cmdSet, "ValidateCommandBehavior", "ExecuteNonQuery", CommandBehavior.KeyInfo); - VerifyException(ex, "not supported"); - } - - #region private methods - - private object CallMethod(object instance, string methodName, params object[] values) - { - var commandSetType = mds.GetType("Microsoft.Data.SqlClient.SqlCommandSet"); - object returnValue = commandSetType.GetMethod(methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Invoke(instance, values); - return returnValue; - } - - private object CallMethod(object instance, string methodName) - { - var commandSetType = mds.GetType("Microsoft.Data.SqlClient.SqlCommandSet"); - object returnValue = commandSetType.GetMethod(methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Invoke(instance, new object[] { }); - return returnValue; - } - - private Exception CallMethod_Throws(object instance, string methodName, params object[] values) - { - var commandSetType = mds.GetType("Microsoft.Data.SqlClient.SqlCommandSet"); - Exception ex = Assert.ThrowsAny(() => - { - commandSetType.GetMethod(methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Invoke(instance, values); - }); - return ex; - } - - private object CreateInstance() - { - var commandSetType = mds.GetType("Microsoft.Data.SqlClient.SqlCommandSet"); - object cmdSet = Activator.CreateInstance(commandSetType, true); - return cmdSet; - } - - private Exception GetProperty_Throws(object instance, string propertyName) - { - var commandSetType = mds.GetType("Microsoft.Data.SqlClient.SqlCommandSet"); - var cmdSet = instance; - Exception ex = Assert.ThrowsAny(() => - { - commandSetType.GetProperty(propertyName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetGetMethod(true).Invoke(cmdSet, new object[] { }); - }); - - return ex; - } - - private SqlCommand GenerateBadCommand(CommandType cType) - { - SqlCommand cmd = new SqlCommand("Test"); - Type sqlCommandType = cmd.GetType(); - // There's validation done on the CommandType property, but we need to create one that avoids the check for the test case. - sqlCommandType.GetField("_commandType", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(cmd, cType); - - return cmd; - } - - private void VerifyException(Exception ex, string contains) - { - Assert.NotNull(ex); - Assert.IsType(ex.InnerException); - Assert.Contains(contains, ex.InnerException.Message, StringComparison.OrdinalIgnoreCase); - } - #endregion - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/LocalAppContextSwitchesTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/LocalAppContextSwitchesTest.cs new file mode 100644 index 0000000000..90ee094f8f --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/LocalAppContextSwitchesTest.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace Microsoft.Data.SqlClient.UnitTests; + +/// +/// Provides unit tests for verifying the default values of all SqlClient-specific AppContext switches. +/// +public class LocalAppContextSwitchesTest +{ + /// + /// Tests the default values of every AppContext switch used by SqlClient. + /// + [Fact] + public void TestDefaultAppContextSwitchValues() + { + Assert.False(LocalAppContextSwitches.LegacyRowVersionNullBehavior); + Assert.False(LocalAppContextSwitches.SuppressInsecureTlsWarning); + Assert.False(LocalAppContextSwitches.MakeReadAsyncBlocking); + Assert.True(LocalAppContextSwitches.UseMinimumLoginTimeout); + Assert.True(LocalAppContextSwitches.LegacyVarTimeZeroScaleBehaviour); + Assert.False(LocalAppContextSwitches.UseCompatibilityProcessSni); + Assert.False(LocalAppContextSwitches.UseCompatibilityAsyncBehaviour); + Assert.False(LocalAppContextSwitches.UseConnectionPoolV2); + #if NETFRAMEWORK + Assert.False(LocalAppContextSwitches.DisableTnirByDefault); + #endif + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBufferTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBufferTest.cs new file mode 100644 index 0000000000..0b2b1fa998 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBufferTest.cs @@ -0,0 +1,148 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Data.SqlTypes; +using Xunit; + +namespace Microsoft.Data.SqlClient.UnitTests; + +/// +/// Tests that null and non-null values assigned to the SqlBuffer round-trip correctly to their CLR and their +/// their SqlTypes representations. +/// +/// +/// Several methods in this class are internal. This is because their parameters are of SqlBuffer.StorageType, +/// which is non-public. +/// +public sealed class SqlBufferTest +{ + private readonly SqlBuffer _target = new(); + + /// + /// Verifies that if a SqlBuffer is directly assigned the value of SqlGuid.Null, accessing its Guid property + /// throws a SqlNullValueException. + /// + [Fact] + public void GuidShouldThrowWhenSqlGuidNullIsSet() + { + _target.SqlGuid = SqlGuid.Null; + + Assert.Throws(() => _target.Guid); + } + + /// + /// Verifies that if a SqlBuffer is set to null of type Guid or SqlGuid, accessing its Guid property throws + /// a SqlNullValueException. + /// + [Theory] + [InlineData(SqlBuffer.StorageType.Guid)] + [InlineData(SqlBuffer.StorageType.SqlGuid)] + internal void GuidShouldThrowWhenSetToNullOfTypeIsCalled(SqlBuffer.StorageType storageType) + { + _target.SetToNullOfType(storageType); + + Assert.Throws(() => _target.Guid); + } + + /// + /// Verifies that the Guid property round-trips correctly. + /// + [Fact] + public void GuidShouldReturnWhenGuidIsSet() + { + var expected = Guid.NewGuid(); + _target.Guid = expected; + + Assert.Equal(expected, _target.Guid); + } + + /// + /// Verifies that the SqlGuid property round-trips to the Guid property correctly. + /// + [Fact] + public void GuidShouldReturnExpectedWhenSqlGuidIsSet() + { + var expected = Guid.NewGuid(); + _target.SqlGuid = expected; + + Assert.Equal(expected, _target.Guid); + } + + /// + /// Verifies that if a SqlBuffer is set to null of type Guid or SqlGuid, accessing its SqlGuid property returns + /// SqlGuid.Null. + /// + [Theory] + [InlineData(SqlBuffer.StorageType.Guid)] + [InlineData(SqlBuffer.StorageType.SqlGuid)] + internal void SqlGuidShouldReturnSqlNullWhenSetToNullOfTypeIsCalled(SqlBuffer.StorageType storageType) + { + _target.SetToNullOfType(storageType); + + Assert.Equal(SqlGuid.Null, _target.SqlGuid); + } + + /// + /// Verifies that if a SqlBuffer is directly assigned the value of SqlGuid.Null, accessing its SqlGuid property + /// returns SqlGuid.Null. + /// + [Fact] + public void SqlGuidShouldReturnSqlGuidNullWhenSqlGuidNullIsSet() + { + _target.SqlGuid = SqlGuid.Null; + + Assert.Equal(SqlGuid.Null, _target.SqlGuid); + } + + /// + /// Verifies that the Guid property round-trips to the SqlGuid property correctly. + /// + [Fact] + public void SqlGuidShouldReturnExpectedWhenGuidIsSet() + { + var guid = Guid.NewGuid(); + SqlGuid expected = guid; + _target.Guid = guid; + + Assert.Equal(expected, _target.SqlGuid); + } + + /// + /// Verifies that the SqlGuid property round-trips correctly. + /// + [Fact] + public void SqlGuidShouldReturnExpectedWhenSqlGuidIsSet() + { + SqlGuid expected = Guid.NewGuid(); + _target.SqlGuid = expected; + + Assert.Equal(expected, _target.SqlGuid); + } + + /// + /// Verifies that the Guid property round-trips to the SqlValue property correctly. + /// + [Fact] + public void SqlValueShouldReturnExpectedWhenGuidIsSet() + { + var guid = Guid.NewGuid(); + SqlGuid expected = guid; + _target.Guid = guid; + + Assert.Equal(expected, _target.SqlValue); + } + + /// + /// Verifies that the SqlGuid property round-trips to the SqlValue property correctly. + /// + [Fact] + public void SqlValueShouldReturnExpectedWhenSqlGuidIsSet() + { + SqlGuid expected = Guid.NewGuid(); + _target.SqlGuid = expected; + + Assert.Equal(expected, _target.SqlValue); + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlCommandSetTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlCommandSetTest.cs new file mode 100644 index 0000000000..fbd6d6657f --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlCommandSetTest.cs @@ -0,0 +1,137 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Data; +using Xunit; + +namespace Microsoft.Data.SqlClient.UnitTests; + +/// +/// Provides unit tests for verifying the behavior of the SqlCommandSet class. +/// +public class SqlCommandSetTest +{ + /// + /// Verifies that key properties throw an ObjectDisposedException after the SqlCommandSet has been disposed. + /// + [Fact] + public void GetDisposedProperty_Throws() + { + SqlCommandSet cmdSet = new(); + cmdSet.Dispose(); + + ObjectDisposedException ex = Assert.Throws(() => _ = cmdSet.BatchCommand); + Assert.Contains("disposed", ex.Message, StringComparison.OrdinalIgnoreCase); + + ex = Assert.Throws(() => _ = cmdSet.CommandList); + Assert.Contains("disposed", ex.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that adding a SqlCommand with an empty CommandText to a SqlCommandSet throws an InvalidOperationException. + /// + [Fact] + public void AppendCommandWithEmptyString_Throws() + { + SqlCommandSet cmdSet = new(); + using SqlCommand cmd = new(""); + + InvalidOperationException ex = Assert.Throws(() => cmdSet.Append(cmd)); + Assert.Contains("CommandText property has not been initialized", ex.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that adding a SqlCommand containing a SqlParameter with an invalid name to a SqlCommandSet throws an ArgumentException. + /// + [Fact] + public void AppendBadParameterName_Throws() + { + SqlCommandSet cmdSet = new(); + using SqlCommand cmd = new("Test"); + cmd.CommandType = CommandType.Text; + cmd.Parameters.Add(new SqlParameter("Test1;=", "1")); + + ArgumentException ex = Assert.Throws(() => cmdSet.Append(cmd)); + Assert.Contains("not valid", ex.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that a SqlParameter containing an array round-trips through a SqlCommandSet correctly. + /// + [Theory] + [InlineData(new byte[] { 1, 2, 3 })] + [InlineData(new char[] { '1', '2', '3' })] + public void AppendParameterArrayWithSize(object array) + { + SqlCommandSet cmdSet = new(); + using SqlCommand cmd = new("Test"); + cmd.CommandType = CommandType.StoredProcedure; + cmd.Parameters.Add(new SqlParameter("@array", array) { Size = 2 }); + cmdSet.Append(cmd); + SqlParameter result = cmdSet.GetParameter(0, 0); + Assert.NotNull(result); + Assert.Equal("@array", result.ParameterName); + Assert.Equal(2, result.Size); + } + + /// + /// Verifies that a SqlParameter containing a scalar value round-trips through a SqlCommandSet correctly. + /// + [Fact] + public void GetParameter() + { + SqlCommandSet cmdSet = new(); + using SqlCommand cmd = new("Test"); + cmd.CommandType = CommandType.Text; + cmd.Parameters.Add(new SqlParameter("@text", "value")); + cmdSet.Append(cmd); + SqlParameter result = cmdSet.GetParameter(0, 0); + Assert.NotNull(result); + Assert.Equal("@text", result.ParameterName); + Assert.Equal("value", (string)result.Value); + } + + /// + /// Verifies that SqlCommandSet.GetParameterCount returns the correct number of parameters for a command + /// at the correct index. + /// + [Fact] + public void GetParameterCount() + { + SqlCommandSet cmdSet = new(); + using SqlCommand cmd = new("Test"); + cmd.CommandType = CommandType.Text; + cmd.Parameters.Add(new SqlParameter("@abc", "1")); + cmd.Parameters.Add(new SqlParameter("@test", "2")); + cmdSet.Append(cmd); + int index = 0; + int count = cmdSet.GetParameterCount(index); + Assert.Equal(2, count); + } + + /// + /// Verifies that SqlCommandSet.ValidateCommandBehavior throws an ArgumentOutOfRangeException when an invalid CommandBehavior is specified. + /// + [Fact] + public void InvalidCommandBehaviorValidateCommandBehavior_Throws() + { + SqlCommandSet cmdSet = new(); + + ArgumentOutOfRangeException ex = Assert.Throws(() => cmdSet.ValidateCommandBehavior("ExecuteNonQuery", (CommandBehavior)64)); + Assert.Contains("CommandBehavior", ex.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that SqlCommandSet.ValidateCommandBehavior throws an ArgumentOutOfRangeException when a valid but unsupported CommandBehavior is specified. + /// + [Fact] + public void NotSupportedCommandBehaviorValidateCommandBehavior_Throws() + { + SqlCommandSet cmdSet = new(); + + ArgumentOutOfRangeException ex = Assert.Throws(() => cmdSet.ValidateCommandBehavior("ExecuteNonQuery", CommandBehavior.KeyInfo)); + Assert.Contains("not supported", ex.Message, StringComparison.OrdinalIgnoreCase); + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlJsonTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlJsonTest.cs index faf44a4e29..d83cedd74d 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlJsonTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlJsonTest.cs @@ -7,10 +7,9 @@ using System; using System.Data.SqlTypes; using System.Text.Json; -using Microsoft.Data.SqlTypes; using Xunit; -namespace Microsoft.Data.SqlClient.UnitTests; +namespace Microsoft.Data.SqlTypes.UnitTests; public class SqlJsonTest { diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/SqlVectorTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlVectorTest.cs similarity index 99% rename from src/Microsoft.Data.SqlClient/tests/UnitTests/SqlVectorTest.cs rename to src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlVectorTest.cs index 3390d95c02..9e6e520b63 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/SqlVectorTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlVectorTest.cs @@ -4,12 +4,12 @@ // See the LICENSE file in the project root for more information. using System; -using Microsoft.Data.SqlTypes; +using Microsoft.Data.SqlClient; using Xunit; #nullable enable -namespace Microsoft.Data.SqlClient.Tests; +namespace Microsoft.Data.SqlTypes.UnitTests; public class SqlVectorTest {