diff --git a/src/Aspire.Hosting.Azure/AzurePublishingContext.cs b/src/Aspire.Hosting.Azure/AzurePublishingContext.cs index 60dcee13bf3..69959518b63 100644 --- a/src/Aspire.Hosting.Azure/AzurePublishingContext.cs +++ b/src/Aspire.Hosting.Azure/AzurePublishingContext.cs @@ -1,10 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only + using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.Pipelines; +using Microsoft.Extensions.DependencyInjection; using Azure.Provisioning; using Azure.Provisioning.Expressions; using Azure.Provisioning.Primitives; @@ -118,6 +121,9 @@ private async Task WriteAzureArtifactsOutputAsync(IReportingStep step, Distribut outputDirectory.Create(); } + var fileSystemService = ServiceProvider.GetRequiredService(); + var tempDirectory = fileSystemService.TempDirectory.CreateTempSubdirectory("aspire-bicep").Path; + var bicepResourcesToPublish = model.Resources.OfType() .Where(r => !r.IsExcludedFromPublish()) .ToList(); @@ -145,7 +151,7 @@ private async Task WriteAzureArtifactsOutputAsync(IReportingStep step, Distribut foreach (var resource in bicepResourcesToPublish) { - var file = resource.GetBicepTemplateFile(); + var file = resource.GetBicepTemplateFile(tempDirectory); var moduleDirectory = outputDirectory.CreateSubdirectory(resource.Name); @@ -336,7 +342,7 @@ void CaptureBicepOutputsFromParameters(IResourceWithParameters resource) var modulePath = Path.Combine(moduleDirectory.FullName, $"{resource.Name}.bicep"); - var file = br.GetBicepTemplateFile(); + var file = br.GetBicepTemplateFile(tempDirectory); File.Copy(file.Path, modulePath, true); diff --git a/src/Aspire.Hosting.Azure/Provisioning/Provisioners/BicepProvisioner.cs b/src/Aspire.Hosting.Azure/Provisioning/Provisioners/BicepProvisioner.cs index a8e5b970fd6..4d0a151f822 100644 --- a/src/Aspire.Hosting.Azure/Provisioning/Provisioners/BicepProvisioner.cs +++ b/src/Aspire.Hosting.Azure/Provisioning/Provisioners/BicepProvisioner.cs @@ -1,5 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. + +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only + using System.Collections.Immutable; using System.Diagnostics; using System.Text.Json.Nodes; @@ -23,6 +26,7 @@ internal sealed class BicepProvisioner( ISecretClientProvider secretClientProvider, IDeploymentStateManager deploymentStateManager, DistributedApplicationExecutionContext executionContext, + IFileSystemService fileSystemService, ILogger logger) : IBicepProvisioner { /// @@ -135,7 +139,8 @@ await notificationService.PublishUpdateAsync(resource, state => state with ]) }).ConfigureAwait(false); - var template = resource.GetBicepTemplateFile(); + var tempDirectory = fileSystemService.TempDirectory.CreateTempSubdirectory("aspire-bicep").Path; + var template = resource.GetBicepTemplateFile(tempDirectory); var path = template.Path; // GetBicepTemplateFile may have added new well-known parameters, so we need diff --git a/src/Aspire.Hosting.Maui/Utilities/MauiAndroidEnvironmentAnnotation.cs b/src/Aspire.Hosting.Maui/Utilities/MauiAndroidEnvironmentAnnotation.cs index d13637a95dd..af39dba4868 100644 --- a/src/Aspire.Hosting.Maui/Utilities/MauiAndroidEnvironmentAnnotation.cs +++ b/src/Aspire.Hosting.Maui/Utilities/MauiAndroidEnvironmentAnnotation.cs @@ -1,9 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only + using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.Eventing; using Aspire.Hosting.Lifecycle; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Aspire.Hosting.Maui.Utilities; @@ -38,7 +41,8 @@ internal sealed class MauiAndroidEnvironmentProcessedAnnotation : IResourceAnnot internal sealed class MauiAndroidEnvironmentSubscriber( DistributedApplicationExecutionContext executionContext, ResourceLoggerService loggerService, - ResourceNotificationService notificationService) : IDistributedApplicationEventingSubscriber + ResourceNotificationService notificationService, + IFileSystemService fileSystemService) : IDistributedApplicationEventingSubscriber { public Task SubscribeAsync(IDistributedApplicationEventing eventing, DistributedApplicationExecutionContext execContext, CancellationToken cancellationToken) { @@ -83,6 +87,7 @@ private async Task OnBeforeResourceStartedAsync(BeforeResourceStartedEvent @even if (generatedFilePath is null) { generatedFilePath = await MauiEnvironmentHelper.CreateAndroidEnvironmentTargetsFileAsync( + fileSystemService, resource, executionContext, logger, diff --git a/src/Aspire.Hosting.Maui/Utilities/MauiEnvironmentHelper.cs b/src/Aspire.Hosting.Maui/Utilities/MauiEnvironmentHelper.cs index 3045f801b78..68b1537b148 100644 --- a/src/Aspire.Hosting.Maui/Utilities/MauiEnvironmentHelper.cs +++ b/src/Aspire.Hosting.Maui/Utilities/MauiEnvironmentHelper.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only + using System.Globalization; using System.Text; using System.Xml.Linq; @@ -22,12 +24,14 @@ internal static class MauiEnvironmentHelper /// /// Creates an MSBuild targets file for Android that sets environment variables. /// + /// The file system service for managing temp files. /// The resource to collect environment variables from. /// The execution context. /// Logger for diagnostic output. /// Cancellation token. /// The path to the generated targets file, or null if no environment variables are present. public static async Task CreateAndroidEnvironmentTargetsFileAsync( + IFileSystemService fileSystemService, IResource resource, DistributedApplicationExecutionContext executionContext, ILogger logger, @@ -62,8 +66,7 @@ internal static class MauiEnvironmentHelper } // Create a temporary targets file - var tempDirectory = Path.Combine(Path.GetTempPath(), "aspire", "maui", "android-env"); - Directory.CreateDirectory(tempDirectory); + var tempDirectory = fileSystemService.TempDirectory.CreateTempSubdirectory("aspire-maui-android-env").Path; // Prune old targets files PruneOldTargets(tempDirectory, logger); @@ -209,12 +212,14 @@ private static string EncodeSemicolons(string value, out bool wasEncoded) /// /// Creates an MSBuild targets file for iOS that sets environment variables. /// + /// The file system service for managing temp files. /// The resource to collect environment variables from. /// The execution context. /// Logger for diagnostic output. /// Cancellation token. /// The path to the generated targets file, or null if no environment variables are present. public static async Task CreateiOSEnvironmentTargetsFileAsync( + IFileSystemService fileSystemService, IResource resource, DistributedApplicationExecutionContext executionContext, ILogger logger, @@ -232,8 +237,7 @@ private static string EncodeSemicolons(string value, out bool wasEncoded) } // Create a temporary targets file - var tempDirectory = Path.Combine(Path.GetTempPath(), "aspire", "maui", "mlaunch-env"); - Directory.CreateDirectory(tempDirectory); + var tempDirectory = fileSystemService.TempDirectory.CreateTempSubdirectory("aspire-maui-mlaunch-env").Path; // Prune old targets files PruneOldTargetsiOS(tempDirectory, logger); diff --git a/src/Aspire.Hosting.Maui/Utilities/MauiiOSEnvironmentAnnotation.cs b/src/Aspire.Hosting.Maui/Utilities/MauiiOSEnvironmentAnnotation.cs index 3a45e11b400..ab3a06f5b1c 100644 --- a/src/Aspire.Hosting.Maui/Utilities/MauiiOSEnvironmentAnnotation.cs +++ b/src/Aspire.Hosting.Maui/Utilities/MauiiOSEnvironmentAnnotation.cs @@ -1,9 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only + using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.Eventing; using Aspire.Hosting.Lifecycle; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Aspire.Hosting.Maui.Utilities; @@ -38,7 +41,8 @@ internal sealed class MauiiOSEnvironmentProcessedAnnotation : IResourceAnnotatio internal sealed class MauiiOSEnvironmentSubscriber( DistributedApplicationExecutionContext executionContext, ResourceLoggerService loggerService, - ResourceNotificationService notificationService) : IDistributedApplicationEventingSubscriber + ResourceNotificationService notificationService, + IFileSystemService fileSystemService) : IDistributedApplicationEventingSubscriber { public Task SubscribeAsync(IDistributedApplicationEventing eventing, DistributedApplicationExecutionContext execContext, CancellationToken cancellationToken) { @@ -83,6 +87,7 @@ private async Task OnBeforeResourceStartedAsync(BeforeResourceStartedEvent @even if (generatedFilePath is null) { generatedFilePath = await MauiEnvironmentHelper.CreateiOSEnvironmentTargetsFileAsync( + fileSystemService, resource, executionContext, logger, diff --git a/src/Aspire.Hosting.MySql/MySqlBuilderExtensions.cs b/src/Aspire.Hosting.MySql/MySqlBuilderExtensions.cs index 94f15764c65..280f9eed279 100644 --- a/src/Aspire.Hosting.MySql/MySqlBuilderExtensions.cs +++ b/src/Aspire.Hosting.MySql/MySqlBuilderExtensions.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only + using Aspire.Hosting; using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.MySql; @@ -260,7 +262,8 @@ public static IResourceBuilder WithPhpMyAdmin(this IResourceBuilder bui } else { - var tempConfigFile = await WritePhpMyAdminConfiguration(mySqlInstances, ct).ConfigureAwait(false); + var fileSystemService = e.Services.GetRequiredService(); + var tempConfigFile = await WritePhpMyAdminConfiguration(fileSystemService, mySqlInstances, ct).ConfigureAwait(false); try { @@ -374,10 +377,10 @@ public static IResourceBuilder WithInitFiles(this IResource return builder.WithContainerFiles(initPath, importFullPath); } - private static async Task WritePhpMyAdminConfiguration(IEnumerable mySqlInstances, CancellationToken cancellationToken) + private static async Task WritePhpMyAdminConfiguration(IFileSystemService fileSystemService, IEnumerable mySqlInstances, CancellationToken cancellationToken) { // This temporary file is not used by the container, it will be copied and then deleted - var filePath = Path.GetTempFileName(); + var filePath = fileSystemService.TempDirectory.CreateTempFile().Path; using var writer = new StreamWriter(filePath); diff --git a/src/Aspire.Hosting.Testing/DistributedApplicationTestingBuilder.cs b/src/Aspire.Hosting.Testing/DistributedApplicationTestingBuilder.cs index 97ba3575ba2..c45d58e65cc 100644 --- a/src/Aspire.Hosting.Testing/DistributedApplicationTestingBuilder.cs +++ b/src/Aspire.Hosting.Testing/DistributedApplicationTestingBuilder.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. #pragma warning disable ASPIREPIPELINES001 +#pragma warning disable ASPIREUSERSECRETS001 +#pragma warning disable ASPIREFILESYSTEM001 using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -9,6 +11,7 @@ using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.Eventing; using Aspire.Hosting.Pipelines; +using Aspire.Hosting.UserSecrets; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -244,6 +247,8 @@ private sealed class Builder(SuspendingDistributedApplicationFactory factory, Di public IDistributedApplicationPipeline Pipeline => innerBuilder.Pipeline; + public IUserSecretsManager UserSecretsManager => innerBuilder.UserSecretsManager; + public IResourceBuilder AddResource(T resource) where T : IResource => innerBuilder.AddResource(resource); public DistributedApplication Build() => BuildAsync(CancellationToken.None).Result; @@ -396,6 +401,8 @@ static Assembly FindApplicationAssembly() public IDistributedApplicationPipeline Pipeline => _innerBuilder.Pipeline; + public IUserSecretsManager UserSecretsManager => _innerBuilder.UserSecretsManager; + public IResourceBuilder AddResource(T resource) where T : IResource => _innerBuilder.AddResource(resource); [MemberNotNull(nameof(_app))] @@ -486,6 +493,12 @@ public interface IDistributedApplicationTestingBuilder : IDistributedApplication /// new IResourceCollection Resources => ((IDistributedApplicationBuilder)this).Resources; + /// + new IFileSystemService FileSystemService => ((IDistributedApplicationBuilder)this).FileSystemService; + + /// + new IUserSecretsManager UserSecretsManager => ((IDistributedApplicationBuilder)this).UserSecretsManager; + /// new IResourceBuilder AddResource(T resource) where T : IResource => ((IDistributedApplicationBuilder)this).AddResource(resource); diff --git a/src/Aspire.Hosting/ApplicationModel/AspireStore.cs b/src/Aspire.Hosting/ApplicationModel/AspireStore.cs index c2d976bd17b..646141d7ef3 100644 --- a/src/Aspire.Hosting/ApplicationModel/AspireStore.cs +++ b/src/Aspire.Hosting/ApplicationModel/AspireStore.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only + using System.IO.Hashing; using Aspire.Hosting.Utils; @@ -11,15 +13,18 @@ internal sealed class AspireStore : IAspireStore internal const string AspireStorePathKeyName = "Aspire:Store:Path"; private readonly string _basePath; + private readonly IFileSystemService _directoryService; /// /// Initializes a new instance of the class with the specified base path. /// /// The base path for the store. + /// The directory service for creating temp directories. /// A new instance of . - public AspireStore(string basePath) + public AspireStore(string basePath, IFileSystemService directoryService) { ArgumentNullException.ThrowIfNull(basePath); + ArgumentNullException.ThrowIfNull(directoryService); if (!Path.IsPathRooted(basePath)) { @@ -27,6 +32,7 @@ public AspireStore(string basePath) } _basePath = basePath; + _directoryService = directoryService; EnsureDirectory(); } @@ -43,7 +49,7 @@ public string GetFileNameWithContent(string filenameTemplate, Stream contentStre filenameTemplate = Path.GetFileName(filenameTemplate); // Create a temporary file to write the content to. - var tempFileName = Path.GetTempFileName(); + var tempFileName = _directoryService.TempDirectory.CreateTempFile().Path; // Fast, non-cryptographic hash. var hash = new XxHash3(); diff --git a/src/Aspire.Hosting/ApplicationModel/ProjectResource.cs b/src/Aspire.Hosting/ApplicationModel/ProjectResource.cs index fd29bb105c5..22cd794ce3c 100644 --- a/src/Aspire.Hosting/ApplicationModel/ProjectResource.cs +++ b/src/Aspire.Hosting/ApplicationModel/ProjectResource.cs @@ -4,6 +4,7 @@ #pragma warning disable ASPIREDOCKERFILEBUILDER001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. #pragma warning disable ASPIRECONTAINERRUNTIME001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. #pragma warning disable ASPIRECOMPUTE001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. @@ -137,9 +138,10 @@ private async Task BuildProjectImage(PipelineStepContext ctx) // Add COPY --from: statements for each source stage.AddContainerFiles(this, containerWorkingDir, logger); - // Write the Dockerfile to a temporary location + // Get the directory service to create temp Dockerfile var projectDir = Path.GetDirectoryName(projectMetadata.ProjectPath)!; - var tempDockerfilePath = Path.GetTempFileName(); + var directoryService = ctx.Services.GetRequiredService(); + var tempDockerfilePath = directoryService.TempDirectory.CreateTempFile().Path; var builtSuccessfully = false; try diff --git a/src/Aspire.Hosting/ApplicationModel/UserSecretsParameterDefault.cs b/src/Aspire.Hosting/ApplicationModel/UserSecretsParameterDefault.cs index faa4c199115..225932ba126 100644 --- a/src/Aspire.Hosting/ApplicationModel/UserSecretsParameterDefault.cs +++ b/src/Aspire.Hosting/ApplicationModel/UserSecretsParameterDefault.cs @@ -1,8 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREUSERSECRETS001 + using System.Diagnostics; -using System.Reflection; using Aspire.Hosting.Publishing; using Aspire.Hosting.UserSecrets; @@ -11,27 +12,20 @@ namespace Aspire.Hosting.ApplicationModel; /// /// Wraps a such that the default value is saved to the project's user secrets store. /// -/// The app host assembly. /// The application name. /// The parameter name. /// The that will produce the default value when it isn't found in the project's user secrets store. -/// The factory to use for creating user secrets managers. -internal sealed class UserSecretsParameterDefault(Assembly appHostAssembly, string applicationName, string parameterName, ParameterDefault parameterDefault, UserSecretsManagerFactory factory) +/// The user secrets manager. +internal sealed class UserSecretsParameterDefault(string applicationName, string parameterName, ParameterDefault parameterDefault, IUserSecretsManager userSecretsManager) : ParameterDefault { - public UserSecretsParameterDefault(Assembly appHostAssembly, string applicationName, string parameterName, ParameterDefault parameterDefault) - : this(appHostAssembly, applicationName, parameterName, parameterDefault, UserSecretsManagerFactory.Instance) - { - } - /// public override string GetDefaultValue() { var value = parameterDefault.GetDefaultValue(); var configurationKey = $"Parameters:{parameterName}"; - - var manager = factory.GetOrCreate(appHostAssembly); - if (!manager.TrySetSecret(configurationKey, value)) + + if (!userSecretsManager.TrySetSecret(configurationKey, value)) { // This is a best-effort operation, so we don't throw if it fails. Common reason for failure is that the user secrets ID is not set // in the application's assembly. Note there's no ILogger available this early in the application lifecycle. diff --git a/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs b/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs index 729a3418732..28e7b75c867 100644 --- a/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs @@ -4,6 +4,7 @@ #pragma warning disable ASPIREPIPELINES001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. #pragma warning disable ASPIREPIPELINES003 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. #pragma warning disable ASPIRECOMPUTE001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only using System.Diagnostics.CodeAnalysis; using System.Text; @@ -679,8 +680,9 @@ public static IResourceBuilder WithDockerfileFactory(this IResourceBuilder var fullyQualifiedContextPath = Path.GetFullPath(contextPath, builder.ApplicationBuilder.AppHostDirectory) .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - // Create a unique temporary Dockerfile path for this resource - var tempDockerfilePath = Path.Combine(Path.GetTempPath(), $"Dockerfile.{builder.Resource.Name}.{Guid.NewGuid():N}"); + // Create a unique temporary Dockerfile path for this resource using the directory service + var directoryService = builder.ApplicationBuilder.FileSystemService; + var tempDockerfilePath = directoryService.TempDirectory.CreateTempFile().Path; var imageName = ImageNameGenerator.GenerateImageName(builder); var imageTag = ImageNameGenerator.GenerateImageTag(builder); diff --git a/src/Aspire.Hosting/Dashboard/DashboardEventHandlers.cs b/src/Aspire.Hosting/Dashboard/DashboardEventHandlers.cs index 77ae160fcbe..b319e461a6c 100644 --- a/src/Aspire.Hosting/Dashboard/DashboardEventHandlers.cs +++ b/src/Aspire.Hosting/Dashboard/DashboardEventHandlers.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only + using System.Collections.Concurrent; using System.Diagnostics; using System.Globalization; @@ -37,7 +39,8 @@ internal sealed class DashboardEventHandlers(IConfiguration configuration, DcpNameGenerator nameGenerator, IHostApplicationLifetime hostApplicationLifetime, IDistributedApplicationEventing eventing, - CodespacesUrlRewriter codespaceUrlRewriter + CodespacesUrlRewriter codespaceUrlRewriter, + IFileSystemService directoryService ) : IDistributedApplicationEventingSubscriber, IAsyncDisposable { // Internal for testing @@ -226,7 +229,7 @@ private string CreateCustomRuntimeConfig(string dashboardPath) } }; - var customConfigPath = Path.ChangeExtension(Path.GetTempFileName(), ".json"); + var customConfigPath = directoryService.TempDirectory.CreateTempFile("runtimeconfig.json").Path; File.WriteAllText(customConfigPath, JsonSerializer.Serialize(defaultConfig, new JsonSerializerOptions { WriteIndented = true })); _customRuntimeConfigPath = customConfigPath; @@ -268,7 +271,7 @@ private string CreateCustomRuntimeConfig(string dashboardPath) } // Create a temporary file for the custom runtime config - var tempPath = Path.ChangeExtension(Path.GetTempFileName(), ".json"); + var tempPath = directoryService.TempDirectory.CreateTempFile("runtimeconfig.json").Path; File.WriteAllText(tempPath, configJson.ToJsonString(new JsonSerializerOptions { WriteIndented = true })); _customRuntimeConfigPath = tempPath; diff --git a/src/Aspire.Hosting/Dcp/Locations.cs b/src/Aspire.Hosting/Dcp/Locations.cs index 38f122e66ed..d44c1c66707 100644 --- a/src/Aspire.Hosting/Dcp/Locations.cs +++ b/src/Aspire.Hosting/Dcp/Locations.cs @@ -1,21 +1,34 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only + namespace Aspire.Hosting.Dcp; internal sealed class Locations { - private string? _basePath; + private readonly IFileSystemService _directoryService; + private string? _dcpSessionDir; - public string DcpSessionDir => GetOrCreateBasePath(); + public Locations(IFileSystemService directoryService) + { + _directoryService = directoryService; + } + + public string DcpSessionDir => GetOrCreateDcpSessionDir(); public string DcpKubeconfigPath => Path.Combine(DcpSessionDir, "kubeconfig"); public string DcpLogSocket => Path.Combine(DcpSessionDir, "output.sock"); - private string GetOrCreateBasePath() + private string GetOrCreateDcpSessionDir() { - _basePath ??= Directory.CreateTempSubdirectory("aspire.").FullName; - return _basePath; + if (_dcpSessionDir == null) + { + // Use the temp directory service to create a DCP-specific subdirectory + _dcpSessionDir = _directoryService.TempDirectory.CreateTempSubdirectory("aspire-dcp").Path; + } + + return _dcpSessionDir; } } diff --git a/src/Aspire.Hosting/DistributedApplicationBuilder.cs b/src/Aspire.Hosting/DistributedApplicationBuilder.cs index 3de6742d5e9..5f0a83cf547 100644 --- a/src/Aspire.Hosting/DistributedApplicationBuilder.cs +++ b/src/Aspire.Hosting/DistributedApplicationBuilder.cs @@ -6,6 +6,8 @@ #pragma warning disable ASPIREPIPELINES002 #pragma warning disable ASPIREPIPELINES004 #pragma warning disable ASPIRECONTAINERRUNTIME001 +#pragma warning disable ASPIREFILESYSTEM001 +#pragma warning disable ASPIREUSERSECRETS001 using System.Diagnostics; using System.Reflection; @@ -65,6 +67,7 @@ public class DistributedApplicationBuilder : IDistributedApplicationBuilder private readonly DistributedApplicationOptions _options; private readonly HostApplicationBuilder _innerBuilder; private readonly IUserSecretsManager _userSecretsManager; + private readonly IFileSystemService _directoryService; /// public IHostEnvironment Environment => _innerBuilder.Environment; @@ -96,6 +99,12 @@ public class DistributedApplicationBuilder : IDistributedApplicationBuilder /// public IDistributedApplicationPipeline Pipeline { get; } = new DistributedApplicationPipeline(); + /// + public IFileSystemService FileSystemService => _directoryService; + + /// + public IUserSecretsManager UserSecretsManager => _userSecretsManager; + /// /// Initializes a new instance of the class with the specified options. /// @@ -293,8 +302,13 @@ public DistributedApplicationBuilder(DistributedApplicationOptions options) } // Core things + // Create and register the directory service (first, so it can be used by other services) + _directoryService = new FileSystemService(); + _innerBuilder.Services.AddSingleton(_directoryService); + // Create and register the user secrets manager - _userSecretsManager = UserSecretsManagerFactory.Instance.GetOrCreate(AppHostAssembly); + var userSecretsFactory = new UserSecretsManagerFactory(_directoryService); + _userSecretsManager = userSecretsFactory.GetOrCreate(AppHostAssembly); // Always register IUserSecretsManager so dependencies can resolve _innerBuilder.Services.AddSingleton(_userSecretsManager); @@ -335,7 +349,8 @@ public DistributedApplicationBuilder(DistributedApplicationOptions options) throw new InvalidOperationException($"Could not determine an appropriate location for local storage. Set the {AspireStore.AspireStorePathKeyName} setting to a folder where the App Host content should be stored."); } - return new AspireStore(Path.Combine(aspireDir, ".aspire")); + var directoryService = sp.GetRequiredService(); + return new AspireStore(Path.Combine(aspireDir, ".aspire"), directoryService); }); #pragma warning disable ASPIRECERTIFICATES001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. _innerBuilder.Services.AddSingleton(); @@ -456,8 +471,8 @@ public DistributedApplicationBuilder(DistributedApplicationOptions options) _innerBuilder.Services.AddSingleton(); _innerBuilder.Services.AddSingleton(); - // We need a unique path per application instance - _innerBuilder.Services.AddSingleton(new Locations()); + // Locations now uses IFileSystemService for DCP session storage + _innerBuilder.Services.AddSingleton(); _innerBuilder.Services.AddSingleton(); Eventing.Subscribe(BuiltInDistributedApplicationEventSubscriptionHandlers.InitializeDcpAnnotations); diff --git a/src/Aspire.Hosting/IDistributedApplicationBuilder.cs b/src/Aspire.Hosting/IDistributedApplicationBuilder.cs index c3da140e310..0274165b28a 100644 --- a/src/Aspire.Hosting/IDistributedApplicationBuilder.cs +++ b/src/Aspire.Hosting/IDistributedApplicationBuilder.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. #pragma warning disable ASPIREPIPELINES001 +#pragma warning disable ASPIREFILESYSTEM001 +#pragma warning disable ASPIREUSERSECRETS001 using System.Diagnostics.CodeAnalysis; using System.Reflection; @@ -9,6 +11,7 @@ using Aspire.Hosting.Pipelines; using Aspire.Hosting.Eventing; using Aspire.Hosting.Lifecycle; +using Aspire.Hosting.UserSecrets; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -136,6 +139,35 @@ public interface IDistributedApplicationBuilder [Experimental("ASPIREPIPELINES001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] public IDistributedApplicationPipeline Pipeline { get; } + /// + /// Gets the service for managing Aspire file system operations. + /// + /// + /// + /// The provides a centralized way to manage temporary files and directories + /// used by Aspire, enabling testability and consistent temp file management. + /// + /// + /// Resources and infrastructure code should use this service instead of static methods like + /// or to ensure + /// consistent directory management across the application. + /// + /// + [Experimental("ASPIREFILESYSTEM001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] + public IFileSystemService FileSystemService => throw new NotImplementedException(); + + /// + /// Gets the service for managing user secrets. + /// + /// + /// + /// The provides a centralized way to manage user secrets + /// used by Aspire, enabling testability and consistent secret management. + /// + /// + [Experimental("ASPIREUSERSECRETS001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] + public IUserSecretsManager UserSecretsManager => throw new NotImplementedException(); + /// /// Adds a resource of type to the distributed application. /// diff --git a/src/Aspire.Hosting/ParameterResourceBuilderExtensions.cs b/src/Aspire.Hosting/ParameterResourceBuilderExtensions.cs index 010676c8a9a..661cbfd6af4 100644 --- a/src/Aspire.Hosting/ParameterResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ParameterResourceBuilderExtensions.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREUSERSECRETS001 + using System.Diagnostics.CodeAnalysis; using Aspire.Dashboard.Model; using Aspire.Hosting.ApplicationModel; @@ -137,9 +139,9 @@ public static IResourceBuilder AddParameter(this IDistributed ArgumentNullException.ThrowIfNull(value); // If it needs persistence, wrap it in a UserSecretsParameterDefault - if (persist && builder.ExecutionContext.IsRunMode && builder.AppHostAssembly is not null) + if (persist && builder.ExecutionContext.IsRunMode) { - value = new UserSecretsParameterDefault(builder.AppHostAssembly, builder.Environment.ApplicationName, name, value); + value = new UserSecretsParameterDefault(builder.Environment.ApplicationName, name, value, builder.UserSecretsManager); } return builder.AddParameter( @@ -346,9 +348,9 @@ public static ParameterResource CreateGeneratedParameter( Default = parameterDefault }; - if (builder.ExecutionContext.IsRunMode && builder.AppHostAssembly is not null) + if (builder.ExecutionContext.IsRunMode) { - parameterResource.Default = new UserSecretsParameterDefault(builder.AppHostAssembly, builder.Environment.ApplicationName, name, parameterResource.Default); + parameterResource.Default = new UserSecretsParameterDefault(builder.Environment.ApplicationName, name, parameterResource.Default, builder.UserSecretsManager); } return parameterResource; diff --git a/src/Aspire.Hosting/Pipelines/Internal/UserSecretsDeploymentStateManager.cs b/src/Aspire.Hosting/Pipelines/Internal/UserSecretsDeploymentStateManager.cs index a4e66b705fd..ec8ed487288 100644 --- a/src/Aspire.Hosting/Pipelines/Internal/UserSecretsDeploymentStateManager.cs +++ b/src/Aspire.Hosting/Pipelines/Internal/UserSecretsDeploymentStateManager.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. #pragma warning disable ASPIREPIPELINES002 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#pragma warning disable ASPIREUSERSECRETS001 using System.Text.Json; using System.Text.Json.Nodes; diff --git a/src/Aspire.Hosting/Pipelines/PipelineOutputService.cs b/src/Aspire.Hosting/Pipelines/PipelineOutputService.cs index 4d3eccec412..8f48aa4d3ba 100644 --- a/src/Aspire.Hosting/Pipelines/PipelineOutputService.cs +++ b/src/Aspire.Hosting/Pipelines/PipelineOutputService.cs @@ -1,9 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only + using System.Diagnostics.CodeAnalysis; using Aspire.Hosting.ApplicationModel; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options; namespace Aspire.Hosting.Pipelines; @@ -20,14 +21,16 @@ internal sealed class PipelineOutputService : IPipelineOutputService private readonly string? _outputPath; /// - /// Lazily creates and stores the path to the temporary directory for pipeline output. + /// Stores the path to the temporary directory for pipeline output. /// - private readonly Lazy _tempDirectory; + private readonly string _tempDirectory; - public PipelineOutputService(IOptions options, IConfiguration configuration) + public PipelineOutputService(IOptions options, IFileSystemService directoryService) { + ArgumentNullException.ThrowIfNull(directoryService); + _outputPath = options.Value.OutputPath is not null ? Path.GetFullPath(options.Value.OutputPath) : null; - _tempDirectory = new Lazy(() => CreateTempDirectory(configuration)); + _tempDirectory = directoryService.TempDirectory.CreateTempSubdirectory("aspire-pipelines").Path; } /// @@ -48,7 +51,7 @@ public string GetOutputDirectory(IResource resource) /// public string GetTempDirectory() { - return _tempDirectory.Value; + return _tempDirectory; } /// @@ -59,23 +62,4 @@ public string GetTempDirectory(IResource resource) var baseTempDir = GetTempDirectory(); return Path.Combine(baseTempDir, resource.Name); } - - /// - /// Creates a temporary directory for pipeline build artifacts. - /// Uses AppHost:PathSha256 from configuration to create an isolated temp directory per app host, - /// enabling multiple app hosts to run concurrently without conflicts. - /// If AppHost:PathSha256 is not available, falls back to a generic "aspire" temp directory. - /// - private static string CreateTempDirectory(IConfiguration configuration) - { - var appHostSha = configuration["AppHost:PathSha256"]; - - if (!string.IsNullOrEmpty(appHostSha)) - { - return Directory.CreateTempSubdirectory($"aspire-{appHostSha}").FullName; - } - - // Fallback if AppHost:PathSha256 is not available - return Directory.CreateTempSubdirectory("aspire").FullName; - } } diff --git a/src/Aspire.Hosting/UserSecrets/IUserSecretsManager.cs b/src/Aspire.Hosting/UserSecrets/IUserSecretsManager.cs index a821782d989..3fdc4f02ad9 100644 --- a/src/Aspire.Hosting/UserSecrets/IUserSecretsManager.cs +++ b/src/Aspire.Hosting/UserSecrets/IUserSecretsManager.cs @@ -1,6 +1,7 @@ // 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.CodeAnalysis; using System.Text.Json.Nodes; using Microsoft.Extensions.Configuration; @@ -9,7 +10,8 @@ namespace Aspire.Hosting.UserSecrets; /// /// Defines an interface for managing user secrets with support for read and write operations. /// -internal interface IUserSecretsManager +[Experimental("ASPIREUSERSECRETS001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] +public interface IUserSecretsManager { /// /// Gets the path to the user secrets file. diff --git a/src/Aspire.Hosting/UserSecrets/NoopUserSecretsManager.cs b/src/Aspire.Hosting/UserSecrets/NoopUserSecretsManager.cs index 1a2e133039e..d183e23c7a4 100644 --- a/src/Aspire.Hosting/UserSecrets/NoopUserSecretsManager.cs +++ b/src/Aspire.Hosting/UserSecrets/NoopUserSecretsManager.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREUSERSECRETS001 + using System.Diagnostics; using System.Text.Json.Nodes; using Microsoft.Extensions.Configuration; diff --git a/src/Aspire.Hosting/UserSecrets/UserSecretsManagerFactory.cs b/src/Aspire.Hosting/UserSecrets/UserSecretsManagerFactory.cs index 281c8d20d8f..5a745b045e0 100644 --- a/src/Aspire.Hosting/UserSecrets/UserSecretsManagerFactory.cs +++ b/src/Aspire.Hosting/UserSecrets/UserSecretsManagerFactory.cs @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only +#pragma warning disable ASPIREUSERSECRETS001 + using System.Diagnostics; using System.Reflection; using System.Text; @@ -20,15 +23,14 @@ namespace Aspire.Hosting.UserSecrets; /// internal sealed class UserSecretsManagerFactory { - // Singleton instance - public static readonly UserSecretsManagerFactory Instance = new(); - // Dictionary to cache instances by file path private readonly Dictionary _managerCache = new(); private readonly object _lock = new(); + private readonly IFileSystemService _fileSystemService; - internal UserSecretsManagerFactory() + internal UserSecretsManagerFactory(IFileSystemService fileSystemService) { + _fileSystemService = fileSystemService; } /// @@ -44,7 +46,7 @@ public IUserSecretsManager GetOrCreate(string filePath) { if (!_managerCache.TryGetValue(normalizedPath, out var manager)) { - manager = new UserSecretsManager(normalizedPath); + manager = new UserSecretsManager(normalizedPath, _fileSystemService); _managerCache[normalizedPath] = manager; } return manager; @@ -79,10 +81,12 @@ private sealed class UserSecretsManager : IUserSecretsManager private static readonly JsonSerializerOptions s_jsonSerializerOptions = new() { WriteIndented = true }; private readonly SemaphoreSlim _semaphore = new(1, 1); + private readonly IFileSystemService _fileSystemService; - public UserSecretsManager(string filePath) + public UserSecretsManager(string filePath, IFileSystemService fileSystemService) { FilePath = filePath; + _fileSystemService = fileSystemService; } public string FilePath { get; } @@ -181,7 +185,7 @@ private void Save(Dictionary secrets) // Create a temp file with the correct Unix file mode before moving it to the expected path. if (!OperatingSystem.IsWindows()) { - var tempFilename = Path.GetTempFileName(); + var tempFilename = _fileSystemService.TempDirectory.CreateTempFile().Path; File.WriteAllText(tempFilename, json, Encoding.UTF8); File.Move(tempFilename, FilePath, overwrite: true); } diff --git a/src/Aspire.Hosting/Utils/FileSystemService.cs b/src/Aspire.Hosting/Utils/FileSystemService.cs new file mode 100644 index 00000000000..0d8c3b8e82f --- /dev/null +++ b/src/Aspire.Hosting/Utils/FileSystemService.cs @@ -0,0 +1,117 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only + +namespace Aspire.Hosting; + +/// +/// Default implementation of . +/// +internal sealed class FileSystemService : IFileSystemService +{ + private readonly TempFileSystemService _tempDirectory = new(); + + /// + public ITempFileSystemService TempDirectory => _tempDirectory; + + /// + /// Implementation of . + /// + private sealed class TempFileSystemService : ITempFileSystemService + { + /// + public TempDirectory CreateTempSubdirectory(string? prefix = null) + { + var path = Directory.CreateTempSubdirectory(prefix ?? "aspire").FullName; + return new DefaultTempDirectory(path); + } + + /// + public TempFile CreateTempFile(string? fileName = null) + { + if (fileName is null) + { + var tempFile = Path.GetTempFileName(); + return new DefaultTempFile(tempFile, deleteParentDirectory: false); + } + + // Create a temp subdirectory and place the named file inside it + var tempDir = Directory.CreateTempSubdirectory("aspire").FullName; + var filePath = Path.Combine(tempDir, fileName); + File.Create(filePath).Dispose(); + return new DefaultTempFile(filePath, deleteParentDirectory: true); + } + } + + /// + /// Default implementation of . + /// + private sealed class DefaultTempDirectory : TempDirectory + { + private readonly string _path; + + public DefaultTempDirectory(string path) + { + _path = path; + } + + public override string Path => _path; + + public override void Dispose() + { + try + { + if (Directory.Exists(_path)) + { + Directory.Delete(_path, recursive: true); + } + } + catch + { + // Ignore errors during cleanup + } + } + } + + /// + /// Default implementation of . + /// + private sealed class DefaultTempFile : TempFile + { + private readonly string _path; + private readonly bool _deleteParentDirectory; + + public DefaultTempFile(string path, bool deleteParentDirectory) + { + _path = path; + _deleteParentDirectory = deleteParentDirectory; + } + + public override string Path => _path; + + public override void Dispose() + { + try + { + if (File.Exists(_path)) + { + File.Delete(_path); + } + + if (_deleteParentDirectory) + { + var parentDir = System.IO.Path.GetDirectoryName(_path); + if (parentDir is not null && Directory.Exists(parentDir)) + { + Directory.Delete(parentDir, recursive: true); + } + } + } + catch + { + // Ignore errors during cleanup + } + } + } +} diff --git a/src/Aspire.Hosting/Utils/IFileSystemService.cs b/src/Aspire.Hosting/Utils/IFileSystemService.cs new file mode 100644 index 00000000000..43704c244f7 --- /dev/null +++ b/src/Aspire.Hosting/Utils/IFileSystemService.cs @@ -0,0 +1,102 @@ +// 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.CodeAnalysis; + +namespace Aspire.Hosting; + +/// +/// Service for managing Aspire directories. +/// +/// +/// This service provides a centralized way to manage directories used by Aspire, +/// including temporary files, cache, and other storage needs. +/// +[Experimental("ASPIREFILESYSTEM001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] +public interface IFileSystemService +{ + /// + /// Gets the temporary directory service for managing temporary files and directories. + /// + /// + /// + /// Use this instead of calling directly + /// to ensure consistent temp file management and enable testability. + /// + /// + ITempFileSystemService TempDirectory { get; } +} + +/// +/// Service for managing temporary directories and files within Aspire. +/// +[Experimental("ASPIREFILESYSTEM001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] +public interface ITempFileSystemService +{ + /// + /// Creates and returns a temporary subdirectory. + /// + /// Optional prefix for the subdirectory name (e.g., "aspire"). + /// A representing the created temporary subdirectory. Dispose to delete. + /// + /// + /// This method creates a unique temporary subdirectory using the system temp folder. + /// The directory is created immediately. Dispose the returned object to delete the directory. + /// + /// + /// Use this instead of calling directly. + /// + /// + TempDirectory CreateTempSubdirectory(string? prefix = null); + + /// + /// Creates a new temporary file and returns it. + /// + /// Optional file name for the temporary file (e.g., "config.json", "script.sh"). If null, uses a random name. + /// A representing the created temporary file. Dispose to delete. + /// + /// + /// This method creates a new temporary file. If a file name is specified, it creates a temporary subdirectory + /// and places the file with that name inside it. If no file name is specified, it uses . + /// Dispose the returned object to delete the file. + /// + /// + /// Use this instead of calling directly. + /// + /// + TempFile CreateTempFile(string? fileName = null); +} + +/// +/// Represents a temporary directory that will be deleted when disposed. +/// +[Experimental("ASPIREFILESYSTEM001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] +public abstract class TempDirectory : IDisposable +{ + /// + /// Gets the full path to the temporary directory. + /// + public abstract string Path { get; } + + /// + /// Deletes the temporary directory and all its contents. + /// + public abstract void Dispose(); +} + +/// +/// Represents a temporary file that will be deleted when disposed. +/// +[Experimental("ASPIREFILESYSTEM001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")] +public abstract class TempFile : IDisposable +{ + /// + /// Gets the full path to the temporary file. + /// + public abstract string Path { get; } + + /// + /// Deletes the temporary file. When created with a file name, also deletes the parent directory. + /// + public abstract void Dispose(); +} diff --git a/src/Aspire.Hosting/VersionChecking/VersionCheckService.cs b/src/Aspire.Hosting/VersionChecking/VersionCheckService.cs index f3fdc0d74b7..a96bf37884a 100644 --- a/src/Aspire.Hosting/VersionChecking/VersionCheckService.cs +++ b/src/Aspire.Hosting/VersionChecking/VersionCheckService.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. #pragma warning disable ASPIREINTERACTION001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#pragma warning disable ASPIREUSERSECRETS001 using System.Diagnostics; using System.Diagnostics.CodeAnalysis; diff --git a/tests/Aspire.Hosting.Azure.Kusto.Tests/KustoFunctionalTests.cs b/tests/Aspire.Hosting.Azure.Kusto.Tests/KustoFunctionalTests.cs index 17c4e4c7fe5..71e2a414d8f 100644 --- a/tests/Aspire.Hosting.Azure.Kusto.Tests/KustoFunctionalTests.cs +++ b/tests/Aspire.Hosting.Azure.Kusto.Tests/KustoFunctionalTests.cs @@ -231,7 +231,7 @@ public async Task KustoEmulator_WithBindMount_IsUsedForPersistence() { using var timeout = new CancellationTokenSource(TestConstants.ExtraLongTimeoutTimeSpan); using var cts = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, TestContext.Current.CancellationToken); - using var temp = new TempDirectory(); + using var temp = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(_testOutputHelper); diff --git a/tests/Aspire.Hosting.Azure.Tests/AzureAppServiceTests.cs b/tests/Aspire.Hosting.Azure.Tests/AzureAppServiceTests.cs index 5e56596cfb2..31aa27ea80d 100644 --- a/tests/Aspire.Hosting.Azure.Tests/AzureAppServiceTests.cs +++ b/tests/Aspire.Hosting.Azure.Tests/AzureAppServiceTests.cs @@ -338,7 +338,7 @@ await RunTest(builder => [ActiveIssue("https://github.com/dotnet/aspire/issues/11818", typeof(PlatformDetection), nameof(PlatformDetection.IsRunningFromAzdo))] public async Task MultipleAzureAppServiceEnvironmentsSupported() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path, step: "publish-manifest"); @@ -368,7 +368,7 @@ await VerifyFile( [Fact] public async Task ResourceWithProbes() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); diff --git a/tests/Aspire.Hosting.Azure.Tests/AzureBicepProvisionerTests.cs b/tests/Aspire.Hosting.Azure.Tests/AzureBicepProvisionerTests.cs index 8327dbfe45c..0a59a4663e1 100644 --- a/tests/Aspire.Hosting.Azure.Tests/AzureBicepProvisionerTests.cs +++ b/tests/Aspire.Hosting.Azure.Tests/AzureBicepProvisionerTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. #pragma warning disable ASPIREPIPELINES002 +#pragma warning disable ASPIREFILESYSTEM001 using Aspire.Dashboard.Model; using Aspire.Hosting.ApplicationModel; @@ -100,6 +101,7 @@ public void BicepProvisioner_CanBeInstantiated() secretClientProvider, services.GetRequiredService(), new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run), + services.GetRequiredService(), NullLogger.Instance); // Assert diff --git a/tests/Aspire.Hosting.Azure.Tests/AzureContainerAppsTests.cs b/tests/Aspire.Hosting.Azure.Tests/AzureContainerAppsTests.cs index 525308b387f..53a05e3a9c5 100644 --- a/tests/Aspire.Hosting.Azure.Tests/AzureContainerAppsTests.cs +++ b/tests/Aspire.Hosting.Azure.Tests/AzureContainerAppsTests.cs @@ -1614,7 +1614,7 @@ await RunTest(builder => [Fact] public async Task MultipleAzureContainerAppEnvironmentsSupported() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path, step: "publish-manifest"); @@ -2003,7 +2003,7 @@ public async Task BindMountNamesWithHyphensAreNormalized() builder.AddAzureContainerAppEnvironment("env"); - using var tempDirectory = new TempDirectory(); + using var tempDirectory = new TestTempDirectory(); // Contents of the Dockerfile are not important for this test File.WriteAllText(Path.Combine(tempDirectory.Path, "Dockerfile"), "FROM alpine"); diff --git a/tests/Aspire.Hosting.Azure.Tests/AzureEnvironmentResourceTests.cs b/tests/Aspire.Hosting.Azure.Tests/AzureEnvironmentResourceTests.cs index 74950f34237..92ac2016391 100644 --- a/tests/Aspire.Hosting.Azure.Tests/AzureEnvironmentResourceTests.cs +++ b/tests/Aspire.Hosting.Azure.Tests/AzureEnvironmentResourceTests.cs @@ -184,7 +184,7 @@ private sealed class TestProject : IProjectMetadata public async Task AzurePublishingContext_IgnoresAzureBicepResourcesWithIgnoreAnnotation() { // Arrange - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); @@ -218,7 +218,7 @@ public async Task AzurePublishingContext_IgnoresAzureBicepResourcesWithIgnoreAnn [Fact] public async Task PublishAsync_WithDockerfileFactory_WritesDockerfileToOutputFolder() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); var containerAppEnv = builder.AddAzureContainerAppEnvironment("env"); diff --git a/tests/Aspire.Hosting.Azure.Tests/AzureFunctionsTests.cs b/tests/Aspire.Hosting.Azure.Tests/AzureFunctionsTests.cs index 4d194a50061..095e9707aff 100644 --- a/tests/Aspire.Hosting.Azure.Tests/AzureFunctionsTests.cs +++ b/tests/Aspire.Hosting.Azure.Tests/AzureFunctionsTests.cs @@ -609,7 +609,7 @@ public void AddAzureFunctionsProject_AppHostConfigurationOverridesDotnetLaunchPr [Fact] public void AddAzureFunctionsProject_WithProjectPath_Works() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(); // Create a temporary project file @@ -634,7 +634,7 @@ public void AddAzureFunctionsProject_WithProjectPath_Works() [Fact] public void AddAzureFunctionsProject_WithProjectPath_NormalizesPath() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(); // Create a temporary project file @@ -656,7 +656,7 @@ public void AddAzureFunctionsProject_WithProjectPath_NormalizesPath() [Fact] public async Task AddAzureFunctionsProject_WithProjectPath_ConfiguresEnvironmentVariables() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(); // Create a temporary project file @@ -687,7 +687,7 @@ public async Task AddAzureFunctionsProject_WithProjectPath_ConfiguresEnvironment [Fact] public void AddAzureFunctionsProject_WithProjectPath_SharesDefaultStorage() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(); // Create temporary project files @@ -710,7 +710,7 @@ public void AddAzureFunctionsProject_WithProjectPath_SharesDefaultStorage() [RequiresDocker] public async Task AddAzureFunctionsProject_WithProjectPath_CanUseCustomHostStorage() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(); // Create a temporary project file @@ -742,7 +742,7 @@ public async Task AddAzureFunctionsProject_WithProjectPath_CanUseCustomHostStora [Fact] public void AddAzureFunctionsProject_WithProjectPath_AddsAzureFunctionsAnnotation() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(); // Create a temporary project file diff --git a/tests/Aspire.Hosting.Containers.Tests/DockerSocketBindMountTests.cs b/tests/Aspire.Hosting.Containers.Tests/DockerSocketBindMountTests.cs index c2d4a8b19d2..a1daa85612c 100644 --- a/tests/Aspire.Hosting.Containers.Tests/DockerSocketBindMountTests.cs +++ b/tests/Aspire.Hosting.Containers.Tests/DockerSocketBindMountTests.cs @@ -19,8 +19,8 @@ public async Task WithDockerSocketBindMountAllowsDockerCliInContainer() CMD sh -c "docker info > /out/docker-info.txt" """; - using var dir = new TempDirectory(); - using var outDir = new TempDirectory(); + using var dir = new TestTempDirectory(); + using var outDir = new TestTempDirectory(); var dockerFilePath = Path.Combine(dir.Path, "Dockerfile"); await File.WriteAllTextAsync(dockerFilePath, dockerfile); diff --git a/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs b/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs index 0f36879ed96..df6cd58d3b3 100644 --- a/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs +++ b/tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs @@ -16,7 +16,7 @@ public class DockerComposePublisherTests(ITestOutputHelper outputHelper) [Fact] public async Task PublishAsync_GeneratesValidDockerComposeFile() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Arrange var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); @@ -106,7 +106,7 @@ await Verify(File.ReadAllText(composePath), "yaml") [Fact] public async Task DockerComposeWithProjectResources() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Arrange var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); @@ -143,7 +143,7 @@ await Verify(File.ReadAllText(composePath), "yaml") [Fact] public async Task DockerComposeCorrectlyEmitsPortMappings() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path) .WithTestAndResourceLogging(outputHelper); @@ -168,7 +168,7 @@ public async Task DockerComposeCorrectlyEmitsPortMappings() [Fact] public void DockerComposeDoesNotHandleImageBuildingDuringPublish() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path, step: "publish-docker-compose") .WithTestAndResourceLogging(outputHelper); @@ -196,7 +196,7 @@ public void DockerComposeDoesNotHandleImageBuildingDuringPublish() [Fact] public async Task DockerComposeAppliesServiceCustomizations() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.Services.AddSingleton(); @@ -249,7 +249,7 @@ await Verify(File.ReadAllText(composePath), "yaml") [Fact] public async Task DockerComposeDoesNotOverwriteEnvFileOnPublish() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var envFilePath = Path.Combine(tempDir.Path, ".env"); void PublishApp() @@ -280,7 +280,7 @@ await Verify(firstContent, "env") [Fact] public async Task DockerComposeAppendsNewKeysToEnvFileOnPublish() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var envFilePath = Path.Combine(tempDir.Path, ".env"); void PublishApp(params string[] paramNames) @@ -321,7 +321,7 @@ await Verify(firstContent, "env") [Fact] public async Task DockerComposeMapsPortsProperly() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.Services.AddSingleton(); @@ -347,7 +347,7 @@ public async Task DockerComposeMapsPortsProperly() [Fact] public async Task PublishAsync_WithDashboardEnabled_IncludesDashboardService() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.Services.AddSingleton(); @@ -373,7 +373,7 @@ public async Task PublishAsync_WithDashboardEnabled_IncludesDashboardService() [Fact] public async Task PublishAsync_WithDashboardDisabled_DoesNotIncludeDashboardService() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.Services.AddSingleton(); @@ -399,7 +399,7 @@ public async Task PublishAsync_WithDashboardDisabled_DoesNotIncludeDashboardServ [Fact] public async Task PublishAsync_WithDashboard_UsesCustomConfiguration() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.Services.AddSingleton(); @@ -427,7 +427,7 @@ public async Task PublishAsync_WithDashboard_UsesCustomConfiguration() [Fact] public async Task PublishAsync_MultipleResourcesWithOtlp_ConfiguresAllForDashboard() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.Services.AddSingleton(); @@ -459,7 +459,7 @@ public async Task PublishAsync_MultipleResourcesWithOtlp_ConfiguresAllForDashboa [Fact] public async Task PublishAsync_WithDockerfileFactory_WritesDockerfileToOutputFolder() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.Services.AddSingleton(); @@ -483,7 +483,7 @@ public async Task PublishAsync_WithDockerfileFactory_WritesDockerfileToOutputFol [Fact] public void PublishAsync_InRunMode_DoesNotCreateDashboard() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Run, tempDir.Path); builder.Services.AddSingleton(); @@ -504,7 +504,7 @@ public void PublishAsync_InRunMode_DoesNotCreateDashboard() [Fact] public async Task PrepareStep_GeneratesCorrectEnvFileWithDefaultEnvironmentName() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path, step: "prepare-docker-compose"); builder.Services.AddSingleton(); @@ -532,7 +532,7 @@ await Verify(envFileContent, "env") [Fact] public async Task PrepareStep_GeneratesCorrectEnvFileWithCustomEnvironmentName() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path, step: "prepare-docker-compose"); builder.Services.AddSingleton(); @@ -564,7 +564,7 @@ await Verify(envFileContent, "env") [Fact] public async Task PrepareStep_GeneratesEnvFileWithVariousParameterTypes() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path, step: "prepare-docker-compose"); builder.Services.AddSingleton(); @@ -595,7 +595,7 @@ await Verify(envFileContent, "env") [Fact] public void PrepareStep_OverwritesExistingEnvFileAndLogsWarning() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path, step: "prepare-docker-compose"); builder.Services.AddSingleton(); @@ -628,7 +628,7 @@ public void PrepareStep_OverwritesExistingEnvFileAndLogsWarning() [Fact] public void PrepareStep_OverwritesExistingEnvFileWithCustomEnvironmentName() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path, step: "prepare-docker-compose"); builder.Services.AddSingleton(); diff --git a/tests/Aspire.Hosting.Docker.Tests/DockerComposeTests.cs b/tests/Aspire.Hosting.Docker.Tests/DockerComposeTests.cs index 560ad1f46db..9e895342a4a 100644 --- a/tests/Aspire.Hosting.Docker.Tests/DockerComposeTests.cs +++ b/tests/Aspire.Hosting.Docker.Tests/DockerComposeTests.cs @@ -123,7 +123,7 @@ await RunTest(builder => [Fact] public async Task MultipleDockerComposeEnvironmentsSupported() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.Services.AddSingleton(); @@ -148,7 +148,7 @@ public async Task MultipleDockerComposeEnvironmentsSupported() [Fact] public async Task DashboardWithForwardedHeadersWritesEnvVar() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.Services.AddSingleton(); @@ -172,7 +172,7 @@ public async Task DashboardWithForwardedHeadersWritesEnvVar() [Fact] public async Task DockerSwarmDeploymentLabelsSerializedCorrectly() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.Services.AddSingleton(); @@ -248,7 +248,7 @@ public Task PushImageAsync(IResource resource, CancellationToken cancellationTok [Fact] public void DockerComposeProjectNameIncludesAppHostShaInArguments() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var testSink = new TestSink(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path, step: WellKnownPipelineSteps.Deploy); diff --git a/tests/Aspire.Hosting.Docker.Tests/EnvFileTests.cs b/tests/Aspire.Hosting.Docker.Tests/EnvFileTests.cs index 6a99431b463..f14069bc65d 100644 --- a/tests/Aspire.Hosting.Docker.Tests/EnvFileTests.cs +++ b/tests/Aspire.Hosting.Docker.Tests/EnvFileTests.cs @@ -8,7 +8,7 @@ public class EnvFileTests [Fact] public void Add_WithOnlyIfMissingTrue_DoesNotAddDuplicate() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var envFilePath = Path.Combine(tempDir.Path, ".env"); // Create initial .env file @@ -34,7 +34,7 @@ public void Add_WithOnlyIfMissingTrue_DoesNotAddDuplicate() [Fact] public void Add_WithOnlyIfMissingFalse_UpdatesExistingKey() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var envFilePath = Path.Combine(tempDir.Path, ".env"); // Create initial .env file @@ -60,7 +60,7 @@ public void Add_WithOnlyIfMissingFalse_UpdatesExistingKey() [Fact] public void Add_WithOnlyIfMissingFalse_UpdatesImageNameWithoutDuplication() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var envFilePath = Path.Combine(tempDir.Path, ".env"); // Create initial .env file simulating a project resource @@ -100,7 +100,7 @@ public void Add_WithOnlyIfMissingFalse_UpdatesImageNameWithoutDuplication() [Fact] public void Add_NewKey_AddsToFile() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var envFilePath = Path.Combine(tempDir.Path, ".env"); // Create initial .env file @@ -125,7 +125,7 @@ public void Add_NewKey_AddsToFile() [Fact] public void Load_EmptyFile_ReturnsEmptyEnvFile() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var envFilePath = Path.Combine(tempDir.Path, ".env"); // Create empty file @@ -142,7 +142,7 @@ public void Load_EmptyFile_ReturnsEmptyEnvFile() [Fact] public void Load_NonExistentFile_ReturnsEmptyEnvFile() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var envFilePath = Path.Combine(tempDir.Path, ".env"); // Don't create the file diff --git a/tests/Aspire.Hosting.JavaScript.Tests/AddJavaScriptAppTests.cs b/tests/Aspire.Hosting.JavaScript.Tests/AddJavaScriptAppTests.cs index 6afab2d5692..032b86677c2 100644 --- a/tests/Aspire.Hosting.JavaScript.Tests/AddJavaScriptAppTests.cs +++ b/tests/Aspire.Hosting.JavaScript.Tests/AddJavaScriptAppTests.cs @@ -13,7 +13,7 @@ public class AddJavaScriptAppTests [Fact] public async Task VerifyDockerfile() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, outputPath: tempDir.Path).WithResourceCleanUp(true); // Create directory to ensure manifest generates correct relative build context path @@ -51,7 +51,7 @@ COPY . . [InlineData(false)] public async Task VerifyPnpmDockerfile(bool hasLockFile) { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, outputPath: tempDir.Path).WithResourceCleanUp(true); // Create directory to ensure manifest generates correct relative build context path @@ -80,7 +80,7 @@ public async Task VerifyPnpmDockerfile(bool hasLockFile) [OuterloopTest("Builds a Docker image to verify the generated pnpm Dockerfile works")] public async Task VerifyPnpmDockerfileBuildSucceeds() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, outputPath: tempDir.Path).WithResourceCleanUp(true); // Create app directory diff --git a/tests/Aspire.Hosting.JavaScript.Tests/AddNodeAppTests.cs b/tests/Aspire.Hosting.JavaScript.Tests/AddNodeAppTests.cs index eb4490266ce..f811f730866 100644 --- a/tests/Aspire.Hosting.JavaScript.Tests/AddNodeAppTests.cs +++ b/tests/Aspire.Hosting.JavaScript.Tests/AddNodeAppTests.cs @@ -86,7 +86,7 @@ public async Task VerifyManifest() [InlineData(false)] public async Task VerifyDockerfile(bool includePackageJson) { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, outputPath: tempDir.Path).WithResourceCleanUp(true); var appDir = Path.Combine(tempDir.Path, "js"); @@ -154,7 +154,7 @@ USER node [Fact] public async Task VerifyDockerfileWithBuildScript() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, outputPath: tempDir.Path).WithResourceCleanUp(true); var appDir = Path.Combine(tempDir.Path, "js"); @@ -201,7 +201,7 @@ USER node [Fact] public async Task VerifyDockerfileWithCustomBaseImage() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, outputPath: tempDir.Path).WithResourceCleanUp(true); var appDir = Path.Combine(tempDir.Path, "js"); @@ -225,7 +225,7 @@ public async Task VerifyDockerfileWithCustomBaseImage() [Fact] public void AddNodeApp_DoesNotAddNpmWhenNoPackageJson() { - var tempDir = new TempDirectory(); + var tempDir = new TestTempDirectory(); File.WriteAllText(Path.Combine(tempDir.Path, "app.js"), "{}"); var builder = DistributedApplication.CreateBuilder(); @@ -252,7 +252,7 @@ public void AddNodeApp_DoesNotAddNpmWhenNoPackageJson() [Fact] public void AddNodeApp_AddsNpmWhenPackageJsonExists() { - var tempDir = new TempDirectory(); + var tempDir = new TestTempDirectory(); File.WriteAllText(Path.Combine(tempDir.Path, "package.json"), "{}"); var builder = DistributedApplication.CreateBuilder(); @@ -307,8 +307,8 @@ public async Task WithRunScript_SetsCustomRunCommand() [Fact] public async Task VerifyNodeAppWithContainerFilesGeneratesCorrectDockerfile() { - using var sourceDir = new TempDirectory(); - using var outputDir = new TempDirectory(); + using var sourceDir = new TestTempDirectory(); + using var outputDir = new TestTempDirectory(); var appDirectory = sourceDir.Path; // Create a simple Node.js app @@ -363,8 +363,8 @@ public async Task VerifyNodeAppWithContainerFilesGeneratesCorrectDockerfile() [Fact] public async Task VerifyNodeAppWithContainerFilesFromResourceWithDashesGeneratesCorrectDockerfile() { - using var sourceDir = new TempDirectory(); - using var outputDir = new TempDirectory(); + using var sourceDir = new TestTempDirectory(); + using var outputDir = new TestTempDirectory(); var appDirectory = sourceDir.Path; // Create a simple Node.js app diff --git a/tests/Aspire.Hosting.JavaScript.Tests/AddViteAppTests.cs b/tests/Aspire.Hosting.JavaScript.Tests/AddViteAppTests.cs index 3582e5c850d..2ce8eaae126 100644 --- a/tests/Aspire.Hosting.JavaScript.Tests/AddViteAppTests.cs +++ b/tests/Aspire.Hosting.JavaScript.Tests/AddViteAppTests.cs @@ -15,7 +15,7 @@ public class AddViteAppTests [Fact] public async Task VerifyDefaultDockerfile() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, outputPath: tempDir.Path).WithResourceCleanUp(true); // Create vite directory to ensure manifest generates correct relative build context path @@ -77,7 +77,7 @@ RUN npm run build [Fact] public async Task VerifyDockerfileWithNodeVersionFromPackageJson() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Create a package.json with engines.node specification var packageJson = """ @@ -105,7 +105,7 @@ public async Task VerifyDockerfileWithNodeVersionFromPackageJson() [Fact] public async Task VerifyDockerfileWithNodeVersionFromNvmrc() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Create an .nvmrc file File.WriteAllText(Path.Combine(tempDir.Path, ".nvmrc"), "18.20.0"); @@ -125,7 +125,7 @@ public async Task VerifyDockerfileWithNodeVersionFromNvmrc() [Fact] public async Task VerifyDockerfileWithNodeVersionFromNodeVersion() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Create a .node-version file File.WriteAllText(Path.Combine(tempDir.Path, ".node-version"), "v21.5.0"); @@ -145,7 +145,7 @@ public async Task VerifyDockerfileWithNodeVersionFromNodeVersion() [Fact] public async Task VerifyDockerfileWithNodeVersionFromToolVersions() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Create a .tool-versions file var toolVersions = """ @@ -170,7 +170,7 @@ python 3.11.0 [Fact] public async Task VerifyDockerfileDefaultsTo22WhenNoVersionFound() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Don't create any version files - should default to 22 using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, outputPath: tempDir.Path).WithResourceCleanUp(true); @@ -193,7 +193,7 @@ public async Task VerifyDockerfileDefaultsTo22WhenNoVersionFound() [InlineData("~19.5.0", "node:19-slim")] public async Task VerifyDockerfileHandlesVariousVersionFormats(string versionString, string expectedImage) { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); File.WriteAllText(Path.Combine(tempDir.Path, ".nvmrc"), versionString); @@ -211,7 +211,7 @@ public async Task VerifyDockerfileHandlesVariousVersionFormats(string versionStr [Fact] public async Task VerifyCustomBaseImage() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, outputPath: tempDir.Path).WithResourceCleanUp(true); var customImage = "node:22-myspecialimage"; @@ -280,7 +280,7 @@ public void AddViteApp_WithoutViteConfigPath_DoesNotApplyConfigArgument() [Fact] public async Task AddViteApp_ServerAuthCertConfig_WithExistingConfigArgument_ReplacesConfigPath() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Create node_modules/.bin directory for Aspire config generation var nodeModulesBinDir = Path.Combine(tempDir.Path, "node_modules", ".bin"); @@ -341,7 +341,7 @@ public async Task AddViteApp_ServerAuthCertConfig_WithExistingConfigArgument_Rep [Fact] public async Task AddViteApp_ServerAuthCertConfig_WithoutExistingConfigArgument_DetectsDefaultConfig() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Create node_modules/.bin directory for Aspire config generation var nodeModulesBinDir = Path.Combine(tempDir.Path, "node_modules", ".bin"); @@ -399,7 +399,7 @@ public async Task AddViteApp_ServerAuthCertConfig_WithoutExistingConfigArgument_ [Fact] public async Task AddViteApp_ServerAuthCertConfig_WithMissingConfigFile_DoesNotAddConfigArgument() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Don't create any vite config file var builder = DistributedApplication.CreateBuilder(); @@ -445,7 +445,7 @@ public async Task AddViteApp_ServerAuthCertConfig_WithMissingConfigFile_DoesNotA [Fact] public async Task AddViteApp_ServerAuthCertConfig_WithPassword_SetsPasswordEnvironmentVariable() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Create node_modules/.bin directory for Aspire config generation var nodeModulesBinDir = Path.Combine(tempDir.Path, "node_modules", ".bin"); @@ -506,7 +506,7 @@ public async Task AddViteApp_ServerAuthCertConfig_WithPassword_SetsPasswordEnvir [InlineData("vite.config.cts")] public async Task AddViteApp_ServerAuthCertConfig_DetectsAllDefaultConfigFileFormats(string configFileName) { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Create node_modules/.bin directory for Aspire config generation var nodeModulesBinDir = Path.Combine(tempDir.Path, "node_modules", ".bin"); diff --git a/tests/Aspire.Hosting.JavaScript.Tests/PackageInstallationTests.cs b/tests/Aspire.Hosting.JavaScript.Tests/PackageInstallationTests.cs index 9023f317bae..7f87fe39fb2 100644 --- a/tests/Aspire.Hosting.JavaScript.Tests/PackageInstallationTests.cs +++ b/tests/Aspire.Hosting.JavaScript.Tests/PackageInstallationTests.cs @@ -454,7 +454,7 @@ public void AddViteApp_DefaultInstallsPackages() [Fact] public void WithNpm_DefaultsArgsInPublishMode() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); File.WriteAllText(Path.Combine(tempDir.Path, "package-lock.json"), "empty"); using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); @@ -481,7 +481,7 @@ public void WithNpm_CanChangeInstallCommandAndArgs() [Fact] public void WithYarn_DefaultsArgsInPublishMode() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); File.WriteAllText(Path.Combine(tempDir.Path, "yarn.lock"), "empty"); using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); @@ -502,7 +502,7 @@ public void WithYarn_DefaultsArgsInPublishMode() [Fact] public void WithYarn_ReturnsImmutable_WhenYarnRcYmlExists() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); File.WriteAllText(Path.Combine(tempDir.Path, "yarn.lock"), "empty"); File.WriteAllText(Path.Combine(tempDir.Path, ".yarnrc.yml"), "empty"); @@ -518,7 +518,7 @@ public void WithYarn_ReturnsImmutable_WhenYarnRcYmlExists() [Fact] public void WithYarn_ReturnsImmutable_WhenYarnReleasesDirExists() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); File.WriteAllText(Path.Combine(tempDir.Path, "yarn.lock"), "empty"); Directory.CreateDirectory(Path.Combine(tempDir.Path, ".yarn", "releases")); @@ -534,7 +534,7 @@ public void WithYarn_ReturnsImmutable_WhenYarnReleasesDirExists() [Fact] public void WithPnpm_DefaultsArgsInPublishMode() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); File.WriteAllText(Path.Combine(tempDir.Path, "pnpm-lock.yaml"), "empty"); using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); diff --git a/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesEnvironmentResourceTests.cs b/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesEnvironmentResourceTests.cs index 93ad432592e..97dbb02f33b 100644 --- a/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesEnvironmentResourceTests.cs +++ b/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesEnvironmentResourceTests.cs @@ -72,7 +72,7 @@ await RunTest(builder => [ActiveIssue("https://github.com/dotnet/aspire/issues/11818", typeof(PlatformDetection), nameof(PlatformDetection.IsRunningFromAzdo))] public async Task MultipleKubernetesEnvironmentsSupported() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); diff --git a/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs b/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs index bf5611ea8b1..a3da353c498 100644 --- a/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs +++ b/tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs @@ -13,7 +13,7 @@ public class KubernetesPublisherTests() [Fact] public async Task PublishAsync_GeneratesValidHelmChart() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.AddKubernetesEnvironment("env"); @@ -79,7 +79,7 @@ public async Task PublishAsync_GeneratesValidHelmChart() [Fact] public async Task PublishAppliesServiceCustomizations() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.AddKubernetesEnvironment("env") @@ -110,7 +110,7 @@ public async Task PublishAppliesServiceCustomizations() [Fact] public async Task PublishAsync_CustomWorkloadAndResourceType() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.AddKubernetesEnvironment("env"); @@ -174,7 +174,7 @@ public async Task PublishAsync_CustomWorkloadAndResourceType() [Fact] public async Task PublishAsync_HandlesSpecialResourceName() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.AddKubernetesEnvironment("env") @@ -228,7 +228,7 @@ public async Task PublishAsync_HandlesSpecialResourceName() [Fact] public async Task PublishAsync_ResourceWithProbes() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.AddKubernetesEnvironment("env"); @@ -283,7 +283,7 @@ public async Task PublishAsync_ResourceWithProbes() [Fact] public async Task PublishAsync_WithDockerfileFactory_WritesDockerfileToOutputFolder() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.AddKubernetesEnvironment("env"); @@ -356,7 +356,7 @@ public sealed class ArgoRolloutSpec [Fact] public async Task KubernetesWithProjectResources() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.AddKubernetesEnvironment("env"); diff --git a/tests/Aspire.Hosting.MySql.Tests/AddMySqlTests.cs b/tests/Aspire.Hosting.MySql.Tests/AddMySqlTests.cs index 85b4df1699f..93d8b99907d 100644 --- a/tests/Aspire.Hosting.MySql.Tests/AddMySqlTests.cs +++ b/tests/Aspire.Hosting.MySql.Tests/AddMySqlTests.cs @@ -258,7 +258,7 @@ public void WithPhpMyAdminProducesValidServerConfigFile() { var builder = DistributedApplication.CreateBuilder(); - using var tempStore = new TempDirectory(); + using var tempStore = new TestTempDirectory(); builder.Configuration["Aspire:Store:Path"] = tempStore.Path; var mysql1 = builder.AddMySql("mysql1").WithPhpMyAdmin(c => c.WithHostPort(8081)); diff --git a/tests/Aspire.Hosting.MySql.Tests/MySqlFunctionalTests.cs b/tests/Aspire.Hosting.MySql.Tests/MySqlFunctionalTests.cs index 6bd728650b4..f10af740541 100644 --- a/tests/Aspire.Hosting.MySql.Tests/MySqlFunctionalTests.cs +++ b/tests/Aspire.Hosting.MySql.Tests/MySqlFunctionalTests.cs @@ -549,7 +549,7 @@ public async Task MySql_WithPersistentLifetime_ReusesContainers(bool useMultiple using var cts = new CancellationTokenSource(TestConstants.ExtraLongTimeoutTimeSpan * 2); // Use the same path for both runs - using var aspireStore = new TempDirectory(); + using var aspireStore = new TestTempDirectory(); var before = await RunContainersAsync(); var after = await RunContainersAsync(); diff --git a/tests/Aspire.Hosting.PostgreSQL.Tests/AddPostgresTests.cs b/tests/Aspire.Hosting.PostgreSQL.Tests/AddPostgresTests.cs index cff56b73699..03d2fb1fc81 100644 --- a/tests/Aspire.Hosting.PostgreSQL.Tests/AddPostgresTests.cs +++ b/tests/Aspire.Hosting.PostgreSQL.Tests/AddPostgresTests.cs @@ -453,7 +453,7 @@ public async Task WithPostgresProducesValidServersJsonFile() { var builder = DistributedApplication.CreateBuilder(); - using var tempStore = new TempDirectory(); + using var tempStore = new TestTempDirectory(); builder.Configuration["Aspire:Store:Path"] = tempStore.Path; var username = builder.AddParameter("pg-user", "myuser"); @@ -515,7 +515,7 @@ public async Task WithPgwebProducesValidBookmarkFiles() { var builder = DistributedApplication.CreateBuilder(); - using var tempStore = new TempDirectory(); + using var tempStore = new TestTempDirectory(); builder.Configuration["Aspire:Store:Path"] = tempStore.Path; var pg1 = builder.AddPostgres("mypostgres1").WithPgWeb(pga => pga.WithHostPort(8081)); diff --git a/tests/Aspire.Hosting.PostgreSQL.Tests/PostgresFunctionalTests.cs b/tests/Aspire.Hosting.PostgreSQL.Tests/PostgresFunctionalTests.cs index ce71cae9a6b..3f30d5791aa 100644 --- a/tests/Aspire.Hosting.PostgreSQL.Tests/PostgresFunctionalTests.cs +++ b/tests/Aspire.Hosting.PostgreSQL.Tests/PostgresFunctionalTests.cs @@ -536,7 +536,7 @@ public async Task Postgres_WithPersistentLifetime_ReusesContainers() var cts = new CancellationTokenSource(TimeSpan.FromMinutes(10)); // Use the same path for both runs - using var aspireStore = new TempDirectory(); + using var aspireStore = new TestTempDirectory(); var before = await RunContainersAsync(); var after = await RunContainersAsync(); diff --git a/tests/Aspire.Hosting.Python.Tests/AddPythonAppTests.cs b/tests/Aspire.Hosting.Python.Tests/AddPythonAppTests.cs index 29e7586c933..989494ad666 100644 --- a/tests/Aspire.Hosting.Python.Tests/AddPythonAppTests.cs +++ b/tests/Aspire.Hosting.Python.Tests/AddPythonAppTests.cs @@ -390,7 +390,7 @@ import logging public void AddPythonApp_DoesNotThrowOnMissingVirtualEnvironment() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Should not throw - validation is deferred until runtime var exception = Record.Exception(() => @@ -403,7 +403,7 @@ public void AddPythonApp_DoesNotThrowOnMissingVirtualEnvironment() public async Task WithVirtualEnvironment_UpdatesCommandToUseNewVirtualEnvironment() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptName = "main.py"; @@ -435,8 +435,8 @@ public async Task WithVirtualEnvironment_UpdatesCommandToUseNewVirtualEnvironmen public async Task WithVirtualEnvironment_SupportsAbsolutePath() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); - using var tempVenvDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); + using var tempVenvDir = new TestTempDirectory(); var scriptName = "main.py"; @@ -477,7 +477,7 @@ public void WithVirtualEnvironment_ThrowsOnNullBuilder() public void WithVirtualEnvironment_ThrowsOnNullOrEmptyPath() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptName = "main.py"; var resourceBuilder = builder.AddPythonApp("pythonProject", tempDir.Path, scriptName); @@ -495,7 +495,7 @@ public void WithVirtualEnvironment_ThrowsOnNullOrEmptyPath() public async Task WithVirtualEnvironment_CanBeChainedWithOtherExtensions() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptName = "main.py"; @@ -525,7 +525,7 @@ public async Task WithVirtualEnvironment_CanBeChainedWithOtherExtensions() public void WithVirtualEnvironment_UsesAppDirectoryWhenVenvExistsThere() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempAppDir = new TempDirectory(); + using var tempAppDir = new TestTempDirectory(); // Create .venv in the app directory var appVenvPath = Path.Combine(tempAppDir.Path, ".venv"); @@ -551,7 +551,7 @@ public void WithVirtualEnvironment_UsesAppDirectoryWhenVenvExistsThere() public void WithVirtualEnvironment_UsesAppHostDirectoryWhenVenvOnlyExistsThere() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempAppDir = new TempDirectory(); + using var tempAppDir = new TestTempDirectory(); // Create app directory as a subdirectory of AppHost (realistic scenario) var appDirName = "python-app"; @@ -639,7 +639,7 @@ public void WithVirtualEnvironment_PrefersAppDirectoryWhenVenvExistsInBoth() public void WithVirtualEnvironment_DefaultsToAppDirectoryWhenVenvExistsInNeither() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempAppDir = new TempDirectory(); + using var tempAppDir = new TestTempDirectory(); // Don't create .venv in either directory @@ -712,7 +712,7 @@ public void WithVirtualEnvironment_ExplicitPath_UsesVerbatim() public void WithUv_CreatesUvEnvironmentResource() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptName = "main.py"; @@ -743,7 +743,7 @@ public void WithUv_CreatesUvEnvironmentResource() public async Task WithUv_AddsUvSyncArgument() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptName = "main.py"; @@ -763,7 +763,7 @@ public async Task WithUv_AddsUvSyncArgument() public async Task WithUv_AddsWaitForCompletionRelationship() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptName = "main.py"; @@ -800,7 +800,7 @@ public void WithUv_ThrowsOnNullBuilder() public void WithUv_IsIdempotent() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptName = "main.py"; @@ -821,7 +821,7 @@ public void WithUv_IsIdempotent() public void WithPip_AfterWithUv_ReplacesPackageManager() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptName = "main.py"; @@ -852,7 +852,7 @@ public void WithPip_AfterWithUv_ReplacesPackageManager() public void WithUv_AfterWithPip_ReplacesPackageManager() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptName = "main.py"; @@ -882,7 +882,7 @@ public void WithUv_AfterWithPip_ReplacesPackageManager() public void AddPythonApp_CreatesResourceWithScriptEntrypoint() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var pythonBuilder = builder.AddPythonApp("python-script", tempDir.Path, "main.py"); @@ -901,7 +901,7 @@ public void AddPythonApp_CreatesResourceWithScriptEntrypoint() public void AddPythonModule_CreatesResourceWithModuleEntrypoint() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var pythonBuilder = builder.AddPythonModule("flask-app", tempDir.Path, "flask"); @@ -920,7 +920,7 @@ public void AddPythonModule_CreatesResourceWithModuleEntrypoint() public void AddPythonExecutable_CreatesResourceWithExecutableEntrypoint() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var pythonBuilder = builder.AddPythonExecutable("pytest", tempDir.Path, "pytest"); @@ -939,7 +939,7 @@ public void AddPythonExecutable_CreatesResourceWithExecutableEntrypoint() public async Task AddPythonApp_SetsCorrectCommandAndArguments() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptName = "main.py"; @@ -975,7 +975,7 @@ public async Task AddPythonApp_SetsCorrectCommandAndArguments() public async Task AddPythonModule_SetsCorrectCommandAndArguments() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var moduleName = "flask"; @@ -1009,7 +1009,7 @@ public async Task AddPythonModule_SetsCorrectCommandAndArguments() public async Task AddPythonExecutable_SetsCorrectCommandAndArguments() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var executableName = "pytest"; @@ -1042,7 +1042,7 @@ public async Task AddPythonExecutable_SetsCorrectCommandAndArguments() public async Task AddPythonModule_WithArgs_AddsArgumentsCorrectly() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var pythonBuilder = builder.AddPythonModule("flask-app", tempDir.Path, "flask") .WithArgs("run", "--debug", "--host=0.0.0.0"); @@ -1066,7 +1066,7 @@ public async Task AddPythonModule_WithArgs_AddsArgumentsCorrectly() public async Task AddPythonApp_WithArgs_AddsArgumentsCorrectly() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var pythonBuilder = builder.AddPythonApp("python-app", tempDir.Path, "main.py") .WithArgs("arg1", "arg2"); @@ -1088,7 +1088,7 @@ public async Task AddPythonApp_WithArgs_AddsArgumentsCorrectly() public async Task AddPythonExecutable_WithArgs_AddsArgumentsCorrectly() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var pythonBuilder = builder.AddPythonExecutable("pytest", tempDir.Path, "pytest") .WithArgs("-q", "--verbose"); @@ -1109,7 +1109,7 @@ public async Task AddPythonExecutable_WithArgs_AddsArgumentsCorrectly() public async Task WithEntrypoint_ChangesEntrypointTypeAndValue() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Start with a script var pythonBuilder = builder.AddPythonApp("python-app", tempDir.Path, "main.py") @@ -1143,7 +1143,7 @@ public async Task WithEntrypoint_ChangesEntrypointTypeAndValue() public void WithEntrypoint_UpdatesCommandForExecutableType() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Start with a script var pythonBuilder = builder.AddPythonApp("python-app", tempDir.Path, "main.py"); @@ -1191,7 +1191,7 @@ public void WithEntrypoint_ThrowsOnNullBuilder() public void WithEntrypoint_ThrowsOnNullOrEmptyEntrypoint() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var resourceBuilder = builder.AddPythonApp("pythonProject", tempDir.Path, "main.py"); @@ -1207,8 +1207,8 @@ public void WithEntrypoint_ThrowsOnNullOrEmptyEntrypoint() [Fact] public async Task WithUv_GeneratesDockerfileInPublishMode() { - using var sourceDir = new TempDirectory(); - using var outputDir = new TempDirectory(); + using var sourceDir = new TestTempDirectory(); + using var outputDir = new TestTempDirectory(); var projectDirectory = sourceDir.Path; // Create a UV-based Python project with pyproject.toml and uv.lock @@ -1277,8 +1277,8 @@ await Verify(scriptDockerfileContent) [Fact] public async Task WithUv_GeneratesDockerfileInPublishMode_WithoutUvLock() { - using var sourceDir = new TempDirectory(); - using var outputDir = new TempDirectory(); + using var sourceDir = new TestTempDirectory(); + using var outputDir = new TestTempDirectory(); var projectDirectory = sourceDir.Path; // Create a UV-based Python project with pyproject.toml but NO uv.lock @@ -1348,7 +1348,7 @@ await Verify(scriptDockerfileContent) public async Task WithDebugSupport_RemovesScriptArgumentForScriptEntrypoint() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Run); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var runSessionInfo = new RunSessionInfo { @@ -1387,7 +1387,7 @@ public async Task WithDebugSupport_RemovesScriptArgumentForScriptEntrypoint() public async Task WithDebugSupport_DoesntRemoveScriptArgumentForScriptEntrypoint_WhenResourceTypeNotSupported() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Run); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Set DEBUG_SESSION_INFO to trigger VS Code debug support callback var runSessionInfo = new RunSessionInfo @@ -1426,7 +1426,7 @@ public async Task WithDebugSupport_DoesntRemoveScriptArgumentForScriptEntrypoint public async Task WithDebugSupport_RemovesModuleArgumentsForModuleEntrypoint() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Run); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var runSessionInfo = new RunSessionInfo { @@ -1464,7 +1464,7 @@ public async Task WithDebugSupport_RemovesModuleArgumentsForModuleEntrypoint() public async Task WithDebugSupport_DoesntRemoveModuleArgumentsForModuleEntrypoint_WhenResourceTypeNotSupported() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Run); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var runSessionInfo = new RunSessionInfo { @@ -1503,7 +1503,7 @@ public async Task WithDebugSupport_DoesntRemoveModuleArgumentsForModuleEntrypoin public async Task WithDebugSupport_ExecutableTypeDoesNotModifyArgs() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Run); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); // Set DEBUG_SESSION_INFO to trigger VS Code debug support callback builder.Configuration["DEBUG_SESSION_INFO"] = "{}"; @@ -1535,7 +1535,7 @@ public async Task WithDebugSupport_ExecutableTypeDoesNotModifyArgs() public async Task PythonApp_SetsPythonUtf8EnvironmentVariable_OnWindowsInRunMode() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Run).WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var pythonApp = builder.AddPythonApp("pythonProject", tempDir.Path, "main.py"); @@ -1557,7 +1557,7 @@ public async Task PythonApp_SetsPythonUtf8EnvironmentVariable_OnWindowsInRunMode public async Task PythonApp_DoesNotSetPythonUtf8EnvironmentVariable_OnNonWindowsPlatforms() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Run).WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var pythonApp = builder.AddPythonApp("pythonProject", tempDir.Path, "main.py"); @@ -1575,7 +1575,7 @@ public async Task PythonApp_DoesNotSetPythonUtf8EnvironmentVariable_OnNonWindows public async Task PythonApp_DoesNotSetPythonUtf8EnvironmentVariable_InPublishMode() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish).WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var pythonApp = builder.AddPythonApp("pythonProject", tempDir.Path, "main.py"); @@ -1590,8 +1590,8 @@ public async Task PythonApp_DoesNotSetPythonUtf8EnvironmentVariable_InPublishMod [Fact] public async Task WithUv_CustomBaseImages_GeneratesDockerfileWithCustomImages() { - using var sourceDir = new TempDirectory(); - using var outputDir = new TempDirectory(); + using var sourceDir = new TestTempDirectory(); + using var outputDir = new TestTempDirectory(); var projectDirectory = sourceDir.Path; // Create a UV-based Python project with pyproject.toml and uv.lock @@ -1648,8 +1648,8 @@ public async Task WithUv_CustomBaseImages_GeneratesDockerfileWithCustomImages() [Fact] public async Task FallbackDockerfile_GeneratesDockerfileWithoutUv_WithRequirementsTxt() { - using var sourceDir = new TempDirectory(); - using var outputDir = new TempDirectory(); + using var sourceDir = new TestTempDirectory(); + using var outputDir = new TestTempDirectory(); var projectDirectory = sourceDir.Path; // Create a Python project without UV but with requirements.txt @@ -1695,8 +1695,8 @@ public async Task FallbackDockerfile_GeneratesDockerfileWithoutUv_WithRequiremen [Fact] public async Task FallbackDockerfile_GeneratesDockerfileWithPyprojectToml() { - using var sourceDir = new TempDirectory(); - using var outputDir = new TempDirectory(); + using var sourceDir = new TestTempDirectory(); + using var outputDir = new TestTempDirectory(); var projectDirectory = sourceDir.Path; // Create a Python project without UV but with pyproject.toml @@ -1734,8 +1734,8 @@ public async Task FallbackDockerfile_GeneratesDockerfileWithPyprojectToml() [Fact] public async Task FallbackDockerfile_GeneratesDockerfileWithoutAnyDependencyFiles() { - using var sourceDir = new TempDirectory(); - using var outputDir = new TempDirectory(); + using var sourceDir = new TestTempDirectory(); + using var outputDir = new TestTempDirectory(); var projectDirectory = sourceDir.Path; // Create a Python project with NO pyproject.toml and NO requirements.txt @@ -1765,8 +1765,8 @@ public async Task FallbackDockerfile_GeneratesDockerfileWithoutAnyDependencyFile [Fact] public async Task FallbackDockerfile_GeneratesDockerfileForAllEntrypointTypes() { - using var sourceDir = new TempDirectory(); - using var outputDir = new TempDirectory(); + using var sourceDir = new TestTempDirectory(); + using var outputDir = new TestTempDirectory(); var projectDirectory = sourceDir.Path; // Create a Python project without UV @@ -1822,7 +1822,7 @@ await Verify(scriptDockerfileContent) public void AutoDetection_PyprojectToml_AddsPip() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptName = "main.py"; var scriptPath = Path.Combine(tempDir.Path, scriptName); @@ -1856,7 +1856,7 @@ public void AutoDetection_PyprojectToml_AddsPip() public void AutoDetection_RequirementsTxt_AddsPip() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptName = "main.py"; var scriptPath = Path.Combine(tempDir.Path, scriptName); @@ -1891,7 +1891,7 @@ public void AutoDetection_RequirementsTxt_AddsPip() public void AutoDetection_PyprojectToml_TakesPrecedenceOverRequirementsTxt() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptName = "main.py"; var scriptPath = Path.Combine(tempDir.Path, scriptName); @@ -1921,7 +1921,7 @@ public void AutoDetection_PyprojectToml_TakesPrecedenceOverRequirementsTxt() public void AutoDetection_NoConfigFile_DoesNotAddPackageManager() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptName = "main.py"; var scriptPath = Path.Combine(tempDir.Path, scriptName); @@ -1944,8 +1944,8 @@ public void AutoDetection_NoConfigFile_DoesNotAddPackageManager() public void WithVirtualEnvironment_DisableCreation_DoesNotCreateVenvCreator() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); - using var tempVenvDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); + using var tempVenvDir = new TestTempDirectory(); var scriptName = "main.py"; var scriptPath = Path.Combine(tempDir.Path, scriptName); @@ -1967,8 +1967,8 @@ public void WithVirtualEnvironment_DisableCreation_DoesNotCreateVenvCreator() public void WithVirtualEnvironment_EnableCreation_CreatesVenvCreator() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); - using var tempVenvDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); + using var tempVenvDir = new TestTempDirectory(); var scriptName = "main.py"; var scriptPath = Path.Combine(tempDir.Path, scriptName); @@ -1995,8 +1995,8 @@ public void WithVirtualEnvironment_EnableCreation_CreatesVenvCreator() public void WithVirtualEnvironment_DefaultBehavior_CreatesVenvCreator() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); - using var tempVenvDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); + using var tempVenvDir = new TestTempDirectory(); var scriptName = "main.py"; var scriptPath = Path.Combine(tempDir.Path, scriptName); @@ -2026,7 +2026,7 @@ public void WithVirtualEnvironment_DefaultBehavior_CreatesVenvCreator() public void WithUv_DisablesVenvCreation_And_SetsPackageManager() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptPath = Path.Combine(tempDir.Path, "main.py"); File.WriteAllText(scriptPath, "print('Hello')"); @@ -2054,7 +2054,7 @@ public void WithUv_DisablesVenvCreation_And_SetsPackageManager() public async Task WithPip_CreatesDefaultVenv_And_WaitsForVenvCreation() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptPath = Path.Combine(tempDir.Path, "main.py"); File.WriteAllText(scriptPath, "print('Hello')"); @@ -2096,8 +2096,8 @@ public async Task WithPip_CreatesDefaultVenv_And_WaitsForVenvCreation() public void WithPip_ThenWithVirtualEnvironment_CreateIfNotExistsTrue_CreatesVenv() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); - using var tempVenvDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); + using var tempVenvDir = new TestTempDirectory(); var scriptPath = Path.Combine(tempDir.Path, "main.py"); File.WriteAllText(scriptPath, "print('Hello')"); @@ -2126,8 +2126,8 @@ public void WithPip_ThenWithVirtualEnvironment_CreateIfNotExistsTrue_CreatesVenv public void WithPip_ThenWithVirtualEnvironment_CreateIfNotExistsFalse_DoesNotCreateVenv() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); - using var tempVenvDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); + using var tempVenvDir = new TestTempDirectory(); var scriptPath = Path.Combine(tempDir.Path, "main.py"); File.WriteAllText(scriptPath, "print('Hello')"); @@ -2160,8 +2160,8 @@ public void WithPip_ThenWithVirtualEnvironment_CreateIfNotExistsFalse_DoesNotCre public async Task MethodOrdering_WithPip_WithVirtualEnvironment_CreateTrue_WithPip_CreatesVenv() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); - using var tempVenvDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); + using var tempVenvDir = new TestTempDirectory(); var scriptPath = Path.Combine(tempDir.Path, "main.py"); File.WriteAllText(scriptPath, "print('Hello')"); @@ -2196,8 +2196,8 @@ public async Task MethodOrdering_WithPip_WithVirtualEnvironment_CreateTrue_WithP public void MethodOrdering_WithPip_WithVirtualEnvironment_CreateFalse_WithPip_DoesNotCreateVenv() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); - using var tempVenvDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); + using var tempVenvDir = new TestTempDirectory(); var scriptPath = Path.Combine(tempDir.Path, "main.py"); File.WriteAllText(scriptPath, "print('Hello')"); @@ -2227,7 +2227,7 @@ public void MethodOrdering_WithPip_WithVirtualEnvironment_CreateFalse_WithPip_Do public void MethodOrdering_WithPip_ThenWithUv_ReplacesPackageManager_And_DisablesVenvCreation() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptPath = Path.Combine(tempDir.Path, "main.py"); File.WriteAllText(scriptPath, "print('Hello')"); @@ -2259,7 +2259,7 @@ public void MethodOrdering_WithPip_ThenWithUv_ReplacesPackageManager_And_Disable public void MethodOrdering_WithUv_ThenWithPip_ReplacesPackageManager_And_EnablesVenvCreation() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptPath = Path.Combine(tempDir.Path, "main.py"); File.WriteAllText(scriptPath, "print('Hello')"); @@ -2291,7 +2291,7 @@ public void MethodOrdering_WithUv_ThenWithPip_ReplacesPackageManager_And_Enables public async Task WithPip_InstallFalse_CreatesInstallerWithExplicitStart() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptPath = Path.Combine(tempDir.Path, "main.py"); File.WriteAllText(scriptPath, "print('Hello')"); @@ -2327,7 +2327,7 @@ public async Task WithPip_InstallFalse_CreatesInstallerWithExplicitStart() public async Task WithUv_InstallFalse_CreatesInstallerWithExplicitStart() { using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var scriptPath = Path.Combine(tempDir.Path, "main.py"); File.WriteAllText(scriptPath, "print('Hello')"); diff --git a/tests/Aspire.Hosting.Python.Tests/AddUvicornAppTests.cs b/tests/Aspire.Hosting.Python.Tests/AddUvicornAppTests.cs index 34e39434336..14a65f999cf 100644 --- a/tests/Aspire.Hosting.Python.Tests/AddUvicornAppTests.cs +++ b/tests/Aspire.Hosting.Python.Tests/AddUvicornAppTests.cs @@ -28,8 +28,8 @@ public void AddUvicornApp_CreatesUvicornAppResource() [Fact] public async Task WithUv_GeneratesDockerfileInPublishMode() { - using var sourceDir = new TempDirectory(); - using var outputDir = new TempDirectory(); + using var sourceDir = new TestTempDirectory(); + using var outputDir = new TestTempDirectory(); var projectDirectory = sourceDir.Path; // Create a UV-based Python project with pyproject.toml and uv.lock diff --git a/tests/Aspire.Hosting.Tests/AspireStoreTests.cs b/tests/Aspire.Hosting.Tests/AspireStoreTests.cs index fa130efbe04..e1f98a4ea0e 100644 --- a/tests/Aspire.Hosting.Tests/AspireStoreTests.cs +++ b/tests/Aspire.Hosting.Tests/AspireStoreTests.cs @@ -123,7 +123,8 @@ public void GetOrCreateFileWithContent_ShouldNotRecreateFile() [InlineData("obj/")] public void AspireStoreConstructor_ShouldThrow_IfNotAbsolutePath(string? basePath) { - Assert.ThrowsAny(() => new AspireStore(basePath!)); + var directoryService = new FileSystemService(); + Assert.ThrowsAny(() => new AspireStore(basePath!, directoryService)); } private static IAspireStore CreateStore() diff --git a/tests/Aspire.Hosting.Tests/Dashboard/DashboardLifecycleHookTests.cs b/tests/Aspire.Hosting.Tests/Dashboard/DashboardLifecycleHookTests.cs index c8990ea5f91..d3231580eef 100644 --- a/tests/Aspire.Hosting.Tests/Dashboard/DashboardLifecycleHookTests.cs +++ b/tests/Aspire.Hosting.Tests/Dashboard/DashboardLifecycleHookTests.cs @@ -522,7 +522,8 @@ private static DashboardEventHandlers CreateHook( new DcpNameGenerator(configuration, Options.Create(new DcpOptions())), new TestHostApplicationLifetime(), new Hosting.Eventing.DistributedApplicationEventing(), - rewriter + rewriter, + new FileSystemService() ); } diff --git a/tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs b/tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs index 5acc5747a46..afdd17f6ec0 100644 --- a/tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs +++ b/tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs @@ -2086,7 +2086,7 @@ private static DcpExecutor CreateAppExecutor( new TestDcpDependencyCheckService(), new DcpNameGenerator(configuration, Options.Create(dcpOptions)), events ?? new DcpExecutorEvents(), - new Locations(), + new Locations(new FileSystemService()), developerCertificateService); #pragma warning restore ASPIRECERTIFICATES001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. } diff --git a/tests/Aspire.Hosting.Tests/Dcp/DcpHostNotificationTests.cs b/tests/Aspire.Hosting.Tests/Dcp/DcpHostNotificationTests.cs index 97ee39fa731..ea21c987b51 100644 --- a/tests/Aspire.Hosting.Tests/Dcp/DcpHostNotificationTests.cs +++ b/tests/Aspire.Hosting.Tests/Dcp/DcpHostNotificationTests.cs @@ -16,6 +16,12 @@ namespace Aspire.Hosting.Tests.Dcp; public sealed class DcpHostNotificationTests { + private static Locations CreateTestLocations() + { + var directoryService = new FileSystemService(); + return new Locations(directoryService); + } + [Fact] public void DcpHost_WithIInteractionService_CanBeConstructed() { @@ -24,7 +30,7 @@ public void DcpHost_WithIInteractionService_CanBeConstructed() var dcpOptions = Options.Create(new DcpOptions()); var dependencyCheckService = new TestDcpDependencyCheckService(); var interactionService = new TestInteractionService(); - var locations = new Locations(); + var locations = CreateTestLocations(); var applicationModel = new DistributedApplicationModel(new ResourceCollection()); var timeProvider = new FakeTimeProvider(); @@ -64,7 +70,7 @@ public async Task DcpHost_WithUnhealthyContainerRuntime_ShowsNotification() } }; var interactionService = new TestInteractionService { IsAvailable = true }; - var locations = new Locations(); + var locations = CreateTestLocations(); var timeProvider = new FakeTimeProvider(); var dcpHost = new DcpHost( @@ -113,7 +119,7 @@ public async Task DcpHost_WithHealthyContainerRuntime_DoesNotShowNotification() } }; var interactionService = new TestInteractionService { IsAvailable = true }; - var locations = new Locations(); + var locations = CreateTestLocations(); var timeProvider = new FakeTimeProvider(); var dcpHost = new DcpHost( @@ -168,7 +174,7 @@ public async Task DcpHost_WithDashboardDisabled_DoesNotShowNotification() } }; var interactionService = new TestInteractionService { IsAvailable = false }; // Dashboard disabled - var locations = new Locations(); + var locations = CreateTestLocations(); var timeProvider = new FakeTimeProvider(); var dcpHost = new DcpHost( @@ -223,7 +229,7 @@ public async Task DcpHost_WithPodmanUnhealthy_ShowsCorrectMessage() } }; var interactionService = new TestInteractionService { IsAvailable = true }; - var locations = new Locations(); + var locations = CreateTestLocations(); var timeProvider = new FakeTimeProvider(); var dcpHost = new DcpHost( @@ -273,7 +279,7 @@ public async Task DcpHost_WithUnhealthyContainerRuntime_NotificationCancelledWhe } }; var interactionService = new TestInteractionService { IsAvailable = true }; - var locations = new Locations(); + var locations = CreateTestLocations(); var timeProvider = new FakeTimeProvider(); var dcpHost = new DcpHost( @@ -341,7 +347,7 @@ public async Task DcpHost_WithContainerRuntimeNotInstalled_ShowsNotificationWith } }; var interactionService = new TestInteractionService { IsAvailable = true }; - var locations = new Locations(); + var locations = CreateTestLocations(); var timeProvider = new FakeTimeProvider(); var dcpHost = new DcpHost( diff --git a/tests/Aspire.Hosting.Tests/FileSystemServiceTests.cs b/tests/Aspire.Hosting.Tests/FileSystemServiceTests.cs new file mode 100644 index 00000000000..3f2eba2abfd --- /dev/null +++ b/tests/Aspire.Hosting.Tests/FileSystemServiceTests.cs @@ -0,0 +1,197 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only + +namespace Aspire.Hosting.Tests; + +public class FileSystemServiceTests +{ + [Fact] + public void CreateTempSubdirectory_CreatesDirectory() + { + var service = new FileSystemService(); + + using var tempDir = service.TempDirectory.CreateTempSubdirectory(); + + Assert.NotNull(tempDir.Path); + Assert.True(Directory.Exists(tempDir.Path)); + } + + [Fact] + public void CreateTempSubdirectory_WithPrefix_CreatesDirectoryWithPrefix() + { + var service = new FileSystemService(); + + using var tempDir = service.TempDirectory.CreateTempSubdirectory("test-prefix"); + + Assert.NotNull(tempDir.Path); + Assert.True(Directory.Exists(tempDir.Path)); + Assert.Contains("test-prefix", tempDir.Path); + } + + [Fact] + public void CreateTempSubdirectory_Dispose_DeletesDirectory() + { + var service = new FileSystemService(); + var tempDir = service.TempDirectory.CreateTempSubdirectory(); + var path = tempDir.Path; + + Assert.True(Directory.Exists(path)); + + tempDir.Dispose(); + + Assert.False(Directory.Exists(path)); + } + + [Fact] + public void CreateTempFile_CreatesFile() + { + var service = new FileSystemService(); + + using var tempFile = service.TempDirectory.CreateTempFile(); + + Assert.NotNull(tempFile.Path); + Assert.True(File.Exists(tempFile.Path)); + } + + [Fact] + public void CreateTempFile_WithFileName_CreatesNamedFile() + { + var service = new FileSystemService(); + + using var tempFile = service.TempDirectory.CreateTempFile("config.json"); + + Assert.NotNull(tempFile.Path); + Assert.True(File.Exists(tempFile.Path)); + Assert.EndsWith("config.json", tempFile.Path); + } + + [Fact] + public void CreateTempFile_Dispose_DeletesFile() + { + var service = new FileSystemService(); + var tempFile = service.TempDirectory.CreateTempFile(); + var path = tempFile.Path; + + Assert.True(File.Exists(path)); + + tempFile.Dispose(); + + Assert.False(File.Exists(path)); + } + + [Fact] + public void CreateTempFile_WithFileName_Dispose_DeletesFileAndParentDirectory() + { + var service = new FileSystemService(); + var tempFile = service.TempDirectory.CreateTempFile("test-file.txt"); + var filePath = tempFile.Path; + var parentDir = Path.GetDirectoryName(filePath); + + Assert.True(File.Exists(filePath)); + Assert.True(Directory.Exists(parentDir)); + + tempFile.Dispose(); + + Assert.False(File.Exists(filePath)); + Assert.False(Directory.Exists(parentDir)); + } + + [Fact] + public void PathExtractionPattern_DirectoryPersistsAfterScopeEnds() + { + // This test verifies the intentional pattern of extracting .Path + // without disposing, which is common in the codebase + var service = new FileSystemService(); + string? extractedPath; + + // Simulate the common pattern: extract path, let object go out of scope + { + var tempDir = service.TempDirectory.CreateTempSubdirectory("path-extraction-test"); + extractedPath = tempDir.Path; + // Note: intentionally NOT disposing here - this is the pattern used in production + } + + // The directory should still exist because we didn't dispose + Assert.True(Directory.Exists(extractedPath)); + + // Clean up manually for test hygiene + Directory.Delete(extractedPath, recursive: true); + } + + [Fact] + public void PathExtractionPattern_FilePersistsAfterScopeEnds() + { + // This test verifies the intentional pattern of extracting .Path + // without disposing, which is common in the codebase + var service = new FileSystemService(); + string? extractedPath; + + // Simulate the common pattern: extract path, let object go out of scope + { + var tempFile = service.TempDirectory.CreateTempFile(); + extractedPath = tempFile.Path; + // Note: intentionally NOT disposing here - this is the pattern used in production + } + + // The file should still exist because we didn't dispose + Assert.True(File.Exists(extractedPath)); + + // Clean up manually for test hygiene + File.Delete(extractedPath); + } + + [Fact] + public void TempDirectory_Property_ReturnsSameInstance() + { + var service = new FileSystemService(); + + var tempDir1 = service.TempDirectory; + var tempDir2 = service.TempDirectory; + + Assert.Same(tempDir1, tempDir2); + } + + [Fact] + public void CreateTempSubdirectory_MultipleCallsCreateDifferentDirectories() + { + var service = new FileSystemService(); + + using var tempDir1 = service.TempDirectory.CreateTempSubdirectory(); + using var tempDir2 = service.TempDirectory.CreateTempSubdirectory(); + + Assert.NotEqual(tempDir1.Path, tempDir2.Path); + Assert.True(Directory.Exists(tempDir1.Path)); + Assert.True(Directory.Exists(tempDir2.Path)); + } + + [Fact] + public void CreateTempFile_MultipleCallsCreateDifferentFiles() + { + var service = new FileSystemService(); + + using var tempFile1 = service.TempDirectory.CreateTempFile(); + using var tempFile2 = service.TempDirectory.CreateTempFile(); + + Assert.NotEqual(tempFile1.Path, tempFile2.Path); + Assert.True(File.Exists(tempFile1.Path)); + Assert.True(File.Exists(tempFile2.Path)); + } + + [Fact] + public void Dispose_CalledMultipleTimes_DoesNotThrow() + { + var service = new FileSystemService(); + var tempDir = service.TempDirectory.CreateTempSubdirectory(); + var tempFile = service.TempDirectory.CreateTempFile(); + + // First dispose + tempDir.Dispose(); + tempFile.Dispose(); + + // Second dispose should not throw + tempDir.Dispose(); + tempFile.Dispose(); + } +} diff --git a/tests/Aspire.Hosting.Tests/MSBuildTests.cs b/tests/Aspire.Hosting.Tests/MSBuildTests.cs index 5d7975ba072..8969f620382 100644 --- a/tests/Aspire.Hosting.Tests/MSBuildTests.cs +++ b/tests/Aspire.Hosting.Tests/MSBuildTests.cs @@ -15,7 +15,7 @@ public class MSBuildTests public void EnsureWarningsAreEmittedWhenProjectReferencingLibraries() { var repoRoot = MSBuildUtils.GetRepoRoot(); - using var tempDirectory = new TempDirectory(); + using var tempDirectory = new TestTempDirectory(); CreateLibraryProject(tempDirectory.Path, "Library"); @@ -72,7 +72,7 @@ the Aspire.AppHost.SDK targets that will automatically add these references to p public async Task ValidateMetadataSources() { var repoRoot = MSBuildUtils.GetRepoRoot(); - using var tempDirectory = new TempDirectory(); + using var tempDirectory = new TestTempDirectory(); CreateAppProject(tempDirectory.Path, "App"); @@ -261,7 +261,7 @@ private static string BuildProject(string workingDirectory) public void TreatProjectReferencesAsResourcesFalse_DisablesMutation() { var repoRoot = MSBuildUtils.GetRepoRoot(); - using var tempDirectory = new TempDirectory(); + using var tempDirectory = new TestTempDirectory(); CreateLibraryProject(tempDirectory.Path, "Library"); @@ -320,7 +320,7 @@ the Aspire.AppHost.SDK targets that will automatically add these references to p public void TreatProjectReferencesAsResourcesTrue_EnablesMutation() { var repoRoot = MSBuildUtils.GetRepoRoot(); - using var tempDirectory = new TempDirectory(); + using var tempDirectory = new TestTempDirectory(); CreateLibraryProject(tempDirectory.Path, "Library"); diff --git a/tests/Aspire.Hosting.Tests/PublishAsDockerfileTests.cs b/tests/Aspire.Hosting.Tests/PublishAsDockerfileTests.cs index fe97b38e8d2..aca78fcb98f 100644 --- a/tests/Aspire.Hosting.Tests/PublishAsDockerfileTests.cs +++ b/tests/Aspire.Hosting.Tests/PublishAsDockerfileTests.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only + using Aspire.Hosting.Utils; using Microsoft.AspNetCore.InternalTesting; @@ -447,9 +449,9 @@ public void PublishProjectAsDockerFile_CalledMultipleTimesWithCallbacks_IsIdempo Assert.Equal(2, callbackCount); } - private static TempDirectory CreateDirectoryWithDockerFile() + private static TestTempDirectory CreateDirectoryWithDockerFile() { - var tempDir = new TempDirectory(); + var tempDir = new TestTempDirectory(); File.WriteAllText(Path.Join(tempDir.Path, "Dockerfile"), "this does not matter"); return tempDir; } diff --git a/tests/Aspire.Hosting.Tests/Publishing/ResourceContainerImageManagerTests.cs b/tests/Aspire.Hosting.Tests/Publishing/ResourceContainerImageManagerTests.cs index 169ce2728f2..1f45d0b5244 100644 --- a/tests/Aspire.Hosting.Tests/Publishing/ResourceContainerImageManagerTests.cs +++ b/tests/Aspire.Hosting.Tests/Publishing/ResourceContainerImageManagerTests.cs @@ -4,6 +4,7 @@ #pragma warning disable ASPIREPIPELINES003 #pragma warning disable ASPIRECONTAINERRUNTIME001 #pragma warning disable ASPIRECOMPUTE001 +#pragma warning disable ASPIREFILESYSTEM001 using Aspire.Hosting.Publishing; using Aspire.Hosting.Tests.Utils; @@ -264,7 +265,7 @@ public async Task CanBuildImageFromDockerfileResource_WithAllOptionsSet() }); var (tempContextPath, tempDockerfilePath) = await DockerfileUtils.CreateTemporaryDockerfileAsync(); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var container = builder.AddDockerfile("container", tempContextPath, tempDockerfilePath) .WithContainerBuildOptions(ctx => { diff --git a/tests/Aspire.Hosting.Tests/SecretsStoreTests.cs b/tests/Aspire.Hosting.Tests/SecretsStoreTests.cs index 46af674af11..18a4e967bf5 100644 --- a/tests/Aspire.Hosting.Tests/SecretsStoreTests.cs +++ b/tests/Aspire.Hosting.Tests/SecretsStoreTests.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only + using System.Reflection; using System.Reflection.Emit; using Aspire.Hosting.Pipelines.Internal; @@ -14,6 +16,8 @@ public class SecretsStoreTests { private static readonly ConstructorInfo s_userSecretsIdAttrCtor = typeof(UserSecretsIdAttribute).GetConstructor([typeof(string)])!; + private static UserSecretsManagerFactory CreateFactory() => new UserSecretsManagerFactory(new FileSystemService()); + [Fact] public void GetOrSetUserSecret_SavesValueToUserSecrets() { @@ -27,7 +31,7 @@ public void GetOrSetUserSecret_SavesValueToUserSecrets() var configuration = new ConfigurationManager(); var value = TokenGenerator.GenerateToken(); - var factory = new UserSecretsManagerFactory(); + var factory = CreateFactory(); var manager = factory.GetOrCreate(testAssembly); manager?.GetOrSetSecret(configuration, key, () => value); var userSecrets = GetUserSecrets(userSecretsId); @@ -53,7 +57,7 @@ public void GetOrSetUserSecret_ReadsValueFromConfiguration() var valueInConfig = TokenGenerator.GenerateToken(); configuration[key] = valueInConfig; - var factory = new UserSecretsManagerFactory(); + var factory = CreateFactory(); var manager = factory.GetOrCreate(testAssembly); manager?.GetOrSetSecret(configuration, key, TokenGenerator.GenerateToken); var userSecrets = GetUserSecrets(userSecretsId); @@ -65,7 +69,8 @@ public void GetOrSetUserSecret_ReadsValueFromConfiguration() private static Dictionary GetUserSecrets(string userSecretsId) { - var manager = UserSecretsManagerFactory.Instance.GetOrCreateFromId(userSecretsId); + var factory = CreateFactory(); + var manager = factory.GetOrCreateFromId(userSecretsId); if (!File.Exists(manager.FilePath)) { return new Dictionary(); diff --git a/tests/Aspire.Hosting.Tests/UserSecretsParameterDefaultTests.cs b/tests/Aspire.Hosting.Tests/UserSecretsParameterDefaultTests.cs index a1142fa79e9..40360a87eda 100644 --- a/tests/Aspire.Hosting.Tests/UserSecretsParameterDefaultTests.cs +++ b/tests/Aspire.Hosting.Tests/UserSecretsParameterDefaultTests.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only + using System.Reflection; using System.Reflection.Emit; using System.Text; @@ -16,6 +18,8 @@ public class UserSecretsParameterDefaultTests { private static readonly ConstructorInfo s_userSecretsIdAttrCtor = typeof(UserSecretsIdAttribute).GetConstructor([typeof(string)])!; + private static UserSecretsManagerFactory CreateFactory() => new UserSecretsManagerFactory(new FileSystemService()); + [Fact] public void UserSecretsParameterDefault_GetDefaultValue_SavesValueInAppHostUserSecrets() { @@ -24,8 +28,10 @@ public void UserSecretsParameterDefault_GetDefaultValue_SavesValueInAppHostUserS var testAssembly = AssemblyBuilder.DefineDynamicAssembly( new("TestAssembly"), AssemblyBuilderAccess.RunAndCollect, [new CustomAttributeBuilder(s_userSecretsIdAttrCtor, [userSecretsId])]); + var factory = CreateFactory(); + var manager = factory.GetOrCreate(testAssembly); var paramDefault = new TestParameterDefault(); - var userSecretDefault = new UserSecretsParameterDefault(testAssembly, "TestApplication.AppHost", "param1", paramDefault); + var userSecretDefault = new UserSecretsParameterDefault("TestApplication.AppHost", "param1", paramDefault, manager); var initialValue = userSecretDefault.GetDefaultValue(); var userSecrets = GetUserSecrets(userSecretsId); @@ -39,8 +45,10 @@ public void UserSecretsParameterDefault_GetDefaultValue_DoesntThrowIfValueCantBe { // Do not set a user secrets id attribute on the assembly var testAssembly = AssemblyBuilder.DefineDynamicAssembly(new("TestAssembly"), AssemblyBuilderAccess.RunAndCollect, []); + var factory = CreateFactory(); + var manager = factory.GetOrCreate(testAssembly); var paramDefault = new TestParameterDefault(); - var userSecretDefault = new UserSecretsParameterDefault(testAssembly, "TestApplication.AppHost", "param1", paramDefault); + var userSecretDefault = new UserSecretsParameterDefault("TestApplication.AppHost", "param1", paramDefault, manager); var initialValue = userSecretDefault.GetDefaultValue(); Assert.NotNull(initialValue); @@ -67,8 +75,10 @@ public void UserSecretsParameterDefault_GetDefaultValue_DoesntThrowIfSecretsFile var testAssembly = AssemblyBuilder.DefineDynamicAssembly( new("TestAssembly"), AssemblyBuilderAccess.RunAndCollect, [new CustomAttributeBuilder(s_userSecretsIdAttrCtor, [userSecretsId])]); + var factory = CreateFactory(); + var manager = factory.GetOrCreate(testAssembly); var paramDefault = new TestParameterDefault(); - var userSecretDefault = new UserSecretsParameterDefault(testAssembly, "TestApplication.AppHost", "param1", paramDefault); + var userSecretDefault = new UserSecretsParameterDefault("TestApplication.AppHost", "param1", paramDefault, manager); var _ = userSecretDefault.GetDefaultValue(); } @@ -83,7 +93,7 @@ public async Task TrySetUserSecret_ConcurrentWrites_PreservesAllSecrets() new("TestAssembly"), AssemblyBuilderAccess.RunAndCollect, [new CustomAttributeBuilder(s_userSecretsIdAttrCtor, [userSecretsId])]); // Create an isolated factory instance for this test to avoid cross-contamination - var factory = new UserSecretsManagerFactory(); + var factory = CreateFactory(); // Simulate concurrent writes from multiple threads (like SQL Server and RabbitMQ generating passwords) var tasks = new List>(); @@ -133,7 +143,7 @@ public async Task TrySetUserSecret_SqlServerAndRabbitMQ_BothSecretsPreserved() new("TestAssembly"), AssemblyBuilderAccess.RunAndCollect, [new CustomAttributeBuilder(s_userSecretsIdAttrCtor, [userSecretsId])]); // Create an isolated factory instance for this test to avoid cross-contamination - var factory = new UserSecretsManagerFactory(); + var factory = CreateFactory(); // Simulate SQL Server and RabbitMQ generating passwords concurrently var sqlTask = Task.Run(() => @@ -172,7 +182,7 @@ public async Task TrySetUserSecret_ConcurrentWritesSameKey_LastWriteWins() new("TestAssembly"), AssemblyBuilderAccess.RunAndCollect, [new CustomAttributeBuilder(s_userSecretsIdAttrCtor, [userSecretsId])]); // Create an isolated factory instance for this test to avoid cross-contamination - var factory = new UserSecretsManagerFactory(); + var factory = CreateFactory(); // Simulate concurrent writes to the same key var tasks = new List>(); @@ -202,8 +212,8 @@ public async Task TrySetUserSecret_ConcurrentWritesSameKey_LastWriteWins() [Fact] public void UserSecretsParameterDefault_WithCustomFactory_UsesProvidedFactory() { - // This test verifies that the constructor overload taking a factory parameter - // uses the provided factory instead of the singleton instance + // This test verifies that the constructor overload taking a manager parameter + // uses the provided manager var userSecretsId = Guid.NewGuid().ToString("N"); ClearUsersSecrets(userSecretsId); @@ -211,9 +221,10 @@ public void UserSecretsParameterDefault_WithCustomFactory_UsesProvidedFactory() new("TestAssembly"), AssemblyBuilderAccess.RunAndCollect, [new CustomAttributeBuilder(s_userSecretsIdAttrCtor, [userSecretsId])]); // Create a custom factory instance for test isolation - var customFactory = new UserSecretsManagerFactory(); + var customFactory = CreateFactory(); + var manager = customFactory.GetOrCreate(testAssembly); var paramDefault = new TestParameterDefault(); - var userSecretDefault = new UserSecretsParameterDefault(testAssembly, "TestApplication.AppHost", "param1", paramDefault, customFactory); + var userSecretDefault = new UserSecretsParameterDefault("TestApplication.AppHost", "param1", paramDefault, manager); var initialValue = userSecretDefault.GetDefaultValue(); @@ -224,10 +235,10 @@ public void UserSecretsParameterDefault_WithCustomFactory_UsesProvidedFactory() } [Fact] - public void UserSecretsParameterDefault_WithCustomFactory_IsolatesFromGlobalInstance() + public void UserSecretsParameterDefault_WithCustomFactory_IsolatesFromOtherInstances() { // This test verifies that using a custom factory provides isolation - // between test runs and doesn't interfere with the singleton instance + // between test runs var userSecretsId1 = Guid.NewGuid().ToString("N"); var userSecretsId2 = Guid.NewGuid().ToString("N"); ClearUsersSecrets(userSecretsId1); @@ -239,13 +250,16 @@ public void UserSecretsParameterDefault_WithCustomFactory_IsolatesFromGlobalInst new("TestAssembly2"), AssemblyBuilderAccess.RunAndCollect, [new CustomAttributeBuilder(s_userSecretsIdAttrCtor, [userSecretsId2])]); // Use custom factory for first parameter default - var customFactory = new UserSecretsManagerFactory(); + var customFactory1 = CreateFactory(); + var manager1 = customFactory1.GetOrCreate(testAssembly1); var paramDefault1 = new TestParameterDefault(); - var userSecretDefault1 = new UserSecretsParameterDefault(testAssembly1, "TestApp1.AppHost", "param1", paramDefault1, customFactory); + var userSecretDefault1 = new UserSecretsParameterDefault("TestApp1.AppHost", "param1", paramDefault1, manager1); - // Use default singleton factory for second parameter default + // Use different factory for second parameter default + var customFactory2 = CreateFactory(); + var manager2 = customFactory2.GetOrCreate(testAssembly2); var paramDefault2 = new TestParameterDefault(); - var userSecretDefault2 = new UserSecretsParameterDefault(testAssembly2, "TestApp2.AppHost", "param2", paramDefault2); + var userSecretDefault2 = new UserSecretsParameterDefault("TestApp2.AppHost", "param2", paramDefault2, manager2); var value1 = userSecretDefault1.GetDefaultValue(); var value2 = userSecretDefault2.GetDefaultValue(); @@ -271,7 +285,8 @@ public async Task UserSecretsParameterDefault_WithCustomFactory_ConcurrentAccess var testAssembly = AssemblyBuilder.DefineDynamicAssembly( new("TestAssembly"), AssemblyBuilderAccess.RunAndCollect, [new CustomAttributeBuilder(s_userSecretsIdAttrCtor, [userSecretsId])]); - var customFactory = new UserSecretsManagerFactory(); + var customFactory = CreateFactory(); + var manager = customFactory.GetOrCreate(testAssembly); // Create multiple UserSecretsParameterDefault instances with different parameter names var tasks = new List>(); @@ -281,7 +296,7 @@ public async Task UserSecretsParameterDefault_WithCustomFactory_ConcurrentAccess tasks.Add(Task.Run(() => { var paramDefault = new TestParameterDefault(); - var userSecretDefault = new UserSecretsParameterDefault(testAssembly, "TestApp.AppHost", paramName, paramDefault, customFactory); + var userSecretDefault = new UserSecretsParameterDefault("TestApp.AppHost", paramName, paramDefault, manager); return userSecretDefault.GetDefaultValue(); })); } @@ -311,8 +326,9 @@ private static void EnsureUserSecretsDirectory(string secretsFilePath) private static Dictionary GetUserSecrets(string userSecretsId) { - var manager = UserSecretsManagerFactory.Instance.GetOrCreateFromId(userSecretsId); - + var factory = CreateFactory(); + var manager = factory.GetOrCreateFromId(userSecretsId); + // Read the secrets file directly var secrets = new Dictionary(); if (File.Exists(manager.FilePath)) @@ -323,7 +339,7 @@ private static void EnsureUserSecretsDirectory(string secretsFilePath) var config = new ConfigurationBuilder() .AddJsonFile(manager.FilePath, optional: true) .Build(); - + foreach (var kvp in config.AsEnumerable()) { if (kvp.Value != null) @@ -367,4 +383,3 @@ public override void WriteToManifest(ManifestPublishingContext context) } } } - diff --git a/tests/Aspire.Hosting.Tests/VersionChecking/VersionCheckServiceTests.cs b/tests/Aspire.Hosting.Tests/VersionChecking/VersionCheckServiceTests.cs index 1159b67a507..8e236df3f5c 100644 --- a/tests/Aspire.Hosting.Tests/VersionChecking/VersionCheckServiceTests.cs +++ b/tests/Aspire.Hosting.Tests/VersionChecking/VersionCheckServiceTests.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable ASPIREUSERSECRETS001 + using System.Globalization; using Aspire.Hosting.UserSecrets; using Aspire.Hosting.VersionChecking; diff --git a/tests/Aspire.Hosting.Yarp.Tests/AddYarpTests.cs b/tests/Aspire.Hosting.Yarp.Tests/AddYarpTests.cs index bc1494b7b5a..ca446481df9 100644 --- a/tests/Aspire.Hosting.Yarp.Tests/AddYarpTests.cs +++ b/tests/Aspire.Hosting.Yarp.Tests/AddYarpTests.cs @@ -142,7 +142,7 @@ public async Task VerifyWithStaticFilesBindMountAddsEnvironmentVariable() testProvider.AddService(new DistributedApplicationOptions()); testProvider.AddService(Options.Create(new DcpOptions())); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var yarp = builder.AddYarp("yarp").WithStaticFiles(tempDir.Path); @@ -157,7 +157,7 @@ public async Task VerifyWithStaticFilesBindMountWorksInPublishOperation() { var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var yarp = builder.AddYarp("yarp").WithStaticFiles(tempDir.Path); @@ -171,7 +171,7 @@ public async Task VerifyWithStaticFilesBindMountWorksInPublishOperation() public void VerifyWithStaticFilesBindMountAddsContainerFileSystemAnnotationInRunMode() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Run); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var yarp = builder.AddYarp("yarp").WithStaticFiles(tempDir.Path); @@ -183,7 +183,7 @@ public void VerifyWithStaticFilesBindMountAddsContainerFileSystemAnnotationInRun public void VerifyWithStaticFilesAddsDockerfileBuildAnnotationInPublishMode() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var yarp = builder.AddYarp("yarp").WithStaticFiles(tempDir.Path); @@ -196,7 +196,7 @@ public void VerifyWithStaticFilesAddsDockerfileBuildAnnotationInPublishMode() public async Task VerifyWithStaticFilesGeneratesCorrectDockerfileInPublishMode() { using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); var yarp = builder.AddYarp("yarp").WithStaticFiles(tempDir.Path); diff --git a/tests/Aspire.Hosting.Yarp.Tests/YarpConfigGeneratorTests.cs b/tests/Aspire.Hosting.Yarp.Tests/YarpConfigGeneratorTests.cs index 18884b3a0fd..3222b537b3f 100644 --- a/tests/Aspire.Hosting.Yarp.Tests/YarpConfigGeneratorTests.cs +++ b/tests/Aspire.Hosting.Yarp.Tests/YarpConfigGeneratorTests.cs @@ -264,7 +264,7 @@ public void EnsureAllEnvVarsAreStrings() [ActiveIssue("https://github.com/dotnet/dnceng/issues/6232", typeof(PlatformDetection), nameof(PlatformDetection.IsRunningFromAzdo))] public async Task GenerateEnvVariablesConfigurationDockerCompose() { - using var tempDir = new TempDirectory(); + using var tempDir = new TestTempDirectory(); using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish, tempDir.Path); builder.AddDockerComposeEnvironment("docker-compose").WithDashboard(db => db.WithHostPort(18888)); diff --git a/tests/Shared/TempDirectory.cs b/tests/Shared/TempDirectory.cs index 2bd417eeea0..33dd14cd18e 100644 --- a/tests/Shared/TempDirectory.cs +++ b/tests/Shared/TempDirectory.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -public sealed class TempDirectory : IDisposable +public sealed class TestTempDirectory : IDisposable { public string Path { get; } = Directory.CreateTempSubdirectory(".aspire-tests").FullName;