using Shouldly;
using Xunit;
namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
///
/// Pins the dependency shape of ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts: it must not
/// reference the OPC UA client SDK.
///
/// Why this matters beyond tidiness. Deferment-register G-7 wired
/// into the deploy-time TagConfig gate, which
/// made ControlPlane project-reference this Contracts assembly. ControlPlane is loaded by
/// every admin- and driver-role host, so whatever Contracts drags in, they all carry. Contracts
/// used to hold NamespaceMap — ISession/NodeId-typed throughout — and with it a
/// OPCFoundation.NetStandard.Opc.Ua.Client PackageReference; NamespaceMap moved to the
/// sibling .Session leaf so the parse-only half could stay light (the same shape as
/// Calculation.Contracts: Core.Abstractions and nothing else).
///
///
/// Direction matters: this is an ABSENCE assertion, and that is what makes it sound here.
/// The assembly manifest is an unreliable witness for PRESENCE — the C# compiler elides a reference
/// nothing in the IL actually uses, so "assembly X is referenced" can be false while the csproj says
/// otherwise (the trap EquipmentTagConfigInspectorCoverageTests documents, which is why that
/// one scans the output directory instead). Absence has no such hole in the direction that matters:
/// the moment someone adds Opc.Ua-typed code to Contracts, the compiler MUST emit the reference and
/// this test goes red. A csproj PackageReference that nothing consumes is elided and slips past —
/// accepted deliberately, because an unused package reference is not the failure being guarded.
///
///
/// Falsified 2026-08-09 by temporarily adding an Opc.Ua.NodeId-typed member to Contracts:
/// the assertion failed naming Opc.Ua.Core, and passed again once removed.
///
///
public sealed class ContractsIsDependencyLightTests
{
/// The Contracts assembly's manifest must name no OPC UA SDK assembly.
[Fact]
public void The_Contracts_assembly_does_not_reference_the_OPC_UA_SDK()
{
var contracts = typeof(OpcUaClientTagDefinitionFactory).Assembly;
// Positive control: name the assembly under test, so a rename/retarget cannot silently point this
// at something else that trivially satisfies the assertion below.
contracts.GetName().Name.ShouldBe("ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts");
var opcUa = contracts.GetReferencedAssemblies()
.Select(a => a.Name ?? string.Empty)
.Where(n => n.StartsWith("Opc.Ua", StringComparison.OrdinalIgnoreCase))
.OrderBy(n => n, StringComparer.Ordinal)
.ToArray();
opcUa.ShouldBeEmpty(
"OpcUaClient.Contracts must stay free of the OPC UA client SDK — ControlPlane project-references "
+ "it for the G-7 deploy-time TagConfig gate, so every host that loads the control plane would "
+ "carry the stack. Put SDK-typed code in Driver.OpcUaClient.Session instead. Found: "
+ string.Join(", ", opcUa));
}
///
/// The counterpart the assertion above is meaningful against: the SDK-typed half really did land
/// somewhere, and that somewhere does reference the SDK. Without this, "no Opc.Ua reference
/// anywhere" would read as a pass.
///
[Fact]
public void The_Session_assembly_is_where_the_OPC_UA_SDK_reference_lives()
{
var session = typeof(NamespaceMap).Assembly;
session.GetName().Name.ShouldBe("ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Session");
session.GetReferencedAssemblies()
.Select(a => a.Name ?? string.Empty)
.ShouldContain(n => n.StartsWith("Opc.Ua", StringComparison.OrdinalIgnoreCase));
}
}