Files
lmxopcua/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/Audit/AuditActorTests.cs
T
Joseph Doherty 075c0e69da
v2-ci / build (push) Failing after 40s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
feat(audit): OtOpcUa IAuditActorAccessor seam + HTTP impl (audit Actor from Auth principal) (Phase 3)
Introduces the IAuditActorAccessor seam and HttpAuditActorAccessor impl so the
ZB.MOM.WW.Audit.AuditEvent Actor field can be sourced from the authenticated Blazor
cookie principal (ZbClaimTypes.Username) when structured emitters are added. Adds the
AuditActor.Resolve static helper (accessor value → SystemFallback/"system") as the
canonical pattern for future emit sites. Wires DI in AddOtOpcUaAuth (TryAddScoped) with
AddHttpContextAccessor(). The structured AuditEvent path remains DORMANT — no live emit
sites exist; seam is forward-looking. SP-based audit path left untouched. 9 new unit
tests all green; Security (54) and ControlPlane (45) test suites fully pass.
2026-06-02 15:25:49 -04:00

82 lines
2.9 KiB
C#

using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Security.Audit;
namespace ZB.MOM.WW.OtOpcUa.Security.Tests.Audit;
/// <summary>
/// Unit tests for <see cref="AuditActor"/> — the static resolution helper that sources the
/// <c>Actor</c> field of a canonical <c>ZB.MOM.WW.Audit.AuditEvent</c> from the current
/// HTTP principal and falls back to a configurable value when no principal is available.
/// </summary>
public sealed class AuditActorTests
{
/// <summary>
/// <see cref="AuditActor.Resolve(IAuditActorAccessor?)"/> returns the accessor's value
/// when the accessor returns a non-null string.
/// </summary>
[Fact]
public void Resolve_returns_accessor_value_when_present()
{
var accessor = new StubAccessor("alice");
AuditActor.Resolve(accessor).ShouldBe("alice");
}
/// <summary>
/// <see cref="AuditActor.Resolve(IAuditActorAccessor?)"/> returns
/// <see cref="AuditActor.SystemFallback"/> when the accessor returns null
/// (unauthenticated / no HTTP context).
/// </summary>
[Fact]
public void Resolve_returns_system_fallback_when_accessor_returns_null()
{
var accessor = new StubAccessor(null);
AuditActor.Resolve(accessor).ShouldBe(AuditActor.SystemFallback);
}
/// <summary>
/// <see cref="AuditActor.Resolve(IAuditActorAccessor?)"/> returns
/// <see cref="AuditActor.SystemFallback"/> when the accessor reference itself is null
/// (e.g. in a background/non-HTTP context where DI did not inject the accessor).
/// </summary>
[Fact]
public void Resolve_returns_system_fallback_when_accessor_is_null()
{
AuditActor.Resolve(null).ShouldBe(AuditActor.SystemFallback);
}
/// <summary>
/// <see cref="AuditActor.Resolve(IAuditActorAccessor?,string)"/> uses the explicit
/// fallback string rather than <see cref="AuditActor.SystemFallback"/> when the accessor
/// returns null.
/// </summary>
[Fact]
public void Resolve_uses_explicit_fallback_when_accessor_returns_null()
{
var accessor = new StubAccessor(null);
AuditActor.Resolve(accessor, "scheduler").ShouldBe("scheduler");
}
/// <summary>
/// <see cref="AuditActor.Resolve(IAuditActorAccessor?,string)"/> prefers the accessor's
/// value over the explicit fallback when the accessor returns a non-null string.
/// </summary>
[Fact]
public void Resolve_prefers_accessor_value_over_explicit_fallback()
{
var accessor = new StubAccessor("bob");
AuditActor.Resolve(accessor, "scheduler").ShouldBe("bob");
}
// ── stub ──────────────────────────────────────────────────────────────────────
private sealed class StubAccessor(string? value) : IAuditActorAccessor
{
public string? CurrentActor { get; } = value;
}
}