| File: NativeProductCliTests.cs | Web Access |
| Project: src\tests\Aspire.Hosting.Native.Core.Tests\Aspire.Hosting.Native.Core.Tests.csproj (Aspire.Hosting.Native.Core.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.Diagnostics; using System.Text.Json; using Aspire.Hosting.Native.Core.Tests.TestServices; namespace Aspire.Hosting.Native.Core.Tests; public class NativeProductCliTests { [Fact] public async Task OptedInTypeScriptAppHostUsesPlatformRpcAndExistingCliBackchannels() { var cli = Environment.GetEnvironmentVariable("ASPIRE_NATIVE_PRODUCT_CLI"); var server = Environment.GetEnvironmentVariable("ASPIRE_NATIVE_CSHARP_TEST_SERVER"); if (cli is null || server is null) { Assert.Skip("The native validation lane builds the CLI and publishes the execution server."); } var directory = Directory.CreateTempSubdirectory("aspire-v2-cli-"); var apphost = Path.Combine(directory.FullName, "apphost.mts"); var proof = Path.Combine(directory.FullName, "proof.json"); Process? process = null; try { await File.WriteAllTextAsync(apphost, """ import { createBuilder } from './.aspire/modules/aspire.mjs'; import { rename, writeFile } from 'node:fs/promises'; import { existsSync } from 'node:fs'; import { setTimeout } from 'node:timers/promises'; const application = (await createBuilder()).build(); try { await application.start(); await writeFile(new URL('./proof.tmp', import.meta.url), JSON.stringify({ started: true })); await rename(new URL('./proof.tmp', import.meta.url), new URL('./proof.json', import.meta.url)); while (!existsSync(new URL('./finish', import.meta.url))) { await setTimeout(25); } } finally { await application.dispose(); } """); await File.WriteAllTextAsync(Path.Combine(directory.FullName, "aspire.config.json"), """{"appHostPath":"apphost.mts","features":{"appHostServerV2":true}}"""); File.Copy(Path.Combine(AppContext.BaseDirectory, "Fixtures", "package.json"), Path.Combine(directory.FullName, "package.json")); File.Copy(Path.Combine(AppContext.BaseDirectory, "Fixtures", "tsconfig.apphost.json"), Path.Combine(directory.FullName, "tsconfig.apphost.json")); var start = StartInfo(["run"]); start.Environment["ASPIRE_CLI_NATIVE_APPHOST_SERVER"] = server; start.Environment.Remove("ASPIRE_LAYOUT_PATH"); start.Environment.Remove("ASPIRE_REPO_ROOT"); start.Environment.Remove("REMOTE_APP_HOST_SOCKET_PATH"); start.Environment.Remove("ASPIRE_REMOTE_APPHOST_TOKEN"); process = Process.Start(start) ?? throw new InvalidOperationException("The CLI did not start."); var stdout = process.StandardOutput.ReadToEndAsync(); var stderr = process.StandardError.ReadToEndAsync(); using var startup = CancellationTokenSource.CreateLinkedTokenSource(TestContext.Current.CancellationToken); startup.CancelAfter(TimeSpan.FromMinutes(2)); while (!File.Exists(proof)) { if (process.HasExited) { Assert.Fail($"CLI exited before the graph started: {await stdout}\n{await stderr}"); } await Task.Delay(25, startup.Token); } var state = JsonSerializer.Deserialize<JsonElement>(await File.ReadAllTextAsync(proof, startup.Token)); Assert.True(state.GetProperty("started").GetBoolean()); var description = await NativeIntegrationAppHostRunner.RunAsync(StartInfo(["describe", "--format", "Json"]), TimeProvider.System); using var resources = JsonDocument.Parse(description); Assert.Empty(resources.RootElement.GetProperty("resources").EnumerateArray()); await File.WriteAllTextAsync(Path.Combine(directory.FullName, "finish"), ""); await process.WaitForExitAsync(TestContext.Current.CancellationToken).WaitAsync(TimeSpan.FromSeconds(30)); Assert.True(process.ExitCode == 0, $"{await stdout}\n{await stderr}"); } finally { if (process is not null) { if (!process.HasExited) { process.Kill(entireProcessTree: true); await process.WaitForExitAsync(); } process.Dispose(); } directory.Delete(recursive: true); } ProcessStartInfo StartInfo(string[] arguments) { var start = new ProcessStartInfo(cli) { WorkingDirectory = directory.FullName, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true }; foreach (var argument in arguments.Concat(["--apphost", apphost, "--non-interactive", "--nologo"])) { start.ArgumentList.Add(argument); } start.Environment["ASPIRE_HOME"] = Path.Combine(directory.FullName, "home"); return start; } } }