e3dcb180c3
Review R1: - M1 (#609): DriverHostActor.StopChild releases the instance's resilience state before evicting the hub. - m2 (#597): DriverHostActor publishes ShouldServiceAsPrimary() to IDriverPrimaryRoleSource at start and on cluster membership changes (deduped), so the native-ack gate denies in a pair's boot window. - m4 (#601): ApplyCachedArtifact returns false on empty/malformed bytes; the cached revision is not adopted, Direct mode goes Stale (no RUNNING FROM CACHE), FetchAndCache lands Steady-with-no-revision and a re-dispatch of the same revision really applies. - m3 (#603): LocalDbReplicationPolicy splits into CapturesChanges (PeerAddress||ApiKey, registration) and SharesRowsWithPeer (ApiKey && (PeerAddress||SyncListenPort>0), alarm drain gate). - m5 (#618): Program.cs catch-all filters HostAbortedException (design-time tooling). - m6 (Cluster-002): ClusterRoleInfo seeds only Up/WeaklyUp members. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
68 lines
3.3 KiB
C#
68 lines
3.3 KiB
C#
using Microsoft.Extensions.Configuration;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Historian;
|
|
|
|
/// <summary>
|
|
/// #603 + review R1 m3: the two "is this node's LocalDb replicated" predicates — permissive for table
|
|
/// registration (<see cref="LocalDbReplicationPolicy.CapturesChanges"/>), strict for the alarm drain gate
|
|
/// (<see cref="LocalDbReplicationPolicy.SharesRowsWithPeer"/>).
|
|
/// </summary>
|
|
public sealed class LocalDbReplicationPolicyTests
|
|
{
|
|
private const string Peer = "LocalDb:Replication:PeerAddress";
|
|
private const string Key = "LocalDb:Replication:ApiKey";
|
|
private const string Port = "LocalDb:SyncListenPort";
|
|
|
|
private static IConfiguration Config(params (string Key, string? Value)[] pairs) =>
|
|
new ConfigurationBuilder()
|
|
.AddInMemoryCollection(pairs.Select(p => new KeyValuePair<string, string?>(p.Key, p.Value)))
|
|
.Build();
|
|
|
|
/// <summary>Registration: PeerAddress OR ApiKey counts; the listen port alone and blank values do not.</summary>
|
|
[Theory]
|
|
[InlineData(Peer, "http://peer:9001", true)]
|
|
[InlineData(Key, "k", true)]
|
|
[InlineData(Port, "9001", false)]
|
|
[InlineData(Key, " ", false)]
|
|
public void CapturesChanges_follows_the_PeerAddress_or_ApiKey_rule(string key, string value, bool expected)
|
|
{
|
|
LocalDbReplicationPolicy.CapturesChanges(Config((key, value))).ShouldBe(expected);
|
|
}
|
|
|
|
/// <summary>Drain gate: ApiKey AND (PeerAddress OR SyncListenPort > 0).</summary>
|
|
[Theory]
|
|
[InlineData("http://peer:9001", "k", null, true)] // dialling half of a pair
|
|
[InlineData(null, "k", "9001", true)] // listening half of a pair
|
|
[InlineData("http://peer:9001", "k", "9001", true)] // both
|
|
[InlineData(null, "k", null, false)] // key, no path: shares nothing (review R1 m3)
|
|
[InlineData(null, "k", "0", false)] // port 0 = no listener
|
|
[InlineData(null, "k", "nope", false)] // unparseable port = no listener
|
|
[InlineData("http://peer:9001", null, "9001", false)] // paths, no key: both auth directions fail closed
|
|
[InlineData(null, " ", "9001", false)] // blank key
|
|
public void SharesRowsWithPeer_needs_a_key_and_a_sync_path(string? peer, string? key, string? port, bool expected)
|
|
{
|
|
LocalDbReplicationPolicy.SharesRowsWithPeer(Config((Peer, peer), (Key, key), (Port, port))).ShouldBe(expected);
|
|
}
|
|
|
|
/// <summary>Every node the gate treats as sharing rows also captures — never a gated node with no capture.</summary>
|
|
[Theory]
|
|
[InlineData("http://peer:9001", "k", null)]
|
|
[InlineData(null, "k", "9001")]
|
|
public void A_node_sharing_rows_always_captures(string? peer, string? key, string? port)
|
|
{
|
|
var config = Config((Peer, peer), (Key, key), (Port, port));
|
|
LocalDbReplicationPolicy.SharesRowsWithPeer(config).ShouldBeTrue();
|
|
LocalDbReplicationPolicy.CapturesChanges(config).ShouldBeTrue();
|
|
}
|
|
|
|
/// <summary>No replication keys at all is an unpaired node under both predicates.</summary>
|
|
[Fact]
|
|
public void Both_predicates_are_false_for_an_empty_configuration()
|
|
{
|
|
LocalDbReplicationPolicy.CapturesChanges(Config()).ShouldBeFalse();
|
|
LocalDbReplicationPolicy.SharesRowsWithPeer(Config()).ShouldBeFalse();
|
|
}
|
|
}
|