forked from ddrilling/AsbCloudServer
Тесты фоновых задач
This commit is contained in:
parent
4767335900
commit
a2fd900cf1
@ -1,113 +0,0 @@
|
||||
using AsbCloudInfrastructure.Background;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NSubstitute;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.Tests.Services;
|
||||
|
||||
public class BackgroundWorkerTest
|
||||
{
|
||||
private IServiceProvider provider;
|
||||
private BackgroundWorker service;
|
||||
|
||||
public BackgroundWorkerTest()
|
||||
{
|
||||
provider = Substitute.For<IServiceProvider, ISupportRequiredService>();
|
||||
var serviceScope = Substitute.For<IServiceScope>();
|
||||
var serviceScopeFactory = Substitute.For<IServiceScopeFactory>();
|
||||
serviceScopeFactory.CreateScope().Returns(serviceScope);
|
||||
((ISupportRequiredService)provider).GetRequiredService(typeof(IServiceScopeFactory)).Returns(serviceScopeFactory);
|
||||
|
||||
service = new BackgroundWorker(provider);
|
||||
typeof(BackgroundWorker)
|
||||
.GetField("minDelay", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.SetValue(service, TimeSpan.FromMilliseconds(1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Enqueue_n_works()
|
||||
{
|
||||
var workCount = 10;
|
||||
var result = 0;
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
result++;
|
||||
return Task.Delay(1);
|
||||
}
|
||||
|
||||
//act
|
||||
for (int i = 0; i < workCount; i++)
|
||||
{
|
||||
var work = Work.CreateByDelegate(i.ToString(), workAction);
|
||||
service.Enqueue(work);
|
||||
}
|
||||
|
||||
await service.ExecuteTask;
|
||||
|
||||
//assert
|
||||
Assert.Equal(workCount, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Enqueue_continues_after_exceptions()
|
||||
{
|
||||
var expectadResult = 42;
|
||||
var result = 0;
|
||||
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
result = expectadResult;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
var goodWork = Work.CreateByDelegate("", workAction);
|
||||
|
||||
Task failAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
=> throw new Exception();
|
||||
|
||||
var badWork = Work.CreateByDelegate("", failAction);
|
||||
badWork.OnErrorAsync = (id, exception, token) => throw new Exception();
|
||||
|
||||
//act
|
||||
service.Enqueue(badWork);
|
||||
service.Enqueue(goodWork);
|
||||
|
||||
await service.ExecuteTask;
|
||||
|
||||
//assert
|
||||
Assert.Equal(expectadResult, result);
|
||||
Assert.Equal(1, service.Felled.Count);
|
||||
Assert.Equal(1, service.Done.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TryRemove()
|
||||
{
|
||||
var workCount = 5;
|
||||
var result = 0;
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
result++;
|
||||
return Task.Delay(10);
|
||||
}
|
||||
|
||||
//act
|
||||
for (int i = 0; i < workCount; i++)
|
||||
{
|
||||
var work = Work.CreateByDelegate(i.ToString(), workAction);
|
||||
service.Enqueue(work);
|
||||
}
|
||||
|
||||
var removed = service.TryRemoveFromQueue((workCount - 1).ToString());
|
||||
|
||||
await service.ExecuteTask;
|
||||
|
||||
//assert
|
||||
Assert.True(removed);
|
||||
Assert.Equal(workCount - 1, result);
|
||||
Assert.Equal(workCount - 1, service.Done.Count);
|
||||
}
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
using AsbCloudInfrastructure.Background;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NSubstitute;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.Tests.Services
|
||||
{
|
||||
public class WorkTest
|
||||
{
|
||||
private IServiceProvider provider;
|
||||
|
||||
public WorkTest()
|
||||
{
|
||||
provider = Substitute.For<IServiceProvider, ISupportRequiredService>();
|
||||
var serviceScope = Substitute.For<IServiceScope>();
|
||||
var serviceScopeFactory = Substitute.For<IServiceScopeFactory>();
|
||||
serviceScopeFactory.CreateScope().Returns(serviceScope);
|
||||
((ISupportRequiredService)provider).GetRequiredService(typeof(IServiceScopeFactory)).Returns(serviceScopeFactory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Work_done_with_success()
|
||||
{
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
=> Task.CompletedTask;
|
||||
|
||||
var work = Work.CreateByDelegate("", workAction);
|
||||
|
||||
//act
|
||||
var begin = DateTime.Now;
|
||||
await work.Start(provider, CancellationToken.None);
|
||||
var done = DateTime.Now;
|
||||
var executionTime = done - begin;
|
||||
|
||||
//assert
|
||||
Assert.Equal(1, work.CountComplete);
|
||||
Assert.Equal(1, work.CountStart);
|
||||
Assert.Equal(0, work.CountErrors);
|
||||
Assert.Null(work.CurrentState);
|
||||
Assert.Null(work.LastError);
|
||||
|
||||
var lastState = work.LastComplete;
|
||||
Assert.NotNull(lastState);
|
||||
Assert.InRange(lastState.Start, begin, done - 0.5 * executionTime);
|
||||
Assert.InRange(lastState.End, done - 0.5 * executionTime, done);
|
||||
Assert.InRange(lastState.ExecutionTime, TimeSpan.Zero, executionTime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Work_calls_callback()
|
||||
{
|
||||
var expectedState = "42";
|
||||
var expectedProgress = 42d;
|
||||
|
||||
var timeout = TimeSpan.FromMilliseconds(40);
|
||||
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
callback.Invoke(expectedState, expectedProgress);
|
||||
return Task.Delay(timeout);
|
||||
}
|
||||
|
||||
var work = Work.CreateByDelegate("", workAction);
|
||||
|
||||
//act
|
||||
var begin = DateTime.Now;
|
||||
_ = work.Start(provider, CancellationToken.None);
|
||||
await Task.Delay(timeout/3);
|
||||
|
||||
//assert
|
||||
Assert.Equal(0, work.CountComplete);
|
||||
Assert.Equal(1, work.CountStart);
|
||||
Assert.Equal(0, work.CountErrors);
|
||||
Assert.NotNull(work.CurrentState);
|
||||
Assert.Null(work.LastComplete);
|
||||
Assert.Null(work.LastError);
|
||||
|
||||
var currentState = work.CurrentState;
|
||||
Assert.NotNull(currentState);
|
||||
Assert.InRange(currentState.Start, begin, begin + timeout);
|
||||
Assert.InRange(currentState.StateUpdate, begin, begin + timeout);
|
||||
Assert.Equal(expectedState, currentState.State);
|
||||
Assert.Equal(expectedProgress, currentState.Progress);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Work_fails_with_info()
|
||||
{
|
||||
var expectedState = "41";
|
||||
var expectedErrorText = "42";
|
||||
var minWorkTime = TimeSpan.FromMilliseconds(10);
|
||||
|
||||
async Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
await Task.Delay(minWorkTime);
|
||||
callback(expectedState, 0);
|
||||
throw new Exception(expectedErrorText);
|
||||
}
|
||||
|
||||
var work = Work.CreateByDelegate("", workAction);
|
||||
|
||||
//act
|
||||
var begin = DateTime.Now;
|
||||
await work.Start(provider, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.Equal(0, work.CountComplete);
|
||||
Assert.Equal(1, work.CountStart);
|
||||
Assert.Equal(1, work.CountErrors);
|
||||
Assert.Null(work.CurrentState);
|
||||
Assert.Null(work.LastComplete);
|
||||
|
||||
var error = work.LastError;
|
||||
Assert.NotNull(error);
|
||||
Assert.InRange(error.Start, begin, DateTime.Now);
|
||||
Assert.InRange(error.End, begin, DateTime.Now);
|
||||
Assert.InRange(error.ExecutionTime, minWorkTime, DateTime.Now - begin);
|
||||
Assert.Contains(expectedErrorText, error.ErrorText, StringComparison.InvariantCultureIgnoreCase);
|
||||
Assert.Equal(expectedState, error.State);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Stop()
|
||||
{
|
||||
var workTime = TimeSpan.FromMilliseconds(1_000);
|
||||
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
=> Task.Delay(workTime, token);
|
||||
|
||||
var work = Work.CreateByDelegate("", workAction);
|
||||
|
||||
//act
|
||||
var begin = DateTime.Now;
|
||||
_ = work.Start(provider, CancellationToken.None);
|
||||
await Task.Delay(10);
|
||||
work.Stop();
|
||||
await Task.Delay(10);
|
||||
|
||||
//assert
|
||||
Assert.Equal(0, work.CountComplete);
|
||||
Assert.Equal(1, work.CountStart);
|
||||
Assert.Equal(0, work.CountErrors);
|
||||
Assert.Null(work.LastComplete);
|
||||
Assert.Null(work.LastError);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AsbCloudInfrastructure.Background;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.Tests.UnitTests.Background;
|
||||
|
||||
public class BackgroundWorkerTest
|
||||
{
|
||||
private readonly IServiceProvider serviceProviderMock = Substitute.For<IServiceProvider, ISupportRequiredService>();
|
||||
private readonly IServiceScope serviceScopeMock = Substitute.For<IServiceScope>();
|
||||
private readonly IServiceScopeFactory serviceScopeFactoryMock = Substitute.For<IServiceScopeFactory>();
|
||||
|
||||
private readonly BackgroundWorker backgroundWorker;
|
||||
|
||||
public BackgroundWorkerTest()
|
||||
{
|
||||
serviceScopeFactoryMock.CreateScope().Returns(serviceScopeMock);
|
||||
((ISupportRequiredService)serviceProviderMock).GetRequiredService(typeof(IServiceScopeFactory)).Returns(serviceScopeFactoryMock);
|
||||
|
||||
backgroundWorker = new BackgroundWorker(serviceProviderMock);
|
||||
typeof(BackgroundWorker)
|
||||
.GetField("minDelay", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
?.SetValue(backgroundWorker, TimeSpan.FromMilliseconds(1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Enqueue_ShouldReturn_WorkCount()
|
||||
{
|
||||
//arrange
|
||||
const int workCount = 10;
|
||||
var result = 0;
|
||||
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
result++;
|
||||
return Task.Delay(1);
|
||||
}
|
||||
|
||||
//act
|
||||
for (int i = 0; i < workCount; i++)
|
||||
{
|
||||
var work = Work.CreateByDelegate(i.ToString(), workAction);
|
||||
backgroundWorker.Enqueue(work);
|
||||
}
|
||||
|
||||
await backgroundWorker.ExecuteTask;
|
||||
|
||||
//assert
|
||||
Assert.Equal(workCount, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Enqueue_Continues_AfterExceptions()
|
||||
{
|
||||
//arrange
|
||||
const int expectadResult = 42;
|
||||
var result = 0;
|
||||
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
result = expectadResult;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var goodWork = Work.CreateByDelegate("", workAction);
|
||||
|
||||
Task failAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
=> throw new Exception();
|
||||
|
||||
var badWork = Work.CreateByDelegate("", failAction);
|
||||
badWork.OnErrorAsync = (id, exception, token) => throw new Exception();
|
||||
|
||||
//act
|
||||
backgroundWorker.Enqueue(badWork);
|
||||
backgroundWorker.Enqueue(goodWork);
|
||||
|
||||
await backgroundWorker.ExecuteTask;
|
||||
|
||||
//assert
|
||||
Assert.Equal(expectadResult, result);
|
||||
Assert.Equal(1, backgroundWorker.Felled.Count);
|
||||
Assert.Equal(1, backgroundWorker.Done.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TryRemoveFromQueue_ShouldReturn_True()
|
||||
{
|
||||
//arrange
|
||||
const int workCount = 5;
|
||||
var result = 0;
|
||||
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
result++;
|
||||
return Task.Delay(10);
|
||||
}
|
||||
|
||||
//act
|
||||
for (int i = 0; i < workCount; i++)
|
||||
{
|
||||
var work = Work.CreateByDelegate(i.ToString(), workAction);
|
||||
backgroundWorker.Enqueue(work);
|
||||
}
|
||||
|
||||
var removed = backgroundWorker.TryRemoveFromQueue((workCount - 1).ToString());
|
||||
|
||||
await backgroundWorker.ExecuteTask;
|
||||
|
||||
//assert
|
||||
Assert.True(removed);
|
||||
Assert.Equal(workCount - 1, result);
|
||||
Assert.Equal(workCount - 1, backgroundWorker.Done.Count);
|
||||
}
|
||||
}
|
@ -1,16 +1,16 @@
|
||||
using AsbCloudInfrastructure.Background;
|
||||
using DocumentFormat.OpenXml.Drawing.Charts;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NSubstitute;
|
||||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AsbCloudInfrastructure.Background;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.Tests.Services;
|
||||
namespace AsbCloudWebApi.Tests.UnitTests.Background;
|
||||
|
||||
//TODO: нужно поправить тесты, иногда они не проходят
|
||||
public class PeriodicBackgroundWorkerTest
|
||||
{
|
||||
private IServiceProvider provider;
|
||||
@ -35,10 +35,10 @@ public class PeriodicBackgroundWorkerTest
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WorkRunsTwice()
|
||||
public async Task WorkRunsTwice_ShouldReturn_WorkCount()
|
||||
{
|
||||
var workCount = 2;
|
||||
var periodMs = 100d;
|
||||
const int workCount = 2;
|
||||
const double periodMs = 100d;
|
||||
|
||||
var period = TimeSpan.FromMilliseconds(periodMs);
|
||||
|
||||
@ -64,7 +64,7 @@ public class PeriodicBackgroundWorkerTest
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task Enqueue_continues_after_exceptions()
|
||||
public async Task Enqueue_Continues_AfterExceptions()
|
||||
{
|
||||
var expectadResult = 42;
|
||||
var result = 0;
|
152
AsbCloudWebApi.Tests/UnitTests/Background/WorkTest.cs
Normal file
152
AsbCloudWebApi.Tests/UnitTests/Background/WorkTest.cs
Normal file
@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AsbCloudInfrastructure.Background;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.Tests.UnitTests.Background;
|
||||
|
||||
public class WorkTest
|
||||
{
|
||||
private readonly IServiceProvider serviceProviderMock = Substitute.For<IServiceProvider, ISupportRequiredService>();
|
||||
private readonly IServiceScope serviceScopeMock = Substitute.For<IServiceScope>();
|
||||
private readonly IServiceScopeFactory serviceScopeFactoryMock = Substitute.For<IServiceScopeFactory>();
|
||||
|
||||
public WorkTest()
|
||||
{
|
||||
serviceScopeFactoryMock.CreateScope().Returns(serviceScopeMock);
|
||||
((ISupportRequiredService)serviceProviderMock).GetRequiredService(typeof(IServiceScopeFactory)).Returns(serviceScopeFactoryMock);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Start_ShouldReturn_Success()
|
||||
{
|
||||
//arrange
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
=> Task.CompletedTask;
|
||||
|
||||
var work = Work.CreateByDelegate("", workAction);
|
||||
|
||||
//act
|
||||
var begin = DateTime.Now;
|
||||
await work.Start(serviceProviderMock, CancellationToken.None);
|
||||
var done = DateTime.Now;
|
||||
var executionTime = done - begin;
|
||||
|
||||
//assert
|
||||
Assert.Equal(1, work.CountComplete);
|
||||
Assert.Equal(1, work.CountStart);
|
||||
Assert.Equal(0, work.CountErrors);
|
||||
Assert.Null(work.CurrentState);
|
||||
Assert.Null(work.LastError);
|
||||
|
||||
var lastState = work.LastComplete;
|
||||
Assert.NotNull(lastState);
|
||||
Assert.InRange(lastState.Start, begin, done - 0.5 * executionTime);
|
||||
Assert.InRange(lastState.End, done - 0.5 * executionTime, done);
|
||||
Assert.InRange(lastState.ExecutionTime, TimeSpan.Zero, executionTime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExecutionWork_Invokes_Callback()
|
||||
{
|
||||
//arrange
|
||||
const string expectedState = "42";
|
||||
const double expectedProgress = 42d;
|
||||
|
||||
var timeout = TimeSpan.FromMilliseconds(40);
|
||||
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
callback.Invoke(expectedState, expectedProgress);
|
||||
return Task.Delay(timeout);
|
||||
}
|
||||
|
||||
var work = Work.CreateByDelegate("", workAction);
|
||||
|
||||
//act
|
||||
var begin = DateTime.Now;
|
||||
_ = work.Start(serviceProviderMock, CancellationToken.None);
|
||||
await Task.Delay(timeout / 3);
|
||||
|
||||
//assert
|
||||
Assert.Equal(0, work.CountComplete);
|
||||
Assert.Equal(1, work.CountStart);
|
||||
Assert.Equal(0, work.CountErrors);
|
||||
Assert.NotNull(work.CurrentState);
|
||||
Assert.Null(work.LastComplete);
|
||||
Assert.Null(work.LastError);
|
||||
|
||||
var currentState = work.CurrentState;
|
||||
Assert.NotNull(currentState);
|
||||
Assert.InRange(currentState.Start, begin, begin + timeout);
|
||||
Assert.InRange(currentState.StateUpdate, begin, begin + timeout);
|
||||
Assert.Equal(expectedState, currentState.State);
|
||||
Assert.Equal(expectedProgress, currentState.Progress);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExecutionWork_ShouldReturn_FailsWithInfo()
|
||||
{
|
||||
//arrange
|
||||
const string expectedState = "41";
|
||||
const string expectedErrorText = "42";
|
||||
var minWorkTime = TimeSpan.FromMilliseconds(10);
|
||||
|
||||
async Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
{
|
||||
await Task.Delay(minWorkTime);
|
||||
callback(expectedState, 0);
|
||||
throw new Exception(expectedErrorText);
|
||||
}
|
||||
|
||||
var work = Work.CreateByDelegate("", workAction);
|
||||
|
||||
//act
|
||||
var begin = DateTime.Now;
|
||||
await work.Start(serviceProviderMock, CancellationToken.None);
|
||||
|
||||
//assert
|
||||
Assert.Equal(0, work.CountComplete);
|
||||
Assert.Equal(1, work.CountStart);
|
||||
Assert.Equal(1, work.CountErrors);
|
||||
Assert.Null(work.CurrentState);
|
||||
Assert.Null(work.LastComplete);
|
||||
|
||||
var error = work.LastError;
|
||||
Assert.NotNull(error);
|
||||
Assert.InRange(error.Start, begin, DateTime.Now);
|
||||
Assert.InRange(error.End, begin, DateTime.Now);
|
||||
Assert.InRange(error.ExecutionTime, minWorkTime, DateTime.Now - begin);
|
||||
Assert.Contains(expectedErrorText, error.ErrorText, StringComparison.InvariantCultureIgnoreCase);
|
||||
Assert.Equal(expectedState, error.State);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Stop_ShouldReturn_Success()
|
||||
{
|
||||
//arrange
|
||||
var workTime = TimeSpan.FromMilliseconds(1_000);
|
||||
|
||||
Task workAction(string id, IServiceProvider services, Action<string, double?> callback, CancellationToken token)
|
||||
=> Task.Delay(workTime, token);
|
||||
|
||||
var work = Work.CreateByDelegate("", workAction);
|
||||
|
||||
//act
|
||||
var begin = DateTime.Now;
|
||||
_ = work.Start(serviceProviderMock, CancellationToken.None);
|
||||
await Task.Delay(10);
|
||||
work.Stop();
|
||||
await Task.Delay(10);
|
||||
|
||||
//assert
|
||||
Assert.Equal(0, work.CountComplete);
|
||||
Assert.Equal(1, work.CountStart);
|
||||
Assert.Equal(0, work.CountErrors);
|
||||
Assert.Null(work.LastComplete);
|
||||
Assert.Null(work.LastError);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user