Skip to content

Commit f9f305b

Browse files
authored
Merge branch 'main' into feature/add-activity-source
2 parents 9b6e579 + 4e8040f commit f9f305b

File tree

8 files changed

+70
-9
lines changed

8 files changed

+70
-9
lines changed

psake-project.ps1

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ Task Test -Depends Merge -Description "Run unit and integration tests against me
2323
# support halting the whole execution pipeline when "dotnet test" command fails due to a failed test,
2424
# silently allowing build process to continue its execution even with failed tests.
2525
Exec { dotnet test -c Release --no-build "tests\Hangfire.Core.Tests" }
26-
Exec { dotnet test -c Release --no-build "tests\Hangfire.SqlServer.Tests" }
27-
Exec { dotnet test -c Release --no-build "tests\Hangfire.SqlServer.Msmq.Tests" }
26+
Exec { dotnet test -c Release --no-build -p:TestTfmsInParallel=false "tests\Hangfire.SqlServer.Tests" }
27+
Exec { dotnet test -c Release --no-build -p:TestTfmsInParallel=false "tests\Hangfire.SqlServer.Msmq.Tests" }
2828
}
2929

3030
Task Collect -Depends Test -Description "Copy all artifacts to the build folder." {

src/Directory.Build.props

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
<DebugType>embedded</DebugType>
88
<EmbedUntrackedSources>true</EmbedUntrackedSources>
99
<LangVersion>Latest</LangVersion>
10+
<NuGetAuditMode>direct</NuGetAuditMode>
11+
<CheckNotRecommendedTargetFramework>false</CheckNotRecommendedTargetFramework>
1012
</PropertyGroup>
1113
<ItemGroup>
1214
<Compile Include="..\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />

src/Hangfire.AspNetCore/Hangfire.AspNetCore.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<TargetFrameworks>net451;net461;netstandard1.3;netstandard2.0;netcoreapp3.0</TargetFrameworks>
44
<GenerateDocumentationFile>true</GenerateDocumentationFile>
5-
<NoWarn>1591</NoWarn>
5+
<NoWarn>$(NoWarn);1591</NoWarn>
66
<RootNamespace>Hangfire</RootNamespace>
77
</PropertyGroup>
88

src/Hangfire.Core/States/BackgroundJobStateChanger.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,10 @@ public IState ChangeState(StateChangeContext context)
160160
}
161161
}
162162

163-
transaction.Commit();
163+
if (context.Transaction == null)
164+
{
165+
transaction.Commit();
166+
}
164167

165168
context.ProcessedJob = backgroundJob;
166169
return appliedState;

src/Hangfire.Core/States/EnqueuedState.cs

+16-4
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ namespace Hangfire.States
6363
/// <threadsafety static="true" instance="false" />
6464
public class EnqueuedState : IState
6565
{
66-
private static readonly Regex ValidationRegex = new Regex(@"^[a-z0-9_-]+$", RegexOptions.Compiled | RegexOptions.CultureInvariant, TimeSpan.FromSeconds(5));
67-
6866
/// <summary>
6967
/// Represents the default queue name. This field is constant.
7068
/// </summary>
@@ -232,7 +230,7 @@ internal static bool TryValidateQueueName([NotNull] string value)
232230
throw new ArgumentNullException(nameof(value));
233231
}
234232

235-
return ValidationRegex.IsMatch(value);
233+
return ValidateQueueNameInner(value);
236234
}
237235

238236
internal static void ValidateQueueName([InvokerParameterName] string parameterName, [NotNull] string value)
@@ -242,14 +240,28 @@ internal static void ValidateQueueName([InvokerParameterName] string parameterNa
242240
throw new ArgumentNullException(parameterName);
243241
}
244242

245-
if (!ValidationRegex.IsMatch(value))
243+
if (!ValidateQueueNameInner(value))
246244
{
247245
throw new ArgumentException(
248246
$"The queue name must consist of lowercase letters, digits, underscore, and dash characters only. Given: '{value}'.",
249247
parameterName);
250248
}
251249
}
252250

251+
private static bool ValidateQueueNameInner(string value)
252+
{
253+
foreach (var ch in value)
254+
{
255+
// ^[a-z0-9_-]+$
256+
if (!((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-' || ch == '_'))
257+
{
258+
return false;
259+
}
260+
}
261+
262+
return true;
263+
}
264+
253265
internal sealed class Handler : IStateHandler
254266
{
255267
public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction)

tests/Hangfire.Core.Tests/Mocks/StateChangeContextMock.cs

+4
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ public StateChangeContextMock()
2626
() => new StateChangeContext(
2727
Storage.Object,
2828
Connection.Object,
29+
Transaction?.Object,
2930
BackgroundJobId,
3031
NewState.Object,
3132
ExpectedStates,
3233
DisableFilters,
34+
CompleteJob?.Object,
3335
CancellationToken,
3436
EmptyProfiler.Instance,
3537
ServerId,
@@ -38,6 +40,8 @@ public StateChangeContextMock()
3840

3941
public Mock<JobStorage> Storage { get; set; }
4042
public Mock<IStorageConnection> Connection { get; set; }
43+
public Mock<JobStorageTransaction> Transaction { get; set; }
44+
public Mock<IFetchedJob> CompleteJob { get; set; }
4145
public string BackgroundJobId { get; set; }
4246
public Mock<IState> NewState { get; set; }
4347
public IEnumerable<string> ExpectedStates { get; set; }

tests/Hangfire.Core.Tests/States/BackgroundJobStateChangerFacts.cs

+39
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,45 @@ public void ChangeState_DoesNotInvokeApplyStateFilters_WhenFiltersDisabled()
495495
_transaction.Verify(x => x.Commit());
496496
}
497497

498+
[Fact]
499+
public void ChangeState_WithTransaction_PassesTheGivenTransaction_AndDoesNotCommitTheImplicitOne()
500+
{
501+
_context.Transaction = new Mock<JobStorageTransaction>();
502+
var stateChanger = CreateStateChanger();
503+
504+
stateChanger.ChangeState(_context.Object);
505+
506+
_stateMachine.Verify(x => x.ApplyState(It.Is<ApplyStateContext>(
507+
ctx => ctx.Transaction == _context.Transaction.Object)));
508+
509+
_connection.Verify(x => x.CreateWriteTransaction(), Times.Never);
510+
_transaction.Verify(x => x.Commit(), Times.Never);
511+
}
512+
513+
[Fact]
514+
public void ChangeState_WithTransaction_DoesNotCommitAndDoesNotDisposeTheExplicitTransaction()
515+
{
516+
_context.Transaction = new Mock<JobStorageTransaction>();
517+
var stateChanger = CreateStateChanger();
518+
519+
stateChanger.ChangeState(_context.Object);
520+
521+
_context.Transaction.Verify(x => x.Commit(), Times.Never);
522+
_context.Transaction.Verify(x => x.Dispose(), Times.Never);
523+
}
524+
525+
[Fact]
526+
public void ChangeState_WithTransaction_AcquiresATransactionLevelLockInstead()
527+
{
528+
_context.Transaction = new Mock<JobStorageTransaction>();
529+
var stateChanger = CreateStateChanger();
530+
531+
stateChanger.ChangeState(_context.Object);
532+
533+
_context.Transaction.Verify(x => x.AcquireDistributedLock($"job:{JobId}:state-lock", It.IsAny<TimeSpan>()));
534+
_connection.Verify(x => x.AcquireDistributedLock(It.IsAny<string>(), It.IsAny<TimeSpan>()), Times.Never);
535+
}
536+
498537
private BackgroundJobStateChanger CreateStateChanger()
499538
{
500539
return new BackgroundJobStateChanger(_filterProvider.Object, _stateMachine.Object);

tests/Hangfire.SqlServer.Tests/SqlServerJobQueueFacts.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Data.Common;
55
using System.Linq;
66
using System.Threading;
7+
using Hangfire.Annotations;
78
using ReferencedDapper::Dapper;
89
using Xunit;
910
// ReSharper disable ArgumentsStyleLiteral
@@ -675,7 +676,7 @@ private static SqlServerJobQueue CreateJobQueue(bool useMicrosoftDataSqlClient,
675676
return new SqlServerJobQueue(storage, new SqlServerStorageOptions { SlidingInvisibilityTimeout = invisibilityTimeout });
676677
}
677678

678-
private static void UseConnection(Action<DbConnection> action, bool useMicrosoftDataSqlClient)
679+
private static void UseConnection([InstantHandle] Action<DbConnection> action, bool useMicrosoftDataSqlClient)
679680
{
680681
using (var connection = ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient))
681682
{

0 commit comments

Comments
 (0)