test(agent): serialise V2.Core RecoveryPath / EnrollmentOrchestrator / CollectorBaseReentrancy

These three classes intermittently failed under the parallel xUnit pool
on a busy box (snapshot/journal IO + Task coordination + Interlocked-gate
SpinUntil all timing-sensitive against ThreadPool contention from
unrelated tests). Confirmed pattern, not real regressions: each class
ran green isolated via --filter.

Now decorated with [Collection("SerialThreading")] — same collection as
DeadlineScheduler, SignalIngress, EnrollmentOrchestratorMaxLifetime,
etc. Verified 5 consecutive full-suite runs green (855/855 each, ~28s).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Oliver Kieselbach
2026-04-30 22:05:04 +02:00
parent 3016c15919
commit 430d83453e
3 changed files with 16 additions and 0 deletions
@@ -18,6 +18,10 @@ using Xunit;
namespace AutopilotMonitor.Agent.V2.Core.Tests.Orchestration
{
// Persistence-IO + Task-coordination tests are timing-sensitive enough that the
// parallel xUnit pool starves the worker threads on a busy CI box. Serialised
// against the other threading-sensitive classes via SerialThreading.
[Collection("SerialThreading")]
public sealed class EnrollmentOrchestratorTests
{
private static DateTime At => new DateTime(2026, 4, 20, 10, 0, 0, DateTimeKind.Utc);
@@ -20,6 +20,10 @@ using Xunit;
namespace AutopilotMonitor.Agent.V2.Core.Tests.Orchestration
{
// Snapshot/journal file IO + ThreadPool-driven persistence races make this class
// intermittently flaky under the parallel xUnit pool. Serialised against other
// threading-sensitive classes via the SerialThreading collection.
[Collection("SerialThreading")]
public sealed class RecoveryPathTests
{
private static DateTime At => new DateTime(2026, 4, 20, 10, 0, 0, DateTimeKind.Utc);
@@ -28,7 +28,15 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Telemetry
/// gate behaviour so a future "let's just remove the guard, it never fires" refactor
/// has a hard signal.
/// </para>
/// <para>
/// Threading-sensitive: the test exercises the <see cref="Interlocked"/> gate via
/// real Tasks + SpinUntil. Under the parallel xUnit pool the ThreadPool gets
/// contended by unrelated tests and the SpinUntil times out before the gate
/// closes. Serialised against the other threading-sensitive classes via
/// SerialThreading.
/// </para>
/// </summary>
[Collection("SerialThreading")]
public sealed class CollectorBaseReentrancyTests
{
private static AgentLogger NewLogger(TempDirectory tmp)