diff --git a/DesignPatterns.Samples.PluginAssemblies/Providers.AlphaConflict/ConflictCard.cs b/DesignPatterns.Samples.PluginAssemblies/Providers.AlphaConflict/ConflictCard.cs new file mode 100644 index 0000000..1ff83c9 --- /dev/null +++ b/DesignPatterns.Samples.PluginAssemblies/Providers.AlphaConflict/ConflictCard.cs @@ -0,0 +1,10 @@ +using DesignPatterns.Behavioral; +using PluginAssemblies.Sample.Contracts; + +namespace PluginAssemblies.Sample.Providers.AlphaConflict; + +[RegisterStrategy("alpha")] +public sealed class ConflictCard : ICardMotion +{ + public string ProviderName => "alpha-conflict"; +} diff --git a/DesignPatterns.Samples.PluginAssemblies/Providers.AlphaConflict/DesignPatterns.Samples.PluginAssemblies.Providers.AlphaConflict.csproj b/DesignPatterns.Samples.PluginAssemblies/Providers.AlphaConflict/DesignPatterns.Samples.PluginAssemblies.Providers.AlphaConflict.csproj new file mode 100644 index 0000000..e56adff --- /dev/null +++ b/DesignPatterns.Samples.PluginAssemblies/Providers.AlphaConflict/DesignPatterns.Samples.PluginAssemblies.Providers.AlphaConflict.csproj @@ -0,0 +1,16 @@ + + + + net8.0 + enable + enable + PluginAssemblies.Sample.Providers.AlphaConflict + true + Duplicate alpha key provider for DP033 demo (plugin-assemblies sample). + + + + + + + diff --git a/DesignPatterns.Samples.PluginAssemblies/README.md b/DesignPatterns.Samples.PluginAssemblies/README.md index c564c90..39a83b4 100644 --- a/DesignPatterns.Samples.PluginAssemblies/README.md +++ b/DesignPatterns.Samples.PluginAssemblies/README.md @@ -9,10 +9,12 @@ Maps to [PluginAssemblies.md](https://github.com/Skymly/DesignPatterns/blob/main ``` Contracts/ Shared interfaces (no provider code) Providers.Alpha/ Card motion — key "alpha" +Providers.AlphaConflict/ Card motion — duplicate key "alpha" (DP033 demo only) Providers.Beta/ Card motion — key "beta" (optional reference) Providers.Gamma/ FC control + error — companion key "gamma" Host/ Autofac host (references Alpha + Gamma, not Beta) Scenarios.InvalidKey/ DP025 compile-time failure demo +Scenarios.DuplicateKey/ DP033 compile-time failure demo ``` Each provider assembly emits its own `{Contract}Registry` in the **contract namespace** (e.g. `PluginAssemblies.Sample.Contracts.CardMotionRegistry` inside `Providers.Alpha.dll`). @@ -24,11 +26,14 @@ Each provider assembly emits its own `{Contract}Registry` in the **contract name | **S1** | `dotnet run --project Host` | Starts; prints `Card=alpha`; `CardMotionRegistry.Keys` contains `alpha`, not `beta` | | **S2** | `dotnet run --project Host -- s2` | Exit code 1; `App.config` key `CardMissing=beta` resolves via `RegistryConfiguration` but `beta` is not registered (Providers.Beta not referenced) | | **S3** | `dotnet test` in sibling DesignPatterns repo (`UnknownRegistryKeyAnalyzerTests`) or IDE on `Scenarios.InvalidKey` | Diagnostic **DP025** for unknown literal key | +| **S4** | `dotnet test` in sibling DesignPatterns repo (`CrossAssemblyRegistryKeyAnalyzerTests`) or IDE on `Scenarios.DuplicateKey` | Diagnostic **DP033** when Alpha and AlphaConflict both register `alpha` | `Host/App.config` selects `Card=alpha` and `FC=gamma` for S1. The host uses `DesignPatterns.Extensions.AppSettings.RegistryConfiguration` to map those keys to strategy registries (`CardMissing=beta` drives S2). `Scenarios.InvalidKey/InvalidKeyUsage.cs` shows the invalid literal pattern; DP025 is enforced by the DesignPatterns analyzer (IDE or NuGet package). Local sibling `ProjectReference` builds may not surface Info-level diagnostics on the command line — CI runs the analyzer unit test instead. +`Scenarios.DuplicateKey` references **Providers.Alpha** and **Providers.AlphaConflict** (both register `ICardMotion` key `alpha`). DP033 is an **Error**; IDE or command-line builds with analyzers should fail. CI falls back to `CrossAssemblyRegistryKeyAnalyzerTests` when the demo project build does not surface DP033. + ## Prerequisites Sibling [DesignPatterns](https://github.com/Skymly/DesignPatterns) clone with `DesignPatterns.Extensions.Autofac` and `DesignPatterns.Extensions.AppSettings` (merged on `main`). @@ -40,4 +45,4 @@ dotnet run --project DesignPatterns.Samples.PluginAssemblies/Host -c Release ## CI -`./build.ps1 --target Ci` builds the host, runs S1, asserts S2 failure output, and runs the sibling DesignPatterns **DP025** analyzer test for S3. +`./build.ps1 --target Ci` builds the host, runs S1, asserts S2 failure output, and runs sibling DesignPatterns analyzer tests for S3 (DP025) and S4 (DP033). diff --git a/DesignPatterns.Samples.PluginAssemblies/Scenarios.DuplicateKey/DesignPatterns.Samples.PluginAssemblies.Scenarios.DuplicateKey.csproj b/DesignPatterns.Samples.PluginAssemblies/Scenarios.DuplicateKey/DesignPatterns.Samples.PluginAssemblies.Scenarios.DuplicateKey.csproj new file mode 100644 index 0000000..d957598 --- /dev/null +++ b/DesignPatterns.Samples.PluginAssemblies/Scenarios.DuplicateKey/DesignPatterns.Samples.PluginAssemblies.Scenarios.DuplicateKey.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + PluginAssemblies.Sample.Scenarios.DuplicateKey + true + Compile-time DP033 demo for the plugin-assemblies sample (see README; CI uses analyzer tests). + + + + + + + + + diff --git a/DesignPatterns.Samples.PluginAssemblies/Scenarios.DuplicateKey/DuplicateKeyUsage.cs b/DesignPatterns.Samples.PluginAssemblies/Scenarios.DuplicateKey/DuplicateKeyUsage.cs new file mode 100644 index 0000000..f072fca --- /dev/null +++ b/DesignPatterns.Samples.PluginAssemblies/Scenarios.DuplicateKey/DuplicateKeyUsage.cs @@ -0,0 +1,12 @@ +namespace PluginAssemblies.Sample.Scenarios.DuplicateKey; + +/// +/// Illustrates DP033: the same strategy key for one contract in multiple referenced provider assemblies. +/// See sibling DesignPatterns repo CrossAssemblyRegistryKeyAnalyzerTests; CI runs that test for S4. +/// +public static class DuplicateKeyUsage +{ + public static void HostReferencesConflictingProviders() + { + } +} diff --git a/build/Program.cs b/build/Program.cs index bc45eb0..ac139d3 100644 --- a/build/Program.cs +++ b/build/Program.cs @@ -37,6 +37,9 @@ static readonly (string RelativePath, bool RunAfterBuild)[] SampleProjects = AbsolutePath PluginAssembliesInvalidKeyProject => Root / "DesignPatterns.Samples.PluginAssemblies/Scenarios.InvalidKey/DesignPatterns.Samples.PluginAssemblies.Scenarios.InvalidKey.csproj"; + AbsolutePath PluginAssembliesDuplicateKeyProject => + Root / "DesignPatterns.Samples.PluginAssemblies/Scenarios.DuplicateKey/DesignPatterns.Samples.PluginAssemblies.Scenarios.DuplicateKey.csproj"; + AbsolutePath DesignPatternsAnalyzerTestsProject => Root / "../DesignPatterns/tests/DesignPatterns.Analyzers.Tests/DesignPatterns.Analyzers.Tests.csproj"; @@ -72,6 +75,7 @@ void RunPluginAssembliesScenarios() { Assert.FileExists(PluginAssembliesHostProject, $"Sample project not found: {PluginAssembliesHostProject}"); Assert.FileExists(PluginAssembliesInvalidKeyProject, $"Sample project not found: {PluginAssembliesInvalidKeyProject}"); + Assert.FileExists(PluginAssembliesDuplicateKeyProject, $"Sample project not found: {PluginAssembliesDuplicateKeyProject}"); DotNetBuild(s => s .SetProjectFile(PluginAssembliesHostProject) @@ -110,6 +114,19 @@ void RunPluginAssembliesScenarios() .SetProjectFile(DesignPatternsAnalyzerTestsProject) .SetConfiguration(Configuration) .SetFilter("FullyQualifiedName~UnknownRegistryKeyAnalyzerTests.ReportsDp025WhenStrategyRegistryKeyIsUnknown")); + + var duplicateKeyBuild = StartDotNet( + $"build \"{PluginAssembliesDuplicateKeyProject}\" -c {Configuration}", + Root); + if (duplicateKeyBuild.ExitCode != 0 && duplicateKeyBuild.Output.Contains("DP033", StringComparison.Ordinal)) + { + return; + } + + DotNetTest(s => s + .SetProjectFile(DesignPatternsAnalyzerTestsProject) + .SetConfiguration(Configuration) + .SetFilter("FullyQualifiedName~CrossAssemblyRegistryKeyAnalyzerTests.ReportsDp033WhenSameStrategyKeyExistsInTwoReferencedAssemblies")); } static (int ExitCode, string Output) StartDotNet(string arguments, AbsolutePath workingDirectory)