Files
2026-03-24 15:26:03 -05:00

990 lines
35 KiB
C#

using LCU.Api.Internal.Dispatcher.Configurations;
using LCU.Api.Internal.Dispatcher.Services;
using LCU.Api.Internal.DispatcherLib.Repositories;
using LCU.Api.Internal.DispatcherSharedLib.Utilities;
using LCU.Library.Internal.DispatcherModels.Enums;
using LCU.Library.Internal.DispatcherModels.Models.Requests;
using LCU.Library.Internal.DispatcherModels.Models.Results;
using NCrontab;
using System.Collections.Generic;
namespace LCU.Api.Internal.DispatcherTests.ServiceTests;
public class JobServiceTest
{
private readonly JobService _jobService;
private readonly Mock<IUnitOfWork> _mockUnitOfWork;
private readonly Mock<IJobRepository> _mockJobRepository;
private readonly Mock<IWorkItemService> _mockWorkItemService;
private readonly Mock<IWorkItemExecutionService> _mockWorkItemExecutionService;
private readonly ApiConfiguration _apiConfiguration;
public JobServiceTest()
{
_mockJobRepository = new Mock<IJobRepository>();
_mockWorkItemService = new Mock<IWorkItemService>();
_mockWorkItemExecutionService = new Mock<IWorkItemExecutionService>();
_mockUnitOfWork = new Mock<IUnitOfWork>();
_apiConfiguration = new ApiConfiguration()
{
NumberOfWorkItemExecutions = 10,
NumberOfExecutionMessages = 20,
ProcessingLimitInMinutes = 30,
JobStatusesToMonitorByTimeLimit = new List<JobStatus>() { JobStatus.Processing }
};
_jobService = new JobService(_mockUnitOfWork.Object,
_mockJobRepository.Object,
_mockWorkItemService.Object,
_mockWorkItemExecutionService.Object,
_apiConfiguration);
}
[Fact]
public async Task GetListAsync_Success()
{
// Arrange
_mockJobRepository.Setup(repo => repo.ListAsync(10, 20)).ReturnsAsync(new List<Job>()
{
new Job(),
new Job()
});
// Act
var result = await _jobService.GetAsync();
// Assert
Assert.NotNull(result);
Assert.Equal(2, result.Count());
}
[Fact]
public async Task GetByIdAsync_Success()
{
// Arrange
_mockJobRepository.Setup(repo => repo.FindByIdAsync(It.IsAny<int>(), 10, 20)).ReturnsAsync(new Job());
// Act
var result = await _jobService.GetByIdAsync(1);
// Assert
Assert.NotNull(result);
}
[Fact]
public async Task SaveAsync_Success()
{
// Arrange
_mockJobRepository.Setup(repo => repo.AddAsync(It.IsAny<Job>())).ReturnsAsync(new Job());
// Act
var result = await _jobService.SaveAsync(new Job());
// Assert
Assert.NotNull(result);
_mockJobRepository.Verify(x => x.AddAsync(It.IsAny<Job>()), Times.Once);
_mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once);
}
[Fact]
public async Task UpdateAsync_Success()
{
// Arrange
var jobToUpdate = new Job()
{
JobId = 1,
RequestingApplication = "App"
};
_mockJobRepository.Setup(repo => repo.Update(It.IsAny<Job>())).ReturnsAsync(jobToUpdate).Verifiable();
// Act
var result = await _jobService.UpdateAsync(jobToUpdate);
// Assert
Assert.NotNull(result);
Assert.Equal(jobToUpdate.JobId, result.JobId);
Assert.Equal(jobToUpdate.RequestingApplication, result.RequestingApplication);
_mockJobRepository.Verify(x => x.FindByIdAsync(It.IsAny<int>(), 10, 20), Times.Never);
_mockJobRepository.Verify(x => x.Update(It.IsAny<Job>()), Times.Once);
_mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once);
}
[Fact]
public async Task CompleteJobAndWorkItem_Success_NoReschedule()
{
// Arrange
var jobToUpdate = new Job()
{
JobId = 1,
RequestingApplication = "App",
NextRunTime = Convert.ToDateTime("1/1/2022")
};
_mockJobRepository.Setup(repo => repo.FindByIdExcludingWorkItemsAsync(1)).ReturnsAsync(jobToUpdate);
var updatedJob = new Job()
{
JobId = 1,
RequestingApplication = "App",
NextRunTime = null
};
_mockJobRepository.Setup(repo => repo.Update(It.IsAny<Job>())).ReturnsAsync(updatedJob).Verifiable();
var updatedWorkitemExecution = new WorkItemExecution()
{
WorkItemExecutionId = 1,
Status = StatusType.Completed
};
_mockWorkItemExecutionService.Setup(repo => repo.UpdateCompleteWorkItemExecutionWithoutSave(It.IsAny<WorkItemExecution>(), StatusType.Completed)).ReturnsAsync(updatedWorkitemExecution);
_mockUnitOfWork.Setup(uow => uow.CompleteAsync()).Returns(Task.CompletedTask);
// Act
await _jobService.CompleteJobAndWorkItem(jobToUpdate, new WorkItemExecution(100), StatusType.Completed);
// Assert
_mockJobRepository.Verify(x => x.FindByIdExcludingWorkItemsAsync(1), Times.Never);
_mockJobRepository.Verify(x => x.Update(It.IsAny<Job>()), Times.Once);
_mockWorkItemExecutionService.Verify(x => x.UpdateCompleteWorkItemExecutionWithoutSave(It.IsAny<WorkItemExecution>(), It.IsAny<StatusType>()), Times.Once);
_mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once);
}
[Fact]
public async Task CompleteJobAndWorkItem_Success_Reschedule()
{
// Arrange
var jobToUpdate = new Job()
{
JobId = 1,
RequestingApplication = "App",
NextRunTime = Convert.ToDateTime("1/1/2022"),
CronSchedule = "5 * * * *"
};
_mockJobRepository.Setup(repo => repo.FindByIdExcludingWorkItemsAsync(1)).ReturnsAsync(jobToUpdate);
var updatedJob = new Job()
{
JobId = 1,
RequestingApplication = "App",
NextRunTime = DateTime.Now.AddSeconds(5)
};
_mockJobRepository.Setup(repo => repo.Update(It.IsAny<Job>())).ReturnsAsync(updatedJob).Verifiable();
var updatedWorkitemExecution = new WorkItemExecution()
{
WorkItemExecutionId = 1,
Status = StatusType.Completed
};
_mockWorkItemExecutionService.Setup(repo => repo.UpdateCompleteWorkItemExecutionWithoutSave(It.IsAny<WorkItemExecution>(), It.IsAny<StatusType>())).ReturnsAsync(updatedWorkitemExecution);
_mockUnitOfWork.Setup(uow => uow.CompleteAsync()).Returns(Task.CompletedTask);
// Act
await _jobService.CompleteJobAndWorkItem(jobToUpdate, new WorkItemExecution(100), StatusType.Completed);
// Assert
_mockJobRepository.Verify(x => x.FindByIdExcludingWorkItemsAsync(1), Times.Never);
_mockJobRepository.Verify(x => x.Update(It.IsAny<Job>()), Times.Once);
_mockWorkItemExecutionService.Verify(x => x.UpdateCompleteWorkItemExecutionWithoutSave(It.IsAny<WorkItemExecution>(), It.IsAny<StatusType>()), Times.Once);
_mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once);
}
[Fact]
public void HolidayHelper_Success_SkipMemorialDayHolidayNextOccurrence()
{
// Arrange
var jobToUpdate = new Job()
{
JobId = 1,
SkipHolidays = true,
RequestingApplication = "App",
CronSchedule = "0 0 1-30 5 *"
};
CrontabSchedule schedule = CrontabSchedule.Parse(jobToUpdate.CronSchedule);
var nextRunTime = schedule.GetNextOccurrence(DateTime.Now);
var memorialDay = HolidayHelpers.GetMemorialDay(nextRunTime.Year); //stage next possible holiday date, reset cron
jobToUpdate.NextRunTime = memorialDay;
jobToUpdate.CronSchedule = $"0 0 {memorialDay.Day} {memorialDay.Month} *";
// Assert
Assert.NotNull(jobToUpdate);
Assert.Equal(memorialDay, jobToUpdate.NextRunTime);
}
[Fact]
public void HolidayHelper_Success_SkipThanksGivingHolidayNextOccurrence()
{
// Arrange
var jobToUpdate = new Job()
{
JobId = 1,
SkipHolidays = true,
RequestingApplication = "App",
CronSchedule = "0 0 22-30 11 *"
};
CrontabSchedule schedule = CrontabSchedule.Parse(jobToUpdate.CronSchedule);
var nextRunTime = schedule.GetNextOccurrence(DateTime.Now);
var thanksgivingDay = HolidayHelpers.GetThanksgivingDay(nextRunTime.Year); //stage next possible holiday date, reset cron
jobToUpdate.NextRunTime = thanksgivingDay;
jobToUpdate.CronSchedule = $"0 0 {thanksgivingDay.Day} {thanksgivingDay.Month} *";
// Assert
Assert.NotNull(jobToUpdate);
Assert.Equal(thanksgivingDay, jobToUpdate.NextRunTime);
}
[Fact]
public void HolidayHelper_Success_SkipIndependenceDayHolidayNextOccurrence()
{
// Arrange
var jobToUpdate = new Job()
{
JobId = 1,
SkipHolidays = true,
RequestingApplication = "App",
CronSchedule = "0 0 4-31 7 *"
};
CrontabSchedule schedule = CrontabSchedule.Parse(jobToUpdate.CronSchedule);
var nextRunTime = schedule.GetNextOccurrence(DateTime.Now);
CronHelpers.SetNextScheduledRunTime(ref jobToUpdate);
var nextRunTimeAfterHoliday = HolidayHelpers.HandleHolidays(nextRunTime);
// Assert
Assert.NotNull(jobToUpdate);
Assert.Equal(nextRunTimeAfterHoliday, jobToUpdate.NextRunTime);
}
[Fact]
public void HolidayHelper_Success_SkipLaborDayHolidayNextOccurrence()
{
// Arrange
var jobToUpdate = new Job()
{
JobId = 1,
SkipHolidays = true,
RequestingApplication = "App",
CronSchedule = "0 0 1-30 9 *"
};
CrontabSchedule schedule = CrontabSchedule.Parse(jobToUpdate.CronSchedule);
var nextRunTime = schedule.GetNextOccurrence(DateTime.Now);
var laborDay = HolidayHelpers.GetLaborDay(nextRunTime.Year); //stage next possible holiday date, reset cron
jobToUpdate.NextRunTime = laborDay;
jobToUpdate.CronSchedule = $"0 0 {laborDay.Day} {laborDay.Month} *";
var nextRunTimeAfterHoliday = HolidayHelpers.GetLaborDay(nextRunTime.Year);
// Assert
Assert.NotNull(jobToUpdate);
Assert.Equal(nextRunTimeAfterHoliday, jobToUpdate.NextRunTime);
}
[Fact]
public void GetJobsProcessingLongerThanTime_Return_OneProcessingJob_LongerThen_30minutes()
{
// Arrange
var jobs = new List<Job>()
{
new Job()
{
Name = "Test Process Job",
NextRunTime = DateTime.Now.AddHours(-1),
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Process WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Processing,
StartedTimestamp = DateTime.Now.AddMinutes(-31)
}
}
}
},
JobStatus = JobStatus.Processing
}
};
// Act
var result = _jobService.GetJobsInStatusLongerThanTimeLimit(jobs, 30);
// Assert
Assert.NotNull(result);
Assert.Single(result);
}
[Fact]
public void GetJobsProcessingLongerThanTime_Return_NoneProcessingJob_NotLongerThen_30minutes()
{
// Arrange
var jobs = new List<Job>()
{
new Job()
{
Name = "Test Failed Job",
NextRunTime = DateTime.Now.AddHours(-1),
JobStatus = JobStatus.Failed,
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Failed WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Failed,
StartedTimestamp = DateTime.Now.AddMinutes(-10)
}
}
}
}
},
new Job()
{
Name = "Test Process Job",
NextRunTime = DateTime.Now.AddHours(-1),
JobStatus = JobStatus.Processing,
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Process WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Processing,
StartedTimestamp = DateTime.Now.AddMinutes(-29)
}
}
}
}
}
};
// Act
var result = _jobService.GetJobsInStatusLongerThanTimeLimit(jobs, 30);
// Assert
Assert.NotNull(result);
Assert.Empty(result);
}
[Fact]
public void GetJobsProcessingLongerThanTime_Return_NoneProcessingJob_NoneAreProcessing()
{
// Arrange
var jobs = new List<Job>()
{
new Job()
{
Name = "Test Failed Job",
NextRunTime = DateTime.Now.AddHours(-1),
JobStatus = JobStatus.Failed,
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Failed WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Failed,
StartedTimestamp = DateTime.Now.AddMinutes(-5)
}
}
}
}
},
new Job()
{
Name = "Test Failed 2 Job",
NextRunTime = DateTime.Now.AddHours(-1),
JobStatus = JobStatus.Processing,
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Failed 2 WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Failed,
StartedTimestamp = DateTime.Now.AddMinutes(-29)
}
}
}
}
}
};
// Act
var result = _jobService.GetJobsInStatusLongerThanTimeLimit(jobs, 30);
// Assert
Assert.NotNull(result);
Assert.Empty(result);
}
[Fact]
public void GetJobsProcessingLongerThanTime_Return_TwoJobs_MultipleStatuses()
{
// Arrange
var jobs = new List<Job>()
{
new Job()
{
Name = "Test Failed Job",
NextRunTime = DateTime.Now.AddHours(-1),
JobStatus = JobStatus.DeterminingOperation,
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Failed WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Failed,
StartedTimestamp = Convert.ToDateTime("4/1/2021")
}
}
}
}
},
new Job()
{
Name = "Test Failed 2 Job",
NextRunTime = DateTime.Now.AddHours(-1),
JobStatus = JobStatus.Processing,
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Failed 2 WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Failed,
StartedTimestamp = DateTime.Now.AddMinutes(-31)
}
}
}
}
}
};
// Act
var result = _jobService.GetJobsInStatusLongerThanTimeLimit(jobs, 30);
// Assert
Assert.NotNull(result);
Assert.Equal(2, result.Count());
}
[Fact]
public async Task GetErroredAndProcessingJobs_Return_TwoJobs_OneFailed_OneProcessingLongerThen_30minutes()
{
var jobs = new List<Job>()
{
new Job()
{
Name = "Test Pending Job",
NextRunTime = DateTime.Now.AddHours(-1),
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Pending WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Pending,
StartedTimestamp = Convert.ToDateTime("4/1/2021")
}
}
}
},
JobStatus = JobStatus.Failed
},
new Job()
{
Name = "Test Failed Job",
NextRunTime = DateTime.Now.AddHours(-1),
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Failed WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Failed,
StartedTimestamp = Convert.ToDateTime("4/1/2021")
}
}
}
},
JobStatus = JobStatus.Failed
},
new Job()
{
Name = "Test Process Job",
NextRunTime = DateTime.Now.AddHours(-1),
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Process WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Processing,
StartedTimestamp = DateTime.Now.AddMinutes(-31)
}
}
}
},
JobStatus = JobStatus.Processing
}
};
_mockJobRepository.Setup(repo => repo.FindJobsPastDateByStatuses(It.IsAny<DateTime>(), 10, 20, It.IsAny<List<JobStatus>>())).ReturnsAsync(jobs);
// Act
var result = await _jobService.GetErrorMonitoringJobs();
// Assert
Assert.NotNull(result);
Assert.Equal(3, result.ToList().Count);
Assert.Single(result.Where(r => r.CurrentStatus == StatusType.Failed));
Assert.Single(result.Where(r => r.CurrentStatus == StatusType.Processing));
}
[Fact]
public async Task GetErroredAndProcessingJobs_Return_OneFailedJob()
{
var jobs = new List<Job>()
{
new Job()
{
Name = "Test Pending Job",
NextRunTime = DateTime.Now.AddHours(-1),
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Pending WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Pending,
StartedTimestamp = DateTime.Now.AddMinutes(-5)
}
}
}
},
JobStatus = JobStatus.PendingToRun
},
new Job()
{
Name = "Test Failed Job",
NextRunTime = DateTime.Now.AddHours(-1),
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Failed WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Failed,
StartedTimestamp = Convert.ToDateTime("4/1/2021")
}
}
}
},
JobStatus = JobStatus.Failed
},
new Job()
{
Name = "Test Process Job",
NextRunTime = DateTime.Now.AddHours(-1),
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Process WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Processing,
StartedTimestamp = DateTime.Now.AddMinutes(-5)
}
}
}
},
JobStatus = JobStatus.Inactive
}
};
_mockJobRepository.Setup(repo => repo.FindJobsPastDateByStatuses(It.IsAny<DateTime>(), 10, 20, It.IsAny<List<JobStatus>>())).ReturnsAsync(jobs);
// Act
var result = await _jobService.GetErrorMonitoringJobs();
// Assert
Assert.NotNull(result);
Assert.Single(result);
Assert.Single(result.Where(r => r.CurrentStatus == StatusType.Failed));
Assert.Empty(result.Where(r => r.CurrentStatus == StatusType.Processing));
}
[Fact]
public async Task GetErroredAndProcessingJobs_Return_Empty()
{
var jobs = new List<Job>()
{
new Job()
{
Name = "Test Pending Job",
NextRunTime = DateTime.Now.AddHours(-1),
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Pending WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Pending,
StartedTimestamp = DateTime.Now.AddMinutes(-12)
}
}
}
}
},
new Job()
{
Name = "Test Process 1 Job",
NextRunTime = DateTime.Now.AddHours(-1),
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Process 1 WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Processing,
StartedTimestamp = DateTime.Now.AddMinutes(-2)
}
}
}
}
},
new Job()
{
Name = "Test Process 2 Job",
NextRunTime = DateTime.Now.AddHours(-1),
WorkItems = new List<WorkItem>() {
new WorkItem()
{
Name = "Test Process 2 WorkItem",
WorkItemExecutions = new List<WorkItemExecution>(){
new WorkItemExecution()
{
Status = StatusType.Processing,
StartedTimestamp = DateTime.Now.AddMinutes(-5)
}
}
}
}
}
};
_mockJobRepository.Setup(repo => repo.FindJobsPastDateByStatuses(It.IsAny<DateTime>(), 10, 20, It.IsAny<List<JobStatus>>())).ReturnsAsync(jobs);
// Act
var result = await _jobService.GetErrorMonitoringJobs();
// Assert
Assert.NotNull(result);
Assert.Empty(result);
}
[Fact]
public async Task FilterPaged_Ok()
{
var pagedJobResult = new PagedResult<Job>();
var filter = new Filter()
{
Page = 1,
Limit = 2,
StatusFilters = null,
SortableFields = null,
TagIdFilters = new List<int> { 1 }
};
_mockJobRepository.Setup(repo => repo.FilteredListAsync(filter, _apiConfiguration.NumberOfWorkItemExecutions, _apiConfiguration.NumberOfExecutionMessages))
.ReturnsAsync(pagedJobResult).Verifiable();
// Act
var result = await _jobService.GetFilteredJobs(filter);
// Assert
Assert.NotNull(result);
Assert.Equal(pagedJobResult, result);
}
[Fact]
public async Task DeleteAsync_Success()
{
var jobReturned = new Job
{
JobId = 1,
WorkItems = new List<WorkItem>
{
new WorkItem
{
WorkItemId = 1
},
new WorkItem
{
WorkItemId = 2
}
}
};
_mockJobRepository.Setup(repo => repo.FindByIdAsync(1, 10, 20)).ReturnsAsync(jobReturned);
// Act
var jobIdToDelete = 1;
await _jobService.DeleteAsync(jobIdToDelete);
// Assert
_mockJobRepository.Verify(service => service.FindByIdAsync(It.Is<int>(x => x == 1), It.Is<int>(x => x == 10), It.Is<int>(x => x == 20)), Times.Once);
_mockWorkItemService.Verify(service => service.DeleteList(It.Is<List<WorkItem>>(workItems => workItems.Count == 2)), Times.Never);
}
[Fact]
public async Task GetJobsReadyToBeScheduled_Return_Empty()
{
var jobs = new List<Job>();
_mockJobRepository.Setup(repo => repo.FindJobsReadyToBeScheduled(It.IsAny<DateTime>())).ReturnsAsync(jobs);
// Act
var result = await _jobService.GetJobsReadyToBeScheduled();
// Assert
Assert.NotNull(result);
Assert.Empty(result);
}
[Fact]
public async Task GetJobsReadyToBeScheduled_Returns_One()
{
var jobs = new List<Job>
{
new Job
{
JobId = 1,
}
};
_mockJobRepository.Setup(repo => repo.FindJobsReadyToBeScheduled(It.IsAny<DateTime>())).ReturnsAsync(jobs);
// Act
var result = await _jobService.GetJobsReadyToBeScheduled();
// Assert
Assert.NotNull(result);
Assert.Single(result);
}
[Fact]
public async Task GetInactiveAsync_Success()
{
// Arrange
_mockJobRepository.Setup(repo => repo.ListInactiveAsync(10, 20)).ReturnsAsync(new List<Job>()
{
new Job(),
new Job()
});
// Act
var result = await _jobService.GetInactiveAsync();
// Assert
Assert.NotNull(result);
Assert.Equal(2, result.Count());
}
[Fact]
public async Task GetSoftDeletedAsync_Success()
{
// Arrange
_mockJobRepository.Setup(repo => repo.ListSoftDeletedTrackedAsync(10, 20, It.IsAny<DateTime>())).ReturnsAsync(new List<Job>()
{
new Job(),
new Job()
});
// Act
var result = await _jobService.GetSoftDeletedTrackedAsync(DateTime.Now.AddDays(-1));
// Assert
Assert.NotNull(result);
Assert.Equal(2, result.Count());
}
[Fact]
public async Task RemoveSoftDeleteById_Success()
{
// Arrange
var existingJob = new Job()
{
JobId = 100,
SoftDeleteDate = DateTime.Now,
};
_mockJobRepository.Setup(repo => repo.FindByIdAsync(It.IsAny<int>(), 10, 20)).ReturnsAsync(existingJob);
_mockJobRepository.Setup(repo => repo.Update(It.IsAny<Job>())).Verifiable();
// Act
var result = await _jobService.RemoveSoftDeleteById(100);
// Assert
Assert.NotNull(result);
Assert.Equal(existingJob.JobId, result.JobId);
Assert.Null(result.SoftDeleteDate);
_mockJobRepository.Verify(x => x.FindByIdAsync(It.IsAny<int>(), 10, 20), Times.Once);
_mockJobRepository.Verify(x => x.Update(It.IsAny<Job>()), Times.Once);
_mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once);
}
[Fact]
public void RemoveSoftDeleteById_Throws()
{
// Arrange
_mockJobRepository.Setup(repo => repo.FindByIdAsync(It.IsAny<int>(), 10, 20)).ReturnsAsync((Job)null);
// Act/Assert
var result = Assert.ThrowsAsync<ArgumentNullException>(async () => await _jobService.RemoveSoftDeleteById(100));
}
[Fact]
public async Task GetInactiveExcludeSoftDeletedAsync_Success()
{
// Arrange
_mockJobRepository.Setup(repo => repo.ListInactiveExcludeSoftDeletedTrackedAsync(10, 20)).ReturnsAsync(new List<Job>()
{
new Job(),
new Job()
});
// Act
var result = await _jobService.GetInactiveExcludeSoftDeletedTrackedAsync();
// Assert
Assert.NotNull(result);
Assert.Equal(2, result.Count());
}
[Fact]
public async void CopyJobAsync_Success_KeepEnvironment()
{
var job = new Job
{
JobId = DateTime.Now.Millisecond,
CronSchedule = new Guid().ToString(),
NextRunTime = DateTime.Now.AddHours(1)
};
var request = new CopyJobRequest
{
JobId = job.JobId,
ChangeEnvironment = false
};
var newJob = new Job
{
JobId = DateTime.Now.Millisecond + 1
};
_mockJobRepository.Setup(x => x.FindByIdAsync(job.JobId, 10, 20))
.ReturnsAsync(job).Verifiable();
_mockJobRepository.Setup(x => x.AddAsync(It.IsAny<Job>()))
.ReturnsAsync(newJob).Verifiable();
var result = await _jobService.CopyJobAsync(request);
Assert.NotNull(result);
Assert.Null(result.CronSchedule);
Assert.Null(result.NextRunTime);
Assert.NotEqual(request.JobId, result.JobId);
Assert.Equal(newJob.JobId, result.JobId);
_mockJobRepository.Verify(x => x.FindByIdAsync(It.IsAny<int>(),
It.IsAny<int>(),
It.IsAny<int>()), Times.Once());
_mockJobRepository.Verify(x => x.AddAsync(It.IsAny<Job>()), Times.Once);
_mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once);
}
[Fact]
public async void CopyJobAsync_Success_ChangeEnvironment()
{
var job = new Job
{
JobId = DateTime.Now.Millisecond,
CronSchedule = new Guid().ToString(),
NextRunTime = DateTime.Now.AddHours(1)
};
var request = new CopyJobRequest
{
JobId = job.JobId,
ChangeEnvironment = true
};
_mockJobRepository.Setup(x => x.FindByIdAsync(job.JobId, 10, 20))
.ReturnsAsync(job).Verifiable();
var result = await _jobService.CopyJobAsync(request);
Assert.NotNull(result);
Assert.Null(result.CronSchedule);
Assert.Null(result.NextRunTime);
Assert.Equal(0, result.JobId);
_mockJobRepository.Verify(x => x.FindByIdAsync(It.IsAny<int>(),
It.IsAny<int>(),
It.IsAny<int>()), Times.Once());
_mockJobRepository.Verify(x => x.AddAsync(It.IsAny<Job>()), Times.Never);
_mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never);
}
[Fact]
public void CopyJobAsync_Failure_NotFound()
{
var request = new CopyJobRequest
{
JobId = DateTime.Now.Millisecond
};
Job? jobReturned = null;
_mockJobRepository.Setup(x => x.FindByIdAsync(request.JobId, 10, 20))
.ReturnsAsync(jobReturned).Verifiable();
Assert.ThrowsAsync<ServiceException>(async () => await _jobService.CopyJobAsync(request));
_mockJobRepository.Verify(x => x.FindByIdAsync(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>()), Times.Once);
_mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never);
}
[Fact]
public void CopyJobAsync_Failure_Exception()
{
_mockJobRepository.Setup(x => x.FindByIdAsync(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>()))
.ThrowsAsync(new Exception("Test")).Verifiable();
Assert.ThrowsAsync<ServiceException>(async () =>
await _jobService.CopyJobAsync(It.IsAny<CopyJobRequest>()));
_mockJobRepository.Verify(x => x.FindByIdAsync(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>()), Times.Never);
_mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never);
}
}