File: Telemetry\InternalMicrosoftDetectorTests.cs
Web Access
Project: src\tests\Aspire.Cli.Tests\Aspire.Cli.Tests.csproj (Aspire.Cli.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.Net;
using System.Text;
using System.Text.Json;
using Aspire.Cli.DotNet;
using Aspire.Cli.Telemetry;
using Aspire.Cli.Tests.TestServices;
using Aspire.Cli.Tests.Utils;
using Microsoft.AspNetCore.InternalTesting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Abstractions;
 
namespace Aspire.Cli.Tests.Telemetry;
 
public sealed class InternalMicrosoftDetectorTests(ITestOutputHelper outputHelper)
{
    [Fact]
    public void ProbeFailureMetadataValues_AreBoundedStaticIdentifiers()
    {
        foreach (var type in new[]
        {
            typeof(InternalMicrosoftProbeFailureCode),
            typeof(InternalMicrosoftProbeFailureStage),
            typeof(InternalMicrosoftProbeExceptionType)
        })
        {
            foreach (var field in type.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static))
            {
                var value = Assert.IsType<string>(field.GetValue(null));
                Assert.InRange(value.Length, 1, 64);
                Assert.Matches("^[A-Za-z0-9._]+$", value);
            }
        }
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_UsesFreshCache()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var now = new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero);
        var cacheFilePath = Path.Combine(workspace.Path, "cache", "detector.json");
        Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)!);
        await File.WriteAllTextAsync(cacheFilePath, """
            {
              "version": 6,
              "isInternalMicrosoft": true,
              "isCIEnvironment": false,
              "source": "cached source",
              "alias": "cached.alias",
              "domain": "CACHED",
              "lastRunUtc": "2026-06-16T11:00:00+00:00"
            }
            """);
        var probeRan = false;
        var detector = CreateDetector(
            cacheFilePath,
            now,
            [
                [
                    new InternalMicrosoftProbe("should not run", _ =>
                    {
                        probeRan = true;
                        return Task.FromResult(InternalMicrosoftProbeResult.NotDetected);
                    })
                ]
            ]);
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Equal("cached source", result.Source);
        Assert.Equal("cached.alias", result.Alias);
        Assert.Equal("CACHED", result.Domain);
        Assert.Equal(InternalMicrosoftDetectorOutcome.Detected, result.Outcome);
        Assert.Equal(InternalMicrosoftDetectorCacheStatus.Hit, result.CacheStatus);
        Assert.False(probeRan);
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_PropagatesCallerCancellationWithoutWritingCache()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var cacheFilePath = Path.Combine(workspace.Path, "cache", "detector.json");
        var probeStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
        var detector = CreateDetector(
            cacheFilePath,
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            [[new InternalMicrosoftProbe("slow", async cancellationToken =>
            {
                probeStarted.TrySetResult();
                await Task.Delay(Timeout.InfiniteTimeSpan, cancellationToken);
                return InternalMicrosoftProbeResult.NotDetected;
            })]]);
        using var cancellationSource = new CancellationTokenSource();
 
        var detectionTask = detector.IsInternalMicrosoftMachineAsync(cancellationSource.Token);
        await probeStarted.Task.DefaultTimeout();
        await cancellationSource.CancelAsync();
 
        await Assert.ThrowsAnyAsync<OperationCanceledException>(() => detectionTask);
        Assert.False(File.Exists(cacheFilePath));
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_UsesFreshNegativeCache()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var now = new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero);
        var cacheFilePath = Path.Combine(workspace.Path, "cache", "detector.json");
        Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)!);
        await File.WriteAllTextAsync(cacheFilePath, """
            {
              "version": 6,
              "isInternalMicrosoft": false,
              "isCIEnvironment": false,
              "lastRunUtc": "2026-06-16T11:00:00+00:00"
            }
            """);
        var detector = CreateDetector(cacheFilePath, now, []);
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftDetectorOutcome.NotDetected, result.Outcome);
        Assert.Equal(InternalMicrosoftDetectorCacheStatus.Hit, result.CacheStatus);
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_RunsProbesWhenCacheIsStaleAndUpdatesCache()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var now = new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero);
        var cacheFilePath = Path.Combine(workspace.Path, "cache", "detector.json");
        Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)!);
        await File.WriteAllTextAsync(cacheFilePath, """
            {
              "isInternalMicrosoft": false,
              "lastRunUtc": "2026-06-16T05:59:59+00:00"
            }
            """);
        var detector = CreateDetector(
            cacheFilePath,
            now,
            [
                [new InternalMicrosoftProbe("positive", _ => Task.FromResult(new InternalMicrosoftProbeResult(IsInternalMicrosoft: true, Alias: "stale.alias", Domain: "STALE")))]
            ]);
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Equal("positive", result.Source);
        Assert.Equal("stale.alias", result.Alias);
        Assert.Equal("STALE", result.Domain);
        Assert.Equal(InternalMicrosoftDetectorCacheStatus.Stale, result.CacheStatus);
 
        var updatedCache = await File.ReadAllTextAsync(cacheFilePath);
        Assert.Contains("\"version\": 6", updatedCache, StringComparison.Ordinal);
        Assert.Contains("\"isInternalMicrosoft\": true", updatedCache, StringComparison.Ordinal);
        Assert.Contains("\"isCIEnvironment\": false", updatedCache, StringComparison.Ordinal);
        Assert.Contains("\"source\": \"positive\"", updatedCache, StringComparison.Ordinal);
        Assert.Contains("\"alias\": \"stale.alias\"", updatedCache, StringComparison.Ordinal);
        Assert.Contains("\"domain\": \"STALE\"", updatedCache, StringComparison.Ordinal);
        Assert.Contains("\"lastRunUtc\": \"2026-06-16T12:00:00+00:00\"", updatedCache, StringComparison.Ordinal);
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_RunsNextStageOnlyWhenPreviousStageDoesNotDetect()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var calls = new List<string>();
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            [
                [new InternalMicrosoftProbe("stage 1", _ =>
                {
                    calls.Add("stage 1");
                    return Task.FromResult(InternalMicrosoftProbeResult.NotDetected);
                })],
                [new InternalMicrosoftProbe("stage 2", _ =>
                {
                    calls.Add("stage 2");
                    return Task.FromResult(new InternalMicrosoftProbeResult(IsInternalMicrosoft: true, Alias: "stage.alias", Domain: "STAGE"));
                })],
                [new InternalMicrosoftProbe("stage 3", _ =>
                {
                    calls.Add("stage 3");
                    return Task.FromResult(new InternalMicrosoftProbeResult(IsInternalMicrosoft: true, Alias: "unused.alias", Domain: "UNUSED"));
                })]
            ]);
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Equal("stage 2", result.Source);
        Assert.Equal("stage.alias", result.Alias);
        Assert.Equal("STAGE", result.Domain);
        Assert.Equal(["stage 1", "stage 2"], calls);
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_StageTimeoutBoundsSlowProbeWhenFastProbeDetects()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var slowProbeStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
        var slowProbeCancelled = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            [
                [
                    new InternalMicrosoftProbe("positive", async _ =>
                    {
                        await slowProbeStarted.Task;
                        return new InternalMicrosoftProbeResult(IsInternalMicrosoft: true, Alias: "positive.alias", Domain: "POSITIVE");
                    }),
                    new InternalMicrosoftProbe("slow", async cancellationToken =>
                    {
                        slowProbeStarted.SetResult();
                        try
                        {
                            await Task.Delay(TimeSpan.FromMinutes(5), cancellationToken);
                        }
                        catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
                        {
                            slowProbeCancelled.SetResult();
                            throw;
                        }
 
                        return InternalMicrosoftProbeResult.NotDetected;
                    })
                ]
            ],
            probeStageTimeout: TimeSpan.FromSeconds(2));
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Equal("positive", result.Source);
        Assert.Equal("positive.alias", result.Alias);
        Assert.Equal("POSITIVE", result.Domain);
        await slowProbeCancelled.Task.DefaultTimeout();
        Assert.Contains(result.ProbeDiagnostics, probe => probe.Source == "slow" && probe.Outcome == InternalMicrosoftProbeOutcome.TimedOut);
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_IgnoresPositiveResultThatCompletesDuringCancellationDrain()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var probeStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
        var stageCancelled = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
        var releaseProbe = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            [[new InternalMicrosoftProbe("late positive", async cancellationToken =>
            {
                using var registration = cancellationToken.Register(stageCancelled.SetResult);
                probeStarted.SetResult();
                await releaseProbe.Task;
                return new InternalMicrosoftProbeResult(IsInternalMicrosoft: true, Alias: "late.alias", Domain: "LATE");
            })]],
            probeStageTimeout: TimeSpan.FromMilliseconds(50));
 
        var detectionTask = detector.IsInternalMicrosoftMachineAsync();
        await probeStarted.Task.DefaultTimeout();
        await stageCancelled.Task.DefaultTimeout();
        releaseProbe.SetResult();
        var result = await detectionTask;
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftDetectorOutcome.TimedOut, result.Outcome);
        Assert.Contains(result.ProbeDiagnostics, probe => probe.Source == "late positive" && probe.Outcome == InternalMicrosoftProbeOutcome.TimedOut);
    }
 
    [Theory]
    [InlineData(false)]
    [InlineData(true)]
    public async Task IsInternalMicrosoftMachineAsync_SelectsDeterministicStrongestResultRegardlessOfCompletionOrder(bool strongCompletesFirst)
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var releaseFirst = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            [
                [
                    new InternalMicrosoftProbe("weak", async _ =>
                    {
                        if (!strongCompletesFirst)
                        {
                            releaseFirst.SetResult();
                        }
                        else
                        {
                            await releaseFirst.Task;
                        }
                        return new InternalMicrosoftProbeResult(IsInternalMicrosoft: true, Alias: null, Domain: null);
                    }),
                    new InternalMicrosoftProbe("strong", async _ =>
                    {
                        if (strongCompletesFirst)
                        {
                            releaseFirst.SetResult();
                        }
                        else
                        {
                            await releaseFirst.Task;
                        }
                        return new InternalMicrosoftProbeResult(IsInternalMicrosoft: true, Alias: "strong.alias", Domain: "STRONG");
                    })
                ]
            ]);
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Equal("strong", result.Source);
        Assert.Equal("strong.alias", result.Alias);
        Assert.Equal("STRONG", result.Domain);
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_RunsLaterStagesWhenProbeThrowsUnexpectedException()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            [
                [new InternalMicrosoftProbe("faulting", _ => throw new NotSupportedException("Unexpected probe failure."))],
                [new InternalMicrosoftProbe("positive", _ => Task.FromResult(new InternalMicrosoftProbeResult(IsInternalMicrosoft: true, Alias: "later.alias", Domain: "LATER")))]
            ]);
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Equal("positive", result.Source);
        Assert.Equal("later.alias", result.Alias);
        Assert.Equal("LATER", result.Domain);
        var failure = Assert.Single(result.ProbeDiagnostics, probe => probe.Source == "faulting");
        Assert.Equal(InternalMicrosoftProbeOutcome.Failed, failure.Outcome);
        Assert.Equal(InternalMicrosoftProbeFailureCode.Exception, failure.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.Probe, failure.Failure?.Stage);
        Assert.Equal(InternalMicrosoftProbeExceptionType.Other, failure.Failure?.ExceptionType);
        Assert.Null(failure.Failure?.ProcessExitCode);
        Assert.Null(failure.Failure?.HttpStatusCode);
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_ReturnsNotDetectedOutcomeWhenNoProbeDetects()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            [[new InternalMicrosoftProbe("negative", _ => Task.FromResult(InternalMicrosoftProbeResult.NotDetected))]]);
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftDetectorOutcome.NotDetected, result.Outcome);
        Assert.Equal(InternalMicrosoftDetectorCacheStatus.Miss, result.CacheStatus);
        Assert.Contains(result.ProbeDiagnostics, probe => probe.Source == "negative" && probe.Outcome == InternalMicrosoftProbeOutcome.NotDetected);
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_ReturnsFailedOutcomeAndDoesNotCacheWhenAllProbesFail()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var cacheFilePath = Path.Combine(workspace.Path, "cache", "detector.json");
        var detector = CreateDetector(
            cacheFilePath,
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            [[new InternalMicrosoftProbe("faulting", _ => throw new NotSupportedException("Unexpected probe failure."))]]);
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftDetectorOutcome.Failed, result.Outcome);
        Assert.Contains(result.ProbeDiagnostics, probe => probe.Source == "faulting" && probe.Outcome == InternalMicrosoftProbeOutcome.Failed);
        Assert.False(File.Exists(cacheFilePath));
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_ReportsElapsedDurationForDetectorWideFailure()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var now = new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero);
        var cacheFilePath = Path.Combine(workspace.Path, "cache", "detector.json");
        Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)!);
        await File.WriteAllTextAsync(cacheFilePath, """
            {
              "version": 6,
              "isInternalMicrosoft": true,
              "isCIEnvironment": false,
              "source": "cached source",
              "lastRunUtc": "2026-06-16T11:00:00+00:00"
            }
            """);
        var detector = CreateDetector(
            cacheFilePath,
            now,
            [],
            timeProvider: new DelayedThrowingTimeProvider());
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.Equal(InternalMicrosoftDetectorOutcome.Failed, result.Outcome);
        Assert.True(result.Duration >= TimeSpan.FromMilliseconds(20));
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_DoesNotCacheNegativeWhenAnyProbeFails()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var cacheFilePath = Path.Combine(workspace.Path, "cache", "detector.json");
        var detector = CreateDetector(
            cacheFilePath,
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            [[
                new InternalMicrosoftProbe("negative", _ => Task.FromResult(InternalMicrosoftProbeResult.NotDetected)),
                new InternalMicrosoftProbe("failed", _ => Task.FromResult(InternalMicrosoftProbeResult.Failed(new(
                    InternalMicrosoftProbeFailureCode.RequestFailed,
                    InternalMicrosoftProbeFailureStage.Probe))))
            ]]);
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.Equal(InternalMicrosoftDetectorOutcome.Failed, result.Outcome);
        Assert.False(File.Exists(cacheFilePath));
        Assert.Contains(result.ProbeDiagnostics, diagnostic => diagnostic.Outcome == InternalMicrosoftProbeOutcome.NotDetected);
        Assert.Contains(result.ProbeDiagnostics, diagnostic => diagnostic.Outcome == InternalMicrosoftProbeOutcome.Failed);
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_ReturnsTimedOutOutcomeWhenProbeStageTimesOut()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var cacheFilePath = Path.Combine(workspace.Path, "cache", "detector.json");
        var detector = CreateDetector(
            cacheFilePath,
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            [[new InternalMicrosoftProbe("slow", async cancellationToken =>
            {
                await Task.Delay(TimeSpan.FromMinutes(5), cancellationToken);
                return InternalMicrosoftProbeResult.NotDetected;
            })]],
            probeStageTimeout: TimeSpan.FromMilliseconds(50));
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftDetectorOutcome.TimedOut, result.Outcome);
        Assert.Contains(result.ProbeDiagnostics, probe => probe.Source == "slow" && probe.Outcome == InternalMicrosoftProbeOutcome.TimedOut);
        Assert.False(File.Exists(cacheFilePath));
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_ReportsStageTimeoutDurationWhenProbeIgnoresCancellation()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var releaseProbe = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
        var probeStageTimeout = TimeSpan.FromMilliseconds(50);
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            [[new InternalMicrosoftProbe("hung", async _ =>
            {
                await releaseProbe.Task;
                return InternalMicrosoftProbeResult.NotDetected;
            })]],
            probeStageTimeout: probeStageTimeout);
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
        releaseProbe.SetResult();
 
        var diagnostic = Assert.Single(result.ProbeDiagnostics);
        Assert.Equal("hung", diagnostic.Source);
        Assert.Equal(InternalMicrosoftProbeOutcome.TimedOut, diagnostic.Outcome);
        Assert.Equal(probeStageTimeout, diagnostic.Duration);
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_TreatsUnknownCacheVersionAsStale()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var now = new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero);
        var cacheFilePath = Path.Combine(workspace.Path, "cache", "detector.json");
        Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)!);
        await File.WriteAllTextAsync(cacheFilePath, """
            {
              "version": 99,
              "isInternalMicrosoft": true,
              "source": "future source",
              "alias": "future.alias",
              "lastRunUtc": "2026-06-16T11:00:00+00:00"
            }
            """);
        var probeRan = false;
        var detector = CreateDetector(
            cacheFilePath,
            now,
            [[new InternalMicrosoftProbe("current source", _ =>
            {
                probeRan = true;
                return Task.FromResult(new InternalMicrosoftProbeResult(IsInternalMicrosoft: true, Alias: "current.alias", Domain: "CURRENT"));
            })]]);
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.True(probeRan);
        Assert.Equal(InternalMicrosoftDetectorCacheStatus.Stale, result.CacheStatus);
        Assert.Equal("current source", result.Source);
        Assert.Equal("current.alias", result.Alias);
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_DoesNotReuseNegativeCacheAcrossCIAndLocalModes()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var now = new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero);
        var cacheFilePath = Path.Combine(workspace.Path, "cache", "detector.json");
        var ciDetector = CreateDetector(
            cacheFilePath,
            now,
            [[new InternalMicrosoftProbe("CI negative", _ => Task.FromResult(InternalMicrosoftProbeResult.NotDetected))]],
            environmentVariables: new Dictionary<string, string?> { ["CI"] = "true" });
 
        var ciResult = await ciDetector.IsInternalMicrosoftMachineAsync();
 
        Assert.Equal(InternalMicrosoftDetectorOutcome.NotDetected, ciResult.Outcome);
        var ciCache = await File.ReadAllTextAsync(cacheFilePath);
        Assert.Contains("\"version\": 6", ciCache, StringComparison.Ordinal);
        Assert.Contains("\"isCIEnvironment\": true", ciCache, StringComparison.Ordinal);
 
        var localProbeRan = false;
        var localDetector = CreateDetector(
            cacheFilePath,
            now,
            [[new InternalMicrosoftProbe("local positive", _ =>
            {
                localProbeRan = true;
                return Task.FromResult(new InternalMicrosoftProbeResult(IsInternalMicrosoft: true, Alias: "local.alias", Domain: null));
            })]]);
 
        var localResult = await localDetector.IsInternalMicrosoftMachineAsync();
 
        Assert.True(localProbeRan);
        Assert.True(localResult.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftDetectorCacheStatus.Stale, localResult.CacheStatus);
        Assert.Equal("local.alias", localResult.Alias);
    }
 
    [Theory]
    [InlineData("Visual Studio Microsoft tenant")]
    [InlineData("WSL Visual Studio Microsoft tenant")]
    public async Task IsInternalMicrosoftMachineAsync_RejectsLegacyVisualStudioCacheEntry(string source)
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var now = new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero);
        var cacheFilePath = Path.Combine(workspace.Path, "cache", "detector.json");
        Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)!);
        await File.WriteAllTextAsync(cacheFilePath, $$"""
            {
              "isInternalMicrosoft": true,
              "source": "{{source}}",
              "alias": "Cached.Alias",
              "domain": "redmond.corp.microsoft.com",
              "lastRunUtc": "2026-06-16T11:00:00+00:00"
            }
            """);
        var detector = CreateDetector(cacheFilePath, now, []);
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftDetectorCacheStatus.Stale, result.CacheStatus);
        Assert.Null(result.Alias);
        Assert.Null(result.Domain);
    }
 
    [Fact]
    public async Task CheckWindowsUserDnsDomainAsync_UsesExecutionContextEnvironment()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            environmentVariables: new Dictionary<string, string?>
            {
                ["USERDNSDOMAIN"] = "redmond.corp.microsoft.com",
                ["USERNAME"] = "test.alias"
            });
 
        var result = await detector.CheckWindowsUserDnsDomainAsync(CancellationToken.None);
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Equal("test.alias", result.Alias);
        Assert.Equal("REDMOND", result.Domain);
    }
 
    [Fact]
    public async Task CheckWindowsUserDnsDomainAsync_RejectsNonAsciiAlias()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            environmentVariables: new Dictionary<string, string?>
            {
                ["USERDNSDOMAIN"] = "redmond.corp.microsoft.com",
                ["USERNAME"] = "tést.alias"
            });
 
        var result = await detector.CheckWindowsUserDnsDomainAsync(CancellationToken.None);
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Null(result.Alias);
        Assert.Equal("REDMOND", result.Domain);
    }
 
    [Fact]
    public async Task CheckWindowsWorkplaceJoinAsync_UsesTenantBoundAliasAndProcessFactory()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        await File.WriteAllTextAsync(Path.Combine(workspace.Path, "dsregcmd"), string.Empty);
        await File.WriteAllTextAsync(Path.Combine(workspace.Path, "dsregcmd.EXE"), string.Empty);
        var processFactory = new TestProcessExecutionFactory
        {
            AttemptCallback = (_, _) => (0, """
                AzureAdJoined : YES
                WorkplaceJoined : NO
                TenantId : 72f988bf-86f1-41af-91ab-2d7cd011db47
                User Email : Bound.Alias@microsoft.com
                """)
        };
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            processFactory: processFactory,
            environmentVariables: new Dictionary<string, string?>
            {
                ["PATH"] = workspace.Path,
                ["PATHEXT"] = ".EXE",
                ["USERDNSDOMAIN"] = "redmond.corp.microsoft.com",
                ["USERNAME"] = "unrelated.local"
            });
 
        var result = await detector.CheckWindowsWorkplaceJoinAsync(CancellationToken.None);
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Equal("bound.alias", result.Alias);
        Assert.Equal("REDMOND", result.Domain);
        Assert.Equal("dsregcmd", processFactory.LastFileName);
        var arguments = Assert.IsType<string[]>(processFactory.LastArguments);
        Assert.Equal(["/status"], arguments);
    }
 
    [Fact]
    public async Task CheckWindowsWorkplaceJoinAsync_DoesNotUseAmbientUserNameAsAlias()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        await File.WriteAllTextAsync(Path.Combine(workspace.Path, "dsregcmd"), string.Empty);
        await File.WriteAllTextAsync(Path.Combine(workspace.Path, "dsregcmd.EXE"), string.Empty);
        var processFactory = new TestProcessExecutionFactory
        {
            AttemptCallback = (_, _) => (0, """
                AzureAdJoined : YES
                WorkplaceJoined : NO
                TenantId : 72f988bf-86f1-41af-91ab-2d7cd011db47
                """)
        };
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            processFactory: processFactory,
            environmentVariables: new Dictionary<string, string?>
            {
                ["PATH"] = workspace.Path,
                ["PATHEXT"] = ".EXE",
                ["USERDNSDOMAIN"] = "redmond.corp.microsoft.com",
                ["USERNAME"] = "unrelated.local"
            });
 
        var result = await detector.CheckWindowsWorkplaceJoinAsync(CancellationToken.None);
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Null(result.Alias);
        Assert.Equal("REDMOND", result.Domain);
    }
 
    [Fact]
    public async Task CheckWindowsWorkplaceJoinAsync_ReturnsSafeFailureWhenProcessStartTimesOutInternally()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        await File.WriteAllTextAsync(Path.Combine(workspace.Path, "dsregcmd"), string.Empty);
        await File.WriteAllTextAsync(Path.Combine(workspace.Path, "dsregcmd.EXE"), string.Empty);
        var processFactory = new TestProcessExecutionFactory
        {
            CreateExecutionWithFileNameCallback = (fileName, arguments, environment, workingDirectory, options) =>
                new StartCancellingProcessExecution(fileName, arguments, environment)
        };
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            processFactory: processFactory,
            environmentVariables: new Dictionary<string, string?>
            {
                ["PATH"] = workspace.Path,
                ["PATHEXT"] = ".EXE",
                ["USERDNSDOMAIN"] = "redmond.corp.microsoft.com",
                ["USERNAME"] = "test.alias"
            });
 
        var result = await detector.CheckWindowsWorkplaceJoinAsync(CancellationToken.None);
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftProbeFailureCode.ProcessTimeout, result.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.ProcessStart, result.Failure?.Stage);
        Assert.Null(result.Failure?.ExceptionType);
        Assert.Equal("dsregcmd", processFactory.LastFileName);
    }
 
    [Theory]
    [InlineData("")]
    [InlineData("/")]
    [InlineData("///")]
    public void EvaluateMacPlatformSso_DetectsManagedMicrosoftFixture(string trailingSeparators)
    {
        var output = MacPlatformSsoOutputFixture
            .Replace("/v2.0\"", $"/v2.0{trailingSeparators}\"", StringComparison.Ordinal)
            .Replace("/getkeydata\"", $"/getkeydata{trailingSeparators}\"", StringComparison.Ordinal)
            .Replace("/oauth2/v2.0/token\"", $"/oauth2/v2.0/token{trailingSeparators}\"", StringComparison.Ordinal);
 
        var result = InternalMicrosoftDetector.EvaluateMacPlatformSso(output);
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Equal("test.alias", result.Alias);
        Assert.Equal("REDMOND", result.Domain);
        Assert.Null(result.Failure);
    }
 
    [Fact]
    public void EvaluateMacPlatformSso_RejectsOtherTenant()
    {
        var output = MacPlatformSsoOutputFixture.Replace(
            MicrosoftTenantIdForTests,
            "0dde70e6-f430-449f-8bce-f4d0a9eca2a4",
            StringComparison.Ordinal);
 
        var result = InternalMicrosoftDetector.EvaluateMacPlatformSso(output);
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftProbeFailureCode.TenantMismatch, result.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.PlatformSsoIssuer, result.Failure?.Stage);
    }
 
    [Fact]
    public void EvaluateMacPlatformSso_RejectsIncompleteRegistration()
    {
        var output = MacPlatformSsoOutputFixture.Replace(
            "\"registrationCompleted\" : true",
            "\"registrationCompleted\" : false",
            StringComparison.Ordinal);
 
        var result = InternalMicrosoftDetector.EvaluateMacPlatformSso(output);
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftProbeFailureCode.RegistrationIncomplete, result.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.PlatformSsoRegistration, result.Failure?.Stage);
    }
 
    [Fact]
    public void EvaluateMacPlatformSso_RejectsMalformedEndpoint()
    {
        var output = MacPlatformSsoOutputFixture.Replace(
            $"https://login.microsoftonline.com/{MicrosoftTenantIdForTests}/getkeydata",
            "not a URI",
            StringComparison.Ordinal);
 
        var result = InternalMicrosoftDetector.EvaluateMacPlatformSso(output);
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftProbeFailureCode.JsonShape, result.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.PlatformSsoKeyEndpoint, result.Failure?.Stage);
    }
 
    [Fact]
    public void EvaluateMacPlatformSso_RequiresRealmAndUpnFromSameKerberosEntry()
    {
        var output = MacPlatformSsoOutputFixture.Replace(
            "test.alias@REDMOND.CORP.MICROSOFT.COM",
            "test.alias@EUROPE.CORP.MICROSOFT.COM",
            StringComparison.Ordinal);
 
        var result = InternalMicrosoftDetector.EvaluateMacPlatformSso(output);
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftProbeFailureCode.IdentityMismatch, result.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.PlatformSsoIdentity, result.Failure?.Stage);
    }
 
    [Theory]
    [InlineData("{}")]
    [InlineData("[\"unexpected\"]")]
    public void EvaluateMacPlatformSso_RejectsMalformedKerberosStatus(string replacement)
    {
        var output = ReplaceMacPlatformSsoKerberosStatus(replacement);
 
        var result = InternalMicrosoftDetector.EvaluateMacPlatformSso(output);
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftProbeFailureCode.JsonShape, result.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.PlatformSsoIdentity, result.Failure?.Stage);
    }
 
    [Fact]
    public void EvaluateMacPlatformSso_RejectsTruncatedSection()
    {
        var output = MacPlatformSsoOutputFixture.Replace(
            """
            Login Configuration:
             {
            """,
            """
            Login Configuration:
             [
            """,
            StringComparison.Ordinal);
 
        var result = InternalMicrosoftDetector.EvaluateMacPlatformSso(output);
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftProbeFailureCode.JsonParse, result.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.PlatformSso, result.Failure?.Stage);
    }
 
    [Fact]
    public async Task CheckMacPlatformSsoAsync_UsesConfiguredSystemPathAndParsesStderr()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var appSsoPath = Path.Combine(workspace.Path, "usr", "bin", "app-sso");
        Directory.CreateDirectory(Path.GetDirectoryName(appSsoPath)!);
        await File.WriteAllTextAsync(appSsoPath, string.Empty);
        var processFactory = new TestProcessExecutionFactory
        {
            CreateExecutionWithFileNameCallback = (fileName, arguments, environment, _, options) =>
                new TestProcessExecution(
                    fileName,
                    arguments,
                    environment,
                    options,
                    (_, _, _) => Task.FromResult((0, (string?)null)),
                    () => 1)
                {
                    WaitForExitAsyncCallback = (invocationOptions, _) =>
                    {
                        invocationOptions.StandardErrorCallback?.Invoke(MacPlatformSsoOutputFixture);
                        return Task.FromResult(0);
                    }
                }
        };
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            processFactory: processFactory,
            macPlatformSsoPath: appSsoPath);
 
        var result = await detector.CheckMacPlatformSsoAsync(CancellationToken.None);
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Null(result.Failure);
        Assert.Equal(appSsoPath, processFactory.LastFileName);
        Assert.Equal(["platform", "-s"], Assert.IsType<string[]>(processFactory.LastArguments));
    }
 
    [Fact]
    public async Task CheckMacPlatformSsoAsync_ReportsMissingCommand()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            macPlatformSsoPath: Path.Combine(workspace.Path, "missing-app-sso"));
 
        var result = await detector.CheckMacPlatformSsoAsync(CancellationToken.None);
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftProbeFailureCode.CommandMissing, result.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.PlatformSso, result.Failure?.Stage);
    }
 
    [Fact]
    public async Task CheckMacPlatformSsoAsync_ReportsProcessTimeout()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var appSsoPath = Path.Combine(workspace.Path, "app-sso");
        await File.WriteAllTextAsync(appSsoPath, string.Empty);
        var timeoutObserved = false;
        var processFactory = new TestProcessExecutionFactory
        {
            CreateExecutionWithFileNameCallback = (fileName, arguments, environment, _, options) =>
                new TestProcessExecution(
                    fileName,
                    arguments,
                    environment,
                    options,
                    (_, _, _) => Task.FromResult((0, (string?)null)),
                    () => 1)
                {
                    WaitForExitAsyncCallback = async (_, cancellationToken) =>
                    {
                        try
                        {
                            await Task.Delay(Timeout.InfiniteTimeSpan, cancellationToken);
                            return 0;
                        }
                        catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
                        {
                            timeoutObserved = true;
                            throw;
                        }
                    }
                }
        };
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            processFactory: processFactory,
            macPlatformSsoPath: appSsoPath);
 
        var probeResult = await detector.CheckMacPlatformSsoAsync(CancellationToken.None);
 
        Assert.False(probeResult.IsInternalMicrosoft);
        Assert.True(timeoutObserved);
        Assert.Equal(InternalMicrosoftProbeFailureCode.ProcessTimeout, probeResult.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.ProcessExit, probeResult.Failure?.Stage);
 
        var fullDetector = CreateDetector(
            Path.Combine(workspace.Path, "full-detector-cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            [[new InternalMicrosoftProbe("Mac Platform SSO", _ => Task.FromResult(probeResult))]]);
 
        var result = await fullDetector.IsInternalMicrosoftMachineAsync();
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(InternalMicrosoftDetectorOutcome.TimedOut, result.Outcome);
        var diagnostic = Assert.Single(result.ProbeDiagnostics);
        Assert.Equal(InternalMicrosoftProbeOutcome.TimedOut, diagnostic.Outcome);
        Assert.Equal(InternalMicrosoftProbeFailureCode.ProcessTimeout, diagnostic.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.ProcessExit, diagnostic.Failure?.Stage);
    }
 
    [Fact]
    public async Task EvaluateMacPlatformSso_LiveManagedMac()
    {
        Assert.SkipUnless(OperatingSystem.IsMacOS(), "Live Platform SSO validation requires macOS.");
        Assert.SkipUnless(
            Environment.GetEnvironmentVariable("ASPIRE_TEST_LIVE_MAC_PLATFORM_SSO") == "1",
            "Run eng/scripts/validate-mac-platform-sso.sh on a managed Mac to enable this test.");
 
        var startInfo = new ProcessStartInfo("/usr/bin/app-sso")
        {
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            UseShellExecute = false
        };
        startInfo.ArgumentList.Add("platform");
        startInfo.ArgumentList.Add("-s");
 
        using var process = new Process { StartInfo = startInfo };
        Assert.True(process.Start(), "Failed to start /usr/bin/app-sso.");
 
        using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
        // Read both streams concurrently to avoid deadlock if a future app-sso version writes enough
        // diagnostic data to fill either pipe.
        var stdoutTask = process.StandardOutput.ReadToEndAsync(timeout.Token);
        var stderrTask = process.StandardError.ReadToEndAsync(timeout.Token);
        try
        {
            await process.WaitForExitAsync(timeout.Token);
            await Task.WhenAll(stdoutTask, stderrTask).WaitAsync(timeout.Token);
        }
        finally
        {
            if (!process.HasExited)
            {
                process.Kill(entireProcessTree: true);
                await process.WaitForExitAsync(CancellationToken.None).WaitAsync(TimeSpan.FromSeconds(5));
            }
        }
 
        var stdout = await stdoutTask;
        var stderr = await stderrTask;
 
        Assert.Equal(0, process.ExitCode);
        var result = InternalMicrosoftDetector.EvaluateMacPlatformSso($"{stdout}{Environment.NewLine}{stderr}");
        outputHelper.WriteLine(
            $"Detected={result.IsInternalMicrosoft}; HasAlias={result.Alias is not null}; HasDomain={result.Domain is not null}; FailureCode={result.Failure?.Code ?? "<none>"}; FailureStage={result.Failure?.Stage ?? "<none>"}");
 
        Assert.True(
            result.IsInternalMicrosoft,
            $"Platform SSO did not detect a managed Microsoft identity. Failure: {result.Failure?.Code ?? InternalMicrosoftProbeOutcome.NotDetected} at {result.Failure?.Stage ?? "<none>"}.");
        Assert.NotNull(result.Alias);
        Assert.NotNull(result.Domain);
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_ReportsPlatformSsoFailure()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            [[new InternalMicrosoftProbe("Mac Platform SSO", _ => Task.FromResult(
                InternalMicrosoftProbeResult.Failed(new(
                    InternalMicrosoftProbeFailureCode.TenantMismatch,
                    InternalMicrosoftProbeFailureStage.PlatformSsoIssuer))))]]);
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.False(result.IsInternalMicrosoft);
        var diagnostic = Assert.Single(result.ProbeDiagnostics);
        Assert.Equal("Mac Platform SSO", diagnostic.Source);
        Assert.Equal(InternalMicrosoftProbeOutcome.Failed, diagnostic.Outcome);
        Assert.Equal(InternalMicrosoftProbeFailureCode.TenantMismatch, diagnostic.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.PlatformSsoIssuer, diagnostic.Failure?.Stage);
    }
 
    [Fact]
    public async Task CheckWindowsWorkplaceJoinAsync_ReturnsOnlyNonSensitiveProcessExitCode()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        await File.WriteAllTextAsync(Path.Combine(workspace.Path, "dsregcmd"), string.Empty);
        await File.WriteAllTextAsync(Path.Combine(workspace.Path, "dsregcmd.EXE"), string.Empty);
        var processFactory = new TestProcessExecutionFactory
        {
            AttemptCallback = (_, _) => (7, "sensitive-host-name sensitive-user-path")
        };
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            processFactory: processFactory,
            environmentVariables: new Dictionary<string, string?>
            {
                ["PATH"] = workspace.Path,
                ["PATHEXT"] = ".EXE"
            });
 
        var result = await detector.CheckWindowsWorkplaceJoinAsync(CancellationToken.None);
 
        Assert.Equal(InternalMicrosoftProbeFailureCode.ProcessExit, result.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.ProcessExit, result.Failure?.Stage);
        Assert.Equal(7, result.Failure?.ProcessExitCode);
        Assert.DoesNotContain("sensitive", JsonSerializer.Serialize(result.Failure), StringComparison.OrdinalIgnoreCase);
    }
 
    [Theory]
    [InlineData("gh")]
    [InlineData("gh.exe")]
    public async Task CheckGhCliExecutableAsync_ReturnsSafeFailureWhenTokenCommandExitsNonZero(string executable)
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        await File.WriteAllTextAsync(Path.Combine(workspace.Path, executable), string.Empty);
        var processFactory = new TestProcessExecutionFactory
        {
            AttemptCallback = (_, _) => (7, "sensitive-token-or-path")
        };
        var environment = TestEnvironment.CreateLinux(new Dictionary<string, string?>
        {
            ["PATH"] = workspace.Path
        });
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            processFactory: processFactory,
            environment: environment);
 
        var result = await detector.CheckGhCliExecutableAsync(executable, CancellationToken.None);
 
        Assert.Equal(InternalMicrosoftProbeFailureCode.ProcessExit, result.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.ProcessExit, result.Failure?.Stage);
        Assert.Equal(7, result.Failure?.ProcessExitCode);
        Assert.Equal(executable, processFactory.LastFileName);
        Assert.DoesNotContain("sensitive", JsonSerializer.Serialize(result.Failure), StringComparison.OrdinalIgnoreCase);
    }
 
    [Theory]
    [InlineData("gh")]
    [InlineData("gh.exe")]
    public async Task CheckGhCliExecutableAsync_ReturnsNotDetectedWhenAuthenticationIsRequired(string executable)
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        await File.WriteAllTextAsync(Path.Combine(workspace.Path, executable), string.Empty);
        var processFactory = new TestProcessExecutionFactory
        {
            AttemptCallback = (_, _) => (4, "sensitive-token-or-path")
        };
        var environment = TestEnvironment.CreateLinux(new Dictionary<string, string?>
        {
            ["PATH"] = workspace.Path
        });
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            processFactory: processFactory,
            environment: environment);
 
        var result = await detector.CheckGhCliExecutableAsync(executable, CancellationToken.None);
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Null(result.Failure);
        Assert.Equal(executable, processFactory.LastFileName);
    }
 
    [Fact]
    public async Task CheckGitHubMembershipWithTokenAsync_ReturnsFailureWhenUserRequestIsUnauthorized()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var handler = new TestGitHubHttpMessageHandler((request, _) =>
            Task.FromResult(request.RequestUri?.AbsolutePath switch
            {
                "/user" => new HttpResponseMessage(HttpStatusCode.Unauthorized),
                _ => new HttpResponseMessage(HttpStatusCode.NotFound)
            }));
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            gitHubHttpMessageHandler: handler);
 
        var result = await detector.CheckGitHubMembershipWithTokenResultForTestingAsync(CreateGitHubToken(1), CancellationToken.None);
 
        Assert.Equal(InternalMicrosoftProbeFailureCode.HttpStatus, result.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.GitHubUser, result.Failure?.Stage);
        Assert.Equal(401, result.Failure?.HttpStatusCode);
        Assert.Equal(["/user"], handler.GetRequestPaths());
    }
 
    [Theory]
    [InlineData(401)]
    [InlineData(408)]
    [InlineData(403)]
    [InlineData(429)]
    [InlineData(503)]
    public async Task CheckGitHubMembershipWithTokenAsync_ReturnsSafeHttpFailureMetadata(int statusCode)
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var handler = new TestGitHubHttpMessageHandler((_, _) =>
            Task.FromResult(new HttpResponseMessage((HttpStatusCode)statusCode)));
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            gitHubHttpMessageHandler: handler);
 
        var result = await detector.CheckGitHubMembershipWithTokenResultForTestingAsync(CreateGitHubToken(1), CancellationToken.None);
 
        Assert.Equal(InternalMicrosoftProbeFailureCode.HttpStatus, result.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.GitHubUser, result.Failure?.Stage);
        Assert.Equal(statusCode, result.Failure?.HttpStatusCode);
        Assert.Null(result.Failure?.ExceptionType);
    }
 
    [Fact]
    public async Task IsInternalMicrosoftMachineAsync_DoesNotCacheUnauthorizedGitHubTokenResult()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var cacheFilePath = Path.Combine(workspace.Path, "cache", "detector.json");
        var handler = new TestGitHubHttpMessageHandler((_, _) =>
            Task.FromResult(new HttpResponseMessage(HttpStatusCode.Unauthorized)));
        InternalMicrosoftDetector? detector = null;
        detector = CreateDetector(
            cacheFilePath,
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            [[new InternalMicrosoftProbe("github", cancellationToken =>
                detector!.CheckGitHubMembershipWithTokenResultForTestingAsync(CreateGitHubToken(1), cancellationToken))]],
            gitHubHttpMessageHandler: handler);
 
        var result = await detector.IsInternalMicrosoftMachineAsync();
 
        Assert.Equal(InternalMicrosoftDetectorOutcome.Failed, result.Outcome);
        Assert.False(File.Exists(cacheFilePath));
    }
 
    [Theory]
    [InlineData("/user", InternalMicrosoftProbeFailureStage.GitHubUser)]
    [InlineData("/user/memberships/orgs/microsoft", InternalMicrosoftProbeFailureStage.GitHubMembership)]
    public async Task CheckGitHubMembershipWithTokenAsync_ReturnsShapeFailureForNonObjectResponse(string malformedPath, string expectedStage)
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var handler = new TestGitHubHttpMessageHandler((request, _) =>
            Task.FromResult(request.RequestUri?.AbsolutePath switch
            {
                var path when path == malformedPath => JsonResponse(HttpStatusCode.OK, "[]"),
                "/user" => JsonResponse(HttpStatusCode.OK, """{"login":"testuser"}"""),
                _ => new HttpResponseMessage(HttpStatusCode.NotFound)
            }));
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            gitHubHttpMessageHandler: handler);
 
        var result = await detector.CheckGitHubMembershipWithTokenResultForTestingAsync(CreateGitHubToken(1), CancellationToken.None);
 
        Assert.Equal(InternalMicrosoftProbeFailureCode.JsonShape, result.Failure?.Code);
        Assert.Equal(expectedStage, result.Failure?.Stage);
        Assert.Null(result.Failure?.ExceptionType);
    }
 
    [Fact]
    public async Task CheckGitHubMembershipWithTokenAsync_ReturnsTrueForActivePrivateMembership()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var handler = new TestGitHubHttpMessageHandler((request, _) =>
            Task.FromResult(request.RequestUri?.AbsolutePath switch
            {
                "/user" => JsonResponse(HttpStatusCode.OK, """{"login":"testuser"}"""),
                "/user/memberships/orgs/microsoft" => JsonResponse(HttpStatusCode.OK, """{"state":"active"}"""),
                _ => new HttpResponseMessage(HttpStatusCode.NotFound)
            }));
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            gitHubHttpMessageHandler: handler);
 
        var result = await detector.CheckGitHubMembershipWithTokenAsync(CreateGitHubToken(1), CancellationToken.None);
 
        Assert.True(result);
        Assert.Equal(["/user", "/user/memberships/orgs/microsoft"], handler.GetRequestPaths());
    }
 
    [Fact]
    public async Task CheckGitHubMembershipWithTokenAsync_ReturnsTrueForExplicitPublicMembership()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var handler = new TestGitHubHttpMessageHandler((request, _) =>
            Task.FromResult(request.RequestUri?.AbsolutePath switch
            {
                "/user" => JsonResponse(HttpStatusCode.OK, """{"login":"testuser"}"""),
                "/user/memberships/orgs/microsoft" => new HttpResponseMessage(HttpStatusCode.NotFound),
                "/orgs/microsoft/public_members/testuser" => new HttpResponseMessage(HttpStatusCode.NoContent),
                _ => new HttpResponseMessage(HttpStatusCode.NotFound)
            }));
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            gitHubHttpMessageHandler: handler);
 
        var result = await detector.CheckGitHubMembershipWithTokenAsync(CreateGitHubToken(1), CancellationToken.None);
 
        Assert.True(result);
        Assert.Equal(["/user", "/user/memberships/orgs/microsoft", "/orgs/microsoft/public_members/testuser"], handler.GetRequestPaths());
    }
 
    [Fact]
    public async Task CheckGitHubMembershipWithTokenAsync_ReturnsFalseForNonMember()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var handler = new TestGitHubHttpMessageHandler((request, _) =>
            Task.FromResult(request.RequestUri?.AbsolutePath switch
            {
                "/user" => JsonResponse(HttpStatusCode.OK, """{"login":"testuser"}"""),
                "/user/memberships/orgs/microsoft" => new HttpResponseMessage(HttpStatusCode.NotFound),
                "/orgs/microsoft/public_members/testuser" => new HttpResponseMessage(HttpStatusCode.NotFound),
                _ => new HttpResponseMessage(HttpStatusCode.NotFound)
            }));
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            gitHubHttpMessageHandler: handler);
 
        var result = await detector.CheckGitHubMembershipWithTokenAsync(CreateGitHubToken(1), CancellationToken.None);
 
        Assert.False(result);
        Assert.Equal(["/user", "/user/memberships/orgs/microsoft", "/orgs/microsoft/public_members/testuser"], handler.GetRequestPaths());
    }
 
    [Fact]
    public async Task CheckCopilotCliAsync_ChecksTokenCandidatesWithoutCopilotCommand()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var handler = new TestGitHubHttpMessageHandler((request, _) =>
            Task.FromResult(request.RequestUri?.AbsolutePath switch
            {
                "/user" => JsonResponse(HttpStatusCode.OK, """{"login":"testuser"}"""),
                "/user/memberships/orgs/microsoft" => JsonResponse(HttpStatusCode.OK, """{"state":"active"}"""),
                _ => new HttpResponseMessage(HttpStatusCode.NotFound)
            }));
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            environmentVariables: new Dictionary<string, string?>
            {
                ["PATH"] = workspace.Path,
                ["PATHEXT"] = ".EXE",
                ["COPILOT_GH_ACCOUNT_1"] = CreateGitHubToken(1)
            },
            gitHubHttpMessageHandler: handler);
 
        var result = await detector.CheckCopilotCliAsync(CancellationToken.None);
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Equal(["/user", "/user/memberships/orgs/microsoft"], handler.GetRequestPaths());
    }
 
    [Fact]
    public async Task CheckCopilotCliAsync_LimitsGitHubTokenCandidates()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var handler = new TestGitHubHttpMessageHandler((request, _) =>
            Task.FromResult(request.RequestUri?.AbsolutePath switch
            {
                "/user" => new HttpResponseMessage(HttpStatusCode.Unauthorized),
                _ => new HttpResponseMessage(HttpStatusCode.NotFound)
            }));
        var environmentVariables = Enumerable.Range(0, 7)
            .ToDictionary(index => $"COPILOT_GH_ACCOUNT_{index}", index => (string?)CreateGitHubToken(index));
        environmentVariables["PATH"] = workspace.Path;
        environmentVariables["PATHEXT"] = ".EXE";
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            environmentVariables: environmentVariables,
            gitHubHttpMessageHandler: handler);
 
        var result = await detector.CheckCopilotCliAsync(CancellationToken.None);
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(5, handler.GetRequestPaths().Count(path => path == "/user"));
    }
 
    [Theory]
    [InlineData("true")]
    [InlineData("2")]
    public async Task CheckCopilotCliAsync_SkipsGitHubTokenCandidatesInCI(string ciValue)
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var handler = new TestGitHubHttpMessageHandler((request, _) =>
            Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)));
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            environmentVariables: new Dictionary<string, string?>
            {
                ["CI"] = ciValue,
                ["COPILOT_GH_ACCOUNT_1"] = CreateGitHubToken(1)
            },
            gitHubHttpMessageHandler: handler);
 
        var result = await detector.CheckCopilotCliAsync(CancellationToken.None);
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Empty(handler.GetRequestPaths());
    }
 
    [Fact]
    public void DetectVisualStudioMicrosoftTenantForTesting_PrefersPersonalizationAccount()
    {
        var store = CreateVisualStudioAccountStore(
            new VisualStudioAccountRecord("fallback.alias@microsoft.com"),
            new VisualStudioAccountRecord("Current.Alias@microsoft.com", IsPersonalizationAccount: true));
 
        var result = InternalMicrosoftDetector.DetectVisualStudioMicrosoftTenantForTesting(
            store,
            CancellationToken.None);
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Equal("current.alias", result.Alias);
    }
 
    [Fact]
    public void DetectVisualStudioMicrosoftTenantForTesting_UsesNonPersonalizationAccountAsFallback()
    {
        var result = InternalMicrosoftDetector.DetectVisualStudioMicrosoftTenantForTesting(
            CreateVisualStudioAccountStore(new VisualStudioAccountRecord("Fallback.Alias@microsoft.com")),
            CancellationToken.None);
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Equal("fallback.alias", result.Alias);
    }
 
    [Theory]
    [InlineData(false)]
    [InlineData(true)]
    public void DetectVisualStudioMicrosoftTenantForTesting_OmitsAmbiguousIdentity(bool personalizationAccounts)
    {
        var store = CreateVisualStudioAccountStore(
            new VisualStudioAccountRecord("first.alias@microsoft.com", IsPersonalizationAccount: personalizationAccounts),
            new VisualStudioAccountRecord("second.alias@microsoft.com", IsPersonalizationAccount: personalizationAccounts));
 
        var result = InternalMicrosoftDetector.DetectVisualStudioMicrosoftTenantForTesting(
            store,
            CancellationToken.None);
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Null(result.Alias);
        Assert.Null(result.Domain);
    }
 
    [Fact]
    public void DetectVisualStudioMicrosoftTenantForTesting_EmitsDuplicateIdentity()
    {
        var store = CreateVisualStudioAccountStore(
            new VisualStudioAccountRecord("same.alias@microsoft.com"),
            new VisualStudioAccountRecord("same.alias@microsoft.com"));
 
        var result = InternalMicrosoftDetector.DetectVisualStudioMicrosoftTenantForTesting(
            store,
            CancellationToken.None);
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Equal("same.alias", result.Alias);
    }
 
    [Theory]
    [InlineData(false)]
    [InlineData(true)]
    public void DetectVisualStudioMicrosoftTenantForTesting_OmitsIdentityForMalformedMicrosoftPeer(bool personalizationAccounts)
    {
        var store = CreateVisualStudioAccountStoreWithMalformedMicrosoftPeer(
            new VisualStudioAccountRecord("valid.alias@microsoft.com", IsPersonalizationAccount: personalizationAccounts),
            peerIsPersonalizationAccount: personalizationAccounts);
 
        var result = InternalMicrosoftDetector.DetectVisualStudioMicrosoftTenantForTesting(
            store,
            CancellationToken.None);
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Null(result.Alias);
        Assert.Null(result.Domain);
    }
 
    [Fact]
    public void DetectVisualStudioMicrosoftTenantForTesting_IgnoresMalformedFallbackWhenPersonalizationIdentityIsValid()
    {
        var store = CreateVisualStudioAccountStoreWithMalformedMicrosoftPeer(
            new VisualStudioAccountRecord("valid.alias@microsoft.com", IsPersonalizationAccount: true),
            peerIsPersonalizationAccount: false);
 
        var result = InternalMicrosoftDetector.DetectVisualStudioMicrosoftTenantForTesting(
            store,
            CancellationToken.None);
 
        Assert.True(result.IsInternalMicrosoft);
        Assert.Equal("valid.alias", result.Alias);
    }
 
    [Theory]
    [InlineData("stale")]
    [InlineData("identity-provider")]
    [InlineData("home-tenant")]
    [InlineData("token-tenant")]
    [InlineData("token-issuer")]
    [InlineData("preferred-username")]
    [InlineData("preferred-username-substring")]
    [InlineData("token-payload")]
    public void DetectVisualStudioMicrosoftTenantForTesting_RejectsIncompleteOrMismatchedEvidence(string mismatch)
    {
        const string OtherTenantId = "11111111-1111-1111-1111-111111111111";
        var account = mismatch switch
        {
            "stale" => new VisualStudioAccountRecord("user@microsoft.com", Stale: true),
            "identity-provider" => new VisualStudioAccountRecord("user@microsoft.com", IdentityProvider: OtherTenantId),
            "home-tenant" => new VisualStudioAccountRecord("user@microsoft.com", HomeTenant: OtherTenantId),
            "token-tenant" => new VisualStudioAccountRecord("user@microsoft.com", TokenTenant: OtherTenantId),
            "token-issuer" => new VisualStudioAccountRecord("user@microsoft.com", TokenIssuer: $"https://login.microsoftonline.com/{OtherTenantId}/v2.0"),
            "preferred-username" => new VisualStudioAccountRecord("user@example.com"),
            "preferred-username-substring" => new VisualStudioAccountRecord("display user@microsoft.com text"),
            "token-payload" => new VisualStudioAccountRecord(
                "user@microsoft.com",
                IdTokenPayload: CreateJwt(MicrosoftTenantIdForTests, "user@microsoft.com")),
            _ => throw new ArgumentOutOfRangeException(nameof(mismatch))
        };
 
        var result = InternalMicrosoftDetector.DetectVisualStudioMicrosoftTenantForTesting(
            CreateVisualStudioAccountStore(account),
            CancellationToken.None);
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Null(result.Alias);
        if (mismatch is "preferred-username" or "preferred-username-substring")
        {
            Assert.Equal(InternalMicrosoftProbeFailureCode.JsonShape, result.Failure?.Code);
            Assert.Equal(InternalMicrosoftProbeFailureStage.IdTokenUsername, result.Failure?.Stage);
        }
        else if (mismatch == "token-payload")
        {
            Assert.Equal(InternalMicrosoftProbeFailureCode.JsonParse, result.Failure?.Code);
            Assert.Equal(InternalMicrosoftProbeFailureStage.IdTokenPayload, result.Failure?.Stage);
            Assert.Equal(InternalMicrosoftProbeExceptionType.Json, result.Failure?.ExceptionType);
        }
        else
        {
            Assert.Null(result.Failure);
        }
    }
 
    [Fact]
    public void DetectVisualStudioMicrosoftTenantForTesting_DoesNotMatchUnrelatedTenantTextOrAlias()
    {
        var store = $$"""
            [
              {
                "Stale": false,
                "IsPersonalizationAccount": true,
                "DisplayInfo": "wrong.alias@microsoft.com",
                "Properties": {
                  "UnrelatedTenant": "{{MicrosoftTenantIdForTests}}"
                }
              }
            ]
            """;
 
        var result = InternalMicrosoftDetector.DetectVisualStudioMicrosoftTenantForTesting(store, CancellationToken.None);
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Null(result.Alias);
        Assert.Equal(InternalMicrosoftProbeFailureCode.JsonShape, result.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.AccountStoreRecordIdentityProvider, result.Failure?.Stage);
    }
 
    [Fact]
    public void DetectVisualStudioMicrosoftTenantForTesting_ReturnsSafeJsonParseFailure()
    {
        var result = InternalMicrosoftDetector.DetectVisualStudioMicrosoftTenantForTesting(
            """[{"Properties":{"preferred_username":"sensitive.user@microsoft.com"}}""",
            CancellationToken.None);
 
        Assert.Equal(InternalMicrosoftProbeFailureCode.JsonParse, result.Failure?.Code);
        Assert.Equal(InternalMicrosoftProbeFailureStage.AccountStore, result.Failure?.Stage);
        Assert.Equal(InternalMicrosoftProbeExceptionType.Json, result.Failure?.ExceptionType);
        Assert.DoesNotContain("sensitive", JsonSerializer.Serialize(result.Failure), StringComparison.OrdinalIgnoreCase);
    }
 
    [Fact]
    public async Task CheckCopilotCliAsync_UsesOverallGitHubTokenCandidateTimeout()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        var handler = new TestGitHubHttpMessageHandler(async (_, cancellationToken) =>
        {
            await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
            return new HttpResponseMessage(HttpStatusCode.OK);
        });
        var environmentVariables = Enumerable.Range(0, 7)
            .ToDictionary(index => $"COPILOT_GH_ACCOUNT_{index}", index => (string?)CreateGitHubToken(index));
        environmentVariables["PATH"] = workspace.Path;
        environmentVariables["PATHEXT"] = ".EXE";
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            environmentVariables: environmentVariables,
            gitHubHttpMessageHandler: handler,
            gitHubCandidateTimeout: TimeSpan.FromMilliseconds(100),
            // HttpClient.Timeout defaults to 3 seconds here, which would independently cancel every
            // probe well inside the assertion bound below and make this test pass even with no candidate
            // budget at all. Disabling the per-request timeout leaves the overall budget as the only
            // thing that can stop the handler's one-minute delay, so the assertion measures what it claims.
            gitHubHttpTimeout: Timeout.InfiniteTimeSpan);
 
        var stopwatch = Stopwatch.StartNew();
        var result = await detector.CheckCopilotCliAsync(CancellationToken.None);
        stopwatch.Stop();
 
        Assert.False(result.IsInternalMicrosoft);
 
        // The handler blocks for a minute per request and HttpClient.Timeout is disabled above, so an
        // unenforced candidate budget takes at least a minute. Ten seconds leaves ample cancellation,
        // drain, and scheduler headroom while still proving the overall budget stops the probes. The
        // previous two-second bound failed at 2.064s on a loaded windows-latest runner:
        // https://github.com/microsoft/aspire/issues/19181.
        Assert.True(stopwatch.Elapsed < TimeSpan.FromSeconds(10), $"Elapsed {stopwatch.Elapsed} exceeded the overall candidate timeout budget.");
        Assert.Equal(5, handler.GetRequestPaths().Count(path => path == "/user"));
    }
 
    [Fact]
    public async Task CheckCopilotCliAsync_ProbesGitHubTokenCandidatesConcurrently()
    {
        using var workspace = TemporaryWorkspace.CreateForCli(outputHelper);
        const int candidateCount = 5;
        var allCandidatesEntered = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
        var releaseCandidates = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
        var enteredCandidates = 0;
        var handler = new TestGitHubHttpMessageHandler(async (_, cancellationToken) =>
        {
            if (Interlocked.Increment(ref enteredCandidates) == candidateCount)
            {
                allCandidatesEntered.TrySetResult();
            }
 
            await releaseCandidates.Task.WaitAsync(cancellationToken);
            return new HttpResponseMessage(HttpStatusCode.Unauthorized);
        });
        var environmentVariables = Enumerable.Range(0, 7)
            .ToDictionary(index => $"COPILOT_GH_ACCOUNT_{index}", index => (string?)CreateGitHubToken(index));
        environmentVariables["PATH"] = workspace.Path;
        environmentVariables["PATHEXT"] = ".EXE";
        var detector = CreateDetector(
            Path.Combine(workspace.Path, "cache", "detector.json"),
            new DateTimeOffset(2026, 6, 16, 12, 0, 0, TimeSpan.Zero),
            probeStages: [],
            environmentVariables: environmentVariables,
            gitHubHttpMessageHandler: handler,
            gitHubCandidateTimeout: Timeout.InfiniteTimeSpan,
            gitHubHttpTimeout: Timeout.InfiniteTimeSpan);
 
        using var safetyTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(30));
        var checkTask = detector.CheckCopilotCliAsync(safetyTimeout.Token);
 
        // The test releases the handlers only after all five have entered. A serial implementation
        // cannot reach that point; the independent timeout keeps that regression from hanging the suite.
        try
        {
            await allCandidatesEntered.Task.WaitAsync(safetyTimeout.Token);
        }
        finally
        {
            releaseCandidates.TrySetResult();
        }
 
        var result = await checkTask.WaitAsync(safetyTimeout.Token);
 
        Assert.False(result.IsInternalMicrosoft);
        Assert.Equal(candidateCount, enteredCandidates);
        Assert.Equal(candidateCount, handler.GetRequestPaths().Count(path => path == "/user"));
    }
 
    private static InternalMicrosoftDetector CreateDetector(
        string cacheFilePath,
        DateTimeOffset now,
        IReadOnlyList<IReadOnlyList<InternalMicrosoftProbe>>? probeStages,
        TestProcessExecutionFactory? processFactory = null,
        IReadOnlyDictionary<string, string?>? environmentVariables = null,
        HttpMessageHandler? gitHubHttpMessageHandler = null,
        TimeSpan? gitHubCandidateTimeout = null,
        TimeSpan? gitHubHttpTimeout = null,
        TimeSpan? probeStageTimeout = null,
        TestEnvironment? environment = null,
        DirectoryInfo? homeDirectory = null,
        TimeProvider? timeProvider = null,
        string? macPlatformSsoPath = null)
    {
        var executionContext = Utils.TestExecutionContextHelper.CreateExecutionContext(
            new DirectoryInfo(Path.GetDirectoryName(cacheFilePath) ?? AppContext.BaseDirectory),
            homeDirectory: homeDirectory);
        var effectiveEnvironment = environment ?? new TestEnvironment(environmentVariables);
        var ciEnvironmentDetector = new CIEnvironmentDetector(
            new ConfigurationBuilder()
                .AddInMemoryCollection(effectiveEnvironment.Variables)
                .Build());
 
        return new InternalMicrosoftDetector(
            executionContext,
            effectiveEnvironment,
            cacheFilePath,
            timeProvider ?? new FixedTimeProvider(now),
            NullLogger<InternalMicrosoftDetector>.Instance,
            processFactory ?? new TestProcessExecutionFactory(),
            ciEnvironmentDetector,
            macPlatformSsoPath ?? "/usr/bin/app-sso",
            probeStages,
            gitHubHttpMessageHandler,
            gitHubCandidateTimeout,
            gitHubHttpTimeout,
            probeStageTimeout);
    }
 
    private static HttpResponseMessage JsonResponse(HttpStatusCode statusCode, string json)
    {
        return new HttpResponseMessage(statusCode)
        {
            Content = new StringContent(json, Encoding.UTF8, "application/json")
        };
    }
 
    private static string CreateGitHubToken(int index)
        => $"gho_{index:D2}{new string('a', 24)}";
 
    private static string ReplaceMacPlatformSsoKerberosStatus(string replacement)
    {
        const string propertyPrefix = "\"kerberosStatus\" : ";
        var propertyIndex = MacPlatformSsoOutputFixture.IndexOf(propertyPrefix, StringComparison.Ordinal);
        Assert.True(propertyIndex >= 0);
        var valueStart = propertyIndex + propertyPrefix.Length;
        var followingPropertyIndex = MacPlatformSsoOutputFixture.IndexOf("\"state\"", valueStart, StringComparison.Ordinal);
        Assert.True(followingPropertyIndex >= 0);
        var valueEnd = MacPlatformSsoOutputFixture.LastIndexOf(',', followingPropertyIndex);
        Assert.True(valueEnd >= valueStart);
 
        return string.Concat(
            MacPlatformSsoOutputFixture.AsSpan(0, valueStart),
            replacement,
            MacPlatformSsoOutputFixture.AsSpan(valueEnd));
    }
 
    private const string MicrosoftTenantIdForTests = "72f988bf-86f1-41af-91ab-2d7cd011db47";
    private const string MacPlatformSsoOutputFixture = """
        Time: 2026-08-25 12:34:56 +0000
 
        Device Configuration:
         {
          "formatNote" : "Braces { } and text such as Login Configuration: do not end a section.",
          "registrationCompleted" : true
        }
 
        Login Configuration:
         {
          "issuer" : "https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/v2.0",
          "keyEndpointURL" : "https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/getkeydata",
          "tokenEndpointURL" : "https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/oauth2/v2.0/token"
        }
 
        User Configuration:
         {
          "kerberosStatus" : [
            {
              "realm" : "KERBEROS.MICROSOFTONLINE.COM",
              "upn" : "test.alias@microsoft.com@KERBEROS.MICROSOFTONLINE.COM"
            },
            {
              "realm" : "REDMOND.CORP.MICROSOFT.COM",
              "upn" : "test.alias@REDMOND.CORP.MICROSOFT.COM"
            }
          ],
          "state" : "POUserStateNormal (0)",
          "userLoginConfiguration" : {
            "loginUserName" : "test.alias@microsoft.com"
          }
        }
 
        SSO Tokens:
        Received:
        2026-08-25T12:34:56Z
        Expiration:
        2026-09-08T12:34:56Z (Not Expired)
        """;
 
    private static string CreateJwt(string tenantId, string userName)
    {
        var payload = JsonSerializer.Serialize(new { tid = tenantId, preferred_username = userName });
        return $"eyJ0eXAiOiJKV1Q.{Base64UrlEncode(Encoding.UTF8.GetBytes(payload))}.signature";
    }
 
    private static string Base64UrlEncode(byte[] bytes)
        => Convert.ToBase64String(bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_');
 
    private static string CreateVisualStudioAccountStore(params VisualStudioAccountRecord[] accounts)
    {
        return JsonSerializer.Serialize(accounts.Select(account => new
        {
            account.Stale,
            account.IsPersonalizationAccount,
            DisplayInfo = account.PreferredUsername,
            Properties = new
            {
                IdentityProvider = account.IdentityProvider ?? MicrosoftTenantIdForTests,
                HomeTenant = account.HomeTenant ?? MicrosoftTenantIdForTests,
                IdTokenPayload = account.IdTokenPayload ?? JsonSerializer.Serialize(new
                {
                    tid = account.TokenTenant ?? MicrosoftTenantIdForTests,
                    iss = account.TokenIssuer ?? $"https://login.microsoftonline.com/{MicrosoftTenantIdForTests}/v2.0",
                    preferred_username = account.PreferredUsername
                })
            }
        }));
    }
 
    private static string CreateVisualStudioAccountStoreWithMalformedMicrosoftPeer(
        VisualStudioAccountRecord validAccount,
        bool peerIsPersonalizationAccount)
    {
        using var validStore = JsonDocument.Parse(CreateVisualStudioAccountStore(validAccount));
        var validRecord = validStore.RootElement[0].Clone();
        return JsonSerializer.Serialize(new object[]
        {
            validRecord,
            new
            {
                Stale = false,
                IsPersonalizationAccount = peerIsPersonalizationAccount,
                Properties = new
                {
                    IdentityProvider = MicrosoftTenantIdForTests
                }
            }
        });
    }
 
    private sealed record VisualStudioAccountRecord(
        string PreferredUsername,
        bool IsPersonalizationAccount = false,
        bool Stale = false,
        string? IdentityProvider = null,
        string? HomeTenant = null,
        string? TokenTenant = null,
        string? TokenIssuer = null,
        string? IdTokenPayload = null);
 
    private sealed class FixedTimeProvider(DateTimeOffset now) : TimeProvider
    {
        public override DateTimeOffset GetUtcNow() => now;
    }
 
    private sealed class DelayedThrowingTimeProvider : TimeProvider
    {
        public override DateTimeOffset GetUtcNow()
        {
            Thread.Sleep(20);
            throw new InvalidOperationException("Simulated detector-wide failure.");
        }
    }
 
    private sealed class StartCancellingProcessExecution(
        string fileName,
        IReadOnlyList<string> arguments,
        IDictionary<string, string>? environment) : IProcessExecution
    {
        public string FileName { get; } = fileName;
 
        public IReadOnlyList<string> Arguments { get; } = arguments;
 
        public IReadOnlyDictionary<string, string?> EnvironmentVariables { get; } =
            environment?.ToDictionary(kvp => kvp.Key, kvp => (string?)kvp.Value)
            ?? new Dictionary<string, string?>();
 
        public int ProcessId => Environment.ProcessId;
 
        public DateTimeOffset? StartTime => DateTimeOffset.UtcNow;
 
        public bool HasExited => false;
 
        public int ExitCode => 0;
 
        public Task<bool> StartAsync(CancellationToken cancellationToken)
            => throw new OperationCanceledException(cancellationToken);
 
        public Task<int> WaitForExitAsync(CancellationToken cancellationToken)
            => throw new InvalidOperationException("The process should not wait after start cancellation.");
 
        public void Kill(bool entireProcessTree)
        {
        }
 
        public ValueTask DisposeAsync() => ValueTask.CompletedTask;
    }
 
    private sealed class TestGitHubHttpMessageHandler(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> sendAsync) : HttpMessageHandler
    {
        private readonly Lock _lock = new();
        private readonly List<string> _requestPaths = [];
 
        public IReadOnlyList<string> GetRequestPaths()
        {
            lock (_lock)
            {
                return [.. _requestPaths];
            }
        }
 
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            lock (_lock)
            {
                _requestPaths.Add(request.RequestUri?.AbsolutePath ?? string.Empty);
            }
 
            return await sendAsync(request, cancellationToken).ConfigureAwait(false);
        }
    }
}