feat: add startup config validation and document ConfigManager pipeline editor
Add ConfigurationValidationRunner with IConfigurationValidator interface for validating required settings at startup. Includes SecureStore and LDAP validators. Expand ConfigManager with pipeline editing UI, dialogs, and step editors. Update documentation with config validation guidance.
This commit is contained in:
+217
@@ -0,0 +1,217 @@
|
||||
using Dapper;
|
||||
using FluentAssertions;
|
||||
using JdeScoping.Database.Tests.Infrastructure;
|
||||
|
||||
namespace JdeScoping.Database.Tests.Procedures;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for usp_ProcessMisStagingData stored procedure.
|
||||
/// Validates MIS staging data processing into MisData_Curr and MisData_Hist tables.
|
||||
/// </summary>
|
||||
[Collection("DatabaseTests")]
|
||||
public class ProcessMisStagingDataProcedureTests : DatabaseTestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Cleans up test data from MIS tables.
|
||||
/// </summary>
|
||||
private async Task CleanupMisTestDataAsync(string testMisNumber)
|
||||
{
|
||||
await Connection.ExecuteAsync(
|
||||
"DELETE FROM MisData_Curr WHERE MisNumber = @MisNumber",
|
||||
new { MisNumber = testMisNumber });
|
||||
await Connection.ExecuteAsync(
|
||||
"DELETE FROM MisData_Hist WHERE MisNumber = @MisNumber",
|
||||
new { MisNumber = testMisNumber });
|
||||
await Connection.ExecuteAsync(
|
||||
"DELETE FROM mis_temp WHERE MIS_IIS_Number = @MisNumber OR MIS_IIS_Number = @MisNumberWithPrefix",
|
||||
new { MisNumber = testMisNumber, MisNumberWithPrefix = $"IIS_{testMisNumber}" });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task usp_ProcessMisStagingData_DebugMode_DoesNotModifyData()
|
||||
{
|
||||
// Arrange
|
||||
var testMisNumber = "TEST_DEBUG_001";
|
||||
await CleanupMisTestDataAsync(testMisNumber);
|
||||
|
||||
try
|
||||
{
|
||||
// Insert test data into staging
|
||||
await Connection.ExecuteAsync(
|
||||
@"INSERT INTO mis_temp (MIS_IIS_Number, PartNumber, Site, Version, CharacterNumber,
|
||||
TestDescription, SamplingType, SamplingValue, ToolsGauges, WorkInstructions, Release_Date)
|
||||
VALUES (@MisNumber, '12345', 'SITE1', 'A', '1',
|
||||
'Test Description', 'Random', '5', 'Gauge1', 'Instruction1', @ReleaseDate)",
|
||||
new { MisNumber = testMisNumber, ReleaseDate = DateTime.Now });
|
||||
|
||||
var initialCurrCount = await Connection.QuerySingleAsync<int>(
|
||||
"SELECT COUNT(*) FROM MisData_Curr WHERE MisNumber = @MisNumber",
|
||||
new { MisNumber = testMisNumber });
|
||||
|
||||
// Act - run in debug mode (SaveChanges = 0)
|
||||
await Connection.ExecuteAsync(
|
||||
"EXEC dbo.usp_ProcessMisStagingData @SaveChanges = 0");
|
||||
|
||||
// Assert - no data should have been inserted
|
||||
var finalCurrCount = await Connection.QuerySingleAsync<int>(
|
||||
"SELECT COUNT(*) FROM MisData_Curr WHERE MisNumber = @MisNumber",
|
||||
new { MisNumber = testMisNumber });
|
||||
|
||||
finalCurrCount.Should().Be(initialCurrCount, "Debug mode should not modify data");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await CleanupMisTestDataAsync(testMisNumber);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task usp_ProcessMisStagingData_NewRecord_InsertsToMisDataCurr()
|
||||
{
|
||||
// Arrange
|
||||
var testMisNumber = "TEST_INSERT_001";
|
||||
await CleanupMisTestDataAsync(testMisNumber);
|
||||
|
||||
try
|
||||
{
|
||||
// Insert test data into staging
|
||||
await Connection.ExecuteAsync(
|
||||
@"INSERT INTO mis_temp (MIS_IIS_Number, PartNumber, Site, Version, CharacterNumber,
|
||||
TestDescription, SamplingType, SamplingValue, ToolsGauges, WorkInstructions, Release_Date)
|
||||
VALUES (@MisNumber, '12345', 'SITE1', 'A', '1',
|
||||
'Test Description', 'Random', '5', 'Gauge1', 'Instruction1', @ReleaseDate)",
|
||||
new { MisNumber = testMisNumber, ReleaseDate = DateTime.Now });
|
||||
|
||||
// Act - run with SaveChanges = 1
|
||||
await Connection.ExecuteAsync(
|
||||
"EXEC dbo.usp_ProcessMisStagingData @SaveChanges = 1");
|
||||
|
||||
// Assert - record should be inserted to MisData_Curr with Status = 'Current'
|
||||
var record = await Connection.QuerySingleOrDefaultAsync<dynamic>(
|
||||
@"SELECT MisNumber, ItemNumber, BranchCode, RevID, CharNumber, Status, ObsoleteDate
|
||||
FROM MisData_Curr
|
||||
WHERE MisNumber = @MisNumber",
|
||||
new { MisNumber = testMisNumber });
|
||||
|
||||
record.Should().NotBeNull("Record should be inserted to MisData_Curr");
|
||||
((string)record.Status).Should().Be("Current");
|
||||
((DateTime?)record.ObsoleteDate).Should().BeNull();
|
||||
}
|
||||
finally
|
||||
{
|
||||
await CleanupMisTestDataAsync(testMisNumber);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task usp_ProcessMisStagingData_NewerVersion_MovesOldToHistory()
|
||||
{
|
||||
// Arrange
|
||||
var testMisNumber = "TEST_VERSION_001";
|
||||
await CleanupMisTestDataAsync(testMisNumber);
|
||||
|
||||
try
|
||||
{
|
||||
var oldReleaseDate = DateTime.Now.AddDays(-30);
|
||||
var newReleaseDate = DateTime.Now;
|
||||
|
||||
// Insert existing current record (version A)
|
||||
await Connection.ExecuteAsync(
|
||||
@"INSERT INTO MisData_Curr (ItemNumber, BranchCode, SequenceNumber, MisNumber, RevID, CharNumber,
|
||||
TestDescription, SamplingType, SamplingValue, ToolsGauges, WorkInstructions,
|
||||
Status, ReleaseDate, ObsoleteDate)
|
||||
VALUES ('12345', 'SITE1', '10', @MisNumber, 'A', '1',
|
||||
'Old Description', 'Random', '5', 'Gauge1', 'Instruction1',
|
||||
'Current', @ReleaseDate, NULL)",
|
||||
new { MisNumber = testMisNumber, ReleaseDate = oldReleaseDate });
|
||||
|
||||
// Insert newer version (version B) into staging
|
||||
await Connection.ExecuteAsync(
|
||||
@"INSERT INTO mis_temp (MIS_IIS_Number, PartNumber, Site, Version, CharacterNumber, OperationNumber,
|
||||
TestDescription, SamplingType, SamplingValue, ToolsGauges, WorkInstructions, Release_Date)
|
||||
VALUES (@MisNumber, '12345', 'SITE1', 'B', '1', '10',
|
||||
'New Description', 'Random', '5', 'Gauge1', 'Instruction1', @ReleaseDate)",
|
||||
new { MisNumber = testMisNumber, ReleaseDate = newReleaseDate });
|
||||
|
||||
// Act
|
||||
await Connection.ExecuteAsync(
|
||||
"EXEC dbo.usp_ProcessMisStagingData @SaveChanges = 1");
|
||||
|
||||
// Assert - old version should be in history with BackLevel status
|
||||
var histRecord = await Connection.QuerySingleOrDefaultAsync<dynamic>(
|
||||
@"SELECT MisNumber, RevID, Status, ObsoleteDate
|
||||
FROM MisData_Hist
|
||||
WHERE MisNumber = @MisNumber AND RevID = 'A'",
|
||||
new { MisNumber = testMisNumber });
|
||||
|
||||
histRecord.Should().NotBeNull("Old version should be moved to MisData_Hist");
|
||||
((string)histRecord.Status).Should().Be("BackLevel");
|
||||
((DateTime?)histRecord.ObsoleteDate).Should().NotBeNull();
|
||||
|
||||
// Assert - new version should be in current with Current status
|
||||
var currRecord = await Connection.QuerySingleOrDefaultAsync<dynamic>(
|
||||
@"SELECT MisNumber, RevID, Status, ObsoleteDate
|
||||
FROM MisData_Curr
|
||||
WHERE MisNumber = @MisNumber AND RevID = 'B'",
|
||||
new { MisNumber = testMisNumber });
|
||||
|
||||
currRecord.Should().NotBeNull("New version should be in MisData_Curr");
|
||||
((string)currRecord.Status).Should().Be("Current");
|
||||
((DateTime?)currRecord.ObsoleteDate).Should().BeNull();
|
||||
}
|
||||
finally
|
||||
{
|
||||
await CleanupMisTestDataAsync(testMisNumber);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task usp_ProcessMisStagingData_IISPrefix_IsRemoved()
|
||||
{
|
||||
// Arrange
|
||||
var testMisNumber = "TEST_PREFIX_001";
|
||||
var prefixedMisNumber = $"IIS_{testMisNumber}";
|
||||
await CleanupMisTestDataAsync(testMisNumber);
|
||||
|
||||
try
|
||||
{
|
||||
// Insert test data with IIS_ prefix
|
||||
await Connection.ExecuteAsync(
|
||||
@"INSERT INTO mis_temp (MIS_IIS_Number, PartNumber, Site, Version, CharacterNumber,
|
||||
TestDescription, SamplingType, SamplingValue, ToolsGauges, WorkInstructions, Release_Date)
|
||||
VALUES (@MisNumber, '12345', 'SITE1', 'A', '1',
|
||||
'Test Description', 'Random', '5', 'Gauge1', 'Instruction1', @ReleaseDate)",
|
||||
new { MisNumber = prefixedMisNumber, ReleaseDate = DateTime.Now });
|
||||
|
||||
// Act
|
||||
await Connection.ExecuteAsync(
|
||||
"EXEC dbo.usp_ProcessMisStagingData @SaveChanges = 1");
|
||||
|
||||
// Assert - record should be inserted without the IIS_ prefix
|
||||
var record = await Connection.QuerySingleOrDefaultAsync<dynamic>(
|
||||
@"SELECT MisNumber FROM MisData_Curr WHERE MisNumber = @MisNumber",
|
||||
new { MisNumber = testMisNumber });
|
||||
|
||||
record.Should().NotBeNull("Record should be inserted with IIS_ prefix removed");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await CleanupMisTestDataAsync(testMisNumber);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task usp_ProcessMisStagingData_EmptyStagingTable_CompletesSuccessfully()
|
||||
{
|
||||
// Arrange - ensure staging table is empty for our test data
|
||||
var testMisNumber = "TEST_EMPTY_001";
|
||||
await CleanupMisTestDataAsync(testMisNumber);
|
||||
|
||||
// Act - should complete without error even with empty staging table
|
||||
var act = async () => await Connection.ExecuteAsync(
|
||||
"EXEC dbo.usp_ProcessMisStagingData @SaveChanges = 1");
|
||||
|
||||
// Assert
|
||||
await act.Should().NotThrowAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using Dapper;
|
||||
using FluentAssertions;
|
||||
using JdeScoping.Database.Tests.Infrastructure;
|
||||
|
||||
namespace JdeScoping.Database.Tests.Procedures;
|
||||
|
||||
/// <summary>
|
||||
/// Tests to verify that all required stored procedures exist in the database.
|
||||
/// These tests ensure the migration scripts properly create all expected procedures.
|
||||
/// </summary>
|
||||
[Collection("DatabaseTests")]
|
||||
public class StoredProcedureExistsTests : DatabaseTestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies that a stored procedure exists in the database.
|
||||
/// </summary>
|
||||
private async Task<bool> ProcedureExistsAsync(string procedureName)
|
||||
{
|
||||
var result = await Connection.QuerySingleOrDefaultAsync<int>(
|
||||
@"SELECT COUNT(*)
|
||||
FROM sys.procedures
|
||||
WHERE name = @Name AND schema_id = SCHEMA_ID('dbo')",
|
||||
new { Name = procedureName });
|
||||
return result > 0;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task usp_SubmitSearch_Exists()
|
||||
{
|
||||
var exists = await ProcedureExistsAsync("usp_SubmitSearch");
|
||||
exists.Should().BeTrue("usp_SubmitSearch should be created by migration 040");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task usp_StartSearch_Exists()
|
||||
{
|
||||
var exists = await ProcedureExistsAsync("usp_StartSearch");
|
||||
exists.Should().BeTrue("usp_StartSearch should be created by migration 041");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task usp_CompleteSearch_Exists()
|
||||
{
|
||||
var exists = await ProcedureExistsAsync("usp_CompleteSearch");
|
||||
exists.Should().BeTrue("usp_CompleteSearch should be created by migration 042");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task usp_ResetPartialSearches_Exists()
|
||||
{
|
||||
var exists = await ProcedureExistsAsync("usp_ResetPartialSearches");
|
||||
exists.Should().BeTrue("usp_ResetPartialSearches should be created by migration 043");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task usp_ValidateSearchCriteria_Exists()
|
||||
{
|
||||
var exists = await ProcedureExistsAsync("usp_ValidateSearchCriteria");
|
||||
exists.Should().BeTrue("usp_ValidateSearchCriteria should be created by migration 048");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task usp_ProcessMisStagingData_Exists()
|
||||
{
|
||||
var exists = await ProcedureExistsAsync("usp_ProcessMisStagingData");
|
||||
exists.Should().BeTrue("usp_ProcessMisStagingData should be created by migration 049");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AllStoredProcedures_HaveExpectedCount()
|
||||
{
|
||||
// This test ensures we haven't accidentally dropped any procedures
|
||||
var expectedProcedures = new[]
|
||||
{
|
||||
"usp_SubmitSearch",
|
||||
"usp_StartSearch",
|
||||
"usp_CompleteSearch",
|
||||
"usp_ResetPartialSearches",
|
||||
"usp_ValidateSearchCriteria",
|
||||
"usp_ProcessMisStagingData"
|
||||
};
|
||||
|
||||
var actualCount = await Connection.QuerySingleAsync<int>(
|
||||
@"SELECT COUNT(*)
|
||||
FROM sys.procedures
|
||||
WHERE schema_id = SCHEMA_ID('dbo')
|
||||
AND name LIKE 'usp_%'");
|
||||
|
||||
actualCount.Should().BeGreaterThanOrEqualTo(
|
||||
expectedProcedures.Length,
|
||||
$"Database should have at least {expectedProcedures.Length} stored procedures");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user