Phase 0 WP-0.10–0.12: Host skeleton, options classes, sample configs, and execution framework
- WP-0.10: Role-based Host startup (Central=WebApplication, Site=generic Host), 15 component AddXxx() extension methods, MapCentralUI/MapInboundAPI stubs - WP-0.11: 12 per-component options classes with config binding - WP-0.12: Sample appsettings for central and site topologies - Add execution procedure and checklist template to generate_plans.md - Add phase-0-checklist.md for execution tracking - Resolve all 21 open questions from plan generation - Update IDataConnection with batch ops and IAsyncDisposable 57 tests pass, zero warnings.
This commit is contained in:
65
tests/ScadaLink.Host.Tests/OptionsTests.cs
Normal file
65
tests/ScadaLink.Host.Tests/OptionsTests.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace ScadaLink.Host.Tests;
|
||||
|
||||
public class OptionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verify no component library (excluding Host) uses IConfiguration or accepts it
|
||||
/// in its AddXxx() extension method. Component libraries should only depend on
|
||||
/// DI abstractions and Options pattern, not on Configuration directly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ComponentLibraries_DoNotAcceptIConfigurationInAddMethods()
|
||||
{
|
||||
// All component assemblies (excluding Host itself and Commons)
|
||||
var componentAssemblies = new[]
|
||||
{
|
||||
typeof(ClusterInfrastructure.ServiceCollectionExtensions).Assembly,
|
||||
typeof(Communication.ServiceCollectionExtensions).Assembly,
|
||||
typeof(HealthMonitoring.ServiceCollectionExtensions).Assembly,
|
||||
typeof(ExternalSystemGateway.ServiceCollectionExtensions).Assembly,
|
||||
typeof(NotificationService.ServiceCollectionExtensions).Assembly,
|
||||
typeof(TemplateEngine.ServiceCollectionExtensions).Assembly,
|
||||
typeof(DeploymentManager.ServiceCollectionExtensions).Assembly,
|
||||
typeof(Security.ServiceCollectionExtensions).Assembly,
|
||||
typeof(ConfigurationDatabase.ServiceCollectionExtensions).Assembly,
|
||||
typeof(SiteRuntime.ServiceCollectionExtensions).Assembly,
|
||||
typeof(DataConnectionLayer.ServiceCollectionExtensions).Assembly,
|
||||
typeof(StoreAndForward.ServiceCollectionExtensions).Assembly,
|
||||
typeof(SiteEventLogging.ServiceCollectionExtensions).Assembly,
|
||||
typeof(CentralUI.ServiceCollectionExtensions).Assembly,
|
||||
typeof(InboundAPI.ServiceCollectionExtensions).Assembly,
|
||||
};
|
||||
|
||||
foreach (var assembly in componentAssemblies)
|
||||
{
|
||||
// Check that the assembly does not reference Microsoft.Extensions.Configuration
|
||||
var configRef = assembly.GetReferencedAssemblies()
|
||||
.FirstOrDefault(a => a.Name == "Microsoft.Extensions.Configuration.Abstractions");
|
||||
|
||||
// Find all public static extension methods named Add*
|
||||
var extensionClasses = assembly.GetExportedTypes()
|
||||
.Where(t => t.IsClass && t.IsAbstract && t.IsSealed); // static classes
|
||||
|
||||
foreach (var cls in extensionClasses)
|
||||
{
|
||||
var addMethods = cls.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||
.Where(m => m.Name.StartsWith("Add") || m.Name.StartsWith("Map"));
|
||||
|
||||
foreach (var method in addMethods)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
foreach (var param in parameters)
|
||||
{
|
||||
Assert.False(
|
||||
typeof(IConfiguration).IsAssignableFrom(param.ParameterType),
|
||||
$"{assembly.GetName().Name}: {cls.Name}.{method.Name} accepts IConfiguration parameter '{param.Name}'. " +
|
||||
"Component libraries should use the Options pattern, not IConfiguration.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user