| File: Acquisition\WingetRegistryProbeTests.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.Collections.Concurrent; using System.Text.Json; using Aspire.Cli.Acquisition; using Microsoft.Extensions.Logging.Abstractions; namespace Aspire.Cli.Tests.Acquisition; public class WingetRegistryProbeTests(ITestOutputHelper outputHelper) { private const string SidecarFileName = ".aspire-install.json"; [Theory] [InlineData(true, true)] [InlineData(false, false)] public void Run_WritesSidecarOnlyWhenRegistryClaimsAspire(bool registryClaim, bool expectSidecar) { using var workspace = TemporaryWorkspace.CreateForCli(outputHelper); var probe = new WingetFirstRunProbe(new FakeWindowsRegistryReader(claim: registryClaim), NullLogger<WingetFirstRunProbe>.Instance); probe.Run(workspace.Path); var sidecarPath = Path.Combine(workspace.Path, SidecarFileName); Assert.Equal(expectSidecar, File.Exists(sidecarPath)); if (expectSidecar) { using var doc = JsonDocument.Parse(File.ReadAllBytes(sidecarPath)); Assert.Equal(JsonValueKind.Object, doc.RootElement.ValueKind); Assert.Equal("winget", doc.RootElement.GetProperty("source").GetString()); } else { Assert.Empty(Directory.GetFiles(workspace.Path)); } } [Fact] public void Run_IsIdempotent_OnSecondRun() { using var workspace = TemporaryWorkspace.CreateForCli(outputHelper); var sidecarPath = Path.Combine(workspace.Path, SidecarFileName); // Pre-seed with a distinctive payload no real producer would write. // The probe's contract is "do not touch an existing sidecar"; if it // ever rewrites unconditionally, this distinctive payload would be // overwritten with the probe's canonical {"source":"winget"} content // and the assertion below would fail — independent of any filesystem // timestamp resolution. const string preSeededContent = "{\"source\":\"winget-pre-seeded\"}"; File.WriteAllText(sidecarPath, preSeededContent); // Even with the registry asserting winget, an existing sidecar must // not be touched. var probe = new WingetFirstRunProbe(new FakeWindowsRegistryReader(claim: true), NullLogger<WingetFirstRunProbe>.Instance); probe.Run(workspace.Path); Assert.Equal(preSeededContent, File.ReadAllText(sidecarPath)); } [Fact] public async Task Run_ConcurrentInvocations_ProduceSingleValidSidecar() { using var workspace = TemporaryWorkspace.CreateForCli(outputHelper); var probe = new WingetFirstRunProbe(new FakeWindowsRegistryReader(claim: true), NullLogger<WingetFirstRunProbe>.Instance); var errors = new ConcurrentBag<Exception>(); var tasks = new Task[16]; for (var i = 0; i < tasks.Length; i++) { tasks[i] = Task.Run(() => { try { probe.Run(workspace.Path); } catch (Exception ex) { errors.Add(ex); } }); } await Task.WhenAll(tasks); Assert.Empty(errors); var sidecarPath = Path.Combine(workspace.Path, SidecarFileName); Assert.True(File.Exists(sidecarPath)); // Race losers must clean up their temp files. var leftovers = Directory.GetFiles(workspace.Path, $"{SidecarFileName}.*.tmp"); Assert.Empty(leftovers); using var doc = JsonDocument.Parse(File.ReadAllBytes(sidecarPath)); Assert.Equal(JsonValueKind.Object, doc.RootElement.ValueKind); Assert.Equal("winget", doc.RootElement.GetProperty("source").GetString()); } } file sealed class FakeWindowsRegistryReader(bool claim) : IWindowsRegistryReader { public bool HasWingetAspireUninstallEntry(string processPath) => claim; }