Files
AutopilotMonitor/src/Backend/AutopilotMonitor.Functions.Tests/SerialNumberHeuristicsTests.cs
T
Oliver Kieselbach d02fa454a7 fix(status): honest verdicts across all misclassification audit findings
Misclassification audit 2026-07-16 (1000 non-succeeded sessions, 22 deep
dives) surfaced six systematic misdeclaration sources. This implements
all of them:

1. DecisionCore: HelloNeverObservedPromote in the esp-exit advisory
   handler — when the Hello policy read never committed and no wizard
   was ever seen, hello_resolution is structurally unresolvable; promote
   to AwaitingHello + hello_safety instead of hard-failing a provably
   finished enrollment (sessions 2dac8298/7635ab18). Placed after the
   enforcement-progress re-arm so active installs still win; advisory-
   anchor variant stays exempt.

2. Ingest: both max-lifetime shapes (enrollment_failed/agent_timeout and
   agent_shutting_down/max_lifetime) now route through the
   EnrollmentTimeoutClassifier instead of hard Failed (tenant a53e67ec:
   fully provisioned WG Part 2, user never logged in => AwaitingUser).
   agent_timeout no longer counts as HasExplicitFailure; failure
   webhooks fire only on actual failure verdicts.

3. Maintenance sweep: dropped the too-broad "IsPreProvisioned ne true"
   exclusion (sealed devices are protected by their Pending status);
   resumed WG Part-2 runs sweep normally with the window anchored to
   ResumedAt. New stage-2 silence guard also fixes a latent bug where an
   actively-reporting >5h enrollment could be terminalized mid-run.

4. Registration supersede: a fresh registration resolves older
   non-terminal sessions of the same device (serial, placeholder-guarded)
   as Incomplete("Superseded by ...") with live SignalR updates — closes
   the forever-Pending orphan gap when WG Part 2 runs under a new id.

5. Admin retro-reconcile: POST /api/maintenance/reclassify-legacy
   (GlobalAdminOnly, DRY-RUN BY DEFAULT) re-runs the classifier over the
   pre-classifier "Session timed out" Failed backlog (~495 rows, sample
   projected ~87% misdeclared) and resolves Pending orphans. Requires the
   new opt-in allowTerminalReclassification override in the status
   writer (admin-marked rows stay untouchable).

6. Polish: "0 of N" AccountSetup rollups keep their total (no more
   fabricated "0/0" reasons, "never observed" wording when absent);
   late-telemetry reconcile heals Incomplete/AwaitingUser to Succeeded
   when straggler evidence proves completion; SLA page knows
   AwaitingUser/Incomplete ordinals; script_failed on Success/exit-0 now
   names the stderr rule in its message.

Tests: backend 2965, DecisionCore 474 (+6 new), agent 1777, web 557 —
all green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:24:21 +02:00

41 lines
1.2 KiB
C#

using AutopilotMonitor.Functions.Helpers;
using Xunit;
namespace AutopilotMonitor.Functions.Tests;
/// <summary>
/// Guards the registration supersede pass (misclassification audit 2026-07-16): OEM
/// placeholder serials would cross-match unrelated devices, so they must never be treated
/// as a device identity.
/// </summary>
public class SerialNumberHeuristicsTests
{
[Theory]
[InlineData("PF4DF1EN")]
[InlineData("5CG5504Y5L")]
[InlineData(" PF4DF1EN ")] // trimmed
[InlineData("VMware-56 4d 8a")]
public void RealSerials_AreUsable(string serial)
{
Assert.True(SerialNumberHeuristics.IsUsableSerialNumber(serial));
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
[InlineData("0")]
[InlineData("abc")] // too short to identify a device
[InlineData("None")]
[InlineData("UNKNOWN")]
[InlineData("Default string")]
[InlineData("To be filled by O.E.M.")]
[InlineData("System Serial Number")]
[InlineData("Not Specified")]
[InlineData("n/a")]
public void PlaceholderOrEmptySerials_AreNotUsable(string? serial)
{
Assert.False(SerialNumberHeuristics.IsUsableSerialNumber(serial));
}
}