fix(cluster): fail start when a late config layer changes Cluster:Roles (role overlays may not set it)

This commit is contained in:
Joseph Doherty
2026-08-20 10:11:25 -04:00
parent 3314a1f98d
commit 4380e2e78a
3 changed files with 92 additions and 1 deletions
@@ -214,4 +214,53 @@ public sealed class AkkaClusterOptionsValidatorTests
result.Failed.ShouldBeFalse(result.FailureMessage);
}
// ---- R3-04 choice 4: the post-overlay config-layer guard --------------------------------------
// A role overlay (or a ${secret:} expansion) may not itself set Cluster:Roles — Program.cs
// resolves EffectiveRoles BEFORE the overlay loads (it needs the answer to pick the overlay's
// filename), so if a later config layer changed Cluster:Roles the bound options would disagree
// with every consumer holding the registered EffectiveRoles instance.
[Fact]
public void A_config_layer_that_changes_Cluster_Roles_after_resolution_fails_naming_both_views()
{
var effectiveRoles = EffectiveRoles.Resolve(["driver"], null);
var options = CentralNode("site-a-1", Seed("site-a-1"));
options.Roles = new[] { "admin", "driver" }; // the post-overlay bound value disagrees
var result = new AkkaClusterOptionsValidator(effectiveRoles).Validate(null, options);
result.Failed.ShouldBeTrue();
result.FailureMessage.ShouldContain("Cluster:Roles");
result.FailureMessage.ShouldContain("driver");
result.FailureMessage.ShouldContain("admin");
}
[Fact]
public void Bound_Roles_matching_the_registered_EffectiveRoles_passes()
{
var effectiveRoles = EffectiveRoles.Resolve(["driver", "cluster-SITE-A"], null);
var options = CentralNode("site-a-1", Seed("site-a-1"));
options.Roles = new[] { "driver", "cluster-SITE-A" };
var result = new AkkaClusterOptionsValidator(effectiveRoles).Validate(null, options);
result.Failed.ShouldBeFalse(result.FailureMessage);
}
/// <summary>
/// A test that never injects <see cref="EffectiveRoles"/> — the pre-T7 construction shape —
/// must keep passing. Without this, giving the ctor a required parameter would silently break
/// every other test in this file rather than a chosen few.
/// </summary>
[Fact]
public void No_EffectiveRoles_injected_skips_the_guard_entirely()
{
var options = CentralNode("site-a-1", Seed("site-a-1"));
options.Roles = new[] { "whatever-this-does-not-validate-roles-either" };
var result = new AkkaClusterOptionsValidator().Validate(null, options);
result.Failed.ShouldBeFalse(result.FailureMessage);
}
}