feat: initial commit

This commit is contained in:
Thom Lamb
2026-06-23 11:18:54 -05:00
parent fc3ce9a074
commit 1bb980f070
790 changed files with 149255 additions and 218 deletions
@@ -0,0 +1,362 @@
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 * *"));
}
}
}