Files
AutopilotMonitor/src/Backend/AutopilotMonitor.Functions.Tests/DeviceBlockFunctionTests.cs
T
Oliver Kieselbach 88e779c1e7 feat(device-block): block/kill via session ID + ops alert shortcuts
When an ExcessiveSessionEvents (or other session-bound) ops alert fires,
the operator only has a sessionId in hand. Looking up tenant + serial to
fill the manual Block form was a friction point during incident response.

- Backend: POST /api/devices/block now accepts optional blockedSessionId
  and forwards it to the existing BlockedDeviceService session-aware path;
  audit log + warn log carry the session id.
- Web (Device Block form): new "Session ID" shortcut input + Resolve
  button fills Tenant + Serial via GET /api/sessions/{id} (GA cross-tenant
  lookup already supported); resolved-device confirmation pill; URL
  deep-linking via ?sessionId=, ?action=, ?reason= for auto-fill on mount;
  blockedSessionId only forwarded when the resolved device still matches
  Serial+Tenant so manual edits don't bind to a stale session.
- Web (Ops Events modal): when the event details JSON carries a sessionId,
  surface "Block this device" / "Kill this device" deep-link buttons with
  an auto-generated reason scoped to the event type.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 17:03:48 +02:00

36 lines
1.3 KiB
C#

using AutopilotMonitor.Functions.Functions.Admin;
using Xunit;
namespace AutopilotMonitor.Functions.Tests;
/// <summary>
/// Covers the small body-parsing helper in <see cref="DeviceBlockFunction"/>.
/// <para>
/// The HTTP entry point is intentionally not tested here — mocking <c>HttpRequestData</c>
/// + the entire middleware chain would be more setup than the test is worth, and the
/// underlying <c>BlockedDeviceService.BlockDeviceAsync</c> session-id behavior is covered
/// by <see cref="BlockedDeviceServiceCrossInstanceTests"/>. The wire-up between the
/// function and the service is a single-line forward.
/// </para>
/// </summary>
public class DeviceBlockFunctionTests
{
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
[InlineData("\t\n")]
public void NormalizeOptionalSessionId_ReturnsNull_ForMissingOrWhitespace(string? raw)
{
Assert.Null(DeviceBlockFunction.NormalizeOptionalSessionId(raw));
}
[Theory]
[InlineData("806f61c3-1978-4e5c-8fd7-a571cb0fe6bc", "806f61c3-1978-4e5c-8fd7-a571cb0fe6bc")]
[InlineData(" 806f61c3-1978-4e5c-8fd7-a571cb0fe6bc ", "806f61c3-1978-4e5c-8fd7-a571cb0fe6bc")]
public void NormalizeOptionalSessionId_TrimsValidValue(string raw, string expected)
{
Assert.Equal(expected, DeviceBlockFunction.NormalizeOptionalSessionId(raw));
}
}