diff --git a/src/Aspire.Hosting/DistributedApplicationBuilder.cs b/src/Aspire.Hosting/DistributedApplicationBuilder.cs
index 5f0a83cf547..cac4a160f38 100644
--- a/src/Aspire.Hosting/DistributedApplicationBuilder.cs
+++ b/src/Aspire.Hosting/DistributedApplicationBuilder.cs
@@ -67,7 +67,7 @@ public class DistributedApplicationBuilder : IDistributedApplicationBuilder
private readonly DistributedApplicationOptions _options;
private readonly HostApplicationBuilder _innerBuilder;
private readonly IUserSecretsManager _userSecretsManager;
- private readonly IFileSystemService _directoryService;
+ private readonly FileSystemService _directoryService;
///
public IHostEnvironment Environment => _innerBuilder.Environment;
@@ -303,8 +303,12 @@ 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);
+ _directoryService = new FileSystemService(_innerBuilder.Configuration);
+ _innerBuilder.Services.AddSingleton(sp =>
+ {
+ _directoryService.SetLogger(sp.GetRequiredService>());
+ return _directoryService;
+ });
// Create and register the user secrets manager
var userSecretsFactory = new UserSecretsManagerFactory(_directoryService);
diff --git a/src/Aspire.Hosting/Utils/FileSystemService.cs b/src/Aspire.Hosting/Utils/FileSystemService.cs
index 0d8c3b8e82f..0099c70f066 100644
--- a/src/Aspire.Hosting/Utils/FileSystemService.cs
+++ b/src/Aspire.Hosting/Utils/FileSystemService.cs
@@ -3,28 +3,146 @@
#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only
+using System.Collections.Concurrent;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+
namespace Aspire.Hosting;
///
/// Default implementation of .
///
-internal sealed class FileSystemService : IFileSystemService
+internal sealed class FileSystemService : IFileSystemService, IDisposable
{
- private readonly TempFileSystemService _tempDirectory = new();
+ private readonly TempFileSystemService _tempDirectory;
+ private ILogger? _logger;
+ private readonly bool _preserveTempFiles;
+
+ // Track allocated temp files and directories as disposable objects using path as key
+ private readonly ConcurrentDictionary _allocatedItems = new();
+
+ public FileSystemService(IConfiguration configuration)
+ {
+ ArgumentNullException.ThrowIfNull(configuration);
+
+ // Check configuration to preserve temp files for debugging
+ _preserveTempFiles = configuration["ASPIRE_PRESERVE_TEMP_FILES"] is not null;
+
+ _tempDirectory = new TempFileSystemService(this);
+ }
+
+ ///
+ /// Sets the logger for this service. Called after service provider is built.
+ ///
+ ///
+ /// The logger cannot be injected via constructor because the FileSystemService
+ /// is allocated before logging is fully initialized in the DistributedApplicationBuilder.
+ ///
+ internal void SetLogger(ILogger logger)
+ {
+ _logger = logger;
+ }
///
public ITempFileSystemService TempDirectory => _tempDirectory;
+ ///
+ /// Gets whether temporary files should be preserved for debugging.
+ ///
+ internal bool ShouldPreserveTempFiles => _preserveTempFiles;
+
+ ///
+ /// Gets the logger for this service, if set.
+ ///
+ internal ILogger? Logger => _logger;
+
+ private bool _disposed;
+ private readonly object _disposeLock = new();
+
+ ///
+ /// Tracks a temporary item for cleanup on service disposal.
+ ///
+ internal void TrackItem(string path, IDisposable item)
+ {
+ lock (_disposeLock)
+ {
+ if (_disposed)
+ {
+ throw new ObjectDisposedException(nameof(FileSystemService), "Cannot allocate temporary files after the service has been disposed.");
+ }
+
+ _allocatedItems.TryAdd(path, item);
+ }
+ }
+
+ ///
+ /// Removes a temporary item from tracking.
+ ///
+ internal void UntrackItem(string path)
+ {
+ _allocatedItems.TryRemove(path, out _);
+ }
+
+ ///
+ /// Cleans up any remaining temporary files and directories.
+ ///
+ public void Dispose()
+ {
+ lock (_disposeLock)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ _disposed = true;
+ }
+
+ if (_preserveTempFiles)
+ {
+ _logger?.LogInformation("Skipping cleanup of {Count} temporary files/directories due to ASPIRE_PRESERVE_TEMP_FILES configuration", _allocatedItems.Count);
+ return;
+ }
+
+ if (_allocatedItems.IsEmpty)
+ {
+ return;
+ }
+
+ _logger?.LogDebug("Cleaning up {Count} remaining temporary files/directories", _allocatedItems.Count);
+
+ foreach (var kvp in _allocatedItems)
+ {
+ try
+ {
+ kvp.Value.Dispose();
+ }
+ catch (Exception ex)
+ {
+ _logger?.LogWarning(ex, "Failed to clean up temporary item");
+ }
+ }
+ }
+
///
/// Implementation of .
///
private sealed class TempFileSystemService : ITempFileSystemService
{
+ private readonly FileSystemService _parent;
+
+ public TempFileSystemService(FileSystemService parent)
+ {
+ _parent = parent;
+ }
+
///
public TempDirectory CreateTempSubdirectory(string? prefix = null)
{
var path = Directory.CreateTempSubdirectory(prefix ?? "aspire").FullName;
- return new DefaultTempDirectory(path);
+ var tempDir = new DefaultTempDirectory(path, _parent);
+ _parent.TrackItem(path, tempDir);
+ return tempDir;
}
///
@@ -33,14 +151,18 @@ public TempFile CreateTempFile(string? fileName = null)
if (fileName is null)
{
var tempFile = Path.GetTempFileName();
- return new DefaultTempFile(tempFile, deleteParentDirectory: false);
+ var file = new DefaultTempFile(tempFile, deleteParentDirectory: false, _parent);
+ _parent.TrackItem(tempFile, file);
+ return file;
}
// 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);
+ var tempFileObj = new DefaultTempFile(filePath, deleteParentDirectory: true, _parent);
+ _parent.TrackItem(filePath, tempFileObj);
+ return tempFileObj;
}
}
@@ -50,21 +172,43 @@ public TempFile CreateTempFile(string? fileName = null)
private sealed class DefaultTempDirectory : TempDirectory
{
private readonly string _path;
+ private readonly FileSystemService _parent;
+ private bool _disposed;
- public DefaultTempDirectory(string path)
+ public DefaultTempDirectory(string path, FileSystemService parent)
{
_path = path;
+ _parent = parent;
+
+ _parent.Logger?.LogDebug("Allocated temporary directory: {Path}", path);
}
public override string Path => _path;
public override void Dispose()
{
+ if (_disposed)
+ {
+ return;
+ }
+
+ _disposed = true;
+
+ // Remove from tracking
+ _parent.UntrackItem(_path);
+
+ // Skip deletion if preserve flag is set
+ if (_parent.ShouldPreserveTempFiles)
+ {
+ return;
+ }
+
try
{
if (Directory.Exists(_path))
{
Directory.Delete(_path, recursive: true);
+ _parent.Logger?.LogDebug("Cleaned up temporary directory: {Path}", _path);
}
}
catch
@@ -81,22 +225,44 @@ private sealed class DefaultTempFile : TempFile
{
private readonly string _path;
private readonly bool _deleteParentDirectory;
+ private readonly FileSystemService _parent;
+ private bool _disposed;
- public DefaultTempFile(string path, bool deleteParentDirectory)
+ public DefaultTempFile(string path, bool deleteParentDirectory, FileSystemService parent)
{
_path = path;
_deleteParentDirectory = deleteParentDirectory;
+ _parent = parent;
+
+ _parent.Logger?.LogDebug("Allocated temporary file: {Path}", path);
}
public override string Path => _path;
public override void Dispose()
{
+ if (_disposed)
+ {
+ return;
+ }
+
+ _disposed = true;
+
+ // Remove from tracking
+ _parent.UntrackItem(_path);
+
+ // Skip deletion if preserve flag is set
+ if (_parent.ShouldPreserveTempFiles)
+ {
+ return;
+ }
+
try
{
if (File.Exists(_path))
{
File.Delete(_path);
+ _parent.Logger?.LogDebug("Cleaned up temporary file: {Path}", _path);
}
if (_deleteParentDirectory)
diff --git a/tests/Aspire.Hosting.Tests/AspireStoreTests.cs b/tests/Aspire.Hosting.Tests/AspireStoreTests.cs
index e1f98a4ea0e..aebdfc2fdc7 100644
--- a/tests/Aspire.Hosting.Tests/AspireStoreTests.cs
+++ b/tests/Aspire.Hosting.Tests/AspireStoreTests.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using Aspire.Hosting.Utils;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Aspire.Hosting.Tests;
@@ -119,11 +120,13 @@ public void GetOrCreateFileWithContent_ShouldNotRecreateFile()
[InlineData(null)]
[InlineData("")]
[InlineData("./folder")]
+ [InlineData(".\\folder")]
[InlineData("folder")]
[InlineData("obj/")]
+ [InlineData("obj\\")]
public void AspireStoreConstructor_ShouldThrow_IfNotAbsolutePath(string? basePath)
{
- var directoryService = new FileSystemService();
+ var directoryService = new FileSystemService(new ConfigurationBuilder().Build());
Assert.ThrowsAny(() => new AspireStore(basePath!, directoryService));
}
diff --git a/tests/Aspire.Hosting.Tests/Dashboard/DashboardLifecycleHookTests.cs b/tests/Aspire.Hosting.Tests/Dashboard/DashboardLifecycleHookTests.cs
index 8dcb0040f27..4dda4322ff4 100644
--- a/tests/Aspire.Hosting.Tests/Dashboard/DashboardLifecycleHookTests.cs
+++ b/tests/Aspire.Hosting.Tests/Dashboard/DashboardLifecycleHookTests.cs
@@ -523,7 +523,7 @@ private static DashboardEventHandlers CreateHook(
new TestHostApplicationLifetime(),
new Hosting.Eventing.DistributedApplicationEventing(),
rewriter,
- new FileSystemService()
+ new FileSystemService(configuration)
);
}
diff --git a/tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs b/tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs
index afdd17f6ec0..067f58eb723 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 FileSystemService()),
+ new Locations(new FileSystemService(configuration ?? new ConfigurationBuilder().Build())),
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 ea21c987b51..e03ef806cd1 100644
--- a/tests/Aspire.Hosting.Tests/Dcp/DcpHostNotificationTests.cs
+++ b/tests/Aspire.Hosting.Tests/Dcp/DcpHostNotificationTests.cs
@@ -5,6 +5,7 @@
using Aspire.Hosting.Dcp;
using Aspire.Hosting.Resources;
using Microsoft.AspNetCore.InternalTesting;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@@ -18,7 +19,7 @@ public sealed class DcpHostNotificationTests
{
private static Locations CreateTestLocations()
{
- var directoryService = new FileSystemService();
+ var directoryService = new FileSystemService(new ConfigurationBuilder().Build());
return new Locations(directoryService);
}
diff --git a/tests/Aspire.Hosting.Tests/FileSystemServiceTests.cs b/tests/Aspire.Hosting.Tests/FileSystemServiceTests.cs
index 3f2eba2abfd..f621cda9631 100644
--- a/tests/Aspire.Hosting.Tests/FileSystemServiceTests.cs
+++ b/tests/Aspire.Hosting.Tests/FileSystemServiceTests.cs
@@ -3,14 +3,29 @@
#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+
namespace Aspire.Hosting.Tests;
public class FileSystemServiceTests
{
+ private static IConfiguration CreateConfiguration(bool preserveTempFiles = false)
+ {
+ var configDict = new Dictionary();
+ if (preserveTempFiles)
+ {
+ configDict["ASPIRE_PRESERVE_TEMP_FILES"] = "true";
+ }
+ return new ConfigurationBuilder()
+ .AddInMemoryCollection(configDict)
+ .Build();
+ }
+
[Fact]
public void CreateTempSubdirectory_CreatesDirectory()
{
- var service = new FileSystemService();
+ var service = new FileSystemService(CreateConfiguration());
using var tempDir = service.TempDirectory.CreateTempSubdirectory();
@@ -21,7 +36,7 @@ public void CreateTempSubdirectory_CreatesDirectory()
[Fact]
public void CreateTempSubdirectory_WithPrefix_CreatesDirectoryWithPrefix()
{
- var service = new FileSystemService();
+ var service = new FileSystemService(CreateConfiguration());
using var tempDir = service.TempDirectory.CreateTempSubdirectory("test-prefix");
@@ -33,7 +48,7 @@ public void CreateTempSubdirectory_WithPrefix_CreatesDirectoryWithPrefix()
[Fact]
public void CreateTempSubdirectory_Dispose_DeletesDirectory()
{
- var service = new FileSystemService();
+ var service = new FileSystemService(CreateConfiguration());
var tempDir = service.TempDirectory.CreateTempSubdirectory();
var path = tempDir.Path;
@@ -47,7 +62,7 @@ public void CreateTempSubdirectory_Dispose_DeletesDirectory()
[Fact]
public void CreateTempFile_CreatesFile()
{
- var service = new FileSystemService();
+ var service = new FileSystemService(CreateConfiguration());
using var tempFile = service.TempDirectory.CreateTempFile();
@@ -58,7 +73,7 @@ public void CreateTempFile_CreatesFile()
[Fact]
public void CreateTempFile_WithFileName_CreatesNamedFile()
{
- var service = new FileSystemService();
+ var service = new FileSystemService(CreateConfiguration());
using var tempFile = service.TempDirectory.CreateTempFile("config.json");
@@ -70,7 +85,7 @@ public void CreateTempFile_WithFileName_CreatesNamedFile()
[Fact]
public void CreateTempFile_Dispose_DeletesFile()
{
- var service = new FileSystemService();
+ var service = new FileSystemService(CreateConfiguration());
var tempFile = service.TempDirectory.CreateTempFile();
var path = tempFile.Path;
@@ -84,7 +99,7 @@ public void CreateTempFile_Dispose_DeletesFile()
[Fact]
public void CreateTempFile_WithFileName_Dispose_DeletesFileAndParentDirectory()
{
- var service = new FileSystemService();
+ var service = new FileSystemService(CreateConfiguration());
var tempFile = service.TempDirectory.CreateTempFile("test-file.txt");
var filePath = tempFile.Path;
var parentDir = Path.GetDirectoryName(filePath);
@@ -103,7 +118,7 @@ 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();
+ var service = new FileSystemService(CreateConfiguration());
string? extractedPath;
// Simulate the common pattern: extract path, let object go out of scope
@@ -125,7 +140,7 @@ 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();
+ var service = new FileSystemService(CreateConfiguration());
string? extractedPath;
// Simulate the common pattern: extract path, let object go out of scope
@@ -145,7 +160,7 @@ public void PathExtractionPattern_FilePersistsAfterScopeEnds()
[Fact]
public void TempDirectory_Property_ReturnsSameInstance()
{
- var service = new FileSystemService();
+ var service = new FileSystemService(CreateConfiguration());
var tempDir1 = service.TempDirectory;
var tempDir2 = service.TempDirectory;
@@ -156,7 +171,7 @@ public void TempDirectory_Property_ReturnsSameInstance()
[Fact]
public void CreateTempSubdirectory_MultipleCallsCreateDifferentDirectories()
{
- var service = new FileSystemService();
+ var service = new FileSystemService(CreateConfiguration());
using var tempDir1 = service.TempDirectory.CreateTempSubdirectory();
using var tempDir2 = service.TempDirectory.CreateTempSubdirectory();
@@ -169,7 +184,7 @@ public void CreateTempSubdirectory_MultipleCallsCreateDifferentDirectories()
[Fact]
public void CreateTempFile_MultipleCallsCreateDifferentFiles()
{
- var service = new FileSystemService();
+ var service = new FileSystemService(CreateConfiguration());
using var tempFile1 = service.TempDirectory.CreateTempFile();
using var tempFile2 = service.TempDirectory.CreateTempFile();
@@ -182,7 +197,7 @@ public void CreateTempFile_MultipleCallsCreateDifferentFiles()
[Fact]
public void Dispose_CalledMultipleTimes_DoesNotThrow()
{
- var service = new FileSystemService();
+ var service = new FileSystemService(CreateConfiguration());
var tempDir = service.TempDirectory.CreateTempSubdirectory();
var tempFile = service.TempDirectory.CreateTempFile();
@@ -194,4 +209,214 @@ public void Dispose_CalledMultipleTimes_DoesNotThrow()
tempDir.Dispose();
tempFile.Dispose();
}
+
+ [Fact]
+ public void ServiceDispose_CleansUpUndisposedFiles()
+ {
+ var service = new FileSystemService(CreateConfiguration());
+
+ // Create temp files/dirs without disposing them
+ var tempDir = service.TempDirectory.CreateTempSubdirectory();
+ var tempFile = service.TempDirectory.CreateTempFile();
+ var dirPath = tempDir.Path;
+ var filePath = tempFile.Path;
+
+ Assert.True(Directory.Exists(dirPath));
+ Assert.True(File.Exists(filePath));
+
+ // Dispose the service - should clean up remaining files
+ service.Dispose();
+
+ Assert.False(Directory.Exists(dirPath));
+ Assert.False(File.Exists(filePath));
+ }
+
+ [Fact]
+ public void ServiceDispose_WithPreserveConfiguration_SkipsCleanup()
+ {
+ var service = new FileSystemService(CreateConfiguration(preserveTempFiles: true));
+
+ var tempDir = service.TempDirectory.CreateTempSubdirectory();
+ var tempFile = service.TempDirectory.CreateTempFile();
+ var dirPath = tempDir.Path;
+ var filePath = tempFile.Path;
+
+ Assert.True(Directory.Exists(dirPath));
+ Assert.True(File.Exists(filePath));
+
+ // Dispose the service - should NOT clean up files
+ service.Dispose();
+
+ // Files should still exist
+ Assert.True(Directory.Exists(dirPath));
+ Assert.True(File.Exists(filePath));
+
+ // Clean up manually
+ Directory.Delete(dirPath, recursive: true);
+ File.Delete(filePath);
+ }
+
+ [Fact]
+ public void TempDirectory_Dispose_WithPreserveConfiguration_SkipsCleanup()
+ {
+ var service = new FileSystemService(CreateConfiguration(preserveTempFiles: true));
+
+ var tempDir = service.TempDirectory.CreateTempSubdirectory();
+ var dirPath = tempDir.Path;
+
+ Assert.True(Directory.Exists(dirPath));
+
+ // Dispose the temp directory - should NOT clean up
+ tempDir.Dispose();
+
+ // Directory should still exist
+ Assert.True(Directory.Exists(dirPath));
+
+ // Clean up manually
+ Directory.Delete(dirPath, recursive: true);
+ }
+
+ [Fact]
+ public void TempFile_Dispose_WithPreserveConfiguration_SkipsCleanup()
+ {
+ var service = new FileSystemService(CreateConfiguration(preserveTempFiles: true));
+
+ var tempFile = service.TempDirectory.CreateTempFile();
+ var filePath = tempFile.Path;
+
+ Assert.True(File.Exists(filePath));
+
+ // Dispose the temp file - should NOT clean up
+ tempFile.Dispose();
+
+ // File should still exist
+ Assert.True(File.Exists(filePath));
+
+ // Clean up manually
+ File.Delete(filePath);
+ }
+
+ [Fact]
+ public void ServiceDispose_WithMixedDisposedAndUndisposedItems_CleansUpOnlyUndisposed()
+ {
+ var service = new FileSystemService(CreateConfiguration());
+
+ // Create multiple temp items
+ var tempDir1 = service.TempDirectory.CreateTempSubdirectory();
+ var tempDir2 = service.TempDirectory.CreateTempSubdirectory();
+ var tempFile1 = service.TempDirectory.CreateTempFile();
+ var tempFile2 = service.TempDirectory.CreateTempFile();
+
+ var dir1Path = tempDir1.Path;
+ var dir2Path = tempDir2.Path;
+ var file1Path = tempFile1.Path;
+ var file2Path = tempFile2.Path;
+
+ // Dispose some of them
+ tempDir1.Dispose();
+ tempFile1.Dispose();
+
+ Assert.False(Directory.Exists(dir1Path));
+ Assert.False(File.Exists(file1Path));
+ Assert.True(Directory.Exists(dir2Path));
+ Assert.True(File.Exists(file2Path));
+
+ // Dispose the service - should clean up remaining undisposed items
+ service.Dispose();
+
+ Assert.False(Directory.Exists(dir2Path));
+ Assert.False(File.Exists(file2Path));
+ }
+
+ [Fact]
+ public void ServiceDispose_EmptyTracking_DoesNotThrow()
+ {
+ var service = new FileSystemService(CreateConfiguration());
+
+ // Dispose without creating any temp items
+ service.Dispose();
+
+ // Dispose again should also not throw
+ service.Dispose();
+ }
+
+ [Fact]
+ public void ServiceDispose_CalledMultipleTimes_IsIdempotent()
+ {
+ var service = new FileSystemService(CreateConfiguration());
+
+ // Create temp items
+ var tempDir = service.TempDirectory.CreateTempSubdirectory();
+ var tempFile = service.TempDirectory.CreateTempFile();
+ var dirPath = tempDir.Path;
+ var filePath = tempFile.Path;
+
+ // First dispose - should clean up
+ service.Dispose();
+
+ Assert.False(Directory.Exists(dirPath));
+ Assert.False(File.Exists(filePath));
+
+ // Second dispose - should be a no-op and not throw
+ service.Dispose();
+ }
+
+ [Fact]
+ public void CreateTempFile_WithFileName_TracksOnlyFile()
+ {
+ var service = new FileSystemService(CreateConfiguration());
+
+ var tempFile = service.TempDirectory.CreateTempFile("test.txt");
+ var filePath = tempFile.Path;
+ var parentDir = Path.GetDirectoryName(filePath)!;
+
+ // Both file and parent dir should exist
+ Assert.True(File.Exists(filePath));
+ Assert.True(Directory.Exists(parentDir));
+
+ // Dispose the temp file - should delete both file and parent directory
+ tempFile.Dispose();
+
+ Assert.False(File.Exists(filePath));
+ Assert.False(Directory.Exists(parentDir));
+ }
+
+ [Fact]
+ public void CreateTempFile_AfterServiceDisposed_ThrowsObjectDisposedException()
+ {
+ var service = new FileSystemService(CreateConfiguration());
+
+ // Dispose the service
+ service.Dispose();
+
+ // Attempting to create a temp file after disposal should throw
+ Assert.Throws(() => service.TempDirectory.CreateTempFile());
+ }
+
+ [Fact]
+ public void CreateTempDirectory_AfterServiceDisposed_ThrowsObjectDisposedException()
+ {
+ var service = new FileSystemService(CreateConfiguration());
+
+ // Dispose the service
+ service.Dispose();
+
+ // Attempting to create a temp directory after disposal should throw
+ Assert.Throws(() => service.TempDirectory.CreateTempSubdirectory());
+ }
+
+ [Fact]
+ public void SetLogger_CanBeCalledWithoutError()
+ {
+ var service = new FileSystemService(CreateConfiguration());
+ var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
+ var logger = loggerFactory.CreateLogger();
+
+ // Should not throw
+ service.SetLogger(logger);
+
+ // Should still work normally
+ using var tempDir = service.TempDirectory.CreateTempSubdirectory();
+ Assert.True(Directory.Exists(tempDir.Path));
+ }
}
diff --git a/tests/Aspire.Hosting.Tests/SecretsStoreTests.cs b/tests/Aspire.Hosting.Tests/SecretsStoreTests.cs
index 18a4e967bf5..bf88084a0d9 100644
--- a/tests/Aspire.Hosting.Tests/SecretsStoreTests.cs
+++ b/tests/Aspire.Hosting.Tests/SecretsStoreTests.cs
@@ -16,7 +16,12 @@ public class SecretsStoreTests
{
private static readonly ConstructorInfo s_userSecretsIdAttrCtor = typeof(UserSecretsIdAttribute).GetConstructor([typeof(string)])!;
- private static UserSecretsManagerFactory CreateFactory() => new UserSecretsManagerFactory(new FileSystemService());
+ private static UserSecretsManagerFactory CreateFactory()
+ {
+ return new UserSecretsManagerFactory(
+ new FileSystemService(
+ new ConfigurationBuilder().Build()));
+ }
[Fact]
public void GetOrSetUserSecret_SavesValueToUserSecrets()
diff --git a/tests/Aspire.Hosting.Tests/UserSecretsParameterDefaultTests.cs b/tests/Aspire.Hosting.Tests/UserSecretsParameterDefaultTests.cs
index 40360a87eda..9e6b01fa173 100644
--- a/tests/Aspire.Hosting.Tests/UserSecretsParameterDefaultTests.cs
+++ b/tests/Aspire.Hosting.Tests/UserSecretsParameterDefaultTests.cs
@@ -18,7 +18,12 @@ public class UserSecretsParameterDefaultTests
{
private static readonly ConstructorInfo s_userSecretsIdAttrCtor = typeof(UserSecretsIdAttribute).GetConstructor([typeof(string)])!;
- private static UserSecretsManagerFactory CreateFactory() => new UserSecretsManagerFactory(new FileSystemService());
+ private static UserSecretsManagerFactory CreateFactory()
+ {
+ return new UserSecretsManagerFactory(
+ new FileSystemService(
+ new ConfigurationBuilder().Build()));
+ }
[Fact]
public void UserSecretsParameterDefault_GetDefaultValue_SavesValueInAppHostUserSecrets()