diff --git a/NEW/src/JdeScoping.DataSync/Configuration/ScheduleConfig.cs b/NEW/src/JdeScoping.DataSync/Configuration/ScheduleConfig.cs new file mode 100644 index 0000000..b78a121 --- /dev/null +++ b/NEW/src/JdeScoping.DataSync/Configuration/ScheduleConfig.cs @@ -0,0 +1,98 @@ +namespace JdeScoping.DataSync.Configuration; + +/// +/// Configuration for a single schedule type (Mass/Daily/Hourly). +/// +public record ScheduleConfig +{ + /// + /// Whether this schedule is enabled. + /// + public bool Enabled { get; init; } = true; + + /// + /// Interval in minutes between syncs. + /// + public int IntervalMinutes { get; init; } + + /// + /// Whether to truncate the table before import (full reload). + /// + public bool PrePurge { get; init; } + + /// + /// Whether to rebuild indexes after import. + /// + public bool ReIndex { get; init; } + + /// + /// Condition for updating existing rows (e.g., "src.LastUpdateDt > tgt.LastUpdateDt"). + /// + public string? UpdateWhen { get; init; } + + /// + /// Merges this config with defaults. Non-null/non-default values in this config override defaults. + /// + public ScheduleConfig MergeWith(ScheduleConfig defaults) + { + return new ScheduleConfig + { + Enabled = Enabled, + IntervalMinutes = IntervalMinutes > 0 ? IntervalMinutes : defaults.IntervalMinutes, + PrePurge = PrePurge || defaults.PrePurge, + ReIndex = ReIndex || defaults.ReIndex, + UpdateWhen = UpdateWhen ?? defaults.UpdateWhen + }; + } +} + +/// +/// Default schedule configurations for all pipelines. +/// +public record ScheduleDefaults +{ + /// + /// Default Mass schedule config (weekly, full reload). + /// + public ScheduleConfig Mass { get; init; } = new() + { + Enabled = true, + IntervalMinutes = 10080, // Weekly + PrePurge = true, + ReIndex = true + }; + + /// + /// Default Daily schedule config (incremental merge). + /// + public ScheduleConfig Daily { get; init; } = new() + { + Enabled = true, + IntervalMinutes = 1440, // Daily + PrePurge = false, + ReIndex = false, + UpdateWhen = "src.LastUpdateDt > tgt.LastUpdateDt" + }; + + /// + /// Default Hourly schedule config (incremental merge). + /// + public ScheduleConfig Hourly { get; init; } = new() + { + Enabled = true, + IntervalMinutes = 60, // Hourly + PrePurge = false, + ReIndex = false, + UpdateWhen = "src.LastUpdateDt > tgt.LastUpdateDt" + }; +} + +/// +/// Per-pipeline schedule overrides. +/// +public record PipelineSchedules +{ + public ScheduleConfig? Mass { get; init; } + public ScheduleConfig? Daily { get; init; } + public ScheduleConfig? Hourly { get; init; } +} diff --git a/NEW/tests/JdeScoping.DataSync.Tests/Configuration/ScheduleConfigTests.cs b/NEW/tests/JdeScoping.DataSync.Tests/Configuration/ScheduleConfigTests.cs new file mode 100644 index 0000000..cce1209 --- /dev/null +++ b/NEW/tests/JdeScoping.DataSync.Tests/Configuration/ScheduleConfigTests.cs @@ -0,0 +1,176 @@ +using JdeScoping.DataSync.Configuration; +using Shouldly; + +namespace JdeScoping.DataSync.Tests.Configuration; + +public class ScheduleConfigTests +{ + [Fact] + public void ScheduleConfig_DefaultValues_AreCorrect() + { + var config = new ScheduleConfig(); + + config.Enabled.ShouldBeTrue(); + config.IntervalMinutes.ShouldBe(0); + config.PrePurge.ShouldBeFalse(); + config.ReIndex.ShouldBeFalse(); + config.UpdateWhen.ShouldBeNull(); + } + + [Fact] + public void ScheduleConfig_WithValues_StoresCorrectly() + { + var config = new ScheduleConfig + { + Enabled = false, + IntervalMinutes = 60, + PrePurge = true, + ReIndex = true, + UpdateWhen = "src.LastUpdateDt > tgt.LastUpdateDt" + }; + + config.Enabled.ShouldBeFalse(); + config.IntervalMinutes.ShouldBe(60); + config.PrePurge.ShouldBeTrue(); + config.ReIndex.ShouldBeTrue(); + config.UpdateWhen.ShouldBe("src.LastUpdateDt > tgt.LastUpdateDt"); + } + + [Fact] + public void ScheduleDefaults_HasCorrectDefaultValues() + { + var defaults = new ScheduleDefaults(); + + defaults.Mass.ShouldNotBeNull(); + defaults.Daily.ShouldNotBeNull(); + defaults.Hourly.ShouldNotBeNull(); + } + + [Fact] + public void ScheduleDefaults_Mass_HasCorrectValues() + { + var defaults = new ScheduleDefaults(); + + defaults.Mass.Enabled.ShouldBeTrue(); + defaults.Mass.IntervalMinutes.ShouldBe(10080); // Weekly + defaults.Mass.PrePurge.ShouldBeTrue(); + defaults.Mass.ReIndex.ShouldBeTrue(); + defaults.Mass.UpdateWhen.ShouldBeNull(); + } + + [Fact] + public void ScheduleDefaults_Daily_HasCorrectValues() + { + var defaults = new ScheduleDefaults(); + + defaults.Daily.Enabled.ShouldBeTrue(); + defaults.Daily.IntervalMinutes.ShouldBe(1440); // Daily + defaults.Daily.PrePurge.ShouldBeFalse(); + defaults.Daily.ReIndex.ShouldBeFalse(); + defaults.Daily.UpdateWhen.ShouldBe("src.LastUpdateDt > tgt.LastUpdateDt"); + } + + [Fact] + public void ScheduleDefaults_Hourly_HasCorrectValues() + { + var defaults = new ScheduleDefaults(); + + defaults.Hourly.Enabled.ShouldBeTrue(); + defaults.Hourly.IntervalMinutes.ShouldBe(60); // Hourly + defaults.Hourly.PrePurge.ShouldBeFalse(); + defaults.Hourly.ReIndex.ShouldBeFalse(); + defaults.Hourly.UpdateWhen.ShouldBe("src.LastUpdateDt > tgt.LastUpdateDt"); + } + + [Fact] + public void PipelineSchedules_AllPropertiesNullable() + { + var schedules = new PipelineSchedules(); + + schedules.Mass.ShouldBeNull(); + schedules.Daily.ShouldBeNull(); + schedules.Hourly.ShouldBeNull(); + } + + [Fact] + public void PipelineSchedules_WithValues_StoresCorrectly() + { + var schedules = new PipelineSchedules + { + Mass = new ScheduleConfig { PrePurge = true }, + Daily = new ScheduleConfig { Enabled = true }, + Hourly = new ScheduleConfig { Enabled = false } + }; + + schedules.Mass.ShouldNotBeNull(); + schedules.Mass!.PrePurge.ShouldBeTrue(); + schedules.Daily.ShouldNotBeNull(); + schedules.Daily!.Enabled.ShouldBeTrue(); + schedules.Hourly.ShouldNotBeNull(); + schedules.Hourly!.Enabled.ShouldBeFalse(); + } + + [Fact] + public void MergeWith_WhenConfigHasNoOverrides_ReturnsDefaultValues() + { + var config = new ScheduleConfig(); + var defaults = new ScheduleConfig + { + Enabled = true, + IntervalMinutes = 60, + PrePurge = false, + ReIndex = false, + UpdateWhen = "src.LastUpdateDt > tgt.LastUpdateDt" + }; + + var merged = config.MergeWith(defaults); + + merged.IntervalMinutes.ShouldBe(60); + merged.UpdateWhen.ShouldBe("src.LastUpdateDt > tgt.LastUpdateDt"); + } + + [Fact] + public void MergeWith_WhenConfigHasOverrides_UsesOverrideValues() + { + var config = new ScheduleConfig + { + IntervalMinutes = 120, + PrePurge = true, + UpdateWhen = "custom condition" + }; + var defaults = new ScheduleConfig + { + IntervalMinutes = 60, + PrePurge = false, + UpdateWhen = "default condition" + }; + + var merged = config.MergeWith(defaults); + + merged.IntervalMinutes.ShouldBe(120); + merged.PrePurge.ShouldBeTrue(); + merged.UpdateWhen.ShouldBe("custom condition"); + } + + [Fact] + public void MergeWith_PreservesEnabledFromConfig() + { + var config = new ScheduleConfig { Enabled = false }; + var defaults = new ScheduleConfig { Enabled = true }; + + var merged = config.MergeWith(defaults); + + merged.Enabled.ShouldBeFalse(); + } + + [Fact] + public void MergeWith_WhenIntervalZero_UsesDefaultInterval() + { + var config = new ScheduleConfig { IntervalMinutes = 0 }; + var defaults = new ScheduleConfig { IntervalMinutes = 1440 }; + + var merged = config.MergeWith(defaults); + + merged.IntervalMinutes.ShouldBe(1440); + } +}