Files
jdescopingtool/NEW/tests/JdeScoping.DataSync.Tests/ScheduleCheckerTests.cs
T
Joseph Doherty fbe58a81e4 refactor(securestoremanager): add platform service abstractions and constants
Implement deferred code review findings:
- Add IDialogService/IClipboardService interfaces for testable platform operations
- Create AvaloniaDialogService and AvaloniaClipboardService implementations
- Extract dialog strings and file extensions to centralized Constants classes
- Refactor ViewModels to use DI instead of event delegates
- Update tests to use mock services
2026-01-19 16:54:35 -05:00

706 lines
26 KiB
C#

using JdeScoping.Core.Models;
using JdeScoping.Core.Models.Enums;
using JdeScoping.Core.Models.Infrastructure;
using JdeScoping.DataSync.Options;
using JdeScoping.DataSync.Contracts;
using JdeScoping.DataSync.Services;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using NSubstitute;
using Shouldly;
namespace JdeScoping.DataSync.Tests;
/// <summary>
/// Unit tests for ScheduleChecker service.
/// </summary>
public class ScheduleCheckerTests
{
private readonly IDataUpdateRepository _repository;
private readonly IOptions<DataSyncOptions> _options;
private readonly ScheduleChecker _sut;
public ScheduleCheckerTests()
{
_repository = Substitute.For<IDataUpdateRepository>();
_options = Microsoft.Extensions.Options.Options.Create(new DataSyncOptions
{
LookbackMultiplier = 3,
DataSources = []
});
_sut = new ScheduleChecker(
_repository,
_options,
NullLogger<ScheduleChecker>.Instance);
}
#region Priority Tests - Mass > Daily > Hourly
[Fact]
public async Task GetPendingTasksAsync_WhenMassNeverRun_ReturnsMassTask()
{
// Arrange
var config = CreateDataSourceConfig("WorkOrder", massEnabled: true, dailyEnabled: true, hourlyEnabled: true);
_options.Value.DataSources.Add(config);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>());
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
tasks.ShouldHaveSingleItem();
tasks[0].TableName.ShouldBe("WorkOrder");
tasks[0].UpdateType.ShouldBe(UpdateTypes.Mass);
tasks[0].MinimumDt.ShouldBeNull(); // Mass updates don't have MinimumDT
}
[Fact]
public async Task GetPendingTasksAsync_WhenMassDue_ReturnsMassOverDaily()
{
// Arrange
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 60,
dailyEnabled: true, dailyInterval: 1440);
_options.Value.DataSources.Add(config);
var now = DateTime.UtcNow;
var lastMass = CreateDataUpdate("WorkOrder", UpdateTypes.Mass, now.AddMinutes(-120), success: true);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>
{
{ "WorkOrder_3", lastMass } // 3 = UpdateTypes.Mass
});
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
tasks.ShouldHaveSingleItem();
tasks[0].UpdateType.ShouldBe(UpdateTypes.Mass);
}
[Fact]
public async Task GetPendingTasksAsync_WhenMassNotDue_ChecksDailyAndHourly()
{
// Arrange
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 10080, // weekly
dailyEnabled: true, dailyInterval: 1440,
hourlyEnabled: true, hourlyInterval: 60);
_options.Value.DataSources.Add(config);
var now = DateTime.UtcNow;
var lastMass = CreateDataUpdate("WorkOrder", UpdateTypes.Mass, now.AddHours(-1), success: true);
var lastDaily = CreateDataUpdate("WorkOrder", UpdateTypes.Daily, now.AddHours(-25), success: true);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>
{
{ "WorkOrder_3", lastMass }, // Mass not due
{ "WorkOrder_2", lastDaily } // Daily is due (25 hrs > 1440 min)
});
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
tasks.ShouldHaveSingleItem();
tasks[0].UpdateType.ShouldBe(UpdateTypes.Daily);
}
[Fact]
public async Task GetPendingTasksAsync_WhenDailyDue_ReturnsDailyOverHourly()
{
// Arrange
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 10080,
dailyEnabled: true, dailyInterval: 1440,
hourlyEnabled: true, hourlyInterval: 60);
_options.Value.DataSources.Add(config);
var now = DateTime.UtcNow;
var lastMass = CreateDataUpdate("WorkOrder", UpdateTypes.Mass, now.AddDays(-1), success: true);
var lastDaily = CreateDataUpdate("WorkOrder", UpdateTypes.Daily, now.AddHours(-25), success: true);
var lastHourly = CreateDataUpdate("WorkOrder", UpdateTypes.Hourly, now.AddHours(-2), success: true);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>
{
{ "WorkOrder_3", lastMass },
{ "WorkOrder_2", lastDaily },
{ "WorkOrder_1", lastHourly }
});
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
tasks.ShouldHaveSingleItem();
tasks[0].UpdateType.ShouldBe(UpdateTypes.Daily);
}
[Fact]
public async Task GetPendingTasksAsync_WhenOnlyHourlyDue_ReturnsHourly()
{
// Arrange
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 10080,
dailyEnabled: true, dailyInterval: 1440,
hourlyEnabled: true, hourlyInterval: 60);
_options.Value.DataSources.Add(config);
var now = DateTime.UtcNow;
var lastMass = CreateDataUpdate("WorkOrder", UpdateTypes.Mass, now.AddDays(-1), success: true);
var lastDaily = CreateDataUpdate("WorkOrder", UpdateTypes.Daily, now.AddHours(-12), success: true);
var lastHourly = CreateDataUpdate("WorkOrder", UpdateTypes.Hourly, now.AddHours(-2), success: true);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>
{
{ "WorkOrder_3", lastMass },
{ "WorkOrder_2", lastDaily },
{ "WorkOrder_1", lastHourly }
});
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
tasks.ShouldHaveSingleItem();
tasks[0].UpdateType.ShouldBe(UpdateTypes.Hourly);
}
#endregion
#region MinimumDT Calculation with Lookback
[Fact]
public async Task GetPendingTasksAsync_DailySync_CalculatesMinimumDTWithLookback()
{
// Arrange: LookbackMultiplier = 3, daily interval = 1440 min
// MinimumDT = lastDaily.EndDT - (3 * 1440) = lastDaily.EndDT - 4320 min = 3 days before lastDaily
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 10080,
dailyEnabled: true, dailyInterval: 1440);
_options.Value.DataSources.Add(config);
var now = DateTime.UtcNow;
var lastMass = CreateDataUpdate("WorkOrder", UpdateTypes.Mass, now.AddDays(-2), success: true);
var lastDaily = CreateDataUpdate("WorkOrder", UpdateTypes.Daily, now.AddHours(-25), success: true);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>
{
{ "WorkOrder_3", lastMass },
{ "WorkOrder_2", lastDaily }
});
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
tasks.ShouldHaveSingleItem();
tasks[0].UpdateType.ShouldBe(UpdateTypes.Daily);
tasks[0].MinimumDt.ShouldNotBeNull();
// Expected: lastDaily.EndDT - (3 * 1440 min) = lastDaily.EndDT - 3 days
var expectedMinimumDt = lastDaily.EndDt!.Value.AddMinutes(-3 * 1440);
tasks[0].MinimumDt!.Value.ShouldBe(expectedMinimumDt, TimeSpan.FromSeconds(1));
}
[Fact]
public async Task GetPendingTasksAsync_HourlySync_UsesHourlyTimestampForMinimumDT()
{
// Arrange: Hourly uses its own timestamp and interval for MinimumDT calculation
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 10080,
dailyEnabled: true, dailyInterval: 1440,
hourlyEnabled: true, hourlyInterval: 60);
_options.Value.DataSources.Add(config);
var now = DateTime.UtcNow;
var lastMass = CreateDataUpdate("WorkOrder", UpdateTypes.Mass, now.AddDays(-1), success: true);
var lastDaily = CreateDataUpdate("WorkOrder", UpdateTypes.Daily, now.AddHours(-12), success: true);
var lastHourly = CreateDataUpdate("WorkOrder", UpdateTypes.Hourly, now.AddHours(-2), success: true);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>
{
{ "WorkOrder_3", lastMass },
{ "WorkOrder_2", lastDaily },
{ "WorkOrder_1", lastHourly }
});
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
tasks.ShouldHaveSingleItem();
tasks[0].UpdateType.ShouldBe(UpdateTypes.Hourly);
tasks[0].MinimumDt.ShouldNotBeNull();
// Hourly uses hourly's timestamp and hourly's interval for lookback calculation
var expectedMinimumDt = lastHourly.EndDt!.Value.AddMinutes(-3 * 60);
tasks[0].MinimumDt!.Value.ShouldBe(expectedMinimumDt, TimeSpan.FromSeconds(1));
}
[Fact]
public async Task GetPendingTasksAsync_WithDifferentLookbackMultiplier_CalculatesCorrectly()
{
// Arrange: Test with multiplier = 5
_options.Value.LookbackMultiplier = 5;
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 10080,
dailyEnabled: true, dailyInterval: 1440);
_options.Value.DataSources.Add(config);
var now = DateTime.UtcNow;
var lastMass = CreateDataUpdate("WorkOrder", UpdateTypes.Mass, now.AddDays(-2), success: true);
var lastDaily = CreateDataUpdate("WorkOrder", UpdateTypes.Daily, now.AddHours(-25), success: true);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>
{
{ "WorkOrder_3", lastMass },
{ "WorkOrder_2", lastDaily }
});
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
var expectedMinimumDt = lastDaily.EndDt!.Value.AddMinutes(-5 * 1440);
tasks[0].MinimumDt!.Value.ShouldBe(expectedMinimumDt, TimeSpan.FromSeconds(1));
}
#endregion
#region Disabled Table Handling
[Fact]
public async Task GetPendingTasksAsync_DisabledDataSource_ReturnsNoTasks()
{
// Arrange
var config = CreateDataSourceConfig("WorkOrder", massEnabled: true);
config.IsEnabled = false;
_options.Value.DataSources.Add(config);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>());
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
tasks.ShouldBeEmpty();
}
[Fact]
public async Task GetPendingTasksAsync_DisabledMassSchedule_SkipsMass()
{
// Arrange: Mass disabled, Daily enabled
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: false,
dailyEnabled: true, dailyInterval: 1440);
_options.Value.DataSources.Add(config);
var now = DateTime.UtcNow;
// Even with no mass ever run, if mass is disabled, should NOT require mass first
// However, current logic requires mass before daily, so this tests that properly
var lastMass = CreateDataUpdate("WorkOrder", UpdateTypes.Mass, now.AddDays(-1), success: true);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>
{
{ "WorkOrder_3", lastMass }
});
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert: Should return Daily since mass is disabled but already ran before
tasks.ShouldHaveSingleItem();
tasks[0].UpdateType.ShouldBe(UpdateTypes.Daily);
}
[Fact]
public async Task GetPendingTasksAsync_DisabledDailySchedule_SkipsDaily()
{
// Arrange
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 10080,
dailyEnabled: false,
hourlyEnabled: true, hourlyInterval: 60);
_options.Value.DataSources.Add(config);
var now = DateTime.UtcNow;
var lastMass = CreateDataUpdate("WorkOrder", UpdateTypes.Mass, now.AddDays(-1), success: true);
var lastHourly = CreateDataUpdate("WorkOrder", UpdateTypes.Hourly, now.AddHours(-2), success: true);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>
{
{ "WorkOrder_3", lastMass },
{ "WorkOrder_1", lastHourly }
});
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert: Should return Hourly, skipping Daily
tasks.ShouldHaveSingleItem();
tasks[0].UpdateType.ShouldBe(UpdateTypes.Hourly);
}
[Fact]
public async Task GetPendingTasksAsync_AllSchedulesDisabled_ReturnsNoTasks()
{
// Arrange
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: false,
dailyEnabled: false,
hourlyEnabled: false);
_options.Value.DataSources.Add(config);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>());
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
tasks.ShouldBeEmpty();
}
#endregion
#region First Sync (No Prior Updates) Scenario
[Fact]
public async Task GetPendingTasksAsync_NoPriorUpdates_RequiresMassFirst()
{
// Arrange: Never synced before, all schedules enabled
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 10080,
dailyEnabled: true, dailyInterval: 1440,
hourlyEnabled: true, hourlyInterval: 60);
_options.Value.DataSources.Add(config);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>());
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert: Must do Mass first
tasks.ShouldHaveSingleItem();
tasks[0].UpdateType.ShouldBe(UpdateTypes.Mass);
tasks[0].MinimumDt.ShouldBeNull();
}
[Fact]
public async Task GetPendingTasksAsync_OnlyMassCompleted_DailyHasNullMinimumDT()
{
// Arrange: Mass completed, no daily yet
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 10080,
dailyEnabled: true, dailyInterval: 1440);
_options.Value.DataSources.Add(config);
var now = DateTime.UtcNow;
var lastMass = CreateDataUpdate("WorkOrder", UpdateTypes.Mass, now.AddHours(-1), success: true);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>
{
{ "WorkOrder_3", lastMass }
});
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert: Should return Daily with null MinimumDT (no prior daily to calculate from)
tasks.ShouldHaveSingleItem();
tasks[0].UpdateType.ShouldBe(UpdateTypes.Daily);
tasks[0].MinimumDt.ShouldBeNull();
}
[Fact]
public async Task GetPendingTasksAsync_NeverHadMass_DoesNotReturnDailyOrHourly()
{
// Arrange: Daily and Hourly enabled but no Mass ever run
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 10080,
dailyEnabled: true, dailyInterval: 1440,
hourlyEnabled: true, hourlyInterval: 60);
_options.Value.DataSources.Add(config);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>());
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert: Only Mass should be returned - can't do daily/hourly without initial mass
tasks.ShouldHaveSingleItem();
tasks[0].UpdateType.ShouldBe(UpdateTypes.Mass);
}
#endregion
#region Failed Sync Recovery
[Fact]
public async Task GetPendingTasksAsync_FailedMass_ReturnsMassAgain()
{
// Arrange
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 10080);
_options.Value.DataSources.Add(config);
var now = DateTime.UtcNow;
var lastMass = CreateDataUpdate("WorkOrder", UpdateTypes.Mass, now.AddMinutes(-5), success: false);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>
{
{ "WorkOrder_3", lastMass }
});
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert: Failed mass should trigger retry regardless of interval
tasks.ShouldHaveSingleItem();
tasks[0].UpdateType.ShouldBe(UpdateTypes.Mass);
}
[Fact]
public async Task GetPendingTasksAsync_FailedDaily_ReturnsDailyAgain()
{
// Arrange
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 10080,
dailyEnabled: true, dailyInterval: 1440);
_options.Value.DataSources.Add(config);
var now = DateTime.UtcNow;
var lastMass = CreateDataUpdate("WorkOrder", UpdateTypes.Mass, now.AddDays(-1), success: true);
var lastDaily = CreateDataUpdate("WorkOrder", UpdateTypes.Daily, now.AddMinutes(-5), success: false);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>
{
{ "WorkOrder_3", lastMass },
{ "WorkOrder_2", lastDaily }
});
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
tasks.ShouldHaveSingleItem();
tasks[0].UpdateType.ShouldBe(UpdateTypes.Daily);
}
[Fact]
public async Task GetPendingTasksAsync_FailedHourly_ReturnsHourlyAgain()
{
// Arrange
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 10080,
dailyEnabled: true, dailyInterval: 1440,
hourlyEnabled: true, hourlyInterval: 60);
_options.Value.DataSources.Add(config);
var now = DateTime.UtcNow;
var lastMass = CreateDataUpdate("WorkOrder", UpdateTypes.Mass, now.AddDays(-1), success: true);
var lastDaily = CreateDataUpdate("WorkOrder", UpdateTypes.Daily, now.AddHours(-1), success: true);
var lastHourly = CreateDataUpdate("WorkOrder", UpdateTypes.Hourly, now.AddMinutes(-5), success: false);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>
{
{ "WorkOrder_3", lastMass },
{ "WorkOrder_2", lastDaily },
{ "WorkOrder_1", lastHourly }
});
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
tasks.ShouldHaveSingleItem();
tasks[0].UpdateType.ShouldBe(UpdateTypes.Hourly);
}
#endregion
#region Multiple Tables
[Fact]
public async Task GetPendingTasksAsync_MultipleTables_ReturnsTasksForEach()
{
// Arrange
var config1 = CreateDataSourceConfig("WorkOrder", massEnabled: true, massInterval: 60);
var config2 = CreateDataSourceConfig("LotUsage", massEnabled: true, massInterval: 60);
_options.Value.DataSources.Add(config1);
_options.Value.DataSources.Add(config2);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>());
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
tasks.Count.ShouldBe(2);
tasks.ShouldContain(t => t.TableName == "WorkOrder");
tasks.ShouldContain(t => t.TableName == "LotUsage");
}
[Fact]
public async Task GetPendingTasksAsync_MultipleTables_DifferentSchedulesDue()
{
// Arrange
var config1 = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 10080,
dailyEnabled: true, dailyInterval: 1440);
var config2 = CreateDataSourceConfig("LotUsage",
massEnabled: true, massInterval: 60);
_options.Value.DataSources.Add(config1);
_options.Value.DataSources.Add(config2);
var now = DateTime.UtcNow;
var lastMassWorkOrder = CreateDataUpdate("WorkOrder", UpdateTypes.Mass, now.AddDays(-1), success: true);
var lastDailyWorkOrder = CreateDataUpdate("WorkOrder", UpdateTypes.Daily, now.AddHours(-25), success: true);
var lastMassLotUsage = CreateDataUpdate("LotUsage", UpdateTypes.Mass, now.AddHours(-2), success: true);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>
{
{ "WorkOrder_3", lastMassWorkOrder },
{ "WorkOrder_2", lastDailyWorkOrder },
{ "LotUsage_3", lastMassLotUsage }
});
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
tasks.Count.ShouldBe(2);
tasks.ShouldContain(t => t.TableName == "WorkOrder" && t.UpdateType == UpdateTypes.Daily);
tasks.ShouldContain(t => t.TableName == "LotUsage" && t.UpdateType == UpdateTypes.Mass);
}
#endregion
#region Edge Cases
[Fact]
public async Task GetPendingTasksAsync_NothingDue_ReturnsEmptyList()
{
// Arrange
var config = CreateDataSourceConfig("WorkOrder",
massEnabled: true, massInterval: 10080,
dailyEnabled: true, dailyInterval: 1440,
hourlyEnabled: true, hourlyInterval: 60);
_options.Value.DataSources.Add(config);
var now = DateTime.UtcNow;
// All syncs completed recently
var lastMass = CreateDataUpdate("WorkOrder", UpdateTypes.Mass, now.AddMinutes(-5), success: true);
var lastDaily = CreateDataUpdate("WorkOrder", UpdateTypes.Daily, now.AddMinutes(-5), success: true);
var lastHourly = CreateDataUpdate("WorkOrder", UpdateTypes.Hourly, now.AddMinutes(-5), success: true);
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>
{
{ "WorkOrder_3", lastMass },
{ "WorkOrder_2", lastDaily },
{ "WorkOrder_1", lastHourly }
});
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
tasks.ShouldBeEmpty();
}
[Fact]
public async Task GetPendingTasksAsync_NoDataSources_ReturnsEmptyList()
{
// Arrange: No data sources configured
_repository.GetLastDataUpdatesAsync(Arg.Any<CancellationToken>())
.Returns(new Dictionary<string, DataUpdate>());
// Act
var tasks = await _sut.GetPendingTasksAsync();
// Assert
tasks.ShouldBeEmpty();
}
#endregion
#region Helper Methods
private static DataSourceConfig CreateDataSourceConfig(
string tableName,
bool massEnabled = false,
int massInterval = 10080,
bool dailyEnabled = false,
int dailyInterval = 1440,
bool hourlyEnabled = false,
int hourlyInterval = 60)
{
return new DataSourceConfig
{
TableName = tableName,
SourceSystem = "JDE",
SourceData = tableName.ToUpper(),
IsEnabled = true,
MassConfig = new ScheduleConfig
{
Enabled = massEnabled,
IntervalMinutes = massInterval
},
DailyConfig = new ScheduleConfig
{
Enabled = dailyEnabled,
IntervalMinutes = dailyInterval
},
HourlyConfig = new ScheduleConfig
{
Enabled = hourlyEnabled,
IntervalMinutes = hourlyInterval
}
};
}
private static DataUpdate CreateDataUpdate(
string tableName,
UpdateTypes updateType,
DateTime endDt,
bool success)
{
return new DataUpdate
{
Id = 1,
TableName = tableName,
SourceSystem = "JDE",
SourceData = tableName.ToUpper(),
UpdateType = updateType,
StartDt = endDt.AddMinutes(-5),
EndDt = endDt,
WasSuccessful = success,
NumberRecords = success ? 1000 : -1
};
}
#endregion
}