test(configmanager): expand unit test coverage to 451 tests
Add comprehensive tests for services (ConnectionTestService, RuntimeConfigValidation), ViewModels (PipelineEditor, dialogs, transformers), and Avalonia headless UI tests for views and forms.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using JdeScoping.ConfigManager.Services;
|
||||
using JdeScoping.DataSync.Configuration;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Tests.Services;
|
||||
|
||||
@@ -51,4 +52,221 @@ public class ConfigFileServiceTests
|
||||
() => _sut.LoadAppSettingsAsync("/config/appsettings.json"));
|
||||
ex.Message.ShouldContain("parse");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadPipelineAsync_WithValidJson_ReturnsPipelineConfig()
|
||||
{
|
||||
// Arrange
|
||||
var json = """
|
||||
{
|
||||
"name": "TestPipeline",
|
||||
"isEnabled": true,
|
||||
"massSyncIntervalMinutes": 1440,
|
||||
"source": {
|
||||
"connectionStringName": "JDE",
|
||||
"query": "SELECT * FROM test"
|
||||
},
|
||||
"destination": {
|
||||
"connectionStringName": "LotFinder",
|
||||
"tableName": "TestTable"
|
||||
}
|
||||
}
|
||||
""";
|
||||
_fileSystem.ReadAllTextAsync(Arg.Any<string>(), Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult(json));
|
||||
|
||||
// Act
|
||||
var result = await _sut.LoadPipelineAsync("/config/pipeline.test.json");
|
||||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.Name.ShouldBe("TestPipeline");
|
||||
result.IsEnabled.ShouldBeTrue();
|
||||
result.MassSyncIntervalMinutes.ShouldBe(1440);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadPipelineAsync_WithInvalidJson_ThrowsConfigLoadException()
|
||||
{
|
||||
// Arrange
|
||||
var json = "{ invalid json }";
|
||||
_fileSystem.ReadAllTextAsync(Arg.Any<string>(), Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult(json));
|
||||
|
||||
// Act & Assert
|
||||
var ex = await Should.ThrowAsync<ConfigLoadException>(
|
||||
() => _sut.LoadPipelineAsync("/config/pipeline.test.json"));
|
||||
ex.Message.ShouldContain("parse");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SavePipelineAsync_SerializesAndWritesPipeline()
|
||||
{
|
||||
// Arrange
|
||||
var path = "/config/pipeline.test.json";
|
||||
var pipeline = new EtlPipelineConfig
|
||||
{
|
||||
Name = "TestPipeline",
|
||||
IsEnabled = true,
|
||||
MassSyncIntervalMinutes = 1440
|
||||
};
|
||||
string? writtenContent = null;
|
||||
_fileSystem.WriteAllTextAsync(Arg.Any<string>(), Arg.Do<string>(c => writtenContent = c), Arg.Any<CancellationToken>())
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
// Act
|
||||
await _sut.SavePipelineAsync(path, pipeline);
|
||||
|
||||
// Assert
|
||||
await _fileSystem.Received(1).WriteAllTextAsync(path, Arg.Any<string>(), Arg.Any<CancellationToken>());
|
||||
writtenContent.ShouldNotBeNull();
|
||||
writtenContent.ShouldContain("TestPipeline");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadAllPipelinesAsync_WithMultiplePipelines_ReturnsAll()
|
||||
{
|
||||
// Arrange
|
||||
var directory = "/config/pipelines";
|
||||
var files = new[]
|
||||
{
|
||||
"/config/pipelines/pipeline.first.json",
|
||||
"/config/pipelines/pipeline.second.json"
|
||||
};
|
||||
|
||||
_fileSystem.DirectoryExists(directory).Returns(true);
|
||||
_fileSystem.GetFilesAsync(directory, "pipeline.*.json", Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult(files));
|
||||
|
||||
_fileSystem.ReadAllTextAsync(files[0], Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult("""{"name": "First", "isEnabled": true}"""));
|
||||
_fileSystem.ReadAllTextAsync(files[1], Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult("""{"name": "Second", "isEnabled": false}"""));
|
||||
|
||||
_fileSystem.GetFileNameWithoutExtension(files[0]).Returns("pipeline.first");
|
||||
_fileSystem.GetFileNameWithoutExtension(files[1]).Returns("pipeline.second");
|
||||
|
||||
// Act
|
||||
var result = await _sut.LoadAllPipelinesAsync(directory);
|
||||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.Count.ShouldBe(2);
|
||||
result.ContainsKey("first").ShouldBeTrue();
|
||||
result.ContainsKey("second").ShouldBeTrue();
|
||||
result["first"].Name.ShouldBe("First");
|
||||
result["second"].IsEnabled.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadAllPipelinesAsync_WithNonExistentDirectory_ReturnsEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var directory = "/config/nonexistent";
|
||||
_fileSystem.DirectoryExists(directory).Returns(false);
|
||||
|
||||
// Act
|
||||
var result = await _sut.LoadAllPipelinesAsync(directory);
|
||||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeletePipelineFileAsync_CallsFileSystemDelete()
|
||||
{
|
||||
// Arrange
|
||||
var path = "/config/pipeline.test.json";
|
||||
|
||||
// Act
|
||||
await _sut.DeletePipelineFileAsync(path);
|
||||
|
||||
// Assert
|
||||
await _fileSystem.Received(1).DeleteFileAsync(path, Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SaveAppSettingsAsync_SerializesAndWritesConfig()
|
||||
{
|
||||
// Arrange
|
||||
var path = "/config/appsettings.json";
|
||||
var config = new ConfigModel
|
||||
{
|
||||
DataSync = new DataSyncSection
|
||||
{
|
||||
Enabled = true,
|
||||
MaxDegreeOfParallelism = 16
|
||||
}
|
||||
};
|
||||
string? writtenContent = null;
|
||||
_fileSystem.WriteAllTextAsync(Arg.Any<string>(), Arg.Do<string>(c => writtenContent = c), Arg.Any<CancellationToken>())
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
// Act
|
||||
await _sut.SaveAppSettingsAsync(path, config);
|
||||
|
||||
// Assert
|
||||
await _fileSystem.Received(1).WriteAllTextAsync(path, Arg.Any<string>(), Arg.Any<CancellationToken>());
|
||||
writtenContent.ShouldNotBeNull();
|
||||
writtenContent.ShouldContain("enabled");
|
||||
writtenContent.ShouldContain("16");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadAllPipelinesAsync_SkipsInvalidPipelineFiles()
|
||||
{
|
||||
// Arrange
|
||||
var directory = "/config/pipelines";
|
||||
var files = new[]
|
||||
{
|
||||
"/config/pipelines/pipeline.valid.json",
|
||||
"/config/pipelines/pipeline.invalid.json"
|
||||
};
|
||||
|
||||
_fileSystem.DirectoryExists(directory).Returns(true);
|
||||
_fileSystem.GetFilesAsync(directory, "pipeline.*.json", Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult(files));
|
||||
|
||||
_fileSystem.ReadAllTextAsync(files[0], Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult("""{"name": "Valid", "isEnabled": true}"""));
|
||||
_fileSystem.ReadAllTextAsync(files[1], Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult("{ invalid json }"));
|
||||
|
||||
_fileSystem.GetFileNameWithoutExtension(files[0]).Returns("pipeline.valid");
|
||||
_fileSystem.GetFileNameWithoutExtension(files[1]).Returns("pipeline.invalid");
|
||||
|
||||
// Act
|
||||
var result = await _sut.LoadAllPipelinesAsync(directory);
|
||||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.Count.ShouldBe(1);
|
||||
result.ContainsKey("valid").ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadAllPipelinesAsync_AssignsPipelineNameFromFilename_WhenNameIsEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var directory = "/config/pipelines";
|
||||
var files = new[] { "/config/pipelines/pipeline.unnamed.json" };
|
||||
|
||||
_fileSystem.DirectoryExists(directory).Returns(true);
|
||||
_fileSystem.GetFilesAsync(directory, "pipeline.*.json", Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult(files));
|
||||
|
||||
// Pipeline with empty name
|
||||
_fileSystem.ReadAllTextAsync(files[0], Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult("""{"name": "", "isEnabled": true}"""));
|
||||
_fileSystem.GetFileNameWithoutExtension(files[0]).Returns("pipeline.unnamed");
|
||||
|
||||
// Act
|
||||
var result = await _sut.LoadAllPipelinesAsync(directory);
|
||||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.Count.ShouldBe(1);
|
||||
result["unnamed"].Name.ShouldBe("unnamed");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user