Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Expand Up @@ -47,10 +47,10 @@
string sql = string.Empty;
int? commandTimeoutMs = TimeoutMS.Expression is null ? (int?)null : TimeoutMS.Get(context);
DatabaseConnection existingConnection = null;
DBExecuteCommandResult affectedRecords = null;
DBExecuteCommandResult affectedRecords = new DBExecuteCommandResult();
Comment thread
viogroza marked this conversation as resolved.
if (commandTimeoutMs.HasValue && commandTimeoutMs.Value < 0)
{
throw new ArgumentException(Resources.TimeoutMSException, nameof(TimeoutMS));

Check warning on line 53 in Activities/Database/UiPath.Database.Activities/ExecuteNonQuery.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Method ExecuteAsync passes 'TimeoutMS' as the paramName argument to a ArgumentException constructor. Replace this argument with one of the method's parameter names. Note that the provided parameter name should have the exact casing as declared on the method.

See more on https://sonarcloud.io/project/issues?id=UiPath_Community.Activities&issues=AaDTXkt8Lk_ceES9ZT_G&open=AaDTXkt8Lk_ceES9ZT_G&pullRequest=600

Check warning on line 53 in Activities/Database/UiPath.Database.Activities/ExecuteNonQuery.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The parameter name 'TimeoutMS' is not declared in the argument list.

See more on https://sonarcloud.io/project/issues?id=UiPath_Community.Activities&issues=AaDTXkt8Lk_ceES9ZT_F&open=AaDTXkt8Lk_ceES9ZT_F&pullRequest=600
}
TimeSpan? commandTimeout = commandTimeoutMs.HasValue ? TimeSpan.FromMilliseconds(commandTimeoutMs.Value) : (TimeSpan?)null;
Dictionary<string, ParameterInfo> parameters = null;
Expand All @@ -66,7 +66,7 @@
parameters = ConnectionHelper.BuildParameters(Parameters, context);
ConnectionHelper.ConnectionValidation(existingConnection, connSecureString, connString, provName);
// create the action for doing the actual work
affectedRecords = await Task.Run(() => ExecuteCommand(connString, connSecureString, provName, sql, parameters, commandTimeout));
affectedRecords = await Task.Run(() => ExecuteCommand(connString, connSecureString, provName, sql, parameters, commandTimeout), cancellationToken);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -110,7 +110,7 @@
return new DBExecuteCommandResult(DbConnection.Execute(sql, parameters, commandTimeout, CommandType), parameters);
}

private class DBExecuteCommandResult
private sealed class DBExecuteCommandResult
{
public int Result { get; }
public Dictionary<string, ParameterInfo> ParametersBind { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ public void DatabaseTransaction_InternalConnection_OutputBound_IsNotDisposed()
try
{
var connVar = new Variable<DatabaseConnection>();
var capture = new CaptureConnection { Input = new InArgument<DatabaseConnection>(connVar) };
var capture = new CaptureValue<DatabaseConnection> { Input = new InArgument<DatabaseConnection>(connVar) };

var workflow = new Sequence
{
Expand Down Expand Up @@ -320,6 +320,67 @@ public void DatabaseTransaction_InternalConnection_OutputNotBound_IsDisposed()
}
}

[Fact, TestPriority(12)]
public void ExecuteNonQuery_ContinueOnError_SwallowsExceptionInsteadOfThrowingNullReferenceException()
{
var activity = new ExecuteNonQuery
{
ExistingDbConnection = new InArgument<DatabaseConnection>(_ => _fixture.Connection),
Sql = new InArgument<string>("SELECT * FROM ThisTableDoesNotExist_Probe"),
ContinueOnError = new InArgument<bool>(true),
AffectedRecords = new OutArgument<int>()
};

var outputs = WorkflowInvoker.Invoke(activity, TimeSpan.FromSeconds(30));

var affected = (int)outputs[nameof(ExecuteNonQuery.AffectedRecords)];
Assert.Equal(0, affected);
Comment thread
viogroza marked this conversation as resolved.
}

[Fact, TestPriority(13)]
public void ExecuteNonQuery_ContinueOnError_ResetsAffectedRecordsInsteadOfLeavingStaleValue()
{
// Pre-seed the bound variable with a value the activity did NOT produce, so the
// assertion can only pass if ExecuteNonQuery actively resets it to 0 on the swallowed
// failure path, not because an unbound OutArgument happens to default to 0.
var affectedVar = new Variable<int> { Default = 42 };
var capture = new CaptureValue<int> { Input = new InArgument<int>(affectedVar) };

var workflow = new Sequence
{
Variables = { affectedVar },
Activities =
{
new ExecuteNonQuery
{
ExistingDbConnection = new InArgument<DatabaseConnection>(_ => _fixture.Connection),
Sql = new InArgument<string>("SELECT * FROM ThisTableDoesNotExist_Probe"),
ContinueOnError = new InArgument<bool>(true),
AffectedRecords = new OutArgument<int>(affectedVar)
},
capture
}
};

WorkflowInvoker.Invoke(workflow, TimeSpan.FromSeconds(30));

Assert.Equal(0, capture.Value);
}

[Fact, TestPriority(14)]
public void ExecuteNonQuery_NegativeTimeoutMS_ThrowsArgumentException()
{
var activity = new ExecuteNonQuery
{
ExistingDbConnection = new InArgument<DatabaseConnection>(_ => _fixture.Connection),
Sql = new InArgument<string>("SELECT 1"),
TimeoutMS = new InArgument<int>(-1),
AffectedRecords = new OutArgument<int>()
};

Assert.Throws<ArgumentException>(() => WorkflowInvoker.Invoke(activity, TimeSpan.FromSeconds(30)));
}

private static string NewTempDbPath()
=> Path.Combine(Path.GetTempPath(), $"uipath_sqlite_txn_{Guid.NewGuid():N}.db");

Expand All @@ -343,13 +404,13 @@ private static DataTable BuildPeopleTable(params (int Id, string Name, int Age)[
}

/// <summary>
/// Reads a <see cref="DatabaseConnection"/> argument at the end of a workflow body and exposes
/// it to the test, so the connection's post-scope state can be asserted.
/// Reads an argument at the end of a workflow body and exposes it to the test, so a
/// variable's post-scope state can be asserted.
/// </summary>
private sealed class CaptureConnection : CodeActivity
private sealed class CaptureValue<T> : CodeActivity
{
public InArgument<DatabaseConnection> Input { get; set; }
public DatabaseConnection Value { get; private set; }
public InArgument<T> Input { get; set; }
public T Value { get; private set; }

protected override void Execute(CodeActivityContext context)
{
Expand Down