using ZB.MOM.WW.Health.Akka;

namespace ZB.MOM.WW.Health.Akka.Tests;

/// <summary>
/// Covers <see cref="ClusterActiveNode.ResolveActiveRole"/> — which role a node competes for. Pure,
/// so no cluster is needed; the age-ordering half of the rule is pinned in
/// <see cref="ClusterActiveNodeTests"/> against a real two-node cluster.
/// </summary>
public sealed class ActiveRoleResolutionTests
{
    [Fact]
    public void EmptyPreference_IsUnscoped()
    {
        // Unscoped: every Up member competes for one active slot (the ScadaBridge central pattern).
        Assert.Null(ClusterActiveNode.ResolveActiveRole(["admin", "driver"], []));
    }

    [Fact]
    public void FusedNode_AnswersForTheHighestPrecedenceRoleItCarries()
    {
        Assert.Equal(
            "admin",
            ClusterActiveNode.ResolveActiveRole(["admin", "driver", "cluster-MAIN"], ["admin", "driver"]));
    }

    [Fact]
    public void SingleRoleNode_AnswersForThatRole()
    {
        // The regression the OtOpcUa fix existed for: under the old role-filtered check every
        // driver-only node reported Healthy — "or not a role member" — so all of them called
        // themselves active and no consumer could find the active node of a site cluster. lmxopcua#494.
        Assert.Equal(
            "driver",
            ClusterActiveNode.ResolveActiveRole(["driver", "cluster-SITE-A"], ["admin", "driver"]));
    }

    [Fact]
    public void PreferenceOrderWins_NotTheOrderTheNodeListsItsRoles()
    {
        // Self-role order is incidental (it comes from config parsing); precedence must come from the
        // caller's list alone, or a fused node's answer would depend on how its roles were spelled.
        Assert.Equal(
            "admin",
            ClusterActiveNode.ResolveActiveRole(["driver", "admin"], ["admin", "driver"]));
    }

    [Fact]
    public void NodeCarryingNoneOfThePreferredRoles_ResolvesToNull()
    {
        // Reachable, not merely defensive: OtOpcUa's RoleParser admits a dev-only or cluster-role-only
        // node. Such a node owns no active work — the health check maps this to Unhealthy so it stays
        // out of the active pool.
        Assert.Null(ClusterActiveNode.ResolveActiveRole(["dev", "cluster-MAIN"], ["admin", "driver"]));
    }
}
