| File: GoStarterReadinessTests.cs | Web Access |
| Project: src\tests\Aspire.Cli.EndToEnd.Tests\Aspire.Cli.EndToEnd.Tests.csproj (Aspire.Cli.EndToEnd.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.Cli.EndToEnd.Tests.Helpers; using Hex1b.Automation; using Xunit; namespace Aspire.Cli.EndToEnd.Tests; public sealed class GoStarterReadinessTests(ITestOutputHelper output) { [Theory] [InlineData(true)] [InlineData(false)] [CaptureWorkspaceOnFailure] public async Task StarterShipsTidyApiAndServesRequests(bool useRedisCache) { var repoRoot = CliE2ETestHelpers.GetRepoRoot(); var strategy = CliInstallStrategy.Detect(output.WriteLine); using var workspace = TemporaryWorkspace.Create(output); // DCP publishes Redis on the Docker host. The API must reach that host's loopback ports. using var terminal = CliE2ETestHelpers.CreateDockerTestTerminal( repoRoot, strategy, output, variant: CliE2ETestHelpers.DockerfileVariant.PolyglotGo, mountDockerSocket: useRedisCache, workspace: workspace, network: "host"); var counter = new SequenceCounter(); var auto = new Hex1bTerminalAutomator(terminal, defaultTimeout: TimeSpan.FromSeconds(500)); await using var terminalRun = CliE2ETestHelpers.StartRun(terminal, workspace, auto, counter, output, TestContext.Current.CancellationToken); await auto.PrepareDockerEnvironmentAsync(counter, workspace); await auto.InstallAspireCliAsync(strategy, counter); if (useRedisCache) { await auto.AspireNewAsync("GoReady", counter, template: AspireTemplate.GoStarter); } else { await auto.RunCommandAsync( "aspire new aspire-go-starter --name GoReady --output GoReady --use-redis-cache false --localhost-tld false --non-interactive --suppress-agent-init", counter, TimeSpan.FromMinutes(3)); } var projectRoot = Path.Combine(workspace.WorkspaceRoot.FullName, "GoReady"); GitIgnoreAssertions.AssertContainsEntry(projectRoot, ".aspire/"); Assert.True(File.Exists(Path.Combine(projectRoot, "api", "go.sum"))); await auto.RunCommandAsync("cd GoReady", counter); // Build before any run/start can repair the template through WithModTidy. await auto.RunCommandAsync("(cd api && go build -mod=readonly -o /tmp/go-ready-api . && go mod tidy -diff)", counter, TimeSpan.FromMinutes(3)); try { await auto.AspireStartAsync(counter, startTimeout: TimeSpan.FromMinutes(4)); await auto.RunCommandAsync("aspire wait api --status up --timeout 120", counter, TimeSpan.FromMinutes(3)); await auto.RunCommandAsync("aspire describe api --format json > api.json", counter); // Describe emits { resources: [{ urls: [{ url: \"http://...\" }] }] }. Use Node, // already provided by the polyglot image, rather than assuming jq is installed. await auto.RunCommandAsync( """API_URL=$(node -e 'const fs = require("node:fs"); const data = JSON.parse(fs.readFileSync("api.json", "utf8")); const url = data.resources[0].urls.find(u => u.url.startsWith("http://")); if (!url) throw new Error("Missing HTTP API URL"); process.stdout.write(url.url.replace(/\/$/, ""));') && curl -fsS --retry 10 --retry-all-errors --retry-delay 1 "$API_URL/health" && curl -fsS "$API_URL/api/weatherforecast" > weather.json && node -e 'const assert = require("node:assert/strict"); const weather = require("./weather.json"); assert.equal(weather.length, 5); for (const forecast of weather) { assert.equal(typeof forecast.temperatureC, "number"); assert.equal(typeof forecast.summary, "string"); }'""", counter, TimeSpan.FromMinutes(2)); } finally { await auto.AspireStopAsync(counter); } } }