| File: WorkflowScripts\ReportCiFailureTests.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 System.Text.Json; using Aspire.TestUtilities; using Xunit; namespace Infrastructure.Tests; /// <summary> /// Tests for the pure decision/formatting helpers in /// .github/workflows/report-ci-failure.js: the per-branch dedup marker, issue /// title/body (autoClose:true stamped), and the per-run failure comment. /// </summary> public sealed class ReportCiFailureTests : IDisposable { private static readonly JsonSerializerOptions s_jsonOptions = new(JsonSerializerDefaults.Web); private readonly TemporaryWorkspace _workspace; private readonly string _repoRoot; private readonly string _harnessPath; private readonly ITestOutputHelper _output; public ReportCiFailureTests(ITestOutputHelper output) { _output = output; _workspace = TemporaryWorkspace.Create(output); _repoRoot = RepoRoot.Path; _harnessPath = Path.Combine(_repoRoot, "tests", "Infrastructure.Tests", "WorkflowScripts", "report-ci-failure.harness.js"); } public void Dispose() => _workspace.Dispose(); [Theory] [InlineData("main", "<!-- ci-failure:ci.yml:push:main -->")] [InlineData("release/13.3", "<!-- ci-failure:ci.yml:push:release/13.3 -->")] [RequiresTools(["node"])] public async Task BuildMarkerEmbedsRef(string @ref, string expected) { var marker = await InvokeHarnessAsync<string>("buildMarker", new { @ref }); Assert.Equal(expected, marker); } [Fact] [RequiresTools(["node"])] public async Task BuildIssueTitleNamesTheBranch() { var title = await InvokeHarnessAsync<string>("buildIssueTitle", new { @ref = "release/13.3" }); Assert.Equal("CI failing on `release/13.3`", title); } [Fact] [RequiresTools(["node"])] public async Task BuildIssueBodyStampsAutoCloseTrueAndCarriesMarker() { var marker = "<!-- ci-failure:ci.yml:push:main -->"; var body = await InvokeHarnessAsync<string>("buildIssueBody", new { marker, @ref = "main" }); Assert.Contains(marker, body); // Self-closing on green, so the watchdog may also close it as a backstop. Assert.Contains("<!-- autoclose:true -->", body); Assert.Contains("on push to `main`", body); } [Fact] [RequiresTools(["node"])] public async Task FormatCommentLinksTheRunAndCommit() { var comment = await InvokeHarnessAsync<string>( "formatComment", new { run = new { runNumber = 42, runUrl = "https://github.com/microsoft/aspire/actions/runs/42", sha = "abcdef0123456789" } }); Assert.Contains("[run #42](https://github.com/microsoft/aspire/actions/runs/42)", comment); Assert.Contains("`abcdef01`", comment); } private async Task<T> InvokeHarnessAsync<T>(string operation, object payload) { var requestPath = Path.Combine(_workspace.Path, $"{Guid.NewGuid():N}.json"); await File.WriteAllTextAsync(requestPath, JsonSerializer.Serialize(new { operation, payload }, s_jsonOptions)); using var command = new NodeCommand(_output, "report-ci-failure"); command.WithWorkingDirectory(_repoRoot); var result = await command.ExecuteScriptAsync(_harnessPath, requestPath); Assert.Equal(0, result.ExitCode); var response = JsonSerializer.Deserialize<HarnessResponse<T>>(result.Output, s_jsonOptions); Assert.NotNull(response); return response!.Result; } private sealed record HarnessResponse<T>(T Result); }