Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 26 additions & 15 deletions src/Aspire.Hosting/LaunchProfileExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,37 @@ internal static class LaunchProfileExtensions

internal static NamedLaunchProfile? GetEffectiveLaunchProfile(this ProjectResource projectResource, bool throwIfNotFound = false)
{
string? launchProfileName = projectResource.SelectLaunchProfileName();
if (string.IsNullOrEmpty(launchProfileName))
try
{
return null;
}
string? launchProfileName = projectResource.SelectLaunchProfileName();
if (string.IsNullOrEmpty(launchProfileName))
{
return null;
}

var profiles = projectResource.GetLaunchSettings()?.Profiles;
if (profiles is null)
{
return null;
}
var profiles = projectResource.GetLaunchSettings()?.Profiles;
if (profiles is null)
{
return null;
}

var found = profiles.TryGetValue(launchProfileName, out var launchProfile);
if (!found && throwIfNotFound)
{
var message = string.Format(CultureInfo.InvariantCulture, Resources.LaunchSettingsFileDoesNotContainProfileExceptionMessage, launchProfileName);
throw new DistributedApplicationException(message);
}

var found = profiles.TryGetValue(launchProfileName, out var launchProfile);
if (!found && throwIfNotFound)
return launchProfile is not null ? new (launchProfileName, launchProfile) : default;
}
catch (JsonException ex)
{
var message = string.Format(CultureInfo.InvariantCulture, Resources.LaunchSettingsFileDoesNotContainProfileExceptionMessage, launchProfileName);
throw new DistributedApplicationException(message);
var metadata = projectResource.GetProjectMetadata();
var projectFileInfo = new FileInfo(metadata.ProjectPath);
var launchSettingsFilePath = Path.Combine(projectFileInfo.DirectoryName!, "Properties", "launchSettings.json");
var message = $"Failed to get effective launch profile for project resource '{projectResource.Name}'. There is malformed JSON in the project's launch settings file at '{launchSettingsFilePath}'.";
throw new DistributedApplicationException(message, ex);
}

return launchProfile is not null ? new (launchProfileName, launchProfile) : default;
}

private static LaunchSettings? GetLaunchSettings(this IProjectMetadata projectMetadata)
Expand Down
41 changes: 41 additions & 0 deletions tests/Aspire.Hosting.Tests/ProjectResourceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,47 @@ namespace Aspire.Hosting.Tests;

public class ProjectResourceTests
{
[Fact]
public async Task AddProjectWithInvalidLaunchSettingsShouldThrowSpecificError()
{
var projectDetails = await PrepareProjectWithMalformedLaunchSettingsAsync();

var ex = Assert.Throws<DistributedApplicationException>(() =>
{
var appBuilder = CreateBuilder();
Comment on lines +22 to +24
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var ex = Assert.Throws<DistributedApplicationException>(() =>
{
var appBuilder = CreateBuilder();
var appBuilder = CreateBuilder();
var ex = Assert.Throws<DistributedApplicationException>(() =>
{

(nit) I like to keep the action inside the Assert.Throws as minimal as possible, so it is obvious what line is throwing the exception.

appBuilder.AddProject("project", projectDetails.ProjectFilePath);
});

var expectedMessage = $"Failed to get effective launch profile for project resource 'project'. There is malformed JSON in the project's launch settings file at '{projectDetails.LaunchSettingsFilePath}'.";
Assert.Equal(expectedMessage, ex.Message);

async static Task<(string ProjectFilePath, string LaunchSettingsFilePath)> PrepareProjectWithMalformedLaunchSettingsAsync()
{
var csProjContent = """
<Project Sdk="Microsoft.NET.Sdk.Web">
<!-- Not a real project, just a stub for testing -->
</Project>
""";

var launchSettingsContent = """
this { is } { mal formed! >
""";

var projectDirectoryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
var projectFilePath = Path.Combine(projectDirectoryPath, "Project.csproj");
var propertiesDirectoryPath = Path.Combine(projectDirectoryPath, "Properties");
var launchSettingsFilePath = Path.Combine(propertiesDirectoryPath, "launchSettings.json");

Directory.CreateDirectory(projectDirectoryPath);
Comment on lines +43 to +48
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var projectDirectoryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
var projectFilePath = Path.Combine(projectDirectoryPath, "Project.csproj");
var propertiesDirectoryPath = Path.Combine(projectDirectoryPath, "Properties");
var launchSettingsFilePath = Path.Combine(propertiesDirectoryPath, "launchSettings.json");
Directory.CreateDirectory(projectDirectoryPath);
var projectDirectoryPath = Directory.CreateTempSubdirectory().FullName;
var projectFilePath = Path.Combine(projectDirectoryPath, "Project.csproj");
var propertiesDirectoryPath = Path.Combine(projectDirectoryPath, "Properties");
var launchSettingsFilePath = Path.Combine(propertiesDirectoryPath, "launchSettings.json");

Using CreateTempSubdirectory is easier.

Comment on lines +43 to +48
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be deleting this directory when the test is done so we don't clutter up the temp directory?

await File.WriteAllTextAsync(projectFilePath, csProjContent);

Directory.CreateDirectory(propertiesDirectoryPath);
await File.WriteAllTextAsync(launchSettingsFilePath, launchSettingsContent);

return (projectFilePath, launchSettingsFilePath);
}
}

[Fact]
public async Task AddProjectAddsEnvironmentVariablesAndServiceMetadata()
{
Expand Down
Loading