feat(configmanager): add DataSyncFormViewModel

Add form ViewModel that wraps DataSyncSection model for two-way binding
with change tracking in the ConfigManager UI.
This commit is contained in:
Joseph Doherty
2026-01-19 19:40:31 -05:00
parent c22e2ed877
commit c416544efe
2 changed files with 231 additions and 0 deletions
@@ -0,0 +1,155 @@
using JdeScoping.ConfigManager.Models;
namespace JdeScoping.ConfigManager.ViewModels.Forms;
/// <summary>
/// ViewModel for editing DataSync configuration section.
/// </summary>
public class DataSyncFormViewModel : ViewModelBase
{
private readonly DataSyncSection _model;
private readonly Action _onChanged;
public DataSyncFormViewModel(DataSyncSection model, Action onChanged)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
_onChanged = onChanged ?? throw new ArgumentNullException(nameof(onChanged));
}
/// <summary>
/// Gets or sets whether data synchronization is enabled.
/// </summary>
public bool Enabled
{
get => _model.Enabled;
set
{
if (_model.Enabled != value)
{
_model.Enabled = value;
OnPropertyChanged();
_onChanged();
}
}
}
/// <summary>
/// Gets or sets the check interval in minutes.
/// </summary>
public int CheckIntervalMinutes
{
get => (int)_model.CheckInterval.TotalMinutes;
set
{
var newValue = TimeSpan.FromMinutes(value);
if (_model.CheckInterval != newValue)
{
_model.CheckInterval = newValue;
OnPropertyChanged();
_onChanged();
}
}
}
/// <summary>
/// Gets or sets the maximum degree of parallelism.
/// </summary>
public int MaxDegreeOfParallelism
{
get => _model.MaxDegreeOfParallelism;
set
{
if (_model.MaxDegreeOfParallelism != value)
{
_model.MaxDegreeOfParallelism = value;
OnPropertyChanged();
_onChanged();
}
}
}
/// <summary>
/// Gets or sets the batch size for sync operations.
/// </summary>
public int BatchSize
{
get => _model.BatchSize;
set
{
if (_model.BatchSize != value)
{
_model.BatchSize = value;
OnPropertyChanged();
_onChanged();
}
}
}
/// <summary>
/// Gets or sets the bulk copy batch size.
/// </summary>
public int BulkCopyBatchSize
{
get => _model.BulkCopyBatchSize;
set
{
if (_model.BulkCopyBatchSize != value)
{
_model.BulkCopyBatchSize = value;
OnPropertyChanged();
_onChanged();
}
}
}
/// <summary>
/// Gets or sets the lookback multiplier.
/// </summary>
public double LookbackMultiplier
{
get => _model.LookbackMultiplier;
set
{
if (Math.Abs(_model.LookbackMultiplier - value) > 0.001)
{
_model.LookbackMultiplier = value;
OnPropertyChanged();
_onChanged();
}
}
}
/// <summary>
/// Gets or sets the purge retention days.
/// </summary>
public int PurgeRetentionDays
{
get => _model.PurgeRetentionDays;
set
{
if (_model.PurgeRetentionDays != value)
{
_model.PurgeRetentionDays = value;
OnPropertyChanged();
_onChanged();
}
}
}
/// <summary>
/// Gets or sets the sync timeout in seconds.
/// </summary>
public int SyncTimeoutSeconds
{
get => _model.SyncTimeoutSeconds;
set
{
if (_model.SyncTimeoutSeconds != value)
{
_model.SyncTimeoutSeconds = value;
OnPropertyChanged();
_onChanged();
}
}
}
}
@@ -0,0 +1,76 @@
using JdeScoping.ConfigManager.Models;
using JdeScoping.ConfigManager.ViewModels.Forms;
namespace JdeScoping.ConfigManager.Tests.ViewModels.Forms;
public class DataSyncFormViewModelTests
{
[Fact]
public void Constructor_InitializesFromModel()
{
// Arrange
var model = new DataSyncSection
{
Enabled = true,
MaxDegreeOfParallelism = 8,
BatchSize = 25000
};
// Act
var sut = new DataSyncFormViewModel(model, () => { });
// Assert
sut.Enabled.ShouldBeTrue();
sut.MaxDegreeOfParallelism.ShouldBe(8);
sut.BatchSize.ShouldBe(25000);
}
[Fact]
public void PropertyChange_UpdatesModel()
{
// Arrange
var model = new DataSyncSection { MaxDegreeOfParallelism = 4 };
var sut = new DataSyncFormViewModel(model, () => { });
// Act
sut.MaxDegreeOfParallelism = 16;
// Assert
model.MaxDegreeOfParallelism.ShouldBe(16);
}
[Fact]
public void PropertyChange_InvokesOnChanged()
{
// Arrange
var model = new DataSyncSection();
var changedInvoked = false;
var sut = new DataSyncFormViewModel(model, () => changedInvoked = true);
// Act
sut.BatchSize = 10000;
// Assert
changedInvoked.ShouldBeTrue();
}
[Fact]
public void PropertyChange_RaisesPropertyChanged()
{
// Arrange
var model = new DataSyncSection();
var sut = new DataSyncFormViewModel(model, () => { });
var propertyChangedRaised = false;
sut.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(DataSyncFormViewModel.LookbackMultiplier))
propertyChangedRaised = true;
};
// Act
sut.LookbackMultiplier = 2.5;
// Assert
propertyChangedRaised.ShouldBeTrue();
}
}