Auto: focas-f4d — password / unlock parameter

Closes #271
This commit is contained in:
Joseph Doherty
2026-04-26 05:45:13 -04:00
parent d676b4056d
commit 86f3fc2733
16 changed files with 1016 additions and 40 deletions

View File

@@ -40,6 +40,37 @@ internal class FakeFocasClient : IFocasClient
return Task.CompletedTask;
}
/// <summary>
/// Plan PR F4-d (issue #271) — count of <see cref="UnlockAsync"/> invocations.
/// Tests assert this to verify the driver routed <c>cnc_wrunlockparam</c> on
/// connect when <c>FocasDeviceOptions.Password</c> was non-null and re-issued
/// unlock on EW_PASSWD retry exactly once.
/// </summary>
public int UnlockCount { get; private set; }
/// <summary>
/// Plan PR F4-d (issue #271) — last password observed by <see cref="UnlockAsync"/>.
/// Used by the round-trip test to confirm the driver passed the configured
/// password through unmodified. <b>This field exists ONLY in the fake — no
/// production wire client retains the password past the wire call.</b>
/// </summary>
public string? LastUnlockPassword { get; private set; }
/// <summary>
/// Plan PR F4-d (issue #271) — when set, <see cref="UnlockAsync"/> throws on
/// invocation so tests can drive the failed-unlock retry path (where the
/// driver surfaces BadUserAccessDenied as-is rather than retrying).
/// </summary>
public bool ThrowOnUnlock { get; set; }
public virtual Task UnlockAsync(string password, CancellationToken ct)
{
UnlockCount++;
LastUnlockPassword = password;
if (ThrowOnUnlock) throw Exception ?? new InvalidOperationException("Unlock fails");
return Task.CompletedTask;
}
public virtual Task<(object? value, uint status)> ReadAsync(
FocasAddress address, FocasDataType type, CancellationToken ct)
{