363 lines
13 KiB
C#
363 lines
13 KiB
C#
using System;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using AutoMapper;
|
|
using Moq;
|
|
using NUnit.Framework;
|
|
using Strata.Analytics.Biz.DbContexts;
|
|
using Strata.Analytics.Biz.Extracts.Models;
|
|
using Strata.Analytics.Biz.Extracts.Services;
|
|
using Strata.Analytics.Biz.Hangfire;
|
|
using Strata.Analytics.Biz.Utils;
|
|
using Strata.Analytics.Test.Unit.Biz.TestData;
|
|
using Strata.CoreLib.Claims;
|
|
using Strata.CoreLib.Claims.Extensions;
|
|
using Strata.Testing.Categories;
|
|
|
|
namespace Strata.Analytics.Test.Unit.Biz.Extracts
|
|
{
|
|
[Category(TestCategory.Unit)]
|
|
[Parallelizable(ParallelScope.None)]
|
|
public class ExtractScheduleTests
|
|
{
|
|
private IClaimsPrincipalAccessor _claimsPrincipalAccessor;
|
|
private AnalyticsDbContext _dbContext;
|
|
private IMapper _mapper;
|
|
private IExtractScheduleService _scheduleService;
|
|
private Mock<IRecurringJobService> _recurringJobService;
|
|
private string _userTimeZone;
|
|
private static readonly DateTime Now = DateTime.Now;
|
|
private static readonly DateOnly NowDate = new(Now.Year, Now.Month, Now.Day);
|
|
private static readonly TimeOnly NowTime = new(Now.Hour, Now.Minute);
|
|
|
|
[OneTimeSetUp]
|
|
public void OneTimeSetup()
|
|
{
|
|
_claimsPrincipalAccessor = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(5, true);
|
|
_dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(_claimsPrincipalAccessor);
|
|
_mapper = AutoMapperMock.GetAutoMapper();
|
|
_recurringJobService = new Mock<IRecurringJobService>();
|
|
_scheduleService = new ExtractScheduleService(_dbContext, _claimsPrincipalAccessor, _mapper, _recurringJobService.Object);
|
|
_dbContext.Extracts.AddRange(ExtractTestData.Extracts());
|
|
_dbContext.ExtractSchedule.AddRange(ExtractTestData.ExtractSchedules());
|
|
_dbContext.SaveChanges();
|
|
_userTimeZone = _claimsPrincipalAccessor.GetTimezone();
|
|
}
|
|
|
|
[OneTimeTearDown]
|
|
public async Task OneTimeTearDown()
|
|
{
|
|
await _dbContext.Database.EnsureDeletedAsync();
|
|
}
|
|
|
|
|
|
[Test]
|
|
public async Task CanCreate_And_Add_RecurringJob()
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
// arrange
|
|
var saveInfo = new ExtractScheduleSaveInfo
|
|
{
|
|
ExtractId = 1,
|
|
Enabled = true,
|
|
Repeater = ScheduleRepeaterType.Daily,
|
|
OnDays = "",
|
|
StartDate = NowDate,
|
|
StartTime = NowTime,
|
|
MaxOccurrences = 12
|
|
};
|
|
|
|
// act
|
|
var result = await _scheduleService.CreateAsync(saveInfo, CancellationToken.None);
|
|
|
|
// assert
|
|
Assert.IsNotNull(result);
|
|
Assert.That(result.ExtractScheduleId, Is.GreaterThan(ExtractTestData.ExtractSchedules().Max(h => h.ExtractScheduleId)));
|
|
Assert.That(result.ExtractId, Is.EqualTo(saveInfo.ExtractId));
|
|
Assert.That(result.Enabled, Is.EqualTo(saveInfo.Enabled));
|
|
Assert.That(result.ToCronString(), Is.EqualTo($"{NowTime.Minute} {NowTime.Hour} * * *")); // utc to CST conversion
|
|
Assert.That(result.StartDate, Is.EqualTo(saveInfo.StartDate));
|
|
Assert.IsNull(result.EndDate);
|
|
Assert.That(result.CreatedByUserGuid, Is.EqualTo(_claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid()));
|
|
Assert.That(result.UpdatedByUserGuid, Is.EqualTo(_claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid()));
|
|
Assert.IsNotNull(result.CreatedDate);
|
|
Assert.IsNotNull(result.UpdatedDate);
|
|
|
|
_recurringJobService.Verify(
|
|
s => s.AddOrUpdateExtractExportJobAsync(It.Is<ExtractExportRequest>(r =>
|
|
r.ExtractScheduleId == result.ExtractScheduleId && r.ExtractId == result.ExtractId &&
|
|
r.Cron == result.ToCronString()), It.IsAny<CancellationToken>()), Times.Once);
|
|
|
|
}
|
|
|
|
|
|
[Test]
|
|
public async Task CanGetById()
|
|
{
|
|
// act
|
|
var result = await _scheduleService.GetByIdAsync(1, CancellationToken.None);
|
|
|
|
// assert
|
|
Assert.IsNotNull(result);
|
|
}
|
|
|
|
|
|
[Test]
|
|
public async Task CanUpdate()
|
|
{
|
|
// arrange
|
|
var scheduleId = _dbContext.ExtractSchedule.First(s => s.MaxOccurrences.HasValue).ExtractId;
|
|
|
|
var schedule = await _scheduleService.GetByIdAsync(scheduleId, CancellationToken.None);
|
|
|
|
var saveInfo = new ExtractScheduleSaveInfo();
|
|
_mapper.Map(schedule, saveInfo);
|
|
|
|
saveInfo.EndDate = NowDate.AddYears(1);
|
|
saveInfo.MaxOccurrences = null;
|
|
saveInfo.Repeater = ScheduleRepeaterType.Monthly;
|
|
saveInfo.OnDays = "";
|
|
_dbContext.ChangeTracker.Clear();
|
|
|
|
// act
|
|
var result = await _scheduleService.UpdateAsync(schedule.ExtractId, saveInfo, CancellationToken.None);
|
|
|
|
// assert
|
|
Assert.IsNotNull(result);
|
|
Assert.That(result.ExtractId, Is.EqualTo(schedule.ExtractId));
|
|
Assert.That(result.EndDate, Is.EqualTo(saveInfo.EndDate));
|
|
Assert.IsNull(result.MaxOccurrences);
|
|
Assert.That(result.ToCronString(), Is.EqualTo($"{schedule.StartTime.Minute} {schedule.StartTime.Hour} {schedule.StartDate.Day} * *"));
|
|
Assert.That(result.CreatedByUserGuid, Is.EqualTo(schedule.CreatedByUserGuid));
|
|
Assert.That(result.UpdatedByUserGuid, Is.EqualTo(_claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid()));
|
|
Assert.That(result.CreatedDate, Is.EqualTo(schedule.CreatedDate));
|
|
Assert.That(result.UpdatedDate, Is.EqualTo(DateTimeOffset.UtcNow).Within(500).Milliseconds); // this could fail in debug
|
|
}
|
|
|
|
[Test]
|
|
public async Task CanGetAll_FilterNotEnabled()
|
|
{
|
|
// arrange
|
|
var now = DateTime.UtcNow;
|
|
// arrange
|
|
var saveInfo = new ExtractScheduleSaveInfo
|
|
{
|
|
ExtractId = 1,
|
|
Enabled = false,
|
|
Repeater = ScheduleRepeaterType.Daily,
|
|
OnDays = "",
|
|
StartDate = NowDate,
|
|
StartTime = NowTime,
|
|
MaxOccurrences = 12
|
|
};
|
|
|
|
var disabledSchedule = await _scheduleService.CreateAsync(saveInfo, CancellationToken.None);
|
|
|
|
// act
|
|
var result = (await _scheduleService.GetAllAsync(CancellationToken.None)).ToList();
|
|
|
|
// assert
|
|
Assert.IsNotNull(result);
|
|
Assert.That(result.Count, Is.GreaterThan(0));
|
|
Assert.IsFalse(result.Select(s => s.ExtractScheduleId).Contains(disabledSchedule.ExtractScheduleId));
|
|
Assert.IsTrue(result.TrueForAll(s => s.Enabled));
|
|
}
|
|
|
|
|
|
[Test]
|
|
public async Task Disabling_Removes_RecurringJob()
|
|
{
|
|
// arrange
|
|
var scheduleId = _dbContext.ExtractSchedule.Last(s => s.Enabled).ExtractId;
|
|
|
|
var schedule = await _scheduleService.GetByIdAsync(scheduleId, CancellationToken.None);
|
|
|
|
var saveInfo = new ExtractScheduleSaveInfo();
|
|
_mapper.Map(schedule, saveInfo);
|
|
|
|
saveInfo.Enabled = false;
|
|
_dbContext.ChangeTracker.Clear();
|
|
|
|
// act
|
|
await _scheduleService.UpdateAsync(schedule.ExtractId, saveInfo, CancellationToken.None);
|
|
|
|
// assert
|
|
|
|
_recurringJobService.Verify(
|
|
s => s.RemoveExtractExportJobAsync(It.IsAny<int>(), It.Is<int>(r => r == schedule.ExtractScheduleId),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
|
|
}
|
|
|
|
[Test]
|
|
public async Task HittingMaxOccurrenceLimit_Removes_RecurringJob()
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
// arrange
|
|
var saveInfo = new ExtractScheduleSaveInfo
|
|
{
|
|
ExtractId = 1,
|
|
Enabled = true,
|
|
Repeater = ScheduleRepeaterType.Daily,
|
|
OnDays = "",
|
|
StartDate = NowDate.AddDays(-7), // starting 7 days ago
|
|
StartTime = NowTime,
|
|
MaxOccurrences = 6 // run a total of 6 times
|
|
};
|
|
|
|
var schedule = await _scheduleService.CreateAsync(saveInfo, CancellationToken.None);
|
|
_dbContext.ChangeTracker.Clear();
|
|
|
|
// act
|
|
var result = await _scheduleService.UpdateLastOccurrence(schedule.ExtractScheduleId, DateTimeOffset.UtcNow,
|
|
_userTimeZone, CancellationToken.None);
|
|
|
|
// assert
|
|
Assert.That(result.Enabled, Is.EqualTo(false));
|
|
|
|
_recurringJobService.Verify(
|
|
s => s.RemoveExtractExportJobAsync(It.IsAny<int>(), It.Is<int>(r => r == schedule.ExtractScheduleId),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
|
|
}
|
|
|
|
[Test]
|
|
public async Task HittingEndDateLimit_Removes_RecurringJob()
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
// arrange
|
|
var saveInfo = new ExtractScheduleSaveInfo
|
|
{
|
|
ExtractId = 1,
|
|
Enabled = true,
|
|
Repeater = ScheduleRepeaterType.Daily,
|
|
OnDays = "",
|
|
StartDate = NowDate.AddDays(-7), // starting 7 days ago
|
|
StartTime = NowTime,
|
|
EndDate = NowDate // run until today
|
|
};
|
|
|
|
var schedule = await _scheduleService.CreateAsync(saveInfo, CancellationToken.None);
|
|
_dbContext.ChangeTracker.Clear();
|
|
|
|
// act
|
|
var result = await _scheduleService.UpdateLastOccurrence(schedule.ExtractScheduleId, DateTimeOffset.UtcNow,
|
|
_userTimeZone, CancellationToken.None);
|
|
|
|
// assert
|
|
Assert.That(result.Enabled, Is.EqualTo(false));
|
|
|
|
_recurringJobService.Verify(
|
|
s => s.RemoveExtractExportJobAsync(It.IsAny<int>(), It.Is<int>(r => r == schedule.ExtractScheduleId),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
|
|
}
|
|
|
|
[Test]
|
|
public async Task CanDelete_And_Remove_RecurringJob()
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
// arrange
|
|
var saveInfo = new ExtractScheduleSaveInfo
|
|
{
|
|
ExtractId = 1,
|
|
Enabled = true,
|
|
Repeater = ScheduleRepeaterType.Daily,
|
|
OnDays = "",
|
|
StartDate = NowDate.AddDays(-7), // starting 7 days ago
|
|
StartTime = NowTime,
|
|
};
|
|
|
|
var schedule = await _scheduleService.CreateAsync(saveInfo, CancellationToken.None);
|
|
_dbContext.ChangeTracker.Clear();
|
|
|
|
// act
|
|
await _scheduleService.DeleteAsync(schedule.ExtractScheduleId, CancellationToken.None);
|
|
|
|
// assert
|
|
var result = await _scheduleService.ContainsAsync(schedule.ExtractScheduleId, CancellationToken.None);
|
|
|
|
Assert.That(result, Is.EqualTo(false));
|
|
|
|
_recurringJobService.Verify(
|
|
s => s.RemoveExtractExportJobAsync(It.IsAny<int>(), It.Is<int>(r => r == schedule.ExtractScheduleId),
|
|
It.IsAny<CancellationToken>()), Times.Once);
|
|
|
|
}
|
|
|
|
[Test]
|
|
public void CanGetDailyCronFromSchedule()
|
|
{
|
|
// arrange
|
|
var schedule = new ExtractSchedule
|
|
{
|
|
Repeater = ScheduleRepeaterType.Daily,
|
|
OnDays = "",
|
|
StartDate = new DateOnly(2023, 01, 01),
|
|
StartTime = new TimeOnly(15, 30, 00)
|
|
};
|
|
|
|
// assert
|
|
|
|
Assert.That(schedule.ToCronString(), Is.EqualTo("30 15 * * *"));
|
|
}
|
|
|
|
|
|
[Test]
|
|
public void CanGetWeekdayCronFromSchedule()
|
|
{
|
|
// arrange
|
|
var schedule = new ExtractSchedule
|
|
{
|
|
Repeater = ScheduleRepeaterType.Weekdays,
|
|
StartDate = new DateOnly(2023, 01, 01),
|
|
StartTime = new TimeOnly(15, 30, 00)
|
|
};
|
|
|
|
// assert
|
|
|
|
Assert.That(schedule.ToCronString(), Is.EqualTo("30 15 * * MON-FRI"));
|
|
}
|
|
|
|
[TestCase("MON,WED,FRI")]
|
|
[TestCase("SUN,SAT")]
|
|
[TestCase("SUN,THU")]
|
|
public void CanGetWeeklyCronFromSchedule(string onDays)
|
|
{
|
|
// arrange
|
|
var schedule = new ExtractSchedule
|
|
{
|
|
Repeater = ScheduleRepeaterType.Weekly,
|
|
OnDays = onDays,
|
|
StartDate = new DateOnly(2023, 01, 01),
|
|
StartTime = new TimeOnly(15, 30, 00)
|
|
};
|
|
|
|
// assert
|
|
|
|
Assert.That(schedule.ToCronString(),
|
|
onDays == string.Empty
|
|
? Is.EqualTo("30 15 * * *")
|
|
: Is.EqualTo($"30 15 * * {onDays}"));
|
|
}
|
|
|
|
[Test]
|
|
public void CanGetMonthlyCronFromSchedule()
|
|
{
|
|
// arrange
|
|
var schedule = new ExtractSchedule
|
|
{
|
|
Repeater = ScheduleRepeaterType.Monthly,
|
|
OnDays = "",
|
|
StartDate = new DateOnly(2023, 01, 01),
|
|
StartTime = new TimeOnly(15, 30, 00)
|
|
};
|
|
|
|
// assert
|
|
|
|
Assert.That(schedule.ToCronString(), Is.EqualTo("30 15 1 * *"));
|
|
}
|
|
}
|
|
}
|