File: WorkflowScripts\CreatePullRequestActionTests.cs
Web Access
Project: src\tests\Infrastructure.Tests\Infrastructure.Tests.csproj (Infrastructure.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Aspire.TestUtilities;
using Xunit;
using YamlDotNet.RepresentationModel;
 
namespace Infrastructure.Tests;
 
public sealed class CreatePullRequestActionTests(ITestOutputHelper output)
{
    [Fact]
    [RequiresTools(["bash"])]
    public async Task ExistingPullRequestMetadataIsUpdatedOnlyWhenEnabled()
    {
        var action = ReadYaml(Path.Combine(RepoRoot.Path, ".github", "actions", "create-pull-request", "action.yml"));
        var runs = (YamlMappingNode)action.Children["runs"];
        var steps = (YamlSequenceNode)runs.Children["steps"];
        var createPrStep = Assert.Single(steps.Children.Cast<YamlMappingNode>(), step =>
            step.Children.TryGetValue("id", out var id) && ((YamlScalarNode)id).Value == "create-pr");
        var script = ((YamlScalarNode)createPrStep.Children["run"]).Value!;
 
        const string title = "[automated] Update actionlint to v1.7.13";
        const string body = "Updates the actionlint pin to v1.7.13.\n\n- Verified release digest.";
 
        using var workspace = TemporaryWorkspace.Create(output);
        var disabled = await RunCreatePrScriptAsync(script, workspace.Path, updateMetadata: false, title, body);
 
        Assert.Equal(0, disabled.ExitCode);
        Assert.Contains("pull-request-operation=none", await File.ReadAllTextAsync(disabled.OutputPath));
        Assert.False(File.Exists(disabled.EditArgumentsPath));
 
        var enabled = await RunCreatePrScriptAsync(script, workspace.Path, updateMetadata: true, title, body);
 
        Assert.Equal(0, enabled.ExitCode);
        Assert.Contains("pull-request-operation=updated", await File.ReadAllTextAsync(enabled.OutputPath));
        Assert.Equal(
            ["pr", "edit", "20610", "--title", title, "--body-file", "-"],
            await File.ReadAllLinesAsync(enabled.EditArgumentsPath));
        Assert.Equal(body + "\n", await File.ReadAllTextAsync(enabled.EditBodyPath));
    }
 
    private async Task<ScriptResult> RunCreatePrScriptAsync(
        string actionScript,
        string workingDirectory,
        bool updateMetadata,
        string title,
        string body)
    {
        var outputPath = Path.Combine(workingDirectory, $"github-output-{Guid.NewGuid():N}.txt");
        var editArgumentsPath = Path.Combine(workingDirectory, $"edit-arguments-{Guid.NewGuid():N}.txt");
        var editBodyPath = Path.Combine(workingDirectory, $"edit-body-{Guid.NewGuid():N}.txt");
        var script = $$"""
            set -euo pipefail
 
            gh() {
              if [[ "$1" == "pr" && "$2" == "list" ]]; then
                printf '%s\n' '{"number":20610,"url":"https://github.com/microsoft/aspire/pull/20610"}'
                return 0
              fi
              if [[ "$1" == "pr" && "$2" == "edit" ]]; then
                printf '%s\n' "$@" > "$EDIT_ARGUMENTS_PATH"
                cat > "$EDIT_BODY_PATH"
                return 0
              fi
              printf 'Unexpected gh invocation: %s\n' "$*" >&2
              return 97
            }
 
            # Drain stdin like the real jq so `echo ... | jq` cannot fail with
            # SIGPIPE under `set -o pipefail` when this fake exits first.
            jq() {
              cat > /dev/null
              case "$2" in
                .number) printf '20610\n' ;;
                .url) printf 'https://github.com/microsoft/aspire/pull/20610\n' ;;
                *) printf 'Unexpected jq query: %s\n' "$2" >&2; return 98 ;;
              esac
            }
 
            {{actionScript}}
            """;
 
        var result = await ProcessRunner.RunAsync(
            output,
            "bash",
            ["-c", script],
            workingDirectory,
            new Dictionary<string, string>
            {
                ["BRANCH"] = "update-actionlint",
                ["BASE"] = "main",
                ["PR_TITLE"] = title,
                ["PR_BODY"] = body,
                ["UPDATE_EXISTING_PR_METADATA"] = updateMetadata ? "true" : "false",
                ["EDIT_ARGUMENTS_PATH"] = editArgumentsPath,
                ["EDIT_BODY_PATH"] = editBodyPath,
                ["GITHUB_OUTPUT"] = outputPath,
                ["GITHUB_REPOSITORY"] = "microsoft/aspire",
                ["GH_TOKEN"] = "test-token",
            });
 
        return new ScriptResult(result.ExitCode, outputPath, editArgumentsPath, editBodyPath);
    }
 
    private static YamlMappingNode ReadYaml(string path)
    {
        var yaml = new YamlStream();
        yaml.Load(new StringReader(File.ReadAllText(path)));
        return (YamlMappingNode)yaml.Documents[0].RootNode;
    }
 
    private sealed record ScriptResult(int ExitCode, string OutputPath, string EditArgumentsPath, string EditBodyPath);
}