117 lines
3.9 KiB
C#
117 lines
3.9 KiB
C#
using KronaStaffTerminal.Models;
|
|
using KronaStaffTerminal.Services;
|
|
using KronaStaffTerminal.Tests.Mocks;
|
|
using Xunit;
|
|
|
|
namespace KronaStaffTerminal.Tests;
|
|
|
|
public class ScannerRecoveryTests
|
|
{
|
|
[Fact]
|
|
public void Scanner_SimulatesUsbDisconnectAndFiresConnectionLost()
|
|
{
|
|
using var scanner = new MockFingerprintScanner();
|
|
scanner.Connect(0, Array.Empty<(int, string)>());
|
|
Assert.True(scanner.IsConnected);
|
|
Assert.Equal(ScannerState.Connected, scanner.State);
|
|
|
|
var connectionLostFired = false;
|
|
ScannerState? reportedState = null;
|
|
|
|
scanner.ConnectionLost += () => connectionLostFired = true;
|
|
scanner.StateChanged += (state, _) => reportedState = state;
|
|
|
|
// Simulate physical USB drop
|
|
scanner.SimulateDisconnect();
|
|
|
|
Assert.False(scanner.IsConnected);
|
|
Assert.Equal(ScannerState.Disconnected, scanner.State);
|
|
Assert.True(connectionLostFired);
|
|
Assert.Equal(ScannerState.Disconnected, reportedState);
|
|
}
|
|
|
|
[Fact]
|
|
public void Scanner_SimulatesUsbReconnectAndRestoresReadyState()
|
|
{
|
|
using var scanner = new MockFingerprintScanner();
|
|
scanner.Connect(0, Array.Empty<(int, string)>());
|
|
scanner.SimulateDisconnect();
|
|
Assert.Equal(ScannerState.Disconnected, scanner.State);
|
|
|
|
ScannerState? reportedState = null;
|
|
scanner.StateChanged += (state, _) => reportedState = state;
|
|
|
|
// Simulate USB re-plug
|
|
scanner.SimulateReconnect();
|
|
|
|
Assert.True(scanner.IsConnected);
|
|
Assert.Equal(ScannerState.Connected, scanner.State);
|
|
Assert.Equal(ScannerState.Connected, reportedState);
|
|
}
|
|
|
|
[Fact]
|
|
public void AdminPinVerification_OperatesNormallyWhenScannerIsDisconnected()
|
|
{
|
|
using var scanner = new MockFingerprintScanner();
|
|
scanner.SimulateDisconnect();
|
|
Assert.Equal(ScannerState.Disconnected, scanner.State);
|
|
|
|
// Verify that AdminPinService functions 100% independently of hardware scanner state
|
|
var (salt, hash, iterations) = AdminPinService.Hash("5555");
|
|
var settings = new TerminalSettings
|
|
{
|
|
AdminPinSalt = salt,
|
|
AdminPinHash = hash,
|
|
AdminPinIterations = iterations
|
|
};
|
|
|
|
var now = DateTimeOffset.UtcNow;
|
|
var allowed = AdminPinService.VerifyAndRecord(
|
|
"5555", settings, now, out var error, out var rehashNeeded);
|
|
|
|
Assert.True(allowed);
|
|
Assert.Null(error);
|
|
Assert.False(rehashNeeded);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Scanner_AutoReconnectLogic_CallsConnectSuccessfully()
|
|
{
|
|
using var scanner = new MockFingerprintScanner();
|
|
scanner.Connect(0, Array.Empty<(int, string)>());
|
|
Assert.Equal(1, scanner.ConnectCallCount);
|
|
|
|
var reconnectTcs = new TaskCompletionSource();
|
|
scanner.ConnectionLost += async () =>
|
|
{
|
|
// Simulate typical application retry loop
|
|
int retries = 0;
|
|
while (retries < 5)
|
|
{
|
|
await Task.Delay(10);
|
|
try
|
|
{
|
|
scanner.Connect(0, Array.Empty<(int, string)>());
|
|
if (scanner.IsConnected)
|
|
{
|
|
reconnectTcs.TrySetResult();
|
|
return;
|
|
}
|
|
}
|
|
catch { }
|
|
retries++;
|
|
}
|
|
reconnectTcs.TrySetException(new Exception("Failed to reconnect"));
|
|
};
|
|
|
|
scanner.SimulateDisconnect();
|
|
// Emulate the hardware coming back after a short delay
|
|
await Task.Delay(20);
|
|
scanner.AddVirtualDevice(0, "SLK20R", "SERIAL"); // ensure device is discoverable if it was cleared
|
|
|
|
await reconnectTcs.Task;
|
|
|
|
Assert.True(scanner.ConnectCallCount > 1);
|
|
Assert.Equal(ScannerState.Connected, scanner.State);
|
|
}
|
|
}
|