File: WorkflowScripts\OrganizationFundedCopilotReviewsTests.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 OrganizationFundedCopilotReviewsTests(ITestOutputHelper output)
{
    [Fact]
    public void WorkflowKeepsPrivilegedExecutionMetadataOnly()
    {
        var root = LoadWorkflow();
        Assert.Empty(Mapping(root, "permissions").Children);
        var triggers = Mapping(root, "on");
        Assert.Equal(["pull_request_target", "schedule", "workflow_dispatch"], triggers.Children.Keys.Select(key => key.ToString()));
        var pullRequestTrigger = Mapping(triggers, "pull_request_target");
        Assert.Equal(["main", "release/**"], Sequence(pullRequestTrigger, "branches"));
        Assert.Equal(["opened", "synchronize"], Sequence(pullRequestTrigger, "types"));
        var schedule = Assert.IsType<YamlMappingNode>(Assert.Single(Assert.IsType<YamlSequenceNode>(triggers.Children[new YamlScalarNode("schedule")])));
        Assert.Equal("7,22,37,52 * * * *", Scalar(schedule, "cron"));
 
        var concurrency = Mapping(root, "concurrency");
        Assert.Equal("organization-funded-copilot-reviews", Scalar(concurrency, "group"));
        Assert.Equal("false", Scalar(concurrency, "cancel-in-progress"));
        var jobs = Mapping(root, "jobs");
        var job = Assert.IsType<YamlMappingNode>(Assert.Single(jobs.Children).Value);
        Assert.Equal(
            "github.repository == 'microsoft/aspire' && vars.COPILOT_REVIEW_MODE != 'disabled' && " +
            "(github.event_name != 'pull_request_target' || github.actor != 'dependabot[bot]') && " +
            "(github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/main')",
            Scalar(job, "if"));
        Assert.Equal("ubuntu-latest", Scalar(job, "runs-on"));
        Assert.Equal("10", Scalar(job, "timeout-minutes"));
        Assert.Empty(Mapping(job, "permissions").Children);
 
        var steps = Assert.IsType<YamlSequenceNode>(job.Children[new YamlScalarNode("steps")]);
        Assert.Equal(2, steps.Children.Count);
        var tokenStep = Assert.IsType<YamlMappingNode>(steps.Children[0]);
        Assert.Equal(["name", "id", "uses", "with"], tokenStep.Children.Keys.Select(key => key.ToString()));
        Assert.Equal("app-token", Scalar(tokenStep, "id"));
        Assert.Matches("^actions/create-github-app-token@[a-f0-9]{40}$", Scalar(tokenStep, "uses"));
        var tokenOptions = Mapping(tokenStep, "with");
        Assert.Equal(
            ["client-id", "private-key", "owner", "repositories", "permission-pull-requests", "skip-token-revoke"],
            tokenOptions.Children.Keys.Select(key => key.ToString()));
        Assert.Equal("${{ secrets.ASPIRE_BOT_APP_ID }}", Scalar(tokenOptions, "client-id"));
        Assert.Equal("${{ secrets.ASPIRE_BOT_PRIVATE_KEY }}", Scalar(tokenOptions, "private-key"));
        Assert.Equal("microsoft", Scalar(tokenOptions, "owner"));
        Assert.Equal("aspire", Scalar(tokenOptions, "repositories"));
        Assert.Equal("${{ vars.COPILOT_REVIEW_MODE == 'enabled' && 'write' || 'read' }}", Scalar(tokenOptions, "permission-pull-requests"));
        Assert.Equal("false", Scalar(tokenOptions, "skip-token-revoke"));
 
        var step = ScriptStep(root);
        Assert.Equal(["name", "uses", "env", "with"], step.Children.Keys.Select(key => key.ToString()));
        Assert.Matches("^actions/github-script@[a-f0-9]{40}$", Scalar(step, "uses"));
        var environment = Mapping(step, "env");
        Assert.Equal(["COPILOT_REVIEW_MODE", "COPILOT_REVIEW_PR_NUMBER"], environment.Children.Keys.Select(key => key.ToString()));
        Assert.Equal("${{ vars.COPILOT_REVIEW_MODE }}", Scalar(environment, "COPILOT_REVIEW_MODE"));
        Assert.Equal("${{ vars.COPILOT_REVIEW_PR_NUMBER }}", Scalar(environment, "COPILOT_REVIEW_PR_NUMBER"));
        var options = Mapping(step, "with");
        Assert.Equal(["github-token", "retries", "script"], options.Children.Keys.Select(key => key.ToString()));
        Assert.Equal("${{ steps.app-token.outputs.token }}", Scalar(options, "github-token"));
        Assert.Equal("0", Scalar(options, "retries"));
        Assert.Equal(-1, Scalar(options, "script").IndexOf("${{", StringComparison.Ordinal));
    }
 
    [Theory]
    [InlineData("default-dry-run")]
    [InlineData("explicit-dry-run")]
    [InlineData("disabled")]
    [InlineData("invalid-mode")]
    [InlineData("invalid-pilot")]
    [InlineData("unsafe-pilot")]
    [InlineData("pilot-other-pr")]
    [InlineData("pilot-scheduled")]
    [InlineData("scheduled-stale")]
    [InlineData("scheduled-stale-boundary")]
    [InlineData("scheduled-recent")]
    [InlineData("scheduled-stale-dry-run")]
    [InlineData("scheduled-stale-pilot")]
    [InlineData("scheduled-invalid-activity")]
    [InlineData("scheduled-missing-activity")]
    [InlineData("stale-push")]
    [InlineData("stale-manual")]
    [InlineData("external-author")]
    [InlineData("author-read")]
    [InlineData("author-none")]
    [InlineData("author-triage")]
    [InlineData("author-maintain")]
    [InlineData("author-admin")]
    [InlineData("author-custom-write")]
    [InlineData("author-unknown-permission")]
    [InlineData("author-missing")]
    [InlineData("author-permission-revoked")]
    [InlineData("external-author-maintainer-push")]
    [InlineData("external-author-scheduled")]
    [InlineData("external-author-manual")]
    [InlineData("external-author-dry-run")]
    [InlineData("api-permission-error")]
    [InlineData("copilot-author")]
    [InlineData("copilot-author-manual")]
    [InlineData("copilot-author-dry-run")]
    [InlineData("copilot-author-scan-continues")]
    [InlineData("copilot-login-human")]
    [InlineData("api-permission-not-found")]
    [InlineData("bot-author")]
    [InlineData("dependabot-scheduled")]
    [InlineData("dependabot-manual")]
    [InlineData("draft")]
    [InlineData("draft-scheduled")]
    [InlineData("closed")]
    [InlineData("unsupported-branch")]
    [InlineData("release-branch")]
    [InlineData("pending")]
    [InlineData("reviewed")]
    [InlineData("dismissed")]
    [InlineData("older-review")]
    [InlineData("human-review")]
    [InlineData("spoofed-reviewer")]
    [InlineData("head-changed")]
    [InlineData("closed-before-write")]
    [InlineData("draft-before-write")]
    [InlineData("retargeted-before-write")]
    [InlineData("pending-before-write")]
    [InlineData("metadata-injection")]
    [InlineData("wrong-repository")]
    [InlineData("wrong-pr-repository")]
    [InlineData("wrong-number")]
    [InlineData("invalid-number")]
    [InlineData("invalid-sha")]
    [InlineData("missing-reviewers")]
    [InlineData("unsupported-event")]
    [InlineData("untrusted-dispatch")]
    [InlineData("manual-dispatch")]
    [InlineData("api-read-error")]
    [InlineData("api-history-error")]
    [InlineData("api-write-error")]
    [InlineData("pagination")]
    [InlineData("push-during-review")]
    [RequiresTools(["node"])]
    public async Task ReconciliationHonorsPolicy(string scenario)
    {
        using var workspace = TemporaryWorkspace.Create(output);
        var script = Scalar(Mapping(ScriptStep(LoadWorkflow()), "with"), "script");
        var scriptPath = Path.Combine(workspace.Path, "review-script.js");
        await File.WriteAllTextAsync(scriptPath, script);
 
        using var node = new NodeCommand(output, nameof(OrganizationFundedCopilotReviewsTests))
            .WithTimeout(TimeSpan.FromMinutes(1));
        var result = await node.ExecuteScriptAsync(
            Path.Combine(RepoRoot.Path, "tests", "Infrastructure.Tests", "WorkflowScripts", "organization-funded-copilot-reviews.harness.mjs"),
            scriptPath,
            scenario);
 
        Assert.True(result.ExitCode == 0, result.Output);
    }
 
    private static YamlMappingNode LoadWorkflow()
    {
        using var reader = File.OpenText(Path.Combine(RepoRoot.Path, ".github", "workflows", "organization-funded-copilot-reviews.yml"));
        var yaml = new YamlStream();
        yaml.Load(reader);
        return Assert.IsType<YamlMappingNode>(yaml.Documents[0].RootNode);
    }
 
    private static YamlMappingNode ScriptStep(YamlMappingNode root)
    {
        var job = Mapping(Mapping(root, "jobs"), "request-reviews");
        var steps = Assert.IsType<YamlSequenceNode>(job.Children[new YamlScalarNode("steps")]);
        return Assert.IsType<YamlMappingNode>(steps.Children[1]);
    }
 
    private static YamlMappingNode Mapping(YamlMappingNode node, string key)
        => Assert.IsType<YamlMappingNode>(node.Children[new YamlScalarNode(key)]);
 
    private static string Scalar(YamlMappingNode node, string key)
        => Assert.IsType<YamlScalarNode>(node.Children[new YamlScalarNode(key)]).Value!;
 
    private static IEnumerable<string> Sequence(YamlMappingNode node, string key)
        => Assert.IsType<YamlSequenceNode>(node.Children[new YamlScalarNode(key)]).Select(value => value.ToString());
}